/* ===================================================================
   KabelFlux Behemoth — Main App
   Bundle-design port. Tweaks panel + design-iteration UI dropped.
   =================================================================== */

/* Minimal local replacement for the bundle's useTweaks hook (which spoke
   to Claude Design via postMessage). Persists to localStorage so the
   user's accent / density / etc. survive a reload. */
function useTweaks(defaults) {
  const [values, setValues] = React.useState(() => {
    try {
      const saved = JSON.parse(localStorage.getItem('kfx_tweaks') || '{}');
      return Object.assign({}, defaults, saved);
    } catch (e) {
      return defaults;
    }
  });
  const setTweak = React.useCallback((keyOrEdits, val) => {
    const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null
      ? keyOrEdits : { [keyOrEdits]: val };
    setValues((prev) => {
      const next = Object.assign({}, prev, edits);
      try { localStorage.setItem('kfx_tweaks', JSON.stringify(next)); } catch (e) {}
      return next;
    });
  }, []);
  return [values, setTweak];
}

const TABS = [
  { group: 'Design', items: [
    { k: 'layout',     l: 'Layout',            icon: 'layout' },
    { k: 'wires',      l: 'Wires',             icon: 'wires' },
    { k: 'connectors', l: 'Connectors',        icon: 'connector' },
    { k: 'nodes',      l: 'Nodes',             icon: 'nodes' },
  ]},
  { group: 'Documentation', items: [
    { k: 'bom',        l: 'BOM',               icon: 'bom' },
    { k: 'notes',      l: 'Notes & Revisions', icon: 'notes' },
  ]},
  { group: 'Project', items: [
    { k: 'projectsettings', l: 'Parameters',  icon: 'tune' },
  ]},
];

