/* app/set-core.jsx — the Settings surface: shell, navigation, and the shared primitive layer.
   ============================================================================================
   WHY THIS FILE EXISTS AS A LAYER, NOT A PAGE

   Settings is 20 pages. The reference we are matching (docs/SETTINGS-REFERENCE.md) gets its polish
   almost entirely from the fact that a row on Billing is dimensionally identical to a row on
   Preferences. The moment a page hand-rolls its own row, that page looks subtly wrong beside its
   neighbours even when it looks fine alone. So: every page is assembled from the primitives below,
   and a page that needs something new adds it HERE rather than locally.

   HOW PAGES REGISTER

   Each page module (app/set-*.jsx) publishes into a shared map, keyed by the route segment:

       window.E8SetPages = window.E8SetPages || {};
       window.E8SetPages.profile = function ProfilePage() { ... };

   The router below resolves `#/settings/<key>` against that map. Two consequences worth knowing:
   pages never import each other, and a page module that throws at parse time takes out ONLY its own
   pages — every other page keeps working and the missing one renders a visible placeholder rather
   than a blank screen. That matters in a no-bundler app where a syntax error in one file would
   otherwise be invisible (app/main.jsx heals a missing global into a silent no-op).

   NAMESPACE: every class in this feature is `.e8-set-*`, defined in app/set-core.css.
   ============================================================================================ */

/* ---------- the navigation model -------------------------------------------------------------
   One list, three groups, matching the reference IA. `key` is the route segment and the map key a
   page module must register under. Order here is the order in the rail. */
const E8_SET_NAV = [
  { group: 'Personal', items: [
    { key: 'preferences', label: 'Preferences', icon: 'tune' },
    { key: 'profile', label: 'Profile', icon: 'person' },
    { key: 'notifications', label: 'Notifications', icon: 'notifications' },
    { key: 'accounts', label: 'Accounts', icon: 'grid_view' },
    { key: 'calls', label: 'Calls', icon: 'call' }
  ] },
  { group: 'Workspace', items: [
    { key: 'general', label: 'General', icon: 'settings' },
    { key: 'stages', label: 'Stages', icon: 'change_history' },
    { key: 'labels', label: 'Labels', icon: 'sell' },
    { key: 'datamodel', label: 'Data Model', icon: 'database' },
    { key: 'features', label: 'Features', icon: 'toggle_on' },
    { key: 'integrations', label: 'Integrations', icon: 'extension' },
    { key: 'import', label: 'Import', icon: 'upload' },
    { key: 'templates', label: 'Templates', icon: 'description' }
  ] },
  { group: 'Admin', items: [
    { key: 'users', label: 'Users', icon: 'group' },
    { key: 'teams', label: 'Teams', icon: 'diversity_3' },
    { key: 'security', label: 'Security', icon: 'shield' },
    { key: 'api', label: 'API', icon: 'code' },
    { key: 'webhooks', label: 'Webhooks', icon: 'webhook' },
    { key: 'domains', label: 'Domains', icon: 'language' },
    { key: 'billing', label: 'Billing', icon: 'credit_card' }
  ] }
];
const E8_SET_DEFAULT = 'preferences';
function e8SetFind(key) {
  for (const g of E8_SET_NAV) { const hit = g.items.find((i) => i.key === key); if (hit) return hit; }
  return null;
}

/* ---------- what the help affordance says ----------------------------------------------------
   The reference puts a circled `i` beside the breadcrumb and calls it the ONLY colour in the top
   bar. Ours shipped as a focusable, accent-coloured button that produced no output on click or
   hover - which is worse than no control, because it advertises help and then withholds it.

   So the colour now buys something: one paragraph saying what the page governs and who it affects,
   plus one real in-app link to the page a reader most often wants next. `see` is a nav key, resolved
   through e8SetFind so a typo degrades to "no link" rather than a dead button. Keyed by route
   segment, same as everything else here; a page with no entry falls back to a generic line rather
   than rendering an empty popover. */
