/* WP Site Beam — App Shell (topbar + sidebar + role switch) */
const { useState, useEffect } = React;

function useWPSBD() {
  const [s, setS] = useState(() => ({ ...window.WPSBD.state }));
  useEffect(() => {
    const on = (e) => setS({ ...e.detail });
    window.addEventListener('wpsbd:change', on);
    return () => window.removeEventListener('wpsbd:change', on);
  }, []);
  return [s, window.WPSBD];
}
window.useWPSBD = useWPSBD;

/* ── NAV ITEMS (role-gated) ──────────────────────────────────── */
/* Role notes:
   - Customer:  manage own sites/account/billing/team/content/tasks; consume support, scanner, estimator, requests
   - Partner:   same as Customer + Marketing (white-label client marketing) + Docs & Wiki (internal kb)
   - SA:        full access; SA-only Command section includes Partners (sub-clients/commissions tracking)
*/
const NAV_CUSTOMER = [
  /* 2026-05-18 (late evening) UX reorder per Jordan: Overview moved to top
     since `dashboard` is the default landing tab. Standard SaaS pattern is
     overview → features → account/settings → help. Fuller menu reorg
     (Option B per `_PRE-LAUNCH-WIRING-AUDIT-2026-05-18.md` §6) still queued
     as ROADMAP §1 #16c — moves Roadmap+Support to a "Help & Resources"
     footer group. */
  { group: 'Overview',   items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
    { id: 'sites',     label: 'Sites',     icon: 'sites', badge: '3' },
  ]},
  { group: 'Account',    items: [
    { id: 'account',      label: 'Account',       icon: 'account' },
    { id: 'billing',      label: 'Billing',       icon: 'billing' },
    { id: 'myaiusage',    label: 'AI Usage',      icon: 'spark' },
    { id: 'apikeys',      label: 'API Keys',      icon: 'lock' },
    { id: 'brandprofile', label: 'Brand Profile', icon: 'brand' },
    { id: 'notices',      label: 'Notices',       icon: 'bell' },
    { id: 'requests',     label: 'Roadmap',       icon: 'requests' },
    { id: 'support',      label: 'Support',       icon: 'support', badge: '2' },
  ]},
  { group: 'Workflow',   items: [
    { id: 'content',   label: 'Site Content',     icon: 'content' },
    { id: 'tasks',     label: 'Task Board',       icon: 'tasks', badge: '7' },
    { id: 'workspace', label: 'Team Workspace',   icon: 'workspace', badge: '4' },
  ]},
  { group: 'Tools', items: [
    { id: 'scanner',   label: 'Site Scanner', icon: 'scanner' },
    { id: 'estimator', label: 'WP Scope Estimator',   icon: 'modules' },
    { id: 'acquisition', label: 'Acquisition Mode',       icon: 'acquisition' }, /* Phase 2 — placeholder */
    { id: 'alttext',   label: 'Alt Text',              icon: 'image' }, /* moved from SA Command 2026-05-18 (Sprint 1 I2) — customer-facing tool */
    { id: 'filemanager', label: 'File Manager',        icon: 'archive' },
    { id: 'redirects',  label: 'Redirect Manager',      icon: 'redirect' },
    { id: 'imagetools', label: 'Image Tools',            icon: 'image' },
    { id: 'seo',       label: 'Content SEO',           icon: 'spark' },
    { id: 'sitebuilder', label: 'Site Builder',         icon: 'sitebuilder' },
    { id: 'kitbuilder',  label: 'Kit Builder',          icon: 'wand' },
    { id: 'marketingmaterials', label: 'Marketing Materials', icon: 'printer' },
  ]},
];
const NAV_SA = [
  { group: 'Overview',   items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
    { id: 'sites',     label: 'Sites',     icon: 'sites', badge: '3' },
  ]},
  { group: 'Account',    items: [
    { id: 'account',   label: 'Account',   icon: 'account' },
    { id: 'billing',       label: 'Billing',       icon: 'billing' },
    { id: 'myaiusage',     label: 'AI Usage',      icon: 'spark' },
    { id: 'apikeys',       label: 'API Keys',      icon: 'lock' },
    { id: 'brandprofile',  label: 'Brand Profile', icon: 'brand' },
    { id: 'requests',     label: 'Roadmap', icon: 'requests' },
    { id: 'support',      label: 'Support',          icon: 'support', badge: '2' },
  ]},
  { group: 'Workflow',   items: [
    { id: 'content',   label: 'Site Content',     icon: 'content' },
    { id: 'tasks',     label: 'Task Board',       icon: 'tasks', badge: '7' },
    { id: 'workspace', label: 'Team Workspace',   icon: 'workspace', badge: '4' },
  ]},
  { group: 'Tools', items: [
    { id: 'scanner',   label: 'Site Scanner', icon: 'scanner' },
    { id: 'estimator', label: 'WP Scope Estimator',   icon: 'modules' },
    { id: 'acquisition', label: 'Acquisition Mode',       icon: 'acquisition' }, /* Phase 2 — placeholder */
    { id: 'alttext',   label: 'Alt Text',              icon: 'image' }, /* moved from SA Command 2026-05-18 (Sprint 1 I2) — customer-facing tool */
    { id: 'filemanager', label: 'File Manager',        icon: 'archive' },
    { id: 'redirects',  label: 'Redirect Manager',      icon: 'redirect' },
    { id: 'imagetools', label: 'Image Tools',            icon: 'image' },
    { id: 'seo',       label: 'Content SEO',           icon: 'spark' },
    { id: 'sitebuilder', label: 'Site Builder',         icon: 'sitebuilder' },
    { id: 'kitbuilder',  label: 'Kit Builder',          icon: 'wand' },
    { id: 'marketingmaterials', label: 'Marketing Materials', icon: 'printer' },
  ]},
  { group: 'SA Command', sa: true, items: [
    { id: 'leads',           label: 'Leads',            icon: 'mail',      sa: true, badge: 'NEW' },
    { id: 'aiusage',         label: 'Platform AI Usage', icon: 'chart',    sa: true, badge: 'NEW' }, /* renamed 2026-05-18 (Sprint 1 I1) to disambiguate from customer Account → AI Usage */
    { id: 'usermgmt',        label: 'User Management',  icon: 'users',     sa: true },
    { id: 'operations',      label: 'Plugin Operations', icon: 'wand',     sa: true, badge: 'NEW' },
    { id: 'businessmetrics', label: 'Business Metrics', icon: 'chart',     sa: true },
    { id: 'pricingintel',   label: 'Pricing Intelligence', icon: 'trend',  sa: true },
    { id: 'brains',      label: 'Brain AI',       icon: 'brain',     sa: true },
    { id: 'brand',       label: 'Brand Manager',  icon: 'brand',     sa: true },
    { id: 'wiki',        label: 'Docs & Wiki',    icon: 'wiki',      sa: true },
    { id: 'team',        label: 'Employee Directory', icon: 'team',  sa: true },
    { id: 'marketing',   label: 'Marketing',      icon: 'marketing', sa: true },
    { id: 'modules',     label: 'Modules',        icon: 'modules',   sa: true },
    { id: 'notices',     label: 'Notices',        icon: 'bell',      sa: true, badge: 'SA' },
    { id: 'permissions', label: 'Permissions',    icon: 'lock',      sa: true },
    { id: 'roles',       label: 'Role Manager',   icon: 'crown',     sa: true },
    { id: 'internalnotes', label: 'Internal Notes', icon: 'notes',   sa: true },
  ]},
];
const NAV_MARKETING = [
  { group: 'Overview',   items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
  ]},
  { group: 'Account',    items: [
    { id: 'account',      label: 'Account',       icon: 'account' },
    { id: 'brandprofile', label: 'Brand Profile', icon: 'brand' },
    { id: 'notices',      label: 'Notices',       icon: 'bell' },
    { id: 'requests',     label: 'Roadmap',       icon: 'requests' },
    { id: 'support',      label: 'Support',       icon: 'support', badge: '2' },
  ]},
  { group: 'Content',    items: [
    { id: 'content',   label: 'Site Content',   icon: 'content' },
    { id: 'seo',       label: 'Content SEO',    icon: 'spark' },
    { id: 'tasks',     label: 'Task Board',     icon: 'tasks', badge: '7' },
    { id: 'workspace', label: 'Team Workspace', icon: 'workspace', badge: '4' },
  ]},
  { group: 'Marketing Tools', items: [
    { id: 'marketing',          label: 'Campaigns',              icon: 'marketing' },
    { id: 'marketingmaterials', label: 'Marketing Materials',    icon: 'printer' },
  ]},
  { group: 'Integrations', items: [
    { id: 'ghl',                label: 'GoHighLevel (GHL)',       icon: 'crm' },
    { id: 'socialmedia',        label: 'Social Media Planner',   icon: 'social' },
    { id: 'emailmarketing',     label: 'Email Marketing',        icon: 'mail' },
  ]},
];
/* NOTE: ghl, socialmedia, emailmarketing tabs are placeholders for future
   3rd-party integrations. Add tab cases to App.jsx and components when ready. */
