// App shell: top nav, sidebar, screen switcher, modals, theme persistence, mobile drawer.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#1d4ed8",
  "density": "comfy",
  "theme": "light",
  "showTopNav": true
}/*EDITMODE-END*/;

// =================== Theme persistence ===================
const THEME_KEY = "catalogue-maker.theme.v1";
const FONT_STACKS = {
  "Geist":          "'Geist', system-ui, sans-serif",
  "Inter":          "'Inter', system-ui, sans-serif",
  "IBM Plex Sans":  "'IBM Plex Sans', system-ui, sans-serif",
  "system":         "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
};

// Derive strong/soft shades from any hex color
function deriveAccentShades(hex) {
  // Strong = darker, Soft = very light tint
  const c = hex.replace("#", "");
  if (c.length !== 6) return { strong: hex, soft: "#eeefff" };
  const r = parseInt(c.slice(0,2), 16), g = parseInt(c.slice(2,4), 16), b = parseInt(c.slice(4,6), 16);
  const strong = `#${Math.max(0, Math.round(r*.75)).toString(16).padStart(2,"0")}${Math.max(0, Math.round(g*.75)).toString(16).padStart(2,"0")}${Math.max(0, Math.round(b*.75)).toString(16).padStart(2,"0")}`;
  const soft   = `#${Math.round(r + (255-r)*.92).toString(16).padStart(2,"0")}${Math.round(g + (255-g)*.92).toString(16).padStart(2,"0")}${Math.round(b + (255-b)*.92).toString(16).padStart(2,"0")}`;
  return { strong, soft };
}

function loadStoredTheme(defaults) {
  try {
    const raw = localStorage.getItem(THEME_KEY);
    if (!raw) return defaults;
    return { ...defaults, ...JSON.parse(raw) };
  } catch (e) { return defaults; }
}
function saveStoredTheme(theme) {
  try { localStorage.setItem(THEME_KEY, JSON.stringify(theme)); } catch (e) {}
}

function applyTheme(theme) {
  const root = document.documentElement;
  const { strong, soft } = deriveAccentShades(theme.accent);
  root.style.setProperty("--accent", theme.accent);
  root.style.setProperty("--accent-strong", strong);
  root.style.setProperty("--accent-soft", soft);
  root.style.setProperty("--r-md", theme.radius + "px");
  root.style.setProperty("--r-sm", Math.max(4, theme.radius - 4) + "px");
  root.style.setProperty("--r-lg", (theme.radius + 4) + "px");
  root.style.setProperty("--font", FONT_STACKS[theme.font] || FONT_STACKS["Geist"]);
  document.body.dataset.density = theme.density;
  document.body.dataset.theme = theme.theme;
  document.body.dataset.sidebar = theme.sidebar || "rail";
}