const E8_SET_HELP = {
  preferences: { see: 'notifications', body: 'Your own defaults for currency, country code, units, language, how dates and times are written, and how ELEV8 looks. Nothing here changes what your teammates see.' },
  profile: { see: 'security', body: 'How you appear to the rest of the workspace - name, initials and avatar - plus the credentials you sign in with. Workspace-wide security policy is set separately.' },
  notifications: { see: 'preferences', body: 'Pick which events reach you in the app and which also arrive by email. Each row is one event; the two columns are the two channels, and they are set independently.' },
  accounts: { see: 'calls', body: 'Connect the email, calendar and messaging accounts ELEV8 sends and schedules from. Until one is connected the sending defaults below it stay dimmed, because they have nothing to apply to.' },
  calls: { see: 'accounts', body: 'Note Taker joins your scheduled calls to record and transcribe them, then writes the summary back to the record. Recording law varies by region - check your obligation to announce it.' },
  general: { see: 'domains', body: 'Workspace identity: the name, logo and favicon your team sees, the public slug your application pages are served under, and the brand colours those pages are painted in.' },
  stages: { see: 'labels', body: 'The pipeline every job and speculative submission moves through. Reordering a stage reorders it everywhere at once, including on boards and in reporting.' },
  labels: { see: 'stages', body: 'Reusable labels for notes, tasks and rejection reasons. Keeping the list short is what makes it useful later - every label here becomes a filter and a reporting bucket.' },
  datamodel: { see: 'features', body: 'The fields that make up each record type. Adding an attribute here adds it to every record of that type, to the forms that create them, and to import mapping.' },
  features: { see: 'integrations', body: 'Optional ELEV8 capabilities, on or off for the whole workspace. A master row turns a group on; while it is off, the settings that depend on it are dimmed and inert rather than hidden.' },
  integrations: { see: 'api', body: 'Third-party services ELEV8 exchanges data with. Connecting one grants it the access its card describes, for everyone in the workspace, until it is disconnected here.' },
  import: { see: 'datamodel', body: 'Bring candidates, companies and contacts in from a spreadsheet or another system. Every import maps incoming columns onto the fields defined in your data model.' },
  templates: { see: 'labels', body: 'The default email, message and document templates your team starts from, and which one is offered first for each type. Editing a template does not alter anything already sent.' },
  users: { see: 'teams', body: 'Everyone who can sign in to this workspace, and what each of them may do. Roles are cumulative - a user gets the widest permission any of their roles grants.' },
  teams: { see: 'users', body: 'Group users into teams so ownership, reporting and notification routing can address several people at once instead of naming them one by one.' },
  security: { see: 'users', body: 'Workspace-wide policy rather than personal settings: whether multi-factor authentication is required of everyone, and the roles that permissions are granted through.' },
  api: { see: 'webhooks', body: 'Programmatic access to your ELEV8 data. An API key carries the permissions of the workspace, not of the person who created it, so treat one as a shared credential.' },
  webhooks: { see: 'api', body: 'Have ELEV8 call your endpoint when records change, instead of you polling for them. Deliveries retry on failure, so your endpoint should tolerate the same event arriving twice.' },
  domains: { see: 'accounts', body: 'The domains your candidate and client email is sent from. A domain has to be verified through DNS before it will send, which is what keeps your mail out of spam folders.' },
  billing: { see: 'users', body: 'Your plan, how many seats it covers and how it is paid for. Seat count is what the price is calculated from, and it has to cover every active user in the workspace.' }
};

/* ---------- primitives -----------------------------------------------------------------------
   Deliberately small and dumb. Each one owns exactly one piece of the grammar in the reference. */

function SetIcon({ name, className }) {
  return <span className={'material-symbols-outlined' + (className ? ' ' + className : '')} aria-hidden="true">{name}</span>;
}

/* The rounded-square glyph container that leads a row. All-or-nothing within a card. */
function SetGlyph({ name, tone }) {
  return <span className={'e8-set-glyph' + (tone ? ' is-' + tone : '')}><SetIcon name={name} /></span>;
}