/* ----- Project switcher dropdown -----
   Single topbar control: shows the current project (or "Select project")
   and on click opens a menu with:
     · Projects home (collection page)
     · Create new project
     · — divider —
     · Other projects in the workspace
*/
const ProjectSwitcher = ({ projects, currentId, onOpenProject, onGoToProjects, onCreateNew }) => {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  const current = projects.find(p => p.id === currentId);
  const others = projects.filter(p => p.id !== currentId);

  // Click-outside + Esc to close.
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
  }, [open]);

  return (
    <div className="kf-psw" ref={ref}>
      <button className={`kf-psw-trigger ${open ? 'on' : ''}`}
              onClick={() => setOpen(o => !o)}
              aria-haspopup="menu" aria-expanded={open}>
        <Icon name="folder" size={14} stroke="var(--t-3)"/>
        {current ? (
          <div className="kf-psw-trigger-text">
            <div className="kf-psw-trigger-code num">{current.code}</div>
            <div className="kf-psw-trigger-name">{current.name}</div>
          </div>
        ) : (
          <div className="kf-psw-trigger-text">
            <div className="kf-psw-trigger-name kf-psw-trigger-empty">Select project</div>
          </div>
        )}
        <Icon name="chevD" size={12} stroke="var(--t-3)"/>
      </button>

      {open && (
        <div className="kf-psw-menu" role="menu">
          <button className="kf-psw-item kf-psw-item-action" role="menuitem"
                  onClick={() => { onGoToProjects && onGoToProjects(); setOpen(false); }}>
            <Icon name="grid" size={14} stroke="var(--t-2)"/>
            <div className="kf-psw-item-text">
              <div className="kf-psw-item-title">Projects home</div>
              <div className="kf-psw-item-meta">Browse all {projects.length} projects</div>
            </div>
            <Icon name="chevR" size={12} stroke="var(--t-3)"/>
          </button>
          <button className="kf-psw-item kf-psw-item-action" role="menuitem"
                  onClick={() => { onCreateNew && onCreateNew(); setOpen(false); }}>
            <Icon name="plus" size={14} stroke="var(--t-2)"/>
            <div className="kf-psw-item-text">
              <div className="kf-psw-item-title">Create new project</div>
              <div className="kf-psw-item-meta">Start a blank harness or import a CSV</div>
            </div>
          </button>

          <div className="kf-psw-section">Switch to</div>
          <div className="kf-psw-list">
            {others.length === 0 && (
              <div className="kf-psw-empty">No other projects in this workspace.</div>
            )}
            {others.map(p => (
              <button key={p.id} className="kf-psw-item kf-psw-item-project" role="menuitem"
                      onClick={() => { onOpenProject(p.id); setOpen(false); }}>
                <span className={`kf-status-dot kf-status-${p.status}`} aria-hidden="true"/>
                <div className="kf-psw-item-text">
                  <div className="kf-psw-item-title">{p.name}</div>
                  <div className="kf-psw-item-meta">
                    <span className="num">{p.code}</span> · Rev {p.revision} · {p.modified}
                  </div>
                </div>
                {p.pinned && <Icon name="star" size={11} stroke="var(--warning)" fill="var(--warning)"/>}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

/* ===================================================================
   Boot states — shown before App renders the main shell.
   - BootSplash:  brief while kfxBootstrap() is in flight
   - LoginScreen: shown when no valid session
   - ErrorScreen: shown when the backend errors out (network down etc.)
   =================================================================== */
const BootSplash = () => (
  <div className="kf-boot">
    <div className="kf-boot-inner">
      <KabelFluxMark size={36}/>
      <Spinner size={18}/>
      <div className="kf-boot-text">Loading workspace…</div>
    </div>
  </div>
);

const ErrorScreen = ({ err, onRetry }) => (
  <div className="kf-boot">
    <div className="kf-boot-card">
      <KabelFluxMark size={36}/>
      <h2>Can't reach the server</h2>
      <p>{(err && err.message) || 'The KabelFlux API didn\'t respond.'}</p>
      <Btn kind="primary" onClick={onRetry}>Retry</Btn>
    </div>
  </div>
);

const LoginScreen = ({ onLogin }) => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [busy, setBusy]         = useState(false);
  const [err, setErr]           = useState('');

  const submit = async (e) => {
    e && e.preventDefault();
    if (!username.trim() || !password) { setErr('Enter username and password.'); return; }
    setBusy(true); setErr('');
    try { await onLogin(username.trim(), password); }
    catch (ex) {
      setErr(ex && ex.status === 401 ? 'Invalid username or password.' :
             (ex && ex.message) || 'Could not sign in.');
      setBusy(false);
    }
  };

  return (
    <div className="kf-boot">
      <form className="kf-login-card" onSubmit={submit}>
        <div className="kf-login-brand">
          <KabelFluxMark size={36}/>
          <div>
            <div className="kf-login-brand-name">KabelFlux</div>
            <div className="kf-login-brand-sub">e-Harness Suite</div>
          </div>
        </div>
        <h2 className="kf-login-title">Sign in</h2>
        <label className="kf-login-field">
          <span>Username or email</span>
          <input type="text" autoComplete="username" autoFocus value={username}
                 onChange={e => setUsername(e.target.value)} disabled={busy}/>
        </label>
        <label className="kf-login-field">
          <span>Password</span>
          <input type="password" autoComplete="current-password" value={password}
                 onChange={e => setPassword(e.target.value)} disabled={busy}/>
        </label>
        {err && <div className="kf-login-err" role="alert">{err}</div>}
        <Btn kind="primary" fullWidth busy={busy} disabled={busy}>
          {busy ? 'Signing in…' : 'Sign in'}
        </Btn>
        <div className="kf-login-foot">
          <a href="/legacy" className="kf-login-link">Register company →</a>
        </div>
      </form>
    </div>
  );
};

const App = () => {
  const [t, setT] = useTweaks(window.TWEAK_DEFAULTS);

  /* Auth bootstrap state — see kfxBootstrap() in data.jsx. */
  const [bootKind, setBootKind] = useState('loading');
  const [me, setMe]             = useState(null);
  const [projects, setProjects] = useState([]);
  const [bootErr, setBootErr]   = useState(null);

  const [tab, setTab] = useState('projects');
  const [pid, setPid] = useState(null);
  const [projectDetail, setProjectDetail] = useState(null);
  const [projectLoading, setProjectLoading] = useState(false);
  const [filter, setFilter] = useState('');
  const [paletteOpen, setPaletteOpen] = useState(false);
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
  const [showBackups, setShowBackups] = useState(false);
  const [showAuditLog, setShowAuditLog] = useState(false);
  const [showUsers, setShowUsers] = useState(false);
  const [showCompanies, setShowCompanies] = useState(false);
  const [showShareLinks, setShowShareLinks] = useState(false);
  const [showSysHealth, setShowSysHealth] = useState(false);
  const [showWorkspace, setShowWorkspace] = useState(false);
  const [wireMode, setWireMode] = useState(false);
  // showFocusDemo removed — design-iteration overlay, not a real app feature
  const [adminMenuOpen, setAdminMenuOpen] = useState(false);
  const [exportMenuOpen, setExportMenuOpen] = useState(false);
  const [userMenuOpen, setUserMenuOpen] = useState(false);

  const project = useMemo(() => projects.find(p => p.id === pid), [projects, pid]);
  const toast = useToast();

  /* Run kfxBootstrap on mount. Result decides whether we render the
     login screen, the error screen, or the main shell with live data. */
  useEffect(() => {
    let cancelled = false;
    (async () => {
      const res = await kfxBootstrap();
      if (cancelled) return;
      if (res.kind === 'authed') {
        setMe(res.me);
        setProjects(res.projects);
        setBootKind('authed');
      } else if (res.kind === 'guest') {
        setBootKind('guest');
      } else {
        setBootErr(res.err);
        setBootKind('error');
      }
    })();
    return () => { cancelled = true; };
  }, []);

  /* Load per-project detail (wires, connectors, nodes) whenever the
     selected project changes. `pid` is the bundle's 'p<num>' id; we
     look up the matching project row to get the backend's realId. */
  const realPid = useMemo(() => {
    const proj = projects.find(p => p.id === pid);
    return proj && proj.realId ? proj.realId : null;
  }, [pid, projects]);

  const refreshProject = useCallback(async () => {
    if (!realPid) return;
    try {
      const detail = await kfxLoadAndMapProject(realPid);
      setProjectDetail(detail);
    } catch (e) {
      toast.err('Could not refresh project', (e && e.message) || 'Unknown error');
    }
  }, [realPid, toast]);

  useEffect(() => {
    if (!pid) { setProjectDetail(null); return; }
    if (!realPid) { setProjectDetail(null); return; }
    let cancelled = false;
    setProjectLoading(true);
    (async () => {
      try {
        const detail = await kfxLoadAndMapProject(realPid);
        if (!cancelled) setProjectDetail(detail);
      } catch (e) {
        if (!cancelled) {
          setProjectDetail({ wires: [], connectors: [], nodes: [], bundles: [], raw: null, error: e });
          toast.err('Could not load project', (e && e.message) || 'Unknown error');
        }
      } finally {
        if (!cancelled) setProjectLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [pid, realPid]);

  /* ⌘K + global shortcuts */
  useEffect(() => {
    const fn = e => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault(); setPaletteOpen(true); return;
      }
      if (e.key === 'Escape') {
        if (wireMode) { setWireMode(false); }
      }
      if (e.key === '/' && !['INPUT','TEXTAREA','SELECT'].includes(document.activeElement.tagName)) {
        e.preventDefault();
        const el = document.querySelector('.kf-search input');
        if (el) el.focus();
      }
      if ((e.metaKey || e.ctrlKey) && e.key === '\\') {
        e.preventDefault(); setSidebarCollapsed(s => !s);
      }
      if (e.key.toLowerCase() === 'w' && !['INPUT','TEXTAREA','SELECT'].includes(document.activeElement.tagName) && !e.metaKey && !e.ctrlKey) {
        setWireMode(w => !w);
      }
    };
    document.addEventListener('keydown', fn);
    return () => document.removeEventListener('keydown', fn);
  }, [wireMode]);

  /* density + accent + theme driven by tweaks */
  useEffect(() => {
    const root = document.documentElement;
    if (t.density === 'compact') { root.style.setProperty('--density-row', '26px'); }
    if (t.density === 'cozy')    { root.style.setProperty('--density-row', '32px'); }
    if (t.density === 'comfortable') { root.style.setProperty('--density-row', '40px'); }

    // Theme palettes recolour the whole shell via data-theme on <html>.
    // Each theme picks a sensible default accent so the swatches the user sees
    // match what's actually applied; the user can still override below.
    const prevTheme = root.getAttribute('data-theme');
    const nextTheme = t.theme || 'midnight';
    if (prevTheme !== nextTheme) {
      // Suppress `transition: all` on themed elements while colors swap —
      // Chrome holds the old variable-resolved color mid-transition otherwise.
      root.classList.add('kf-theme-switching');
      requestAnimationFrame(() => requestAnimationFrame(() => root.classList.remove('kf-theme-switching')));
    }
    root.setAttribute('data-theme', nextTheme);

    // Other tweakable design surfaces — each maps to a data-* attribute the
    // stylesheet selects on. Defaults match the original look so missing keys
    // (older saved tweaks) are a no-op.
    root.setAttribute('data-type',        t.typeface  || 'plex');
    root.setAttribute('data-corners',     t.corners   || 'soft');
    root.setAttribute('data-nav-active',  t.navActive || 'tint');
    root.setAttribute('data-elev',        t.elevation || 'flat');

    const accentMap = {
      midnight: { red:'#ef4e4e', blue:'#4a9eff', teal:'#00d4aa' },
      graphite: { red:'#f06a3a', blue:'#6fa8ff', teal:'#2dd4a7' },
      daylight: { red:'#d63838', blue:'#2563eb', teal:'#009972' },
    };
    const softMap = {
      midnight: { red:'rgba(239,78,78,.15)',  blue:'rgba(74,158,255,.15)',  teal:'rgba(0,212,170,.15)' },
      graphite: { red:'rgba(240,106,58,.16)', blue:'rgba(111,168,255,.16)', teal:'rgba(45,212,167,.16)' },
      daylight: { red:'rgba(214,56,56,.10)',  blue:'rgba(37,99,235,.10)',   teal:'rgba(0,153,114,.12)' },
    };
    const themeKey = (t.theme && accentMap[t.theme]) ? t.theme : 'midnight';
    const accent = (t.accent && accentMap[themeKey][t.accent]) ? t.accent : 'red';
    root.style.setProperty('--accent',      accentMap[themeKey][accent]);
    root.style.setProperty('--accent-soft', softMap[themeKey][accent]);

  }, [t.density, t.accent, t.theme, t.typeface, t.corners, t.navActive, t.elevation]);

  /* Hidden file input for CSV import — triggered by Import buttons. */
  const csvImportInputRef = useRef(null);
  const triggerCsvImport = useCallback(() => {
    if (!realPid) { toast.warn('Open a project first'); return; }
    if (csvImportInputRef.current) csvImportInputRef.current.click();
  }, [realPid, toast]);
  const handleCsvImportFile = useCallback(async (e) => {
    const f = e.target.files && e.target.files[0];
    e.target.value = '';
    if (!f || !realPid) return;
    try {
      const res = await kfxImportCsv(realPid, f);
      toast.ok('CSV imported', (res && res.message) || f.name);
      await refreshProject();
    } catch (ex) {
      toast.err('Import failed', (ex && ex.message) || 'Unknown error');
    }
  }, [realPid, refreshProject, toast]);

  /* New-project modal state — wired below. */
  const [showNewProject, setShowNewProject] = useState(false);
  const reloadProjects = useCallback(async () => {
    try {
      const ps = await kfxLoadProjects();
      setProjects(ps);
      return ps;
    } catch (e) {
      toast.err('Could not reload projects', (e && e.message) || 'Unknown error');
      return projects;
    }
  }, [toast, projects]);

  /* Export action dispatcher. Names match backend /api/projects/{pid}/export-X. */
  const EXPORT_KIND_TO_NAME = {
    prod:           'combined',
    cont:           'continuity',
    'cont-xlsx':    'continuity-xlsx',
    komax:          'komax',
    as50881:        'as50881',
    dxf:            'dxf',
    'bom-xlsx':     'bom-xlsx',
    bom:            'bom',
    'wiretable':    'wiretable',
    'wiretable-xlsx': 'wiretable-xlsx',
    schematic:      'pdf',
    'all-zip':      'all-zip',
    csv:            'csv',
    kfx:            'kfx',
    'combined-xlsx': 'combined-xlsx',
  };
  const doExport = useCallback((kind) => {
    if (!realPid) { toast.warn('Open a project first'); return; }
    const name = EXPORT_KIND_TO_NAME[kind] || kind;
    try {
      kfxDownload(realPid, name);
      toast.ok('Export started', `Downloading ${name}…`);
    } catch (e) {
      toast.err('Export failed', (e && e.message) || 'Unknown error');
    }
  }, [realPid, toast]);

  /* palette → action */
  const onNav = (kind) => {
    const [type, value] = kind.split(':');
    if (type === 'goto') setTab(value);
    if (type === 'project') { setPid(value); setTab('home'); }
    if (type === 'action') {
      if (value === 'wire-mode') { setWireMode(true); setTab('layout'); }
      if (value === 'new-wire')  { setTab('wires'); }
      if (value === 'new-conn')  { setTab('connectors'); }
      if (value === 'new-node')  { setTab('nodes'); }
      if (value === 'import')    triggerCsvImport();
    }
    if (type === 'export') {
      doExport(value);
    }
    if (type === 'admin') {
      if (!isAdmin) { toast.warn('Admin role required'); return; }
      // SYSTEM items (super-admin only): backups / health / companies
      const superOnly = new Set(['backups', 'health', 'companies']);
      if (superOnly.has(value) && !isSuper) { toast.warn('Super-admin role required'); return; }
      if      (value === 'backups')   setShowBackups(true);
      else if (value === 'users')     setShowUsers(true);
      else if (value === 'share')     setShowShareLinks(true);
      else if (value === 'workspace') setShowWorkspace(true);
      else if (value === 'companies') setShowCompanies(true);
      else if (value === 'health')    setShowSysHealth(true);
      else if (value === 'audit')     setShowAuditLog(true);
      else toast.info(`Admin · ${value}`);
    }
  };

  const filteredProjects = projects.filter(p => !filter || (p.name + ' ' + p.code).toLowerCase().includes(filter.toLowerCase()));

  /* Logout: kfxLogout clears storage, then we flip back to the login screen. */
  const handleLogout = async () => {
    await kfxLogout();
    setMe(null);
    setProjects([]);
    setPid(null);
    setTab('projects');
    setUserMenuOpen(false);
    setBootKind('guest');
  };

  /* Render gates. Auth bootstrap runs once on mount; until it lands we
     show a splash; on `guest` we render the login screen; on `error` an
     error screen with a retry hint. */
  if (bootKind === 'loading') return <BootSplash/>;
  if (bootKind === 'guest') return (
    <LoginScreen onLogin={async (u, p) => {
      const user = await kfxLogin(u, p);
      setMe(user);
      const ps = await kfxLoadProjects();
      setProjects(ps);
      setBootKind('authed');
    }}/>
  );
  if (bootKind === 'error') return <ErrorScreen err={bootErr} onRetry={() => window.location.reload()}/>;

  /* userInitials sourced from the live `me` object — falls back gracefully
     if neither full_name nor username is present. */
  const userInitials = (() => {
    const src = (me && (me.full_name || me.username)) || '';
    const parts = src.trim().split(/\s+/).filter(Boolean);
    if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
    if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
    return '??';
  })();
  const userDisplayName = (me && (me.full_name || me.username)) || 'User';
  const userRoleLine    = me ? `${me.role || 'designer'}${me.company_name ? ' · ' + me.company_name : ''}` : '';
  const isAdmin = !!(me && (me.role === 'admin' || me.role === 'superadmin'));
  const isSuper = !!(me && me.role === 'superadmin');

  return (
    <div className={`app ${sidebarCollapsed ? 'sidebar-collapsed' : ''}`}>
      {/* TOPBAR */}
      <header className="topbar">
        <div className="kf-brand">
          <KabelFluxMark size={28}/>
          <div className="kf-brand-text">
            <div className="kf-brand-name">KabelFlux</div>
            <div className="kf-brand-sub">e-Harness Suite</div>
          </div>
        </div>

        {/* Workspace switcher — single dropdown.
            First item routes to the Projects collection page, second item
            creates a new project, then a list of the rest. */}
        <ProjectSwitcher
          projects={projects}
          currentId={pid}
          onOpenProject={(id) => { setPid(id); setTab('home'); }}
          onGoToProjects={() => setTab('projects')}
          onCreateNew={() => setShowNewProject(true)}
        />

        <div className="spacer"/>

        {/* Command palette trigger */}
        <button className="kf-cmd-trigger" onClick={() => setPaletteOpen(true)} aria-label="Open command palette">
          <Icon name="search" size={14} stroke="var(--t-3)"/>
          <span>Search · ⌘K</span>
        </button>

        {/* Symbol library — workspace-level, not tied to a single project. */}
        <Tip side="bottom" label="Symbol library">
          <IconBtn icon="library" label="Symbol library" active={tab === 'library'} onClick={() => setTab('library')}/>
        </Tip>

        {/* Advanced (was: Admin) — dropdown for workspace ops + platform admin.
            Hidden for non-admins; superadmin sees the extra "Companies" item. */}
        {isAdmin && (
          <div className="kf-rel">
            <Tip side="bottom" label="Advanced">
              <IconBtn icon="sliders" label="Advanced menu" active={adminMenuOpen} onClick={() => setAdminMenuOpen(o => !o)}/>
            </Tip>
            {adminMenuOpen && (
              <Menu align="right" onClose={() => setAdminMenuOpen(false)} width={260}>
                {/* OPERATIONS — admin + super-admin */}
                <MenuLabel>Operations</MenuLabel>
                <MenuItem icon="folder"  label="Workspace settings" hint="Company · billing · branding · SSO" onClick={() => { setShowWorkspace(true);  setAdminMenuOpen(false); }}/>
                <MenuItem icon="user"    label="Users"             hint="Workspace roles · permissions"    onClick={() => { setShowUsers(true);      setAdminMenuOpen(false); }}/>
                <MenuItem icon="share"   label="Share links"       hint="Read-only · revoke"               onClick={() => { setShowShareLinks(true); setAdminMenuOpen(false); }}/>
                <MenuItem icon="history" label="Audit log"         hint="All edits · 90-day window"        onClick={() => { setShowAuditLog(true);   setAdminMenuOpen(false); }}/>
                {/* SYSTEM — super-admin only (cross-tenant + platform-level)
                    Tenant admins shouldn't see Companies (cross-tenant view),
                    Database backups (platform storage), or System health
                    (platform diagnostics). */}
                {isSuper && (
                  <React.Fragment>
                    <MenuSep/>
                    <MenuLabel>System</MenuLabel>
                    <MenuItem icon="folder" label="Companies"        hint="All tenants · plans · members · edit" onClick={() => { setShowCompanies(true);  setAdminMenuOpen(false); }}/>
                    <MenuItem icon="backup" label="Database backups" hint="Manual + pause switch"                onClick={() => { setShowBackups(true);    setAdminMenuOpen(false); }}/>
                    <MenuItem icon="info"   label="System health"    hint="API · DB · queue · uptime"            onClick={() => { setShowSysHealth(true);  setAdminMenuOpen(false); }}/>
                  </React.Fragment>
                )}
              </Menu>
            )}
          </div>
        )}

        {/* Export */}
        <div className="kf-rel">
          <Btn kind="primary" size="sm" icon="download" iconRight="chevD" onClick={() => setExportMenuOpen(o => !o)}>Export</Btn>
          {exportMenuOpen && (
            <Menu align="right" onClose={() => setExportMenuOpen(false)} width={300}>
              <MenuLabel>Recommended</MenuLabel>
              <MenuItem icon="file"  label="Production Drawing"
                        hint="Combined PDF — formboard, wire table, breakout, flat list"
                        kbd="⌘⇧E"
                        onClick={() => { doExport('prod'); setExportMenuOpen(false); }}/>
              <MenuSep/>
              <MenuLabel>Manufacturing & QA</MenuLabel>
              <MenuItem icon="flask" label="Continuity Test Plan (PDF)"   hint="QA-ready · sign-off block" onClick={() => { doExport('cont'); setExportMenuOpen(false); }}/>
              <MenuItem icon="flask" label="Continuity Test Plan (XLSX)"  onClick={() => { doExport('cont-xlsx'); setExportMenuOpen(false); }}/>
              <MenuItem icon="file"  label="Komax / Schleuniger CSV"      hint="Wire-prep machines"        onClick={() => { doExport('komax'); setExportMenuOpen(false); }}/>
              <MenuItem icon="ok"    label="AS50881 Compliance Audit"     hint="Pass/Fail/Warn/Manual"     onClick={() => { doExport('as50881'); setExportMenuOpen(false); }}/>
              <MenuItem icon="ruler" label="Formboard DXF"                hint="AutoCAD / Fusion 360"      onClick={() => { doExport('dxf'); setExportMenuOpen(false); }}/>
              <MenuSep/>
              <MenuLabel>Tables</MenuLabel>
              <MenuItem icon="bom"   label="Wire Table (PDF)"             onClick={() => { doExport('wiretable'); setExportMenuOpen(false); }}/>
              <MenuItem icon="bom"   label="Wire Table (XLSX)"            onClick={() => { doExport('wiretable-xlsx'); setExportMenuOpen(false); }}/>
              <MenuItem icon="bom"   label="BOM (PDF)"                    onClick={() => { doExport('bom'); setExportMenuOpen(false); }}/>
              <MenuItem icon="bom"   label="BOM (XLSX)"                   onClick={() => { doExport('bom-xlsx'); setExportMenuOpen(false); }}/>
              <MenuItem icon="bom"   label="Combined (XLSX)"              onClick={() => { doExport('combined-xlsx'); setExportMenuOpen(false); }}/>
              <MenuSep/>
              <MenuLabel>Other</MenuLabel>
              <MenuItem icon="file"  label="Schematic PDF"                onClick={() => { doExport('schematic'); setExportMenuOpen(false); }}/>
              <MenuItem icon="file"  label="Wires CSV"                    onClick={() => { doExport('csv'); setExportMenuOpen(false); }}/>
              <MenuItem icon="file"  label=".kfx Exchange File"           onClick={() => { doExport('kfx'); setExportMenuOpen(false); }}/>
              <MenuItem icon="backup" label="All Files (.zip)"            onClick={() => { doExport('all-zip'); setExportMenuOpen(false); }}/>
            </Menu>
          )}
        </div>

        {/* User */}
        <div className="kf-rel">
          <button className="kf-user" onClick={() => setUserMenuOpen(o => !o)} aria-label="User menu">
            <span className="kf-user-avatar">{userInitials}</span>
            <div className="kf-user-text">
              <div className="kf-user-name">{userDisplayName}</div>
              <div className="kf-user-role">{userRoleLine}</div>
            </div>
          </button>
          {userMenuOpen && (
            <Menu align="right" onClose={() => setUserMenuOpen(false)} width={220}>
              <MenuItem icon="user" label="Profile"           onClick={() => { setTab('settings'); setUserMenuOpen(false); }}/>
              <MenuItem icon="settings" label="Settings"      onClick={() => { setTab('settings'); setUserMenuOpen(false); }}/>
              <MenuItem icon="keyboard" label="Keyboard shortcuts" kbd="?" onClick={() => { setTab('settings'); setUserMenuOpen(false); }}/>
              <MenuSep/>
              <MenuItem icon="x" label="Sign out" danger onClick={handleLogout}/>
            </Menu>
          )}
        </div>
      </header>

      {/* SIDEBAR */}
      <aside className="sidebar" aria-label="Workspace navigation">
        {!sidebarCollapsed && (
          <>
            {/* Tools nav — always visible. Project context lives in the topbar
                dropdown; the sidebar is purely about which tool you're using. */}
            <nav className="kf-side-nav" aria-label="Project sections" aria-disabled={!project}>
              <button className={`kf-side-nav-item ${tab === 'home' ? 'on' : ''} ${!project ? 'kf-side-nav-item-locked' : ''}`}
                      onClick={() => project ? setTab('home') : setTab('projects')}>
                <Icon name="grid" size={14}/> <span>Dashboard</span>
              </button>
              {TABS.map(g => (
                <div key={g.group} className="kf-side-nav-group">
                  <div className="kf-side-nav-group-label">{g.group}</div>
                  {g.items.map(it => (
                    <button key={it.k}
                            className={`kf-side-nav-item ${tab === it.k ? 'on' : ''} ${!project ? 'kf-side-nav-item-locked' : ''}`}
                            onClick={() => project ? setTab(it.k) : setTab('projects')}
                            title={!project ? 'Open a project first' : undefined}>
                      <Icon name={it.icon} size={14}/> <span>{it.l}</span>
                    </button>
                  ))}
                </div>
              ))}
            </nav>

            <div className="kf-side-foot">
              <button className="kf-side-collapse" onClick={() => setSidebarCollapsed(true)} aria-label="Collapse sidebar">
                <Icon name="chevL" size={12}/> <span>Collapse</span>
                <Kbd>⌘ \</Kbd>
              </button>
            </div>
          </>
        )}

        {sidebarCollapsed && (
          <>
            <div className="kf-side-mini">
              {project && <Tip label="Dashboard" side="right"><IconBtn icon="grid" label="Dashboard" active={tab === 'home'} onClick={() => setTab('home')}/></Tip>}
              {project && TABS.flatMap(g => g.items).map(it => (
                <Tip key={it.k} label={it.l} side="right">
                  <IconBtn icon={it.icon} label={it.l} active={tab === it.k} onClick={() => setTab(it.k)}/>
                </Tip>
              ))}
            </div>
            {/* Expand button — sits in the same .kf-side-foot slot as the
                Collapse button does in the expanded state, so the toggle
                lives in a single, stable place. */}
            <div className="kf-side-foot kf-side-foot-collapsed">
              <Tip label="Expand sidebar (⌘ \\)" side="right">
                <IconBtn icon="chevR" label="Expand sidebar" onClick={() => setSidebarCollapsed(false)}/>
              </Tip>
            </div>
          </>
        )}
      </aside>

      {/* MAIN */}
      <main className="main">
        {!project && tab !== 'projects' && tab !== 'settings' && tab !== 'library' && <WelcomeView onPickProject={() => setTab('projects')} onImportCsv={triggerCsvImport}/>}
        {tab === 'projects' && <ProjectsView projects={projects} currentId={pid}
                                             onOpen={(id) => { setPid(id); setTab('home'); }}
                                             onCreateNew={() => setShowNewProject(true)}
                                             onImportCsv={triggerCsvImport}/>}
        {project && tab === 'home'       && <ProjectHome project={project} onGoto={setTab}
                                                         realPid={realPid}
                                                         onExport={doExport}
                                                         wires={projectDetail?.wires || []}
                                                         connectors={projectDetail?.connectors || []}
                                                         nodes={projectDetail?.nodes || []}
                                                         bundles={projectDetail?.bundles || []}/>}
        {project && tab === 'layout'     && <LayoutView wireMode={wireMode} setWireMode={setWireMode} renderMode={t.renderMode} density={t.density}
                                                        project={project} toast={toast}
                                                        wires={projectDetail?.wires || []}
                                                        connectors={projectDetail?.connectors || []}
                                                        nodes={projectDetail?.nodes || []}
                                                        bundles={projectDetail?.bundles || []}
                                                        loading={projectLoading}/>}
        {project && tab === 'wires'      && <WiresView density={t.density}
                                                       realPid={realPid}
                                                       onRefresh={refreshProject}
                                                       onImportCsv={triggerCsvImport}
                                                       wires={projectDetail?.wires || []}
                                                       connectors={projectDetail?.connectors || []}
                                                       loading={projectLoading}/>}
        {project && tab === 'connectors' && <ConnectorsView connectors={projectDetail?.connectors || []}
                                                             realPid={realPid}
                                                             onRefresh={refreshProject}
                                                             wires={projectDetail?.wires || []}
                                                             loading={projectLoading}/>}
        {project && tab === 'bom'        && <BomView wires={projectDetail?.wires || []}
                                                     connectors={projectDetail?.connectors || []}
                                                     nodes={projectDetail?.nodes || []}
                                                     bundles={projectDetail?.bundles || []}
                                                     realPid={realPid}
                                                     onExport={doExport}/>}
        {project && tab === 'nodes'      && <NodesView nodes={projectDetail?.nodes || []}
                                                       realPid={realPid}
                                                       onRefresh={refreshProject}
                                                       connectors={projectDetail?.connectors || []}
                                                       wires={projectDetail?.wires || []}
                                                       loading={projectLoading}/>}
        {project && tab === 'notes'      && <NotesView project={project}/>}
        {tab === 'library'    && <LibraryView/>}
        {tab === 'settings'   && <SettingsView/>}
        {project && tab === 'projectsettings' && <ProjectSettingsView project={project}/>}

        {/* Admin slide-overs — role-gated. Designers/viewers can't open them
            even via deep link. Superadmin-only panels (Companies) get a
            stricter check. */}
        {showBackups    && isSuper && <BackupsPanel     onClose={() => setShowBackups(false)}/>}
        {showAuditLog   && isAdmin && <AuditLogPanel    onClose={() => setShowAuditLog(false)}/>}
        {showUsers      && isAdmin && <UsersPanel       onClose={() => setShowUsers(false)} me={me}/>}
        {showCompanies  && isSuper && <CompaniesPanel   onClose={() => setShowCompanies(false)}/>}
        {showShareLinks && isAdmin && <ShareLinksPanel  onClose={() => setShowShareLinks(false)} realPid={realPid} projectName={project ? project.name : ''}/>}
        {showSysHealth  && isSuper && <SystemHealthPanel onClose={() => setShowSysHealth(false)}/>}
        {showWorkspace  && isAdmin && <WorkspaceSettingsPanel onClose={() => setShowWorkspace(false)} me={me}/>}

        {/* New project modal */}
        {showNewProject && (
          <NewProjectModal
            onClose={() => setShowNewProject(false)}
            onCreated={async (created) => {
              const ps = await reloadProjects();
              setShowNewProject(false);
              const fresh = ps.find(p => p.realId === created.id);
              if (fresh) { setPid(fresh.id); setTab('home'); }
              toast.ok('Project created', created.name);
            }}/>
        )}

        {/* Hidden file input — used by every "Import CSV" button. */}
        <input ref={csvImportInputRef} type="file" accept=".csv,text/csv"
               style={{ display: 'none' }} onChange={handleCsvImportFile}/>
      </main>

      {/* STATUS BAR */}
      <footer className="statusbar" role="contentinfo">
        <span><span className="kf-status-dot kf-status-active"/> Connected · API 0.4.32</span>
        <span>·</span>
        <span>{project ? `${project.code} · Rev ${project.revision}` : 'No project'}</span>
        <span>·</span>
        <span>{wireMode ? 'Wire mode ON' : 'Idle'}</span>
        <div className="spacer"/>
        <span>Autosave on · last sync 4s ago</span>
        <span>·</span>
        <button className="kf-status-link" onClick={() => setPaletteOpen(true)}>⌘K</button>
      </footer>

      {/* Palette */}
      <Palette open={paletteOpen} onClose={() => setPaletteOpen(false)} onNav={onNav} project={project} projects={projects} isAdmin={isAdmin} isSuper={isSuper}/>
    </div>
  );
};

/* ---------- Placeholder for the views we didn't fully build ---------- */
const PlaceholderView = ({ title, icon, hint, cta }) => (
  <div className="kf-view">
    <ViewHeader title={title} actions={
      <>
        <Btn kind="ghost" icon="filter" size="sm">Filter</Btn>
        <Btn kind="primary" icon="plus">{cta}</Btn>
      </>
    }/>
    <div style={{ flex: 1, display: 'flex', alignItems: 'center' }}>
      <EmptyState
        icon={icon}
        title={`No ${title.toLowerCase()} yet`}
        hint={hint}
        action={<Btn kind="primary" icon="plus">{cta}</Btn>}
        secondary={<Btn kind="outline" icon="import_">Import</Btn>}/>
    </div>
  </div>
);

/* ---------- Backups slide-over — wired to /api/admin/backup* ---------- */
const BackupsPanel = ({ onClose }) => {
  const toast = useToast();
  const [backups, setBackups] = useState([]);
  const [loading, setLoading] = useState(true);
  const [paused, setPaused] = useState(false);     // backups_enabled === false
  const [savingPause, setSavingPause] = useState(false);
  const [confirm, setConfirm] = useState(null);
  const [importOpen, setImportOpen] = useState(false);

  const refresh = useCallback(async () => {
    try {
      const [list, settings] = await Promise.all([
        kfxListBackups(),
        kfxGetBackupSettings(),
      ]);
      setBackups(Array.isArray(list) ? list : []);
      setPaused(!settings.backups_enabled);
    } catch (e) {
      toast.err('Could not load backups', (e && e.message) || 'Unknown error');
    } finally {
      setLoading(false);
    }
  }, [toast]);

  useEffect(() => { refresh(); }, [refresh]);

  const togglePause = async () => {
    setSavingPause(true);
    try {
      const next = !paused;  // next = paused?
      const r = await kfxSetBackupSettings({ backups_enabled: !next });
      setPaused(!r.backups_enabled);
      toast.ok(r.backups_enabled ? 'Backups enabled' : 'Backups paused');
    } catch (e) {
      toast.err('Could not save', (e && e.message) || 'Unknown error');
    } finally {
      setSavingPause(false);
    }
  };

  const restoreOne = (b) => setConfirm({
    title: `Restore from ${b.filename}?`,
    body: <>Projects in this snapshot will be appended into the live database. Same-name projects get renamed (no overwrites).</>,
    confirmLabel: 'Restore',
    onConfirm: async () => {
      try {
        const r = await kfxRestoreBackup(b.filename);
        toast.ok('Restore complete', `${r.restored || 0} project(s) appended`);
        await refresh();
      } catch (e) {
        toast.err('Restore failed', (e && e.message) || 'Unknown error');
      }
    },
  });

  return (
    <div className="kf-slideover-bg fade-in" onClick={onClose} role="dialog" aria-modal="true" aria-label="Database backups">
      <div className="kf-slideover" onClick={e => e.stopPropagation()}>
        <div className="kf-slideover-head">
          <div>
            <h2>Database backups</h2>
            <p>Manual snapshots + automatic 3 AM job + pre-push git hook. All restorable.</p>
          </div>
          <IconBtn icon="x" label="Close backups panel" onClick={onClose}/>
        </div>

        {paused && (
          <div className="kf-banner kf-banner-warning" role="status" style={{ display:'flex', alignItems:'center', gap:8, padding:'10px 18px', background:'var(--warning-soft)', borderBottom:'1px solid rgba(245,183,58,.32)', color:'var(--warning)', fontSize:12 }}>
            <Icon name="warn" size={14}/>
            <span>Automatic + manual backups are <b>paused</b>. Existing snapshots remain restorable.</span>
          </div>
        )}

        <div className="kf-backup-toolbar">
          <AsyncBtn kind="primary" icon="backup" disabled={paused} onClick={async () => {
            try {
              const r = await kfxTriggerBackup();
              toast.ok('Snapshot created', r.filename || '');
              await refresh();
            } catch (e) {
              toast.err('Snapshot failed', (e && e.message) || 'Unknown error');
            }
          }} busyLabel="Snapshotting…">Backup now</AsyncBtn>
          <Btn kind="outline" icon="import_" onClick={() => setImportOpen(true)}>
            Import from legacy DB
          </Btn>
          <Chip
            tone={paused ? 'warning' : 'success'}
            size="lg" dot
            onClick={savingPause ? undefined : togglePause}>
            {paused ? 'Backups paused' : 'Backups ON'}
          </Chip>
          <div className="spacer"/>
          <span className="kf-toolbar-info num">{backups.length} snapshots</span>
        </div>

        <div className="kf-backup-list">
          {loading && <div style={{ padding: 20, color:'var(--t-3)', fontSize:12 }}>Loading…</div>}
          {!loading && backups.length === 0 && (
            <EmptyState compact icon="backup" title="No backups yet" hint="Click 'Backup now' to take the first snapshot."/>
          )}
          {!loading && backups.map(b => (
            <div key={b.filename} className="kf-backup-row">
              <div className="kf-backup-time num">{b.timestamp || b.filename}</div>
              <Chip size="sm" tone={b.label === 'manual' ? 'info' : b.label === 'pre-push' ? 'violet' : b.label === 'import' ? 'warning' : 'success'}>{b.label || 'auto'}</Chip>
              <div className="kf-backup-meta">{(b.size_kb || 0).toFixed ? b.size_kb.toFixed(1) : b.size_kb} KB</div>
              <div className="spacer"/>
              <Btn size="sm" kind="ghost" icon="refresh" onClick={() => restoreOne(b)}>Restore</Btn>
              <IconBtn icon="trash" label={`Delete backup ${b.filename}`} danger onClick={() => setConfirm({
                title: `Delete backup ${b.filename}?`,
                body: <>Deletion is permanent — no undo.</>,
                confirmLabel: 'Delete backup',
                danger: true,
                onConfirm: async () => {
                  try {
                    await kfxDeleteBackupFile(b.filename);
                    toast.ok('Backup deleted');
                    await refresh();
                  } catch (e) {
                    toast.err('Delete failed', (e && e.message) || 'Unknown error');
                  }
                },
              })}/>
            </div>
          ))}
        </div>

        {confirm && <ConfirmModal {...confirm} onClose={() => setConfirm(null)}/>}

        {importOpen && (
          <LegacyImportModal
            onClose={() => setImportOpen(false)}
            onImport={async (file) => {
              try {
                const r = await kfxImportLegacyDb(file);
                toast.ok('Legacy DB imported', `${r.restored || 0} project(s) appended`);
                setImportOpen(false);
                await refresh();
              } catch (e) {
                toast.err('Import failed', (e && e.message) || 'Unknown error');
              }
            }}/>
        )}
      </div>
    </div>
  );
};

/* ---------- Legacy-DB import modal ----------
   Wired version: picks a SQLite .db file and POSTs it to
   /api/admin/backup/import (multipart). Backend then runs the
   restore_backup_append flow. Other formats (.sql/.zip/.json) are
   queued for v5.1, surfaced via a hint. */
const LegacyImportModal = ({ onClose, onImport }) => {
  const [file, setFile] = useState(null);
  const [busy, setBusy] = useState(false);
  const onFilePick = (e) => {
    const f = e.target.files && e.target.files[0];
    if (f) setFile(f);
  };
  const submit = async () => {
    if (!file) return;
    setBusy(true);
    try { await onImport(file); }
    finally { setBusy(false); }
  };
  return (
    <Modal title="Import from legacy database" size="md" onClose={busy ? () => {} : onClose} footer={
      <>
        <Btn kind="ghost" onClick={onClose} disabled={busy}>Cancel</Btn>
        <Btn kind="primary" icon="check" disabled={!file || busy} busy={busy} onClick={submit}>
          {busy ? 'Importing…' : 'Run import'}
        </Btn>
      </>
    }>
      <div className="kf-form">
        <div className="kf-form-row">
          <div className="kf-field">
            <label>Database file (.db SQLite)</label>
            <div className="kf-ws-logo-drop">
              <Icon name="import_" size={20} stroke="var(--t-3)"/>
              <div>
                <div style={{ fontWeight: 600, color: 'var(--t-1)' }}>
                  {file ? file.name : 'Pick a SQLite .db file'}
                </div>
                <div className="kf-admin-muted">
                  {file
                    ? `${(file.size / 1024).toFixed(1)} KB — ready to import`
                    : 'Backend supports SQLite (.db) today. CSV / JSON / Postgres are queued for v5.1.'}
                </div>
              </div>
              <label className="kf-btn kf-btn-outline kf-btn-md" style={{ cursor: 'pointer' }}>
                <span className="kf-btn-label">{file ? 'Replace' : 'Browse files'}</span>
                <input type="file" accept=".db,.sqlite,.sqlite3"
                       style={{ display: 'none' }} onChange={onFilePick}/>
              </label>
            </div>
            <div className="kf-field-hint">
              A pre-import snapshot is taken automatically. Same-name projects get renamed (no overwrites).
            </div>
          </div>
        </div>
      </div>
    </Modal>
  );
};


/* ---------- New project modal ---------- */
const NewProjectModal = ({ onClose, onCreated }) => {
  const [name, setName] = useState('');
  const [harnessName, setHarnessName] = useState('');
  const [description, setDescription] = useState('');
  const toast = useToast();
  const canSave = name.trim().length > 0;
  return (
    <Modal title="New project" onClose={onClose} size="sm" footer={
      <>
        <Btn kind="ghost" onClick={onClose}>Cancel</Btn>
        <AsyncBtn kind="primary" icon="plus" disabled={!canSave} busyLabel="Creating…" onClick={async () => {
          if (!canSave) return;
          try {
            const created = await kfxCreateProject({
              name: name.trim(),
              harness_name: harnessName.trim() || name.trim(),
              description: description.trim(),
            });
            await onCreated(created);
          } catch (e) {
            toast.err('Could not create project', (e && e.message) || 'Unknown error');
          }
        }}>Create project</AsyncBtn>
      </>
    }>
      <div className="kf-form">
        <div className="kf-form-row">
          <div className="kf-field" style={{ flex: 1 }}>
            <label htmlFor="np-name">Project name <span className="kf-req">*</span></label>
            <input id="np-name" className="kf-input" autoFocus value={name}
                   onChange={e => setName(e.target.value)} placeholder="e.g. Mira-S Tail Boom"/>
          </div>
        </div>
        <div className="kf-form-row">
          <div className="kf-field" style={{ flex: 1 }}>
            <label htmlFor="np-harness">Harness name / part code</label>
            <input id="np-harness" className="kf-input num" value={harnessName}
                   onChange={e => setHarnessName(e.target.value)} placeholder="e.g. HA0000168"/>
          </div>
        </div>
        <div className="kf-form-row">
          <div className="kf-field" style={{ flex: 1 }}>
            <label htmlFor="np-desc">Description</label>
            <textarea id="np-desc" className="kf-input" rows="3" value={description}
                      onChange={e => setDescription(e.target.value)}
                      placeholder="What this harness is for, where it lives in the assembly…"/>
          </div>
        </div>
      </div>
    </Modal>
  );
};

/* ---------- App styles ---------- */
const APP_STYLE = `
.kf-brand { display: flex; align-items: center; gap: 10px; }
.kf-brand-text { line-height: 1.05; }
.kf-brand-name { font-family: var(--f-display); font-size: 15px; font-weight: 800; letter-spacing: -.3px; }
.kf-brand-sub  { font-size: 9.5px; color: var(--t-3); letter-spacing: 1.4px; text-transform: uppercase; font-weight: 600; }

/* ---------------------------------------------------------------
   Project switcher — single topbar control with a dropdown menu.
   --------------------------------------------------------------- */
.kf-psw { position: relative; margin-left: 14px; min-width: 0; }

.kf-psw-trigger {
  display: flex; align-items: center; gap: 10px;
  height: 36px; padding: 0 12px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  color: var(--t-1);
  font-family: inherit;
  cursor: pointer;
  transition: background .12s var(--ease), border-color .12s var(--ease), box-shadow .12s var(--ease);
  min-width: 0; max-width: 340px;
}
.kf-psw-trigger:hover { background: var(--bg-3); border-color: var(--line-strong); }
.kf-psw-trigger.on    { background: var(--bg-3); border-color: var(--line-strong); box-shadow: 0 0 0 3px var(--accent-soft); }
.kf-psw-trigger-text { display: flex; flex-direction: column; line-height: 1.1; min-width: 0; flex: 1; text-align: left; }
.kf-psw-trigger-code { font-family: var(--f-mono); font-size: 10.5px; font-weight: 500; color: var(--t-3); margin-bottom: 2px; }
.kf-psw-trigger-name { font-size: 13px; font-weight: 600; color: var(--t-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kf-psw-trigger-empty { color: var(--t-3); font-weight: 500; }

.kf-psw-menu {
  position: absolute; top: calc(100% + 6px); left: 0;
  min-width: 340px; max-width: 420px;
  background: var(--bg-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-3);
  box-shadow: 0 16px 48px -12px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.2);
  padding: 6px;
  z-index: 1100;
  animation: kfPswFade .12s var(--ease);
}
@keyframes kfPswFade {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

.kf-psw-item {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 9px 10px;
  background: transparent;
  border: none;
  border-radius: var(--r-2);
  color: var(--t-1);
  font-family: inherit; text-align: left;
  cursor: pointer;
  transition: background .1s var(--ease);
}
.kf-psw-item:hover { background: var(--bg-4); }
.kf-psw-item svg   { stroke: currentColor; flex-shrink: 0; }
.kf-psw-item-text  { flex: 1; min-width: 0; }
.kf-psw-item-title { font-size: 13px; font-weight: 600; color: var(--t-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kf-psw-item-meta  { font-size: 11px; color: var(--t-3); margin-top: 2px; }
.kf-psw-item-meta .num { font-family: var(--f-mono); }
.kf-psw-item-action .kf-psw-item-title { color: var(--t-1); }

.kf-psw-section {
  font-family: var(--f-mono); font-size: 10px; font-weight: 600;
  color: var(--t-3); text-transform: uppercase; letter-spacing: .08em;
  padding: 12px 10px 6px;
  border-top: 1px solid var(--line);
  margin-top: 4px;
}
.kf-psw-list { max-height: 320px; overflow-y: auto; }
.kf-psw-empty { padding: 10px; font-size: 12px; color: var(--t-3); text-align: center; }

@media (max-width: 720px) {
  .kf-psw-trigger { max-width: 200px; }
  .kf-psw-menu { min-width: 280px; }
}

.kf-project-pill {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  padding: 5px 12px 5px 10px;
  cursor: pointer; font-family: inherit;
  color: var(--t-1);
  height: 36px;
  transition: all .12s var(--ease);
  margin-left: 10px;
}
.kf-project-pill:hover { background: var(--bg-4); border-color: var(--line-strong); }
.kf-project-pill-text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.1; }
.kf-project-pill-code { font-size: 10px; color: var(--t-3); font-weight: 700; letter-spacing: .4px; }
.kf-project-pill-name { font-size: 12px; color: var(--t-1); font-weight: 600; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.kf-cmd-trigger {
  display: flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 12px;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  color: var(--t-3); font-family: inherit; font-size: 12px;
  cursor: pointer; flex: 1; max-width: 480px; min-width: 220px;
  transition: all .12s var(--ease);
}
.kf-cmd-trigger:hover { background: var(--bg-4); border-color: var(--line-strong); color: var(--t-1); }

.kf-rel { position: relative; display: inline-block; }

.kf-user {
  display: flex; align-items: center; gap: 8px;
  background: transparent; border: none; cursor: pointer; padding: 4px 8px;
  border-radius: var(--r-2);
  color: var(--t-1);
  flex-shrink: 0;
}
.kf-user:hover { background: var(--bg-3); }
.kf-user-avatar {
  width: 28px; height: 28px;
  background: linear-gradient(135deg, var(--info), var(--violet));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; font-size: 11px; font-weight: 700;
  flex-shrink: 0;
}
.kf-user-text { line-height: 1.1; text-align: left; }
.kf-user-name { font-size: 11.5px; font-weight: 600; }
.kf-user-role { font-size: 9.5px; color: var(--t-3); }
@media (max-width: 1280px) { .kf-user-text, .kf-brand-sub { display: none; } }
@media (max-width: 1080px) { .kf-cmd-trigger { min-width: 0; padding: 0 10px; } .kf-cmd-trigger > span { display: none; } .kf-project-pill-name { max-width: 160px; } }

/* — Sidebar — */
.kf-side-section { display: flex; flex-direction: column; }

/* Current-project strip shown above the tools nav. */
.kf-side-project-strip {
  display: flex; align-items: center; gap: 10px;
  width: calc(100% - 24px);
  margin: 4px 12px 6px;
  padding: 10px 12px;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  color: var(--t-1); text-align: left;
}
.kf-side-project-strip-text { flex: 1; min-width: 0; }
.kf-side-project-strip-name { font-size: 12.5px; font-weight: 600; color: var(--t-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kf-side-project-strip-meta { font-size: 10.5px; color: var(--t-3); margin-top: 2px; font-family: var(--f-mono); }

/* Empty-state variant of the strip (no project open) — clickable. */
.kf-side-project-strip-empty {
  border-style: dashed;
  background: transparent;
  cursor: pointer;
  font-family: inherit;
  transition: background .12s var(--ease), border-color .12s var(--ease);
}
.kf-side-project-strip-empty:hover { background: var(--bg-3); border-color: var(--line-strong); }
.kf-side-project-strip-empty .kf-side-project-strip-name { color: var(--t-2); }

/* Locked nav items — visible but indicate they need a project context. */
.kf-side-nav-item-locked { opacity: .5; }
.kf-side-nav-item-locked:hover { opacity: .85; }

.kf-side-empty-tools { padding: 16px 14px; display: flex; flex-direction: column; gap: 10px; align-items: stretch; flex: 1; }

.kf-side-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px 6px;
}
.kf-side-search {
  display: flex; align-items: center; gap: 6px;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  margin: 0 12px 8px;
  padding: 0 8px;
  height: 28px;
}
.kf-side-search input {
  flex: 1; background: transparent; border: none; outline: none;
  color: var(--t-1); font-size: 11.5px; font-family: var(--f-sans);
}
.kf-side-search input::placeholder { color: var(--t-4); }

.kf-side-projects { padding: 0 6px; flex: 1; overflow-y: auto; }
.kf-side-project {
  display: flex; align-items: flex-start; gap: 8px;
  width: 100%;
  padding: 7px 10px;
  background: transparent; border: none; cursor: pointer;
  color: var(--t-1); text-align: left;
  border-radius: var(--r-2);
  font-family: inherit;
  transition: background .12s var(--ease);
}
.kf-side-project:hover { background: var(--bg-3); }
.kf-side-project.on { background: var(--info-soft); }
.kf-side-project.on .kf-side-project-name { color: var(--info); }
.kf-side-project-status { padding-top: 5px; }
.kf-side-project-text { flex: 1; min-width: 0; }
.kf-side-project-name { font-size: 12.5px; font-weight: 600; color: var(--t-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kf-side-project-meta { font-size: 10.5px; color: var(--t-3); margin-top: 2px; font-family: var(--f-mono); }

.kf-status-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.kf-status-active   { background: var(--success); box-shadow: 0 0 0 3px var(--success-soft); }
.kf-status-review   { background: var(--warning); box-shadow: 0 0 0 3px var(--warning-soft); }
.kf-status-draft    { background: var(--t-3);     box-shadow: 0 0 0 3px rgba(128,137,160,.18); }
.kf-status-released { background: var(--info);    box-shadow: 0 0 0 3px var(--info-soft); }

.kf-side-nav {
  border-top: 1px solid var(--line);
  padding: 8px 6px;
  flex: 1; overflow-y: auto;
}
.kf-side-nav-group { margin-top: 4px; }
.kf-side-nav-group-label {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: 1px;
  color: var(--t-3); font-weight: 700;
  padding: 8px 10px 4px;
}
.kf-side-nav-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 7px 10px;
  background: transparent; border: none; cursor: pointer;
  font-family: inherit; font-size: 12.5px; font-weight: 500;
  color: var(--t-2); text-align: left;
  border-radius: var(--r-2);
}
.kf-side-nav-item:hover { background: var(--bg-3); color: var(--t-1); }
.kf-side-nav-item.on    { background: var(--info-soft); color: var(--info); font-weight: 600; }
.kf-side-nav-item.on svg { stroke: var(--info); }

.kf-side-foot { padding: 10px; border-top: 1px solid var(--line); }
/* Collapsed-state footer: same border, centered, only one button. */
.kf-side-foot-collapsed { display: flex; justify-content: center; padding: 8px; }
.kf-side-collapse {
  display: flex; align-items: center; gap: 8px;
  background: transparent; border: none; cursor: pointer;
  color: var(--t-3); font-size: 11px; font-family: inherit;
  padding: 6px 8px;
  border-radius: var(--r-2);
  width: 100%;
}
.kf-side-collapse:hover { background: var(--bg-3); color: var(--t-1); }
.kf-side-collapse span { flex: 1; text-align: left; }

.kf-side-mini { display: flex; flex-direction: column; gap: 4px; padding: 8px 4px; align-items: center; flex: 1; overflow-y: auto; }

/* — Status bar — */
.kf-status-link { background: transparent; border: none; color: var(--info); cursor: pointer; font-family: inherit; font-size: 11px; }
.kf-status-link:hover { color: var(--t-1); }

/* — Slide-over — */
.kf-slideover-bg {
  position: fixed; inset: 0;
  background: rgba(7,9,15,.6);
  backdrop-filter: blur(3px);
  z-index: var(--z-modal-backdrop);
  display: flex; justify-content: flex-end;
}
.kf-slideover {
  width: 580px; max-width: 100vw; height: 100%;
  background: var(--bg-2);
  border-left: 1px solid var(--line-strong);
  box-shadow: -20px 0 60px rgba(0,0,0,.6);
  display: flex; flex-direction: column;
  animation: slideoverIn .25s var(--ease);
}
@keyframes slideoverIn { from { transform: translateX(20px); opacity: 0; } to { transform: none; opacity: 1; } }

.kf-slideover-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--line);
}
.kf-slideover-head h2 { font-family: var(--f-display); font-size: 18px; font-weight: 700; }
.kf-slideover-head p  { font-size: 12px; color: var(--t-3); margin-top: 4px; max-width: 420px; }

.kf-backup-toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line);
}
.kf-backup-list { flex: 1; overflow-y: auto; padding: 6px 12px; }
.kf-backup-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 8px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
}
.kf-backup-row:last-child { border-bottom: none; }
.kf-backup-time { font-family: var(--f-mono); color: var(--t-2); min-width: 140px; }
.kf-backup-meta { color: var(--t-3); font-size: 11px; }

/* ---------------------------------------------------------------
   Boot / login / error full-page surfaces.
   Match the bundle's design language: bg-2 card, line border,
   --r-4 radius, IBM Plex.
   --------------------------------------------------------------- */
.kf-boot {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-1);
  font-family: var(--f-sans);
  padding: 24px;
}
.kf-boot-inner {
  display: flex; align-items: center; gap: 14px;
  color: var(--t-2); font-size: 13px;
}
.kf-boot-text { font-size: 13px; color: var(--t-3); }

.kf-boot-card {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-4);
  padding: 32px 28px;
  width: 100%; max-width: 420px;
  text-align: center;
  box-shadow: 0 16px 48px -12px rgba(0,0,0,.5);
}
.kf-boot-card h2 { font-family: var(--f-display); font-size: 18px; font-weight: 700; color: var(--t-1); }
.kf-boot-card p  { font-size: 13px; color: var(--t-3); margin: 4px 0 12px; }

.kf-login-card {
  display: flex; flex-direction: column; gap: 14px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-4);
  padding: 24px;
  width: 100%; max-width: 420px;
  box-shadow: 0 16px 48px -12px rgba(0,0,0,.5);
}
.kf-login-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.kf-login-brand-name { font-family: var(--f-display); font-size: 16px; font-weight: 800; letter-spacing: -.3px; color: var(--t-1); }
.kf-login-brand-sub  { font-size: 10px; color: var(--t-3); letter-spacing: 1.4px; text-transform: uppercase; font-weight: 600; }
.kf-login-title { font-family: var(--f-display); font-size: 20px; font-weight: 700; color: var(--t-1); margin: 6px 0 4px; }

.kf-login-field { display: flex; flex-direction: column; gap: 6px; }
.kf-login-field > span {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--t-3);
}
.kf-login-field input {
  height: 38px; padding: 0 12px;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  color: var(--t-1);
  font-family: var(--f-sans); font-size: 13px;
  outline: none;
  transition: border-color .12s var(--ease), box-shadow .12s var(--ease);
}
.kf-login-field input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.kf-login-field input:disabled { opacity: .55; cursor: not-allowed; }

.kf-login-err {
  font-size: 12px; color: var(--danger, #ef4e4e);
  background: rgba(239,78,78,.10);
  border: 1px solid rgba(239,78,78,.32);
  border-radius: var(--r-2);
  padding: 8px 10px;
}

.kf-login-foot { text-align: center; margin-top: 4px; }
.kf-login-link {
  font-size: 12px; color: var(--t-3); text-decoration: none;
  transition: color .12s var(--ease);
}
.kf-login-link:hover { color: var(--t-1); }
`;
document.head.appendChild(Object.assign(document.createElement('style'), { textContent: APP_STYLE }));

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<ToastProvider><App/></ToastProvider>);