// =================== Data fixtures ===================
const initialData = () => ({
  user: { name: "Abhishek Sinha", email: "2bsinha@gmail.com", role: "SuperAdmin" },
  org: { name: "Demo Workspace", id: "org_demo" },
  currentProject: "Demo Workspace",
  projectOptions: ["Demo Workspace", "Sheetal Furniture", "Books Import", "Jewellery Pilot"],

  monthlySpend: 184.20,
  credits: 426,
  autoRecharge: true,
  autoRechargeThreshold: 100,
  autoRechargeAmount: 500,
  paymentMethod: { brand: "VISA", last4: "9015", exp: "08/27" },
  invoiceAddress: "Abhishek Sinha\n264–265 Doctor Annie Besant Road\nGround floor\nMumbai, Maharashtra 400030\nIndia",
  taxId: "22AAAAA0000A1Z5",
  taxIdType: "GSTIN",
  subscription: { plan: "Growth", status: "Active", includedRuns: 500, usedRuns: 163, renewal: "Jun 16, 2026" },
  creditLedger: { balance: 426, usedThisMonth: 163, reserved: 12, lastTopUp: "May 14, 2026" },

  invoices: [
    { id: "INV-2026-051", date: "May 14, 2026", desc: "Growth plan and script credits", amount: "$184.20", status: "Open" },
    { id: "INV-2026-042", date: "Apr 30, 2026", desc: "Catalogue extraction usage", amount: "$92.40", status: "Paid" },
    { id: "INV-2026-031", date: "Mar 31, 2026", desc: "Credit top-up", amount: "$300.00", status: "Paid" },
  ],

  projects: [
    { id: "p1", name: "Demo Workspace", default: true, current: true, created: "Feb 17, 2026", cost: 184.20, collab: 4 },
    { id: "p2", name: "Sheetal Furniture", created: "Apr 12, 2026", cost: 92.40, collab: 3 },
    { id: "p3", name: "Books Import", created: "May 02, 2026", cost: 28.18, collab: 2 },
  ],

  members: [
    { id: "m1", name: "Abhishek Sinha", email: "2bsinha@gmail.com", role: "SuperAdmin", lastActive: "Just now", status: "Active" },
    { id: "m2", name: "Priya Nair", email: "priya@cataloguemaker.dev", role: "AppManager", lastActive: "2h ago", status: "Active" },
    { id: "m3", name: "Karan Mehta", email: "karan@example.com", role: "Customer", lastActive: "Yesterday", status: "Active" },
    { id: "m4", name: "Sasha Volkov", email: "sasha@partner.io", role: "Customer", lastActive: "4 days", status: "Pending" },
  ],

  apiKeys: [
    { id: "k1", name: "backend-service", key: "cm-live-9aF3kQ2pLm8nXvR0wY1zT5bN", created: "Apr 12, 2026", lastUsed: "12 min ago", revealed: false },
    { id: "k2", name: "local-dev", key: "cm-test-3kL9pM5wT2vN8xR1qY7zB0sH", created: "Mar 28, 2026", lastUsed: "Yesterday", revealed: false },
  ],

  profile: { name: "Abhishek Sinha", email: "2bsinha@gmail.com", username: "2bsinha", tz: "Asia/Kolkata (GMT+5:30)" },

  spendByDay: [4, 8, 6, 12, 14, 9, 18, 21, 16, 19, 24, 28, 20, 18, 22, 26, 31, 34, 29, 27, 33, 36, 40, 38, 42, 44, 39, 41, 46, 50],
  spendByModel: [
    { model: "catalogue-extractor", requests: "163", tokens: "PDF pages", spend: 92.4 },
    { model: "price-list-normalizer", requests: "48", tokens: "Rows", spend: 28.18 },
    { model: "image-crop-review", requests: "22", tokens: "Images", spend: 18.72 },
  ],

  scripts: [
    { id: "catalogue-extractor", name: "Catalogue Extractor", version: "1.0.3", status: "Active", creditCost: 1, input: "PDF, image", output: "Excel, images, JSON", owner: "SuperAdmin", lastRun: "12 min ago" },
    { id: "price-list-normalizer", name: "Price List Normalizer", version: "0.4.1", status: "Active", creditCost: 1, input: "Excel, CSV", output: "Clean Excel", owner: "AppManager", lastRun: "1h ago" },
    { id: "visual-page-crop", name: "Visual Page Crop", version: "0.2.0", status: "Testing", creditCost: 2, input: "PDF", output: "Cropped images", owner: "SuperAdmin", lastRun: "Yesterday" },
  ],
  scriptVersions: [
    { id: "v1", script: "Catalogue Extractor", version: "1.0.3", status: "Active", packageId: "pkg_101", approvedBy: "Abhishek", date: "May 15, 2026" },
    { id: "v2", script: "Visual Page Crop", version: "0.2.0", status: "Testing", packageId: "pkg_102", approvedBy: "-", date: "May 16, 2026" },
  ],
  jobs: [
    { id: "job_6817", script: "Catalogue Extractor", org: "Sheetal Furniture", user: "Abhishek", status: "Succeeded", files: 1, artifacts: 164, credits: 26, created: "May 16, 2026 10:12", duration: "4m 18s", error: "" },
    { id: "job_6818", script: "Visual Page Crop", org: "Sheetal Furniture", user: "Priya", status: "Running", files: 1, artifacts: 42, credits: 12, created: "May 16, 2026 11:04", duration: "2m 05s", error: "" },
    { id: "job_6819", script: "Price List Normalizer", org: "Books Import", user: "Karan", status: "Queued", files: 2, artifacts: 0, credits: 0, created: "May 16, 2026 11:18", duration: "-", error: "" },
    { id: "job_6820", script: "Catalogue Extractor", org: "Jewellery Pilot", user: "Sasha", status: "Failed", files: 1, artifacts: 0, credits: 1, created: "May 15, 2026 18:22", duration: "38s", error: "Input file was encrypted" },
  ],
  artifacts: [
    { id: "art_1", jobId: "job_6817", name: "sheetal_catalogue.xlsx", type: "Excel", size: "418 KB", created: "May 16, 2026 10:16" },
    { id: "art_2", jobId: "job_6817", name: "extracted_images.zip", type: "Images", size: "84.1 MB", created: "May 16, 2026 10:16" },
    { id: "art_3", jobId: "job_6818", name: "visual_crops_preview.zip", type: "Images", size: "18.7 MB", created: "May 16, 2026 11:06" },
  ],
  packages: [
    { id: "pkg_101", name: "catalogue-extractor", version: "1.0.3", status: "Approved", uploadedBy: "Abhishek", uploadedAt: "May 15, 2026", tests: "Passed" },
    { id: "pkg_102", name: "visual-page-crop", version: "0.2.0", status: "Testing", uploadedBy: "Priya", uploadedAt: "May 16, 2026", tests: "Running" },
    { id: "pkg_103", name: "jewellery-normalizer", version: "0.1.0", status: "Validation failed", uploadedBy: "Karan", uploadedAt: "May 14, 2026", tests: "Blocked" },
  ],
  packageEvents: [
    { id: "e1", packageId: "pkg_102", event: "ZIP uploaded", at: "May 16, 2026 10:42", actor: "Priya" },
    { id: "e2", packageId: "pkg_102", event: "Manifest validated", at: "May 16, 2026 10:43", actor: "System" },
    { id: "e3", packageId: "pkg_102", event: "Container build started", at: "May 16, 2026 10:45", actor: "System" },
  ],
  analyticsByScript: [
    { id: "a1", script: "Catalogue Extractor", runs: 163, success: "94%", credits: 326, avgDuration: "4m 02s" },
    { id: "a2", script: "Price List Normalizer", runs: 48, success: "98%", credits: 48, avgDuration: "51s" },
    { id: "a3", script: "Visual Page Crop", runs: 22, success: "86%", credits: 44, avgDuration: "5m 22s" },
  ],
  usageAnalytics: { totalRuns: 233, successRate: "94%", creditsUsed: 418, storageGb: 7.8 },
  clients: [
    { id: "c1", name: "Sheetal Furniture", plan: "Growth", status: "Active", runs: 163, credits: 426, lastSeen: "Today" },
    { id: "c2", name: "Books Import", plan: "Starter", status: "Active", runs: 48, credits: 72, lastSeen: "Today" },
    { id: "c3", name: "Jewellery Pilot", plan: "Custom", status: "Support", runs: 22, credits: 310, lastSeen: "Yesterday" },
  ],
  supportQueue: [
    { id: "sup_1", client: "Jewellery Pilot", topic: "Encrypted PDF failed", priority: "High", status: "Open", owner: "AppManager" },
    { id: "sup_2", client: "Sheetal Furniture", topic: "Review cropped images", priority: "Medium", status: "Waiting", owner: "AI App Manager" },
  ],
  aiManagerTasks: [
    { id: "ai_1", area: "Marketing", task: "Draft onboarding email for Catalogue Extractor users", status: "Ready for review" },
    { id: "ai_2", area: "Sales", task: "Identify clients likely to need custom plan", status: "Queued" },
    { id: "ai_3", area: "Support", task: "Summarize failed jobs and suggested fixes", status: "Running" },
  ],

  files: [
    { id:"f1", name:"SOFA PRICELIST .pdf", purpose:"script input", size:"18.4 MB", created:"May 16, 2026" },
    { id:"f2", name:"LOUNGE PRICELIST.pdf", purpose:"script input", size:"24.1 MB", created:"May 15, 2026" },
    { id:"f3", name:"sheetal_catalogue.xlsx", purpose:"artifact", size:"418 KB", created:"May 16, 2026" },
  ],

  integrations: [
    { name:"Cloudflare R2", desc:"Store uploaded files and generated artifacts", color:"#f48120", connected:true },
    { name:"Stripe", desc:"Manage subscriptions, invoices, and credits", color:"#635bff", connected:false },
    { name:"Slack", desc:"Get alerts on failed jobs and support tickets", color:"#4a154b", connected:true },
  ],

  sshKeys: [
    { id:"s1", label:"abhishek-macbook",   fp:"SHA256:1a:f3:8b:c9:2e:74:5d:9c:b1:a8:6f:0d:e2:4a:9b:5c", added:"Feb 18, 2026" },
    { id:"s2", label:"abhishek-workstation",fp:"SHA256:8d:c2:91:7e:5a:b0:42:13:fc:8e:6a:9d:71:0c:42:38", added:"Mar 22, 2026" },
  ],
});

// Resolve icon-by-name from config to React component
const ICON_MAP = {
  IconUser, IconKey, IconPlug, IconUpload, IconFolder, IconCpu, IconBuilding,
  IconUsers, IconCard, IconChart, IconPalette, IconShield, IconList, IconWebhook,
  IconFile, IconQuestion, IconSettings, IconDownload,
};