function SetPage({ title, subtitle, action, children }) {
  return (
    <div className="e8-set-page">
      <div className="e8-set-pagehead">
        <div className="e8-set-pagehead-txt">
          <h1>{title}</h1>
          {subtitle ? <p>{subtitle}</p> : null}
        </div>
        {action ? <div className="e8-set-pagehead-act">{action}</div> : null}
      </div>
      {children}
    </div>
  );
}

function SetSection({ title, desc, action, children }) {
  return (
    <section className="e8-set-section">
      {title || action ? (
        <div className="e8-set-sectionhead">
          <div>
            <h2>{title}</h2>
            {desc ? <p>{desc}</p> : null}
          </div>
          {action || null}
        </div>
      ) : null}
      {children}
    </section>
  );
}

/* `locked` dims and disables an entire dependent block in one place — the signature detail from the
   reference. Never dim children by hand: doing it per-control is what produces the tell-tale
   half-state where the label stays black and only the select greys out.

   THE DIM IS NOT THE DISABLE. `.e8-set-card.is-locked` carries `opacity` and `pointer-events: none`
   (app/set-core.css), and `pointer-events` stops a MOUSE and nothing else — every control inside a
   visibly-dimmed block stayed in the tab order and stayed operable with Enter or Space, so a
   keyboard user could change a setting the screen was showing as unavailable. `inert` is what
   actually removes a subtree from the tab order, from hit testing and from the accessibility tree,
   and it applies to the same subtree the dim does, so the two can no longer disagree.

   Written as `inert: ''` rather than `inert` / `inert={true}`: React 18.3 has no property entry for
   it, so a boolean value is dropped with a "Received `true` for a non-boolean attribute" warning
   while a string is written through. An empty string is a PRESENT boolean attribute in HTML, which
   is what the browser reads. `aria-disabled` stays for the case where a reader surfaces the node
   anyway. */
function SetCard({ children, locked, className }) {
  return (
    <div className={'e8-set-card' + (locked ? ' is-locked' : '') + (className ? ' ' + className : '')}
      {...(locked ? { 'aria-disabled': 'true', inert: '' } : {})}>
      {children}
    </div>
  );
}

/* The card's own header strip: glyph + title (+ description) + an action on the right. */
function SetCardHead({ icon, title, desc, action }) {
  return (
    <div className="e8-set-cardhead">
      {icon ? <SetGlyph name={icon} /> : null}
      <div className="e8-set-cardhead-txt">
        <div className="e8-set-cardhead-t">{title}</div>
        {desc ? <div className="e8-set-cardhead-d">{desc}</div> : null}
      </div>
      {action ? <div className="e8-set-cardhead-act">{action}</div> : null}
    </div>
  );
}

/* The workhorse. `control` is right-aligned and sizes to its own content — do NOT wrap it in
   anything fixed-width. The control stays centred against the whole label+description block, which
   is why this is a flex row with align-items:center rather than a grid with a baseline. */
function SetRow({ icon, label, desc, control, children, locked, tinted, className }) {
  return (
    <div className={'e8-set-row' + (tinted ? ' is-tinted' : '') + (locked ? ' is-locked' : '')
      + (className ? ' ' + className : '')}
      {...(locked ? { 'aria-disabled': 'true', inert: '' } : {})}>
      {icon ? <SetGlyph name={icon} /> : null}
      <div className="e8-set-row-txt">
        <div className="e8-set-row-label">{label}</div>
        {desc ? <div className="e8-set-row-desc">{desc}</div> : null}
        {children || null}
      </div>
      {control ? <div className="e8-set-row-ctl">{control}</div> : null}
    </div>
  );
}

/* A row that is itself a navigation target. Real <button>, so it is keyboard-reachable for free. */
function SetNavRow({ icon, label, desc, meta, onClick, locked }) {
  return (
    <button type="button" className={'e8-set-row e8-set-navrow' + (locked ? ' is-locked' : '')}
      onClick={onClick} disabled={!!locked}>
      {icon ? <SetGlyph name={icon} /> : null}
      <span className="e8-set-row-txt">
        <span className="e8-set-row-label">{label}</span>
        {desc ? <span className="e8-set-row-desc">{desc}</span> : null}
      </span>
      {meta ? <span className="e8-set-row-meta">{meta}</span> : null}
      <SetIcon name="chevron_right" className="e8-set-chev" />
    </button>
  );
}