const NAV_DEV = [
  { group: 'Overview',   items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
    { id: 'sites',     label: 'Sites',     icon: 'sites', badge: '3' },
  ]},
  { group: 'Account',    items: [
    { id: 'account',   label: 'Account',  icon: 'account' },
    { id: 'apikeys',   label: 'API Keys', icon: 'lock' },
    { id: 'notices',   label: 'Notices',  icon: 'bell' },
    { id: 'requests',  label: 'Roadmap',  icon: 'requests' },
    { id: 'support',   label: 'Support',  icon: 'support', badge: '2' },
  ]},
  { group: 'Workflow',   items: [
    { id: 'content',   label: 'Site Content',     icon: 'content' },
    { id: 'tasks',     label: 'Task Board',       icon: 'tasks', badge: '7' },
    { id: 'workspace', label: 'Team Workspace',   icon: 'workspace', badge: '4' },
  ]},
  { group: 'Tools', items: [
    { id: 'scanner',   label: 'Site Scanner', icon: 'scanner' },
    { id: 'estimator', label: 'WP Scope Estimator',   icon: 'modules' },
    { id: 'acquisition', label: 'Acquisition Mode',       icon: 'acquisition' }, /* Phase 2 — placeholder */
    { id: 'filemanager', label: 'File Manager',        icon: 'archive' },
    { id: 'redirects',  label: 'Redirect Manager',      icon: 'redirect' },
    { id: 'imagetools', label: 'Image Tools',            icon: 'image' },
    { id: 'seo',       label: 'Content SEO',           icon: 'spark' },
    { id: 'sitebuilder', label: 'Site Builder',         icon: 'sitebuilder' },
    { id: 'kitbuilder',  label: 'Kit Builder',          icon: 'wand' },
    { id: 'marketingmaterials', label: 'Marketing Materials', icon: 'printer' },
  ]},
  { group: 'Dev Tools',  items: [
    { id: 'brains',          label: 'Brain AI',        icon: 'brain' },
    { id: 'wiki',            label: 'Docs & Wiki',     icon: 'wiki' },
    { id: 'modules',         label: 'Modules',         icon: 'modules' },
  ]},
];
const NAV_ADMIN = [
  { group: 'Overview',   items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
    { id: 'sites',     label: 'Sites',     icon: 'sites', badge: '3' },
  ]},
  { group: 'Account',    items: [
    { id: 'account',   label: 'Account',   icon: 'account' },
    { id: 'billing',       label: 'Billing',       icon: 'billing' },
    { id: 'apikeys',       label: 'API Keys',      icon: 'lock' },
    { id: 'brandprofile',  label: 'Brand Profile', icon: 'brand' },
    { id: 'requests',     label: 'Roadmap', icon: 'requests' },
    { id: 'support',      label: 'Support',          icon: 'support', badge: '2' },
  ]},
  { group: 'Workflow',   items: [
    { id: 'content',   label: 'Site Content',     icon: 'content' },
    { id: 'tasks',     label: 'Task Board',       icon: 'tasks', badge: '7' },
    { id: 'workspace', label: 'Team Workspace',   icon: 'workspace', badge: '4' },
  ]},
  { group: 'Tools', items: [
    { id: 'scanner',   label: 'Site Scanner', icon: 'scanner' },
    { id: 'estimator', label: 'WP Scope Estimator',   icon: 'modules' },
    { id: 'acquisition', label: 'Acquisition Mode',       icon: 'acquisition' }, /* Phase 2 — placeholder */
    { id: 'alttext',   label: 'Alt Text',              icon: 'image' }, /* moved from SA Command 2026-05-18 (Sprint 1 I2) — customer-facing tool */
    { id: 'filemanager', label: 'File Manager',        icon: 'archive' },
    { id: 'redirects',  label: 'Redirect Manager',      icon: 'redirect' },
    { id: 'imagetools', label: 'Image Tools',            icon: 'image' },
    { id: 'seo',       label: 'Content SEO',           icon: 'spark' },
    { id: 'sitebuilder', label: 'Site Builder',         icon: 'sitebuilder' },
    { id: 'kitbuilder',  label: 'Kit Builder',          icon: 'wand' },
    { id: 'marketingmaterials', label: 'Marketing Materials', icon: 'printer' },
  ]},
  { group: 'Agency',     items: [
    { id: 'wiki',      label: 'Docs & Wiki', icon: 'wiki' },
    { id: 'marketing', label: 'Marketing',   icon: 'marketing' },
  ]},
  { group: 'Admin',      items: [
    { id: 'leads',           label: 'Leads',              icon: 'mail',  badge: 'NEW' },
    { id: 'aiusage',         label: 'AI Usage',           icon: 'chart', badge: 'NEW' },
    { id: 'usermgmt',        label: 'User Management',    icon: 'users' },
    { id: 'operations',      label: 'Plugin Operations',  icon: 'wand',  badge: 'NEW' },
    { id: 'businessmetrics', label: 'Business Metrics',   icon: 'chart' },
    { id: 'notices',         label: 'Notices',            icon: 'bell' },
    { id: 'brains',          label: 'Brain AI',           icon: 'brain' },
    { id: 'brand',           label: 'Brand System',       icon: 'brand' },
    { id: 'team',            label: 'Employee Directory', icon: 'team' },
    { id: 'permissions',     label: 'Permissions',        icon: 'lock' },
    { id: 'roles',           label: 'Role Manager',       icon: 'crown' },
  ]},
];
const NAV_SUPPORT = [
  { group: 'Overview',   items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
    { id: 'sites',     label: 'Sites',     icon: 'sites', badge: '3' },
  ]},
  { group: 'Account',    items: [
    { id: 'account',   label: 'Account',   icon: 'account' },
    { id: 'apikeys',   label: 'API Keys',  icon: 'lock' },
    { id: 'notices',   label: 'Notices',   icon: 'bell' },
    { id: 'requests',  label: 'Roadmap',   icon: 'requests' },
    { id: 'support',   label: 'Support',   icon: 'support', badge: '2' },
  ]},
  { group: 'Workflow',   items: [
    { id: 'tasks',     label: 'Task Board',     icon: 'tasks', badge: '7' },
    { id: 'workspace', label: 'Team Workspace', icon: 'workspace', badge: '4' },
  ]},
  { group: 'Customer Tools', items: [
    { id: 'scanner',   label: 'Site Scanner', icon: 'scanner' },
  ]},
  { group: 'Support Admin', items: [
    { id: 'usermgmt',  label: 'User Management', icon: 'users' },
  ]},
];
const NAV_BY_ROLE = { customer: NAV_CUSTOMER, marketing: NAV_MARKETING, dev: NAV_DEV, admin: NAV_ADMIN, support: NAV_SUPPORT, sa: NAV_SA };
window.NAV_BY_ROLE = NAV_BY_ROLE;