// =================== App ===================
function App() {
  const cfg = window.APP_CONFIG;
  const [data, setData] = React.useState(initialData);
  // Expose the App data globally so the data layer can fall back to
  // it when an endpoint is in mock mode. Real apps would skip this.
  React.useEffect(() => { window.__appData = data; }, [data]);
  // Hash-based routing — URL drives state. Refresh/back/forward all work.
  const [route, routeParam, setHashRoute] = useHashRoute(cfg.initialRoute || "dashboard");
  const setRoute = React.useCallback((r, p) => setHashRoute(r, p), [setHashRoute]);
  const setRouteParam = (p) => setHashRoute(route, p);
  const [modal, setModal] = React.useState(null);
  const [modalCtx, setModalCtx] = React.useState(null);       // payload for the active modal
  const [drawerOpen, setDrawerOpen] = React.useState(false);
  const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false);
  const [paletteOpen, setPaletteOpen] = React.useState(false);
  const [apiFallback, setApiFallback] = React.useState(() => window.__apiFixtureFallback || null);
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Runtime theme — user-controlled, persisted in localStorage.
  const [theme, setThemeState] = React.useState(() => loadStoredTheme(cfg.defaultTheme));
  const setTheme = (patch) => setThemeState(prev => {
    const next = { ...prev, ...patch };
    saveStoredTheme(next);
    return next;
  });
  const resetTheme = () => { saveStoredTheme(cfg.defaultTheme); setThemeState({ ...cfg.defaultTheme }); window.__toast && window.__toast("Theme reset to defaults"); };

  // Apply theme on every change
  React.useEffect(() => { applyTheme(theme); }, [theme]);

  // Dev tweaks panel mirrors a subset to runtime theme
  React.useEffect(() => {
    setTheme({ accent: tweaks.accent, density: tweaks.density, theme: tweaks.theme });
  // eslint-disable-next-line
  }, [tweaks.accent, tweaks.density, tweaks.theme]);

  // Expose nav + edit triggers globally for inline links and row menus
  React.useEffect(() => {
    window.__nav = (r, p) => { setRoute(r, p); setDrawerOpen(false); };
    window.__editProject = (id) => { setRoute("edit-project", id); };
    window.__changeRole = (id) => { setModalCtx(data.members.find(m => m.id === id)); setModal("changeRole"); };
    window.__removeMember = (id) => { setModalCtx(data.members.find(m => m.id === id)); setModal("removeMember"); };
    window.__rotateKey = (id) => { setModalCtx(data.apiKeys.find(k => k.id === id)); setModal("rotateKey"); };
    window.__revokeKey = (id) => { setModalCtx(data.apiKeys.find(k => k.id === id)); setModal("revokeKey"); };
    window.__openPalette = () => setPaletteOpen(true);
  }, [data, setRoute]);

  // Global keyboard shortcut: cmd/ctrl + K opens the command palette
  React.useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setPaletteOpen(o => !o);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);
  // Close drawer on resize-up to desktop
  React.useEffect(() => {
    const onResize = () => { if (window.innerWidth >= 1024) setDrawerOpen(false); };
    window.addEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize);
  }, []);
  // Close drawer on route change
  React.useEffect(() => { setDrawerOpen(false); }, [route]);
  React.useEffect(() => {
    const onFallback = (event) => setApiFallback(event.detail);
    window.addEventListener("api-fixture-fallback", onFallback);
    return () => window.removeEventListener("api-fixture-fallback", onFallback);
  }, []);

  const openModal = (k) => setModal(k);
  const closeModal = () => setModal(null);

  const screen = (() => {
    switch (route) {
      // Dashboard / product (no sidebar)
      case "dashboard":      return <DashboardScreen data={data}/>;
      case "run-script":     return <RunScriptScreen data={data} setData={setData}/>;
      case "jobs":           return <JobsScreen data={data}/>;
      case "job-detail":     return <JobDetailScreen data={data} jobId={routeParam}/>;
      case "scripts":        return <ScriptsScreen data={data}/>;
      case "artifacts":      return <ArtifactsScreen data={data}/>;
      case "analytics":      return <AnalyticsScreen data={data}/>;
      case "admin":          return <AdminScreen data={data}/>;
      case "credits":        return <CreditsScreen data={data}/>;
      case "package-manager": return <PackageManagerScreen data={data}/>;
      case "script-versions": return <ScriptVersionsScreen data={data}/>;
      case "clients":        return <ClientsScreen data={data}/>;
      case "all-jobs":       return <JobsScreen data={data} admin/>;
      case "usage-analytics": return <AnalyticsScreen data={data} admin/>;
      case "support":        return <SupportScreen data={data}/>;
      case "ai-app-manager": return <AiAppManagerScreen data={data}/>;

      // Settings screens (with sidebar)
      case "billing":        return <BillingScreen data={data} setData={setData} openModal={openModal}/>;
      case "projects":       return <ClientsScreen data={data}/>;
      case "members":        return <MembersScreen data={data} openModal={openModal}/>;
      case "api-keys":       return <ApiKeysScreen data={data} setData={setData} openModal={openModal}/>;
      case "profile":        return <ProfileScreen data={data} setData={setData}/>;
      case "appearance":     return <AppearanceScreen theme={theme} setTheme={setTheme} resetTheme={resetTheme}/>;
      case "cookies":        return <CookiesScreen/>;
      case "org-general":    return <ClientsScreen data={data}/>;
      case "proj-general":   return <ScriptsScreen data={data}/>;
      case "cost-analytics": return <AnalyticsScreen data={data}/>;
      case "files":          return <ArtifactsScreen data={data}/>;
      case "integrations":   return <AdminScreen data={data}/>;
      case "ssh-key":        return <PackageManagerScreen data={data}/>;
      case "invoice-usage":  return <BillingScreen data={data} setData={setData} openModal={openModal}/>;
      // Create-flow pages
      case "new-project":    return <ClientsScreen data={data}/>;
      case "invite-member":  return <InviteMemberPage data={data} setData={setData}/>;
      case "new-api-key":    return <NewApiKeyPage data={data} setData={setData}/>;
      case "new-ssh-key":    return <PackageManagerScreen data={data}/>;
      case "upload-file":    return <RunScriptScreen data={data} setData={setData}/>;
      // Edit-flow pages
      case "edit-project":   return <ClientsScreen data={data}/>;
      // Auth pages — rendered without app shell (see early-return below)
      case "sign-in":          return <SignInPage/>;
      case "sign-up":          return <SignUpPage/>;
      case "forgot-password":  return <ForgotPasswordPage/>;
      case "check-email":      return <CheckEmailPage routeParam={routeParam}/>;
      case "reset-password":   return <ResetPasswordPage/>;
      case "verify-email":     return <VerifyEmailPage/>;
      case "two-factor":       return <TwoFactorPage/>;
      case "sso":              return <SsoSignInPage/>;
      default:               return <EmptyScreen route={route}/>;
    }
  })();

  // Auth routes (sign-in, sign-up, forgot-password, etc.) render WITHOUT the
  // app shell — no top nav, no sidebar, no modals, no command palette.
  const isAuthRoute = window.AUTH_ROUTES && window.AUTH_ROUTES.has(route);
  if (isAuthRoute) {
    return (
      <div className="hx-app hx-app-auth" data-screen-label={`Auth / ${route}`}>
        <ErrorBoundary key={route}>{screen}</ErrorBoundary>
        <ToastHost/>
        <AppStyles/>
      </div>
    );
  }

  // Breadcrumb chain for current route
  const findItem = (id) => {
    for (const g of cfg.sidebar) {
      const it = g.items.find(i => i.id === id);
      if (it) return { item: it, group: g.group };
    }
    return null;
  };
  const found = findItem(route);
  // Categorize route
  const isProductRoute = cfg.topNav.some(n => n.id === route);
  const usesSidebar = true;
  const isSettingsRoute = usesSidebar || !!found || [
    "new-project","invite-member","new-api-key","new-ssh-key","upload-file",
    "edit-project","invoice-usage","appearance","cookies"
  ].includes(route);
  // Parent map for create/edit pages → settings group
  const PARENT_MAP = {
    "new-project": "projects", "edit-project": "projects",
    "invite-member": "members",
    "new-api-key": "api-keys",
    "new-ssh-key": "ssh-key",
    "upload-file": "run-script",
    "invoice-usage": "billing",
  };
  const parentRoute = PARENT_MAP[route];
  const parentFound = parentRoute ? findItem(parentRoute) : null;
  const crumbs = isProductRoute
    ? [{ label: cfg.brand.productName }, { label: cfg.topNav.find(n => n.id === route)?.label || route }]
    : found
      ? [{ label: "Settings" }, { label: data.org.name }, { label: found.item.label }]
      : parentFound
        ? [{ label: "Settings" }, { label: data.org.name }, { label: parentFound.item.label, route: parentRoute }, { label: route.startsWith("edit") ? "Edit" : "New" }]
        : [{ label: "Settings" }];

  return (
    <div className="hx-app">
      <TopNav data={data} setData={setData} onMenuClick={() => setDrawerOpen(o => !o)} drawerOpen={drawerOpen} currentRoute={route} showHamburger={isSettingsRoute} showLinks={tweaks.showTopNav} openPalette={() => setPaletteOpen(true)}/>

      <div className="hx-shell" data-shell="with-sidebar" data-collapsed={sidebarCollapsed ? "true" : "false"}>
        <Sidebar data={data} route={route} onNav={(r) => { setRoute(r); setDrawerOpen(false); }} drawerOpen={drawerOpen} onClose={() => setDrawerOpen(false)} collapsed={sidebarCollapsed} onToggleCollapsed={() => setSidebarCollapsed(v => !v)}/>

        <main className="hx-main" data-screen-label={isProductRoute ? `Product / ${cfg.topNav.find(n => n.id === route)?.label}` : `Settings / ${found?.item.label || route}`}>
          <div className="hx-main-inner">
            {crumbs.length > 1 && <Crumbs items={crumbs} onClick={(it)=> it.route && setRoute(it.route)}/>}
            {apiFallback && (
              <Banner tone="warn" icon={<IconInfo size={14}/>}>
                Development fixture fallback is active for {apiFallback.name}. Reason: {apiFallback.reason}.
              </Banner>
            )}
            <ErrorBoundary key={route}>{screen}</ErrorBoundary>
          </div>
        </main>
      </div>

      {/* Modals */}
      <AddCreditsModal open={modal==="addCredits"} onClose={closeModal}
        onConfirm={async (amt) => {
          try {
            await window.api.call("addCredits", { id: data.org.id }, { amount: amt, reason: "Manual admin credit adjustment" });
            setData(d => ({...d, credits: d.credits + amt, creditLedger: {...d.creditLedger, balance: d.creditLedger.balance + amt} }));
            window.__toast(`Requested ${amt} credit adjustment`);
          } catch (e) {
            window.__toast(e.message || "Credit API failed", "warn");
          }
        }}/>
      <EditAddressModal open={modal==="editAddress"} onClose={closeModal} value={data.invoiceAddress}
        onConfirm={(v) => { setData(d => ({...d, invoiceAddress: v})); window.__toast("Invoice address updated"); }}/>
      <EditPaymentModal open={modal==="editPayment"} onClose={closeModal}
        onConfirm={() => window.__toast("Payment method updated")}/>
      <EditTaxModal open={modal==="editTax"} onClose={closeModal}
        onConfirm={({type, val}) => { setData(d => ({...d, taxId: val, taxIdType: type})); window.__toast("Tax ID saved"); }}/>
      <AutoRechargeModal open={modal==="autoRecharge"} onClose={closeModal}
        value={{threshold: data.autoRechargeThreshold, amount: data.autoRechargeAmount}}
        onConfirm={({threshold,amount}) => { setData(d => ({...d, autoRechargeThreshold: threshold, autoRechargeAmount: amount, autoRecharge: true})); window.__toast("Thresholds saved"); }}/>

      {/* Edit modals */}
      <ChangeRoleModal open={modal==="changeRole"} onClose={closeModal} member={modalCtx}
        onConfirm={async (role) => {
          try {
            await window.api.call("updateMemberRole", { id: data.org.id, memberId: modalCtx.id }, { roleKey: role });
            setData(d => ({...d, members: d.members.map(m => m.id === modalCtx.id ? {...m, role} : m)}));
            window.__toast(`${modalCtx.name} is now ${role}`);
          } catch (e) {
            window.__toast(e.message || "Member update failed", "warn");
          }
        }}/>
      <RemoveMemberModal open={modal==="removeMember"} onClose={closeModal} member={modalCtx}
        onConfirm={async () => {
          try {
            await window.api.call("removeMember", { id: data.org.id, memberId: modalCtx.id });
            setData(d => ({...d, members: d.members.filter(m => m.id !== modalCtx.id)}));
            window.__toast(`${modalCtx.name} removed`);
          } catch (e) {
            window.__toast(e.message || "Member removal failed", "warn");
          }
        }}/>
      <RotateKeyModal open={modal==="rotateKey"} onClose={closeModal} apiKey={modalCtx}
        onConfirm={async () => {
          let newKey = `cm-live-${Math.random().toString(36).slice(2,12)}${Math.random().toString(36).slice(2,12)}`;
          try {
            const result = await window.api.call("rotateApiKey", { id: data.org.id, keyId: modalCtx.id }, {});
            newKey = result?.apiKey?.key || result?.key || newKey;
          } catch (e) {
            window.__toast(e.message || "Key rotation failed", "warn");
            return;
          }
          setData(d => ({...d, apiKeys: d.apiKeys.map(k => k.id === modalCtx.id ? {...k, key: newKey, revealed: true, lastUsed: "—"} : k)}));
          setModalCtx({ ...modalCtx, key: newKey });
          setTimeout(() => setModal("keyRotated"), 100);
        }}/>
      <KeyRotatedModal open={modal==="keyRotated"} onClose={closeModal} newKey={modalCtx?.key}/>
      <RevokeKeyModal open={modal==="revokeKey"} onClose={closeModal} apiKey={modalCtx}
        onConfirm={async () => {
          try {
            await window.api.call("revokeApiKey", { id: data.org.id, keyId: modalCtx.id });
            setData(d => ({...d, apiKeys: d.apiKeys.filter(k => k.id !== modalCtx.id)}));
            window.__toast(`Key "${modalCtx.name}" revoked`);
          } catch (e) {
            window.__toast(e.message || "Key revoke failed", "warn");
          }
        }}/>

      {/* Toasts */}
      <ToastHost/>

      {/* Cookie consent banner (first visit) */}
      <CookieBanner/>

      {/* Command palette (cmd/ctrl + K) */}
      <CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} data={data} cfg={cfg}/>

      {/* Dev Tweaks panel */}
      <TweaksPanel title="Tweaks">
        <TweakSection title="Quick theme">
          <TweakRadio label="Mode" value={tweaks.theme} onChange={(v)=>setTweak("theme", v)} options={[{label:"Light", value:"light"},{label:"Dark", value:"dark"}]} />
          <TweakColor label="Accent" value={tweaks.accent} onChange={(v)=>setTweak("accent", v)} options={["#1d4ed8","#0f766e","#7c3aed","#b45309","#be123c","#0b0d12"]} />
          <TweakSelect label="Density" value={tweaks.density} onChange={(v)=>setTweak("density", v)} options={[{label:"Compact", value:"compact"},{label:"Comfortable", value:"comfy"},{label:"Airy", value:"airy"}]} />
        </TweakSection>
        <TweakSection title="Layout">
          <TweakToggle label="Show menu items" value={tweaks.showTopNav} onChange={(v)=>setTweak("showTopNav", v)} />
        </TweakSection>
        <TweakSection title="Try it">
          <TweakButton label="Open Appearance settings" onClick={()=>setRoute("appearance")} />
          <TweakButton label="Open Add Credits" onClick={()=>setModal("addCredits")} />
        </TweakSection>
        <TweakSection title="Auth pages">
          <TweakButton label="Sign in" onClick={()=>setRoute("sign-in")} />
          <TweakButton label="Sign up" onClick={()=>setRoute("sign-up")} />
          <TweakButton label="Forgot password" onClick={()=>setRoute("forgot-password")} />
          <TweakButton label="Reset password" onClick={()=>setRoute("reset-password")} />
          <TweakButton label="Verify email (code)" onClick={()=>setRoute("verify-email")} />
          <TweakButton label="Two-factor (2FA)" onClick={()=>setRoute("two-factor")} />
          <TweakButton label="SSO sign-in" onClick={()=>setRoute("sso")} />
        </TweakSection>
      </TweaksPanel>

      <AppStyles/>
    </div>
  );
}