function SetToggle({ checked, onChange, label, disabled }) {
  return (
    <button type="button" role="switch" aria-checked={!!checked} aria-label={label} disabled={!!disabled}
      className={'e8-set-toggle' + (checked ? ' is-on' : '')}
      onClick={() => !disabled && onChange && onChange(!checked)}>
      <span className="e8-set-toggle-knob" />
    </button>
  );
}

function SetCheck({ checked, onChange, label, disabled }) {
  return (
    <button type="button" role="checkbox" aria-checked={!!checked} aria-label={label} disabled={!!disabled}
      className={'e8-set-check' + (checked ? ' is-on' : '')}
      onClick={() => !disabled && onChange && onChange(!checked)}>
      {checked ? <SetIcon name="check" /> : null}
    </button>
  );
}

/* Sizes to content by design — see the reference note on variable select widths. `prefix` renders a
   secondary-ink word before the value (USD), `dot` a colour chip (stage pickers), `flag` an emoji. */
function SetSelect({ value, options, onChange, disabled, prefix, dot, flag, ariaLabel }) {
  return (
    <span className={'e8-set-select' + (disabled ? ' is-disabled' : '')}>
      {flag ? <span className="e8-set-select-flag" aria-hidden="true">{flag}</span> : null}
      {dot ? <span className="e8-set-dot" style={{ background: dot }} aria-hidden="true" /> : null}
      {prefix ? <span className="e8-set-select-prefix">{prefix}</span> : null}
      <select value={value} disabled={!!disabled} aria-label={ariaLabel}
        onChange={(e) => onChange && onChange(e.target.value)}>
        {(options || []).map((o) => {
          const val = typeof o === 'string' ? o : o.value;
          const lbl = typeof o === 'string' ? o : o.label;
          return <option key={val} value={val}>{lbl}</option>;
        })}
      </select>
      <SetIcon name="expand_more" className="e8-set-select-chev" />
    </span>
  );
}

function SetInput({ value, onChange, placeholder, size, prefix, type, ariaLabel, disabled }) {
  return (
    <span className={'e8-set-input' + (size ? ' is-' + size : '') + (disabled ? ' is-disabled' : '')}>
      {prefix ? <span className="e8-set-input-prefix">{prefix}</span> : null}
      <input type={type || 'text'} value={value} placeholder={placeholder} aria-label={ariaLabel}
        disabled={!!disabled} onChange={(e) => onChange && onChange(e.target.value)} />
    </span>
  );
}

function SetSeg({ value, options, onChange, ariaLabel }) {
  return (
    <div className="e8-set-seg" role="group" aria-label={ariaLabel}>
      {(options || []).map((o) => {
        const val = typeof o === 'string' ? o : o.value;
        const lbl = typeof o === 'string' ? o : o.label;
        return (
          <button key={val} type="button" aria-pressed={value === val}
            onClick={() => onChange && onChange(val)}>{lbl}</button>
        );
      })}
    </div>
  );
}

function SetRadioCard({ title, desc, checked, onChange, name }) {
  return (
    <button type="button" role="radio" aria-checked={!!checked} name={name}
      className={'e8-set-radiocard' + (checked ? ' is-on' : '')} onClick={() => onChange && onChange()}>
      <span className="e8-set-radiocard-txt">
        <span className="e8-set-radiocard-t">{title}</span>
        {desc ? <span className="e8-set-radiocard-d">{desc}</span> : null}
      </span>
      <span className="e8-set-radio" />
    </button>
  );
}