/* ── ROLE SWITCHER ───────────────────────────────────────────── */
function RoleSwitch({ role, setRole, actualRole }) {
  const ALL_ROLES = [
    { id: 'customer',  label: 'Customer'  },
    { id: 'marketing', label: 'Marketing' },
    { id: 'dev',       label: 'Dev'       },
    { id: 'admin',     label: 'Admin'     },
    { id: 'support',   label: 'Support'   },
    { id: 'sa',        label: 'SA', extra: 'role-sa' },
  ];
  const allowed = (window.WPSBD?.IMPERSONATE?.[actualRole]) || [actualRole];
  const roles = ALL_ROLES.filter(r => allowed.includes(r.id));
  useEffect(() => {
    if (!allowed.includes(role) && allowed.length) setRole(allowed[0]);
  }, [actualRole]);

  // Compact dropdown mode for tablet/mobile. Desktop keeps inline tablist.
  const [open, setOpen] = useState(false);
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (!e.target.closest('.role-switch')) setOpen(false); };
    document.addEventListener('click', onDoc);
    return () => document.removeEventListener('click', onDoc);
  }, [open]);
  const current = roles.find(r => r.id === role) || roles[0];

  /* SECURITY (Phase A 2026-05-18): hide the View-as switcher entirely when
     the user has no impersonation options (e.g. customers, marketing, etc.).
     Previously this rendered an empty/single-option switcher which created
     UI noise and hinted at the impersonation feature existing. */
  if (roles.length < 2) return null;

  return (
    <div className={'role-switch' + (open ? ' open' : '')} role="tablist" aria-label="View as role"
         title={`Impersonate roles you have permission to view. Logged-in as ${actualRole}. All access is server-enforced; this toggle never escalates privilege.`}>
      <span className="role-switch-lbl mono" style={{ fontSize:'.58rem', color:'var(--dim)', letterSpacing:1, marginLeft:12 }}>VIEW AS</span>
      {/* Inline tabs (desktop) */}
      <div className="role-switch-tabs">
        {roles.map(r => (
          <button key={r.id} role="tab"
            className={`role-btn ${r.extra || ''} ${role === r.id ? 'active' : ''}`}
            aria-selected={role === r.id}
            onClick={() => setRole(r.id)}>{r.label}</button>
        ))}
      </div>
      {/* Compact trigger (tablet/mobile) */}
      <button type="button" className="role-switch-trigger"
              aria-haspopup="menu" aria-expanded={open}
              onClick={(e) => { e.stopPropagation(); setOpen(v => !v); }}>
        <span className="mono" style={{ fontSize:'.58rem', color:'var(--dim)', letterSpacing:1 }}>VIEW</span>
        <strong>{current?.label || 'Role'}</strong>
        <Icon name="chevron-down" size={12}/>
      </button>
      {open && (
        <div className="role-switch-menu" role="menu" onClick={e => e.stopPropagation()}>
          <div className="role-switch-menu-head mono">View as</div>
          {roles.map(r => (
            <button key={r.id} role="menuitemradio" aria-checked={role === r.id}
              className={`role-switch-menu-item ${r.extra || ''} ${role === r.id ? 'active' : ''}`}
              onClick={() => { setRole(r.id); setOpen(false); }}>
              <span>{r.label}</span>
              {role === r.id && <Icon name="check" size={12}/>}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

/* ── AUTO-SAVE INDICATOR ─────────────────────────────────────────
   Global status chip in the topbar. Reflects WPSBD.autosave state.
   "Saved · just now" / "Saving…" / "Saved · 12s ago" / "Offline".  */
function AutoSaveIndicator() {
  const [st, setSt] = useState(() => window.WPSBD.autosave.get());
  const [, tick]    = useState(0);
  const [online, setOnline] = useState(() => navigator.onLine);

  useEffect(() => window.WPSBD.autosave.subscribe(setSt), []);
  useEffect(() => { const i = setInterval(() => tick(n => n+1), 10000); return () => clearInterval(i); }, []);
  useEffect(() => {
    const on = () => setOnline(true), off = () => setOnline(false);
    window.addEventListener('online', on); window.addEventListener('offline', off);
    return () => { window.removeEventListener('online', on); window.removeEventListener('offline', off); };
  }, []);

  const ago = (ts) => {
    if (!ts) return '';
    const s = Math.floor((Date.now() - ts) / 1000);
    if (s < 5)   return 'just now';
    if (s < 60)  return s + 's ago';
    if (s < 3600) return Math.floor(s/60) + 'm ago';
    return Math.floor(s/3600) + 'h ago';
  };

  let tone = 'ok', icon = 'check', label = 'All changes saved', sub = st.lastSaved ? ago(st.lastSaved) : '';
  if (!online)                   { tone='warn'; icon='warn';  label='Offline';   sub='changes stashed'; }
  else if (st.status === 'saving'){ tone='beam'; icon='loader'; label='Saving…';  sub=st.label || ''; }
  else if (st.status === 'error') { tone='bad';  icon='warn';  label='Save failed'; sub=st.label || 'retry'; }
  else if (st.status === 'saved' || st.lastSaved) { tone='ok'; icon='check'; label='Saved'; sub=ago(st.lastSaved); }
  else                            { tone='';    icon='check'; label='Auto-save on'; sub=''; }

  return (
    <Tooltip text="Every change you make auto-saves to your account. No manual save needed — works across devices when online.">
      <div className={`autosave-chip autosave-${tone}`} role="status" aria-live="polite" aria-label={`${label}${sub ? ' — ' + sub : ''}`}>
        <span className={`autosave-dot ${st.status === 'saving' ? 'spin' : ''}`}><Icon name={icon} size={12}/></span>
        <span className="autosave-label">{label}</span>
        {sub && <span className="autosave-sub">· {sub}</span>}
      </div>
    </Tooltip>
  );
}
window.AutoSaveIndicator = AutoSaveIndicator;

/* ── TOPBAR ──────────────────────────────────────────────────── */

/* Tab ID → human label map for breadcrumbs */
var BREADCRUMB_LABELS = {
  dashboard:'Dashboard', sites:'Sites', account:'Account', billing:'Billing',
  support:'Support', requests:'Roadmap', scanner:'Site Scanner',
  imagetools:'Image Optimizer', filemanager:'File Manager', redirects:'Redirects',
  seo:'SEO Tracker', estimator:'Estimator', usermgmt:'User Management',
  operations:'Operations', leads:'Leads', aiusage:'AI Usage', alttext:'Alt Text',
  myaiusage:'My AI Usage', apikeys:'API Keys', acquisition:'Acquisition',
  sitebuilder:'Site Builder', kitbuilder:'Kit Builder', marketingmaterials:'Marketing',
  businessmetrics:'Business Metrics', workspace:'Workspace', modules:'Modules',
  permissions:'Permissions', notices:'Notices', brains:'Brains', roles:'Roles',
  team:'Team', brand:'Brand', tasks:'Tasks', content:'Content', marketing:'Marketing',
  wiki:'Wiki', internalnotes:'Internal Notes', agencytools:'Agency Tools',
  brandprofile:'Brand Profile',
};

function BreadcrumbBar({ tab, onHome }) {
  var label = BREADCRUMB_LABELS[tab] || tab;
  if (!tab || tab === 'dashboard') return null;
  return (
    <nav aria-label="Breadcrumb" style={{
      display:'flex', alignItems:'center', gap:4,
      fontSize:'.78rem', color:'var(--dim)', minWidth:0,
    }}>
      <button
        onClick={onHome}
        style={{ background:'none', border:'none', padding:0, cursor:'pointer',
          color:'var(--dim)', fontSize:'.78rem', fontWeight:500 }}
        aria-label="Go to Dashboard">
        Workspace
      </button>
      <span aria-hidden="true" style={{ color:'var(--border-2,rgba(0,0,0,.25))', fontSize:'.7rem' }}>›</span>
      <span style={{ color:'var(--text)', fontWeight:600, fontSize:'.78rem',
        whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', maxWidth:180 }}
        aria-current="page">
        {label}
      </span>
    </nav>
  );
}

function Topbar({ s, api, onOpenTweaks, onToggleSidebar }) {
  const nav = NAV_BY_ROLE[s.role] || NAV_CUSTOMER;
  const r = window.WPSBD.ROLES[s.role];
  /* SECURITY (Phase A 2026-05-18): user identity (name, email, initials)
     now comes from window.currentUser via WPSBD helpers — NOT from the
     ROLES table which only contains persona metadata (plan label, css class).
     Previously ROLES had hardcoded "Jordan Davis / jordan@acme.co" demo data
     that was displayed to every real customer in the topbar. */
  const userName     = window.WPSBD.getUserName ? window.WPSBD.getUserName() : (window.currentUser?.email?.split('@')[0] || 'User');
  const userEmail    = window.WPSBD.getUserEmail ? window.WPSBD.getUserEmail() : (window.currentUser?.email || '');
  const userInitials = window.WPSBD.getUserInitials ? window.WPSBD.getUserInitials() : '?';
  return (
    <header className="topbar">
      <button type="button" className="sidebar-toggle" aria-label="Toggle navigation"
              onClick={onToggleSidebar}>
        <Icon name="menu" size={18}/>
      </button>
      <a className="brand" href="#" onClick={(e) => { e.preventDefault(); api.switchTab('dashboard'); }} aria-label="WPSiteBeam — home">
        <img className="brand-mark" src={`design/brand/icon-${s.logoVariant || 'black-amber'}.svg`} alt="" aria-hidden="true" />
        <span className="brand-word"><span className="brand-word-wp">WPSite</span><span className="brand-word-beam">Beam</span></span>
      </a>
      {/* ── Breadcrumbs (design review 2026-05-26) ─────────────────
           Workspace > [Current Page] pattern. Reads s.tab via prop.
           Clicking "Workspace" navigates to dashboard. */}
      <BreadcrumbBar tab={s.tab} onHome={() => api.switchTab('dashboard')} />
      <div className="topbar-nav-spacer"/>
      <div className="topbar-right">
        <AutoSaveIndicator/>
        <RoleSwitch role={s.role} actualRole={s.actualRole || 'customer'} setRole={(role) => api.set('role', role)} />
        <button className="icon-btn" aria-label={`Switch to ${s.theme === 'dark' ? 'light' : 'dark'} theme`}
          aria-pressed={s.theme === 'dark'}
          onClick={() => api.set('theme', s.theme === 'dark' ? 'light' : 'dark')}>
          <Icon name={s.theme === 'dark' ? 'sun' : 'moon'} size={16} />
        </button>
        <button className="icon-btn" aria-label="Open tweaks" onClick={onOpenTweaks}
          style={{ position:'relative' }}>
          <Icon name="sliders" size={16} />
          {/* A11y active indicator — shows when any a11y setting is non-default */}
          {((s.textSize && s.textSize !== 'normal') || (s.contrast && s.contrast !== 'normal') || (s.motion && s.motion !== 'normal')) && (
            <span aria-label="Accessibility tweaks active" title="Accessibility tweaks active"
                  style={{
                    position:'absolute', top:4, right:4, width:7, height:7, borderRadius:'50%',
                    background:'var(--beam)', border:'1.5px solid var(--surface)',
                  }}/>
          )}
        </button>
        <div className="user-chip" title={userEmail} tabIndex={0}
             onClick={() => api.switchTab('account')}
             style={{ cursor:'pointer' }}>
          <div className={`user-avatar ${s.role === 'sa' ? 'sa' : ''}`}>{userInitials}</div>
          <span className={r.planClass}>{r.plan}</span>
          <div className="user-menu" role="menu" onClick={e => e.stopPropagation()}>
            <div className="user-menu-head">
              <div className={`user-avatar ${s.role === 'sa' ? 'sa' : ''}`} style={{ width:36, height:36, fontSize:'.78rem' }}>{userInitials}</div>
              <div style={{ minWidth:0 }}>
                <div style={{ fontWeight:600, fontSize:'.86rem', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{userName}</div>
                <div style={{ fontSize:'.7rem', color:'var(--dim)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{userEmail}</div>
              </div>
            </div>
            <div className="user-menu-sect">Account</div>
            <button className="user-menu-item" onClick={() => api.switchTab('account')}><Icon name="account" size={14}/>Account</button>
            <button className="user-menu-item" onClick={() => api.switchTab('billing')}><Icon name="billing" size={14}/>Billing</button>
            <button className="user-menu-item" onClick={() => api.switchTab('brandprofile')}><Icon name="brand" size={14}/>Brand Profile</button>
            <div className="user-menu-divider"/>
            <button className="user-menu-item" onClick={() => api.switchTab('support')}><Icon name="support" size={14}/>Support</button>
            <button className="user-menu-item" style={{ color:'var(--rose)' }} onClick={() => window.wpsbSignOut?.()}><Icon name="signout" size={14}/>Sign out</button>
          </div>
        </div>
      </div>
    </header>
  );
}
window.Topbar = Topbar;

/* ── SIDEBAR ─────────────────────────────────────────────────── */
function Sidebar({ s, api, collapsed, onToggleCollapse }) {
  const nav = NAV_BY_ROLE[s.role] || NAV_CUSTOMER;
  return (
    <nav className="sidebar" aria-label="Sidebar">
      {/* Collapse toggle — desktop only (hidden via CSS below 980px where
          the mobile drawer hamburger lives in the topbar). */}
      <button type="button" className="sb-collapse-btn"
              aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
              aria-pressed={collapsed ? 'true' : 'false'}
              title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
              onClick={onToggleCollapse}>
        <Icon name={collapsed ? 'chevron-right' : 'chevron-left'} size={14}/>
        <span className="sb-label">Collapse</span>
      </button>
      {nav.map((group, gi) => (
        <React.Fragment key={group.group + gi}>
          <div className={`sb-sect ${group.sa ? 'sa' : ''}`} style={group.sa ? { color: 'var(--rose)' } : null}>{group.group}</div>
          {group.items.map(it => (
            <button key={it.id}
              className={`sb-item ${it.sa ? 'sa' : ''} ${s.tab === it.id ? 'active' : ''}`}
              aria-current={s.tab === it.id ? 'page' : undefined}
              title={collapsed ? it.label : undefined}
              onClick={() => api.switchTab(it.id)}>
              <span className="sb-icon" aria-hidden="true"><Icon name={it.icon} size={16}/></span>
              <span className="sb-label">{it.label}</span>
              {it.badge && <span className={`sb-badge ${it.sa ? 'sa' : 'count'}`} aria-label={`${it.badge} items`}>{it.badge}</span>}
            </button>
          ))}
        </React.Fragment>
      ))}
      {!['sa','dev','admin'].includes(s.role) && (
        <AISidebarCredits collapsed={collapsed} onTopUp={() => api.set('tab','billing')}/>
      )}
            <div className="sb-footer">
        <button className="sb-item" style={{ color: 'var(--red)' }} title={collapsed ? 'Sign Out' : undefined}
                onClick={() => window.wpsbSignOut?.()}>
          <span className="sb-icon" aria-hidden="true"><Icon name="signout" size={16}/></span>
          <span className="sb-label">Sign Out</span>
        </button>
      </div>
    </nav>
  );
}

function AISidebarCredits({ collapsed, onTopUp }) {
  const { useState, useEffect } = React;
  const [credits,setCredits]=useState(null); const [total,setTotal]=useState(null);
  const [resetAt,setResetAt]=useState(null); const [err,setErr]=useState(false);
  useEffect(()=>{
    let live=true;
    const token=window.WPSBD?.getToken?.();
    const base=window.WPSBD?.apiBase||'https://api.wpsitebeam.io';
    if (!token){setErr(true);return;}
    Promise.all([
      fetch(base+'/account/usage',{headers:{Authorization:'Bearer '+token}}).then(r=>r.ok?r.json():null),
      fetch(base+'/billing/subscription',{headers:{Authorization:'Bearer '+token}}).then(r=>r.ok?r.json():null),
    ]).then(([usage,sub])=>{
      if (!live) return;
      if (usage){const u=usage.ai_calls_used??0,l=usage.ai_calls_limit??null;setCredits(l!==null?Math.max(0,l-u):null);setTotal(l);}
      if (sub?.current_period_end){const d=new Date(sub.current_period_end*1000);setResetAt(d.toLocaleDateString('en-US',{month:'short',day:'numeric'}));}
    }).catch(()=>{if(live)setErr(true);});
    return ()=>{live=false;};
  },[]);
  if (collapsed) return (
    <div style={{ padding:'8px 0 4px', display:'flex', justifyContent:'center' }} title="AI Credits">
      <span style={{ fontSize:'1rem' }}>⚡</span>
    </div>
  );
  if (err||credits===null) return null;
  const pct=total&&total>0?Math.min(100,Math.round(((total-credits)/total)*100)):0;
  const tone=pct>=90?'var(--rose)':pct>=70?'var(--warn)':'var(--cyan,#06b6d4)';
  return (
    <div style={{ margin:'0 8px 6px', padding:'10px 12px', borderRadius:8, background:'var(--surface-2)', border:'1px solid var(--border)' }}>
      <div style={{ display:'flex', alignItems:'center', gap:6, marginBottom:6 }}>
        <span style={{ fontSize:'.82rem' }}>⚡</span>
        <span style={{ fontWeight:600, fontSize:'.78rem', color:tone, flex:1 }}>{credits.toLocaleString()} AI credits</span>
      </div>
      <div style={{ height:4, borderRadius:2, background:'var(--border)', marginBottom:6, overflow:'hidden' }}>
        <div style={{ height:'100%', width:pct+'%', background:tone, borderRadius:2, transition:'width .4s ease' }}/>
      </div>
      <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
        <span style={{ fontSize:'.68rem', color:'var(--dim)' }}>{resetAt?'Resets '+resetAt:'This period'}</span>
        <button className="btn btn-ghost btn-sm" style={{ padding:'2px 8px', fontSize:'.68rem', lineHeight:1.3 }} onClick={onTopUp}>Top up</button>
      </div>
    </div>
  );
}

window.Sidebar = Sidebar;

/* ── SIGN-OUT FLOW (shared) ─────────────────────────────────────
   Replaces the orphaned alert() stubs in the topbar user-menu and
   sidebar footer. Opens a themed confirm modal; on confirm it flashes
   a toast and (in a real app) would clear the session + redirect to /login.
   For the demo it's a no-op after closing. */
window.wpsbSignOut = function wpsbSignOut() {
  if (document.getElementById('wpsb-signout-modal')) return; // already open

  const overlay = document.createElement('div');
  overlay.id = 'wpsb-signout-modal';
  overlay.setAttribute('role', 'dialog');
  overlay.setAttribute('aria-modal', 'true');
  overlay.setAttribute('aria-labelledby', 'wpsb-signout-title');
  overlay.style.cssText = `
    position:fixed; inset:0; z-index:10000;
    background:rgba(0,0,0,.6); backdrop-filter:blur(4px);
    display:flex; align-items:center; justify-content:center;
    animation: wpsbSignOutFade .15s ease-out;
  `;

  // Animation keyframes (inject once)
  if (!document.getElementById('wpsb-signout-styles')) {
    const style = document.createElement('style');
    style.id = 'wpsb-signout-styles';
    style.textContent = `
      @keyframes wpsbSignOutFade { from{opacity:0} to{opacity:1} }
      @keyframes wpsbSignOutPop  { from{transform:scale(.95); opacity:0} to{transform:scale(1); opacity:1} }
    `;
    document.head.appendChild(style);
  }

  const card = document.createElement('div');
  card.style.cssText = `
    width:420px; max-width:90vw;
    background:var(--surface); border:1px solid var(--border); border-radius:12px;
    box-shadow:0 20px 60px rgba(0,0,0,.5);
    padding:24px; color:var(--text);
    animation: wpsbSignOutPop .18s cubic-bezier(.16,1,.3,1);
  `;
  card.innerHTML = `
    <div style="display:flex; align-items:center; gap:12px; margin-bottom:14px;">
      <div style="width:40px; height:40px; border-radius:10px; background:rgba(220,38,38,.1); color:var(--red); display:flex; align-items:center; justify-content:center; font-size:20px;">⎋</div>
      <div>
        <h2 id="wpsb-signout-title" style="margin:0; font-size:1.05rem; font-weight:700;">Sign out of WP Site Beam?</h2>
        <div style="font-size:.78rem; color:var(--text-2); margin-top:2px;">You'll return to the login screen.</div>
      </div>
    </div>
    <div style="padding:12px; background:var(--surface-2); border:1px solid var(--border); border-radius:8px; font-size:.78rem; color:var(--text-2); line-height:1.5; margin-bottom:18px;">
      Your work is auto-saved. Active scans will continue running in the background, and you'll find any new results waiting when you sign back in.
    </div>
    <div style="display:flex; gap:8px; justify-content:flex-end;">
      <button id="wpsb-signout-cancel" class="btn btn-ghost btn-sm">Cancel</button>
      <button id="wpsb-signout-confirm" class="btn btn-sm" style="background:var(--red); color:#fff; border-color:var(--red);">Sign out</button>
    </div>
  `;
  overlay.appendChild(card);

  const close = () => { overlay.remove(); document.removeEventListener('keydown', onKey); };
  const onKey = (e) => { if (e.key === 'Escape') close(); };
  document.addEventListener('keydown', onKey);
  overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });
  card.querySelector('#wpsb-signout-cancel').addEventListener('click', close);
  card.querySelector('#wpsb-signout-confirm').addEventListener('click', async () => {
    close();
    try {
      /* Real sign-out: clear Supabase session + inactivity key, redirect to login */
      if (window.supabase_client && window.supabase_client.auth) {
        await window.supabase_client.auth.signOut();
      }
      try { localStorage.removeItem('wpsb-last-activity'); } catch {}
      try { localStorage.removeItem('wpsb-auth'); } catch {}
      window.location.replace('https://login.wpsitebeam.io/login');
    } catch(err) {
      console.error('[WPSB] Sign-out error:', err);
      /* Still redirect — belt and suspenders */
      window.location.replace('https://login.wpsitebeam.io/login');
    }
  });

  document.body.appendChild(overlay);
  // Focus the cancel button so Enter doesn't accidentally confirm
  setTimeout(() => card.querySelector('#wpsb-signout-cancel')?.focus(), 50);
};