// =================== TopNav ===================
const TopNav = ({ data, setData, onMenuClick, drawerOpen, currentRoute, showHamburger, showLinks }) => {
  const cfg = window.APP_CONFIG;
  const [open, setOpen] = React.useState(false);

  return (
    <header className="hx-topnav">
      <div className="hx-topnav-left">
        {showHamburger && (
          <button className="hx-hamburger" onClick={onMenuClick} aria-label="Toggle menu" aria-expanded={drawerOpen}>
            <IconMenu size={18}/>
          </button>
        )}
        <a className="hx-brand" href="#" onClick={(e)=>e.preventDefault()}>
          {cfg.brand.showLogo && <Logo size={26}/>}
          <span className="hx-brand-name">{cfg.brand.name}{cfg.brand.suffix && <span className="hx-brand-dot">{cfg.brand.suffix}</span>}</span>
        </a>
        <button className="hx-project-pick" onClick={()=>setOpen(o=>!o)}>
          <span className="hx-pp-label">{data.currentProject}</span>
          <IconChevD size={14}/>
          {open && (
            <div className="hx-pp-menu" onClick={e=>e.stopPropagation()}>
              {data.projectOptions.map(p => (
                <button key={p} className={`hx-pp-item ${p===data.currentProject ? "on": ""}`} onClick={()=>{ setData(d=>({...d, currentProject:p})); setOpen(false); }}>
                  <IconFolder size={13}/>{p}
                  {p===data.currentProject && <IconCheck size={13} style={{marginLeft:"auto"}}/>}
                </button>
              ))}
              <div className="hx-pp-sep"/>
              <button className="hx-pp-item" onClick={()=>{ setOpen(false); window.__nav("clients"); }}>
                <IconPlus size={13}/>Manage workspaces
              </button>
            </div>
          )}
        </button>
      </div>

      {showLinks && (
        <nav className="hx-topnav-mid">
          {cfg.topNav.map((entry) => {
            const active = entry.id === currentRoute;
            return (
              <a key={entry.id} href="#"
                className={`hx-topnav-link ${active ? "on" : ""}`}
                onClick={(e) => { e.preventDefault(); window.__nav(entry.id); }}>
                {entry.label}
                {entry.caret && <IconChevD size={12}/>}
              </a>
            );
          })}
        </nav>
      )}
      {!showLinks && <div className="hx-topnav-spacer"/>}

      <div className="hx-topnav-right">
        <Tooltip content={<>Search <kbd className="hx-kbd">⌘K</kbd></>}>
          <button className="hx-tn-icon hx-tn-search" aria-label="Search" onClick={() => window.__openPalette()}>
            <IconSearch size={16}/>
          </button>
        </Tooltip>
        <Tooltip content="Help">
          <a href="#" className="hx-tn-icon hx-tn-hidesm" aria-label="Help" onClick={(e)=>e.preventDefault()}><IconQuestion size={16}/></a>
        </Tooltip>
        <NotificationsDropdown/>
        <AvatarDropdown data={data}/>
      </div>
    </header>
  );
};