function SetBtn({ children, onClick, kind, icon, disabled, href, title }) {
  const cls = 'e8-set-btn is-' + (kind || 'secondary');
  const inner = <>{icon ? <SetIcon name={icon} /> : null}{children}</>;
  if (href) {
    return <a className={cls} href={href} target="_blank" rel="noopener noreferrer" title={title}>{inner}
      <SetIcon name="open_in_new" className="e8-set-btn-ext" /></a>;
  }
  return <button type="button" className={cls} onClick={onClick} disabled={!!disabled} title={title}>{inner}</button>;
}

function SetPill({ children, tone }) {
  return <span className={'e8-set-pill' + (tone ? ' is-' + tone : '')}>{children}</span>;
}

function SetBanner({ tone, icon, children, inCard }) {
  return (
    <div className={'e8-set-banner is-' + (tone || 'info') + (inCard ? ' is-incard' : '')}>
      <SetIcon name={icon || (tone === 'warn' ? 'warning' : 'info')} />
      <div>{children}</div>
    </div>
  );
}

/* Two shapes, and picking the wrong one is a named failure mode in the reference spec.
   `inline` — one quiet line inside a card that already has its own "+ Add" header action.
   `centred` (default) — the big block for a page that is otherwise empty. */
function SetEmpty({ icon, title, desc, action, inline }) {
  if (inline) return <div className="e8-set-empty-inline">{title}</div>;
  return (
    <div className="e8-set-empty">
      <span className="e8-set-empty-badge"><SetIcon name={icon || 'inbox'} /></span>
      <div className="e8-set-empty-t">{title}</div>
      {desc ? <div className="e8-set-empty-d">{desc}</div> : null}
      {action ? <div className="e8-set-empty-a">{action}</div> : null}
    </div>
  );
}

/* ---------- the shell ------------------------------------------------------------------------ */

function SetRail({ active, onGo, query, setQuery, onClose }) {
  const q = (query || '').trim().toLowerCase();
  const groups = E8_SET_NAV
    .map((g) => ({ ...g, items: g.items.filter((i) => !q || i.label.toLowerCase().includes(q)) }))
    .filter((g) => g.items.length);
  return (
    /* tabIndex -1 so the drawer can put focus on the region itself when it opens - see the effect
       in SettingsScreen. It is never in the tab order; only script can land on it. */
    <nav className="e8-set-rail" id="e8-set-railnav" aria-label="Settings" tabIndex={-1}>
      <button type="button" className="e8-set-rail-back" onClick={() => window.navigate && window.navigate('#/home')}>
        <SetIcon name="chevron_left" />Back to Home
      </button>
      <div className="e8-set-rail-search">
        <SetIcon name="search" />
        <input type="search" value={query} placeholder="Search settings…" aria-label="Search settings"
          onChange={(e) => setQuery(e.target.value)} />
      </div>
      <div className="e8-set-rail-scroll">
        {groups.map((g) => (
          <div className="e8-set-rail-group" key={g.group}>
            <div className="e8-set-rail-cap">{g.group}</div>
            {g.items.map((i) => (
              <button key={i.key} type="button" aria-current={active === i.key ? 'page' : undefined}
                className={'e8-set-rail-item' + (active === i.key ? ' is-on' : '')}
                onClick={() => { onGo(i.key); if (onClose) onClose(); }}>
                <SetIcon name={i.icon} />{i.label}
              </button>
            ))}
          </div>
        ))}
        {!groups.length ? <div className="e8-set-rail-none">No settings match “{query}”.</div> : null}
      </div>
    </nav>
  );
}

/* Put focus back on the help button AFTER a settings navigation has committed.
   Why this cannot just be `btnRef.current.focus()`: app/main.jsx:561 keys the error boundary that
   wraps every screen on `route.page + '/' + route.id`, and for settings `route.id` IS the page
   segment - so going from Preferences to Notifications does not re-render this shell, it UNMOUNTS
   and rebuilds it. Measured: the rail's search box loses its text across a nav, and focus lands on
   <body>. Any ref captured before the navigation is null by the time a callback could use it, so
   this re-finds the button in the new tree on the next frame. There is exactly one settings shell
   mounted at a time, so the selector is unambiguous.
   Delete this in favour of the ref once main.jsx stops keying settings on its tab segment. */