// =================== Notifications dropdown ===================
const NotificationsDropdown = () => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef();
  const [items, setItems] = React.useState([
    { id: "n1", icon: IconCheck,   title: "Job completed",          body: "SOFA PRICELIST extraction finished", at: "12 min ago", unread: true },
    { id: "n2", icon: IconUpload,  title: "Package testing",        body: "visual-page-crop build is running", at: "2 hr ago", unread: true },
    { id: "n3", icon: IconCard,    title: "Credits updated",        body: "Growth plan balance refreshed", at: "Yesterday", unread: false },
    { id: "n4", icon: IconChart,   title: "Usage alert",            body: "Sheetal Furniture used 80% of monthly runs", at: "Mar 28", unread: false },
  ]);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("click", onDoc);
    return () => document.removeEventListener("click", onDoc);
  }, [open]);
  const unread = items.filter(i => i.unread).length;
  const markAllRead = () => setItems(items.map(i => ({ ...i, unread: false })));
  return (
    <div className="hx-dd" ref={ref}>
      <button className="hx-tn-icon" aria-label="Notifications" onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}>
        <IconBell size={16}/>
        {unread > 0 && <span className="hx-dot"/>}
      </button>
      {open && (
        <div className="hx-dd-menu hx-notif-menu" onClick={e => e.stopPropagation()}>
          <div className="hx-dd-head">
            <span style={{fontWeight:600,fontSize:14}}>Notifications</span>
            {unread > 0 && <button className="hx-link-btn" onClick={markAllRead}>Mark all read</button>}
          </div>
          <div className="hx-notif-list">
            {items.length === 0 ? <EmptyState icon={<IconBell size={32}/>} title="All caught up" description="No new notifications."/> :
              items.map(n => {
                const I = n.icon;
                return (
                  <button key={n.id} className={`hx-notif-row ${n.unread ? "unread" : ""}`} onClick={() => setItems(items.map(i => i.id === n.id ? { ...i, unread: false } : i))}>
                    <span className="hx-notif-icon"><I size={14}/></span>
                    <span className="hx-notif-meta">
                      <span className="hx-notif-title">{n.title}</span>
                      <span className="hx-notif-body">{n.body}</span>
                      <span className="hx-notif-time">{n.at}</span>
                    </span>
                    {n.unread && <span className="hx-notif-dot"/>}
                  </button>
                );
              })
            }
          </div>
          <div className="hx-dd-foot">
            <button className="hx-link-btn" onClick={() => { setOpen(false); window.__nav("dashboard"); }}>View all activity</button>
          </div>
        </div>
      )}
    </div>
  );
};

// =================== Avatar dropdown ===================
const AvatarDropdown = ({ data }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef();
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("click", onDoc);
    return () => document.removeEventListener("click", onDoc);
  }, [open]);
  const go = (r) => { setOpen(false); window.__nav(r); };
  return (
    <div className="hx-dd" ref={ref}>
      <button className="hx-avatar-btn" aria-label="Account menu" onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}>
        <Avatar name={data.user.name} size={30}/>
      </button>
      {open && (
        <div className="hx-dd-menu hx-avatar-menu" onClick={e => e.stopPropagation()}>
          <div className="hx-dd-user">
            <Avatar name={data.user.name} size={40}/>
            <div style={{minWidth:0}}>
              <div style={{fontWeight:600,fontSize:14,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}}>{data.user.name}</div>
              <div style={{color:"var(--muted)",fontSize:12,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}}>{data.user.email}</div>
            </div>
          </div>
          <div className="hx-dd-sep"/>
          <button className="hx-dd-item" onClick={() => go("profile")}><IconUser size={14}/>Profile</button>
          <button className="hx-dd-item" onClick={() => go("appearance")}><IconPalette size={14}/>Appearance</button>
          <button className="hx-dd-item" onClick={() => go("billing")}><IconCard size={14}/>Billing</button>
          <button className="hx-dd-item" onClick={() => go("cookies")}><IconShield size={14}/>Cookies &amp; Privacy</button>
          <div className="hx-dd-sep"/>
          <button className="hx-dd-item" onClick={() => window.__openPalette()}><IconCommand size={14}/>Command palette<span className="hx-kbd" style={{marginLeft:"auto"}}>⌘K</span></button>
          <a className="hx-dd-item" href="#" onClick={(e)=>e.preventDefault()}><IconQuestion size={14}/>Help &amp; documentation</a>
          <div className="hx-dd-sep"/>
          <button className="hx-dd-item danger" onClick={() => { setOpen(false); window.__toast("Signed out"); window.__nav("sign-in"); }}><IconLogout size={14}/>Sign out</button>
        </div>
      )}
    </div>
  );
};