function e8SetRefocusHelp() {
  requestAnimationFrame(() => {
    const btn = document.querySelector('.e8-set-help');
    if (btn) btn.focus();
  });
}

/* The accent `i` beside the breadcrumb, and the popover it owes the reader.
   Non-modal on purpose: it explains the page you are already looking at, so trapping focus in it
   would be hostile. Focus moves in on open so a keyboard reader hears the text, and Escape puts it
   back on the button rather than dropping it to <body>. The "Go to" link navigates, which unmounts
   this whole subtree, so IT restores focus on the next frame via e8SetRefocusHelp above - a ref
   captured before the navigation is already null by then. */
function SetHelp({ item, onGo }) {
  const [open, setOpen] = React.useState(false);
  const wrapRef = React.useRef(null);
  const btnRef = React.useRef(null);
  const popRef = React.useRef(null);
  const activeKey = item ? item.key : '';

  React.useEffect(() => { setOpen(false); }, [activeKey]);
  React.useEffect(() => { if (open && popRef.current) popRef.current.focus(); }, [open]);
  React.useEffect(() => {
    if (!open) return undefined;
    const shut = () => { setOpen(false); if (btnRef.current) btnRef.current.focus(); };
    const onKey = (e) => { if (e.key === 'Escape') { e.stopPropagation(); shut(); } };
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('keydown', onKey);
    document.addEventListener('mousedown', onDown);
    return () => { document.removeEventListener('keydown', onKey); document.removeEventListener('mousedown', onDown); };
  }, [open]);

  const label = item ? item.label : 'Settings';
  const help = E8_SET_HELP[activeKey];
  const body = help ? help.body
    : 'This page is part of ELEV8 Settings. Changes save as you make them and apply the next time the affected screen loads.';
  const see = help ? e8SetFind(help.see) : null;
  return (
    <div className="e8-set-helpwrap" ref={wrapRef}>
      <button type="button" ref={btnRef} className="e8-set-help" aria-haspopup="dialog"
        aria-expanded={open} aria-label={'About ' + label} onClick={() => setOpen((v) => !v)}
        {...(open ? { 'aria-controls': 'e8-set-helppop' } : {})}>
        <SetIcon name="info" />
      </button>
      {open ? (
        <div className="e8-set-helppop" id="e8-set-helppop" role="dialog" aria-label={'About ' + label}
          tabIndex={-1} ref={popRef}>
          <div className="e8-set-helppop-t">{label}</div>
          <div className="e8-set-helppop-b">{body}</div>
          {see ? (
            <button type="button" className="e8-set-helppop-link"
              onClick={() => { setOpen(false); onGo(see.key); e8SetRefocusHelp(); }}>
              {'Go to ' + see.label}<SetIcon name="arrow_forward" />
            </button>
          ) : null}
        </div>
      ) : null}
    </div>
  );
}