// =================== Command Palette (cmd+K) ===================
const CommandPalette = ({ open, onClose, data, cfg }) => {
  const [q, setQ] = React.useState("");
  const [active, setActive] = React.useState(0);
  const inputRef = React.useRef();
  React.useEffect(() => { if (open) { setQ(""); setActive(0); setTimeout(() => inputRef.current?.focus(), 30); } }, [open]);
  // Build full searchable index
  const all = React.useMemo(() => {
    const items = [];
    cfg.topNav.forEach(n => items.push({ id: "nav-"+n.id, label: n.label, group: "Navigation", icon: IconChevR, action: () => window.__nav(n.id) }));
    cfg.sidebar.forEach(g => g.items.forEach(it => items.push({ id: "set-"+it.id, label: it.label, group: g.group, icon: ICON_MAP[it.icon] || IconFolder, action: () => window.__nav(it.id) })));
    items.push({ id: "act-1", label: "Run script", group: "Actions", icon: IconPlus, action: () => window.__nav("run-script") });
    items.push({ id: "act-2", label: "Open jobs", group: "Actions", icon: IconList, action: () => window.__nav("jobs") });
    items.push({ id: "act-3", label: "Upload package", group: "Actions", icon: IconUpload, action: () => window.__nav("package-manager") });
    items.push({ id: "act-4", label: "Invite member", group: "Actions", icon: IconUsers, action: () => window.__nav("invite-member") });
    // Auth pages — discoverable via cmd+K too
    items.push({ id: "auth-1", label: "Sign in",         group: "Auth pages", icon: IconLogout, action: () => window.__nav("sign-in") });
    items.push({ id: "auth-2", label: "Sign up",         group: "Auth pages", icon: IconPlus,   action: () => window.__nav("sign-up") });
    items.push({ id: "auth-3", label: "Forgot password", group: "Auth pages", icon: IconKey,    action: () => window.__nav("forgot-password") });
    items.push({ id: "auth-4", label: "Reset password",  group: "Auth pages", icon: IconKey,    action: () => window.__nav("reset-password") });
    items.push({ id: "auth-5", label: "Verify email",    group: "Auth pages", icon: IconShield, action: () => window.__nav("verify-email") });
    items.push({ id: "auth-6", label: "Two-factor (2FA)",group: "Auth pages", icon: IconShield, action: () => window.__nav("two-factor") });
    items.push({ id: "auth-7", label: "SSO sign-in",     group: "Auth pages", icon: IconKey,    action: () => window.__nav("sso") });
    data.clients.forEach(c => items.push({ id: "c-"+c.id, label: c.name, sub: c.plan, group: "Clients", icon: IconBuilding, action: () => window.__nav("clients") }));
    data.members.forEach(m => items.push({ id: "m-"+m.id, label: m.name, sub: m.email, group: "Members", icon: IconUser, action: () => window.__nav("members") }));
    return items;
  }, [data, cfg]);
  const filtered = q.trim() === "" ? all.slice(0, 12) : all.filter(i => (i.label + " " + (i.sub||"")).toLowerCase().includes(q.toLowerCase())).slice(0, 16);
  React.useEffect(() => { setActive(0); }, [q]);
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => {
      if (e.key === "Escape") onClose();
      if (e.key === "ArrowDown") { e.preventDefault(); setActive(a => Math.min(filtered.length - 1, a + 1)); }
      if (e.key === "ArrowUp")   { e.preventDefault(); setActive(a => Math.max(0, a - 1)); }
      if (e.key === "Enter")     { e.preventDefault(); const it = filtered[active]; if (it) { it.action(); onClose(); } }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, filtered, active, onClose]);
  if (!open) return null;
  // Group by group label
  const grouped = filtered.reduce((acc, it) => { (acc[it.group] = acc[it.group] || []).push(it); return acc; }, {});
  let cursor = 0;
  return (
    <div className="hx-pal-overlay" onClick={onClose}>
      <div className="hx-pal" onClick={e => e.stopPropagation()}>
        <div className="hx-pal-input">
          <IconSearch size={16}/>
          <input ref={inputRef} value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search pages, actions, clients, members..."/>
          <kbd className="hx-kbd">esc</kbd>
        </div>
        <div className="hx-pal-list">
          {filtered.length === 0 ? (
            <div className="hx-pal-empty">No results for "{q}"</div>
          ) : Object.entries(grouped).map(([group, items]) => (
            <div key={group} className="hx-pal-group">
              <div className="hx-pal-group-label">{group}</div>
              {items.map(it => {
                const idx = cursor++;
                const I = it.icon;
                return (
                  <button key={it.id} className={`hx-pal-item ${idx === active ? "on" : ""}`}
                    onMouseEnter={() => setActive(idx)}
                    onClick={() => { it.action(); onClose(); }}>
                    <span className="hx-pal-icon"><I size={14}/></span>
                    <span className="hx-pal-label">{it.label}</span>
                    {it.sub && <span className="hx-pal-sub">{it.sub}</span>}
                    <IconChevR size={12} className="hx-pal-chev"/>
                  </button>
                );
              })}
            </div>
          ))}
        </div>
        <div className="hx-pal-foot">
          <span><kbd className="hx-kbd">↑↓</kbd> navigate</span>
          <span><kbd className="hx-kbd">↵</kbd> select</span>
          <span><kbd className="hx-kbd">esc</kbd> close</span>
        </div>
      </div>
      <style>{`
        .hx-pal-overlay { position: fixed; inset: 0; background: rgba(13,17,28,.45); z-index: 95; display: flex; align-items: flex-start; justify-content: center; padding: 80px 16px 16px; animation: hxfade .12s ease both; }
        .hx-pal { background: var(--panel); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow-modal); width: 100%; max-width: 600px; max-height: calc(100vh - 120px); display: flex; flex-direction: column; overflow: hidden; animation: hxpop .14s cubic-bezier(.2,.9,.3,1.2) both; }
        .hx-pal-input { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line-2); color: var(--muted); }
        .hx-pal-input input { flex: 1; border: none; outline: none; background: transparent; font: inherit; font-size: 15px; color: var(--ink); }
        .hx-pal-list { overflow-y: auto; padding: 6px; flex: 1; }
        .hx-pal-empty { padding: 40px 16px; text-align: center; color: var(--muted); font-size: 13.5px; }
        .hx-pal-group { padding: 4px 0; }
        .hx-pal-group-label { padding: 6px 10px 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 600; }
        .hx-pal-item { display: grid; grid-template-columns: auto 1fr auto auto; gap: 10px; align-items: center; padding: 9px 10px; width: 100%; background: transparent; border: none; cursor: pointer; border-radius: 8px; font: inherit; text-align: left; color: var(--ink); }
        .hx-pal-item.on { background: var(--hover); }
        .hx-pal-icon { color: var(--muted); display: inline-flex; }
        .hx-pal-item.on .hx-pal-icon { color: var(--accent); }
        .hx-pal-label { font-size: 13.5px; }
        .hx-pal-sub { font-size: 12px; color: var(--muted); }
        .hx-pal-chev { color: var(--muted-2); }
        .hx-pal-foot { padding: 10px 16px; border-top: 1px solid var(--line-2); display: flex; gap: 14px; font-size: 11.5px; color: var(--muted); background: var(--panel-2); }
        .hx-kbd { font-family: var(--mono); font-size: 11px; padding: 1px 5px; border-radius: 4px; background: var(--line-2); color: var(--ink-2); border: 1px solid var(--line); }
      `}</style>
    </div>
  );
};

// =================== Sidebar ===================
const Sidebar = ({ data, route, onNav, drawerOpen, onClose, collapsed, onToggleCollapsed }) => {
  const cfg = window.APP_CONFIG;
  const role = data.user.role || "Customer";
  const groups = cfg.sidebar.filter(g => !g.roles || g.roles.includes(role));
  return (
    <>
      {drawerOpen && <div className="hx-drawer-backdrop" onClick={onClose}/>}
      <aside className={`hx-sidebar ${drawerOpen ? "hx-sidebar-open" : ""} ${collapsed ? "hx-sidebar-collapsed" : ""}`}>
        <div className="hx-sidebar-close-row">
          <button className="hx-sidebar-close" onClick={onClose} aria-label="Close menu"><IconClose size={16}/></button>
        </div>
        <div className="hx-sidebar-tools">
          <button className="hx-sidebar-collapse" onClick={onToggleCollapsed} aria-label={collapsed ? "Expand menu" : "Collapse menu"}>
            {collapsed ? <IconChevR size={15}/> : <IconChevL size={15}/>}
          </button>
        </div>
        <div className="hx-user-block">
          <Avatar name={data.user.name} size={40}/>
          <div className="hx-user-meta">
            <div className="hx-user-name">{data.user.name}</div>
            <div className="hx-user-email">{role} · {data.user.email}</div>
          </div>
        </div>

        {groups.map((g) => (
          <div className="hx-side-group" key={g.group}>
            <div className="hx-side-label">
              {g.group}
            </div>
            <nav className="hx-side-items">
              {g.items.map(it => {
                const IconC = ICON_MAP[it.icon] || IconFolder;
                const active = it.id === route;
                return (
                  <button key={it.id} className={`hx-side-item ${active ? "on" : ""}`} onClick={() => onNav(it.id)} title={it.label}>
                    <IconC size={14}/>
                    <span>{it.label}</span>
                  </button>
                );
              })}
            </nav>
          </div>
        ))}
      </aside>
    </>
  );
};

// =================== Empty fallback ===================
const EmptyScreen = ({ route }) => (
  <div style={{padding: 40, textAlign: "center"}}>
    <div style={{fontSize: 16, fontWeight: 600}}>{route}</div>
    <div style={{color: "var(--muted)", marginTop: 8}}>This page isn't part of the prototype scope.</div>
  </div>
);

// =================== App-wide styles ===================
const AppStyles = () => (
  <style>{`
    .hx-app { display:flex; flex-direction: column; min-height: 100vh; background: var(--bg); }

    /* ----- Top nav ----- */
    .hx-topnav {
      position: sticky; top: 0; z-index: 50;
      display:flex; align-items: center; gap: 12px;
      padding: 0 22px; height: 60px;
      background: var(--panel);
      border-bottom: 1px solid var(--line);
    }
    .hx-topnav-left { display:flex; align-items: center; gap: 12px; min-width: 0; }
    .hx-hamburger {
      display: none;
      width: 36px; height: 36px; align-items: center; justify-content: center;
      background: transparent; border: 1px solid var(--line); border-radius: 8px;
      cursor: pointer; color: var(--ink); padding: 0;
    }
    .hx-hamburger:hover { background: var(--hover); }
    .hx-brand { display:flex; gap: 8px; align-items: center; text-decoration: none; color: inherit; flex-shrink: 0; }
    .hx-brand-name { font-weight: 600; font-size: 16px; letter-spacing: -.01em; }
    .hx-brand-dot { color: var(--accent); }
    .hx-project-pick {
      position: relative;
      display:flex; align-items: center; gap: 6px;
      height: 34px; padding: 0 10px;
      background: transparent;
      border: 1px solid var(--line);
      border-radius: 8px;
      font: inherit;
      cursor: pointer;
      color: var(--ink-2);
      min-width: 0;
    }
    .hx-pp-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
    .hx-project-pick:hover { background: var(--hover); }
    .hx-pp-menu {
      position: absolute; top: calc(100% + 6px); left: 0;
      background: var(--panel); border: 1px solid var(--line);
      border-radius: 10px; box-shadow: var(--shadow-modal);
      min-width: 220px; padding: 6px;
      z-index: 30; text-align: left;
    }
    .hx-pp-item {
      display:flex; align-items: center; gap: 8px;
      width: 100%; padding: 8px 10px; border-radius: 6px;
      background: transparent; border: none; color: var(--ink-2);
      cursor: pointer; font: inherit; font-size: 13.5px;
    }
    .hx-pp-item:hover { background: var(--hover); }
    .hx-pp-item.on { color: var(--accent); font-weight: 500; }
    .hx-pp-sep { height: 1px; background: var(--line-2); margin: 4px 0; }

    .hx-topnav-mid { display:flex; gap: 2px; flex: 1; justify-content: flex-end; padding-right: 16px; overflow: hidden; }
    .hx-topnav-spacer { flex: 1; }
    .hx-topnav-link {
      display:inline-flex; align-items: center; gap: 4px;
      height: 36px; padding: 0 12px; border-radius: 8px;
      color: var(--ink-2); text-decoration: none; font-size: 14px; font-weight: 500;
      white-space: nowrap;
    }
    .hx-topnav-link:hover { background: var(--hover); color: var(--ink); }
    .hx-topnav-link.on { color: var(--ink); }

    .hx-topnav-right { display:flex; gap: 4px; align-items: center; flex-shrink: 0; }
    .hx-tn-icon { position: relative; background: transparent; border: none; cursor: pointer; padding: 8px; border-radius: 8px; color: var(--ink-2); display: inline-flex; }
    .hx-tn-icon:hover { background: var(--hover); color: var(--ink); }
    .hx-dot { position: absolute; top: 7px; right: 8px; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

    /* ----- Dropdowns (avatar, notifications) ----- */
    .hx-dd { position: relative; display: inline-flex; }
    .hx-avatar-btn { background: transparent; border: none; cursor: pointer; padding: 0; border-radius: 999px; transition: box-shadow .12s; }
    .hx-avatar-btn:hover { box-shadow: 0 0 0 2px var(--accent-soft); }
    .hx-avatar-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent); }
    .hx-dd-menu {
      position: absolute; right: 0; top: calc(100% + 8px);
      background: var(--panel); border: 1px solid var(--line);
      border-radius: 12px; box-shadow: var(--shadow-modal);
      min-width: 240px; padding: 6px;
      z-index: 60;
      animation: hxpop .12s ease both;
    }
    .hx-avatar-menu { min-width: 260px; }
    .hx-notif-menu { width: 380px; max-width: calc(100vw - 24px); }
    .hx-dd-head { display: flex; justify-content: space-between; align-items: center; padding: 8px 10px; }
    .hx-dd-foot { display: flex; justify-content: center; padding: 8px; border-top: 1px solid var(--line-2); margin-top: 4px; }
    .hx-dd-user { display: flex; gap: 12px; align-items: center; padding: 10px; }
    .hx-dd-sep { height: 1px; background: var(--line-2); margin: 4px 6px; }
    .hx-dd-item {
      display: flex; align-items: center; gap: 10px;
      width: 100%; padding: 9px 10px; border-radius: 8px;
      background: transparent; border: none; cursor: pointer;
      font: inherit; font-size: 13.5px; color: var(--ink-2);
      text-align: left; text-decoration: none;
    }
    .hx-dd-item:hover { background: var(--hover); color: var(--ink); }
    .hx-dd-item.danger { color: var(--bad); }
    .hx-dd-item.danger:hover { background: color-mix(in oklab, var(--bad) 8%, transparent); }
    .hx-link-btn { background: transparent; border: none; cursor: pointer; color: var(--accent); font: inherit; font-size: 12.5px; padding: 2px 6px; border-radius: 4px; }
    .hx-link-btn:hover { background: var(--accent-soft); }
    .hx-kbd { font-family: var(--mono); font-size: 11px; padding: 1px 5px; border-radius: 4px; background: var(--line-2); color: var(--ink-2); border: 1px solid var(--line); }

    /* Notification list */
    .hx-notif-list { max-height: 400px; overflow-y: auto; }
    .hx-notif-row {
      display: flex; align-items: flex-start; gap: 10px;
      padding: 12px 10px; border-radius: 8px;
      background: transparent; border: none; cursor: pointer;
      width: 100%; font: inherit; text-align: left;
      position: relative;
    }
    .hx-notif-row:hover { background: var(--hover); }
    .hx-notif-icon { width: 30px; height: 30px; border-radius: 8px; background: var(--line-2); color: var(--ink-2); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .hx-notif-row.unread .hx-notif-icon { background: var(--accent-soft); color: var(--accent); }
    .hx-notif-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
    .hx-notif-title { font-size: 13.5px; font-weight: 500; color: var(--ink); }
    .hx-notif-body { font-size: 12.5px; color: var(--muted); line-height: 1.4; }
    .hx-notif-time { font-size: 11.5px; color: var(--muted-2); margin-top: 2px; }
    .hx-notif-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex-shrink: 0; margin-top: 14px; }

    /* ----- Shell ----- */
    .hx-shell { display: grid; grid-template-columns: 260px 1fr; min-height: calc(100vh - 60px); }
    .hx-shell[data-shell="full"] { grid-template-columns: 1fr; }
    .hx-shell[data-collapsed="true"] { grid-template-columns: 76px 1fr; }
    body[data-sidebar="compact"] .hx-shell[data-shell="with-sidebar"] { grid-template-columns: 220px 1fr; }
    body[data-sidebar="compact"] .hx-shell[data-collapsed="true"] { grid-template-columns: 76px 1fr; }

    /* ----- Sidebar ----- */
    .hx-sidebar {
      background: var(--bg);
      border-right: 1px solid var(--line-2);
      padding: 22px 14px;
      display: flex; flex-direction: column; gap: 22px;
      position: sticky; top: 60px; align-self: start;
      height: calc(100vh - 60px); overflow-y: auto;
    }
    body[data-sidebar="compact"] .hx-sidebar { padding: 18px 10px; gap: 18px; }
    body[data-sidebar="compact"] .hx-user-block { padding: 2px 6px 10px; gap: 10px; }
    body[data-sidebar="compact"] .hx-side-label { font-size: 10px; padding: 0 8px; }
    body[data-sidebar="compact"] .hx-side-item { padding: 7px 8px; font-size: 13px; }

    .hx-sidebar-close-row { display: none; justify-content: flex-end; }
    .hx-sidebar-tools { display: flex; justify-content: flex-end; }
    .hx-sidebar-collapse {
      width: 30px; height: 30px; border: 1px solid var(--line); border-radius: 8px;
      background: var(--panel); color: var(--ink-2); cursor: pointer;
      display: inline-flex; align-items: center; justify-content: center;
    }
    .hx-sidebar-collapse:hover { background: var(--hover); color: var(--ink); }
    .hx-sidebar-close {
      width: 32px; height: 32px;
      background: transparent; border: none; cursor: pointer;
      border-radius: 8px; display: inline-flex; align-items: center; justify-content: center;
      color: var(--ink-2);
    }
    .hx-sidebar-close:hover { background: var(--hover); }
    .hx-drawer-backdrop { display: none; }

    .hx-user-block { display:flex; gap: 12px; align-items: center; padding: 4px 8px 12px; border-bottom: 1px solid var(--line-2); }
    .hx-user-name { font-weight: 600; font-size: 14px; color: var(--ink); line-height: 1.2; }
    .hx-user-email { color: var(--muted); font-size: 12px; margin-top: 2px; word-break: break-all; }
    .hx-side-label {
      font-size: 11px; font-weight: 600; letter-spacing: .06em;
      text-transform: uppercase;
      color: var(--muted); padding: 0 10px; margin-bottom: 6px;
    }
    .hx-side-items { display:flex; flex-direction: column; gap: 1px; }
    .hx-side-item {
      display: flex; align-items: center; gap: 10px;
      padding: 9px 10px;
      background: transparent; border: 1px solid transparent; border-radius: 8px;
      cursor: pointer; font: inherit; font-size: 13.5px;
      color: var(--ink-2);
      text-align: left;
      transition: background .12s ease, color .12s ease, border-color .12s ease;
      min-height: 36px; /* touch target */
    }
    .hx-side-item:hover { background: var(--hover); color: var(--ink); }
    .hx-side-item.on {
      background: var(--accent-soft);
      color: var(--accent);
      border-color: color-mix(in oklab, var(--accent) 22%, transparent);
      font-weight: 500;
    }
    .hx-sidebar-collapsed { padding-left: 10px; padding-right: 10px; align-items: center; overflow-x: hidden; }
    .hx-sidebar-collapsed .hx-sidebar-tools { width: 100%; justify-content: center; }
    .hx-sidebar-collapsed .hx-user-block { justify-content: center; padding: 0 0 12px; width: 100%; }
    .hx-sidebar-collapsed .hx-user-meta,
    .hx-sidebar-collapsed .hx-side-label,
    .hx-sidebar-collapsed .hx-side-item span { display: none; }
    .hx-sidebar-collapsed .hx-side-group,
    .hx-sidebar-collapsed .hx-side-items { width: 100%; align-items: center; }
    .hx-sidebar-collapsed .hx-side-item { justify-content: center; padding: 9px; width: 40px; }

    /* ----- Main + screens ----- */
    .hx-main { padding: 22px 36px 48px; min-width: 0; }
    .hx-main-inner { display: flex; flex-direction: column; gap: 18px; max-width: 1240px; margin: 0 auto; }
    .hx-screen { display: flex; flex-direction: column; gap: var(--gap); margin-top: 6px; }

    .hx-card-grid { display: grid; gap: var(--gap); }
    .hx-card-grid.hx-3 { grid-template-columns: repeat(3, 1fr); }
    .hx-card-grid.hx-2 { grid-template-columns: repeat(2, 1fr); }

    /* ================ RESPONSIVE ================ */

    /* Tablet landscape & smaller desktops: tighten layout, 2-col grids */
    @media (max-width: 1180px) {
      .hx-card-grid.hx-3 { grid-template-columns: repeat(2, 1fr); }
      .hx-shell[data-shell="with-sidebar"] { grid-template-columns: 230px 1fr; }
      .hx-main { padding: 20px 24px 40px; }
      .hx-topnav-link { padding: 0 9px; font-size: 13.5px; }
    }

    /* iPad portrait & small tablets: collapse sidebar to drawer, hide top nav links */
    @media (max-width: 1023px) {
      .hx-shell[data-shell="with-sidebar"] { grid-template-columns: 1fr; }
      .hx-shell[data-collapsed="true"] { grid-template-columns: 1fr; }
      .hx-hamburger { display: inline-flex; }
      .hx-topnav-mid { display: none; }
      .hx-topnav { padding: 0 16px; gap: 10px; }

      .hx-sidebar {
        position: fixed; left: 0; top: 0; height: 100vh; width: 300px; max-width: 88vw;
        z-index: 60; transform: translateX(-100%); transition: transform .22s ease;
        background: var(--panel);
        border-right: 1px solid var(--line);
        box-shadow: 0 0 0 transparent;
        padding-top: 12px;
      }
      .hx-sidebar.hx-sidebar-open { transform: translateX(0); box-shadow: 8px 0 32px rgba(13,17,28,.18); }
      .hx-sidebar-close-row { display: flex; padding: 0 6px 6px; }
      .hx-sidebar-tools { display: none; }
      .hx-sidebar-collapsed .hx-user-meta,
      .hx-sidebar-collapsed .hx-side-label,
      .hx-sidebar-collapsed .hx-side-item span { display: block; }
      .hx-sidebar-collapsed { align-items: stretch; }
      .hx-sidebar-collapsed .hx-side-item { justify-content: flex-start; width: 100%; }
      .hx-drawer-backdrop {
        display: block;
        position: fixed; inset: 0; background: rgba(13,17,28,.42); z-index: 55;
        animation: hxfade .15s ease both;
      }

      .hx-main { padding: 18px 20px 40px; }
      .hx-pp-label { max-width: 130px; }
    }

    /* Phone */
    @media (max-width: 640px) {
      .hx-topnav { padding: 0 12px; height: 56px; }
      .hx-topnav { position: sticky; }
      .hx-brand-name { font-size: 15px; }
      .hx-project-pick { padding: 0 8px; }
      .hx-pp-label { max-width: 88px; font-size: 13px; }
      .hx-tn-hidesm { display: none; }

      .hx-card-grid.hx-3,
      .hx-card-grid.hx-2 { grid-template-columns: 1fr; }

      .hx-main { padding: 16px 14px 32px; }
      .hx-main-inner { gap: 14px; }

      /* tables become horizontally scrollable */
      .hx-table th, .hx-table td { padding: 12px 12px; font-size: 13px; }

      /* Card header stack */
      .hx-card-head { flex-direction: column; align-items: flex-start; }

      /* Stat numbers smaller */
      .hx-stat-num { font-size: 28px; }

      /* Modal full-bleed */
      .hx-modal-overlay { padding: 0; align-items: flex-end; }
      .hx-modal { width: 100% !important; max-width: 100%; border-radius: 14px 14px 0 0; }
    }

    /* High-touch: bump tap target heights */
    @media (pointer: coarse) {
      .hx-side-item { min-height: 44px; padding: 10px 12px; font-size: 14px; }
      .hx-btn-md { height: 42px; }
      .hx-tn-icon, .hx-hamburger { width: 40px; height: 40px; }
    }
  `}</style>
);

// Mount
ReactDOM.createRoot(document.getElementById("app")).render(<ErrorBoundary><App/></ErrorBoundary>);