function SettingsScreen() {
  const route = window.useRoute();
  const active = e8SetFind(route.id) ? route.id : E8_SET_DEFAULT;
  const [query, setQuery] = React.useState('');
  const [drawer, setDrawer] = React.useState(false);
  const item = e8SetFind(active);
  const go = React.useCallback((key) => {
    if (window.navigate) window.navigate('#/settings/' + key);
    else location.hash = '#/settings/' + key;
  }, []);

  const railRef = React.useRef(null);
  const selRef = React.useRef(null);
  const wasOpen = React.useRef(false);

  /* Close the narrow-width drawer on Escape, and never leave it open across a route change. */
  React.useEffect(() => { setDrawer(false); }, [active]);
  React.useEffect(() => {
    if (!drawer) return undefined;
    const onKey = (e) => { if (e.key === 'Escape') setDrawer(false); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [drawer]);

  /* MOVE FOCUS WITH THE DRAWER. The rail sits BEFORE the body in the DOM, so opening the drawer and
     leaving focus on the trigger put every one of its items behind the reader rather than in front
     of them - tabbing forward walked into the page content and never reached the menu that had just
     appeared. Focus goes to the rail's own <nav> (tabIndex -1) rather than to its first control, so
     the reader hears which region it landed in and Tab then walks the menu in order.
     Coming back out: only if focus is still inside the rail when it closes. Escape leaves it there,
     so that path returns focus to the trigger instead of dropping it on <body>; a scrim click or a
     navigation has already moved focus elsewhere, and yanking it back would be the hostile case. */
  React.useEffect(() => {
    if (drawer) {
      wasOpen.current = true;
      const nav = railRef.current && railRef.current.querySelector('.e8-set-rail');
      if (nav) nav.focus();
      return;
    }
    if (!wasOpen.current) return;
    wasOpen.current = false;
    const inRail = railRef.current && railRef.current.contains(document.activeElement);
    if (inRail && selRef.current) selRef.current.focus();
  }, [drawer]);

  const Page = (window.E8SetPages || {})[active];
  return (
    <div className={'e8-set' + (drawer ? ' is-drawer' : '')}>
      <div className="e8-set-railwrap" ref={railRef}>
        <SetRail active={active} onGo={go} query={query} setQuery={setQuery} onClose={() => setDrawer(false)} />
      </div>
      {drawer ? <button type="button" className="e8-set-scrim" aria-label="Close settings menu"
        onClick={() => setDrawer(false)} /> : null}
      <div className="e8-set-body">
        <div className="e8-set-topbar">
          {/* Narrow-width drawer trigger. NOT a hamburger: the app's own mobile bar already puts one
              in the top-left corner, and a second identical glyph 17px below it at a different size
              opening a different menu is a coin toss for the reader. The reference explicitly allows
              "a top select" at this width, so this is the current page name wearing a select's
              chrome - a different shape saying what it will show you. */}
          <button type="button" ref={selRef} className="e8-set-pagesel" aria-expanded={drawer} aria-controls="e8-set-railnav"
            aria-label={'Settings page: ' + (item ? item.label : 'Settings') + '. Choose another'}
            onClick={() => setDrawer(true)}>
            <span className="e8-set-pagesel-t">{item ? item.label : 'Settings'}</span>
            <SetIcon name="unfold_more" className="e8-set-pagesel-chev" />
          </button>
          <nav className="e8-set-crumbs" aria-label="Breadcrumb">
            <button type="button" onClick={() => window.navigate && window.navigate('#/home')}>Home</button>
            <SetIcon name="chevron_right" />
            <button type="button" onClick={() => go(E8_SET_DEFAULT)}>Settings</button>
            <SetIcon name="chevron_right" />
            <span className="e8-set-crumb-on">{item ? item.label : 'Settings'}</span>
          </nav>
          <SetHelp item={item} onGo={go} />
        </div>
        <div className="e8-set-scroll">
          <div className="e8-set-col">
            {Page ? <Page /> : (
              <SetPage title={item ? item.label : 'Settings'}>
                <SetCard>
                  <SetEmpty icon="construction" title="Not built yet"
                    desc={'The ' + (item ? item.label : 'this') + ' page has no module registered.'} />
                </SetCard>
              </SetPage>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* The primitive layer is a global so every page module can reach it without an import. */
window.E8Set = {
  Icon: SetIcon, Glyph: SetGlyph, Page: SetPage, Section: SetSection, Card: SetCard,
  CardHead: SetCardHead, Row: SetRow, NavRow: SetNavRow, Toggle: SetToggle, Check: SetCheck,
  Select: SetSelect, Input: SetInput, Seg: SetSeg, RadioCard: SetRadioCard, Btn: SetBtn,
  Pill: SetPill, Banner: SetBanner, Empty: SetEmpty, NAV: E8_SET_NAV
};
window.E8SetPages = window.E8SetPages || {};
/* Exported under its OWN name, not `SettingsScreen`. app/shell.jsx already assigns that global for
   the legacy 156-line panel, and since this file parses after shell.jsx a same-name assignment would
   silently win - which works right up until someone reorders the script tags. main.jsx names this
   one explicitly instead, and the legacy screen stays reachable while its controls are ported. */
window.SettingsShell = SettingsScreen;
