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

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

// =================== Theme persistence ===================
const THEME_KEY = "helix.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" },
  org: { name: "2bsinha_de6e", id: "org_de6e91a4b3" },
  currentProject: "Default Project",
  projectOptions: ["Default Project", "research-finetune", "prod-inference"],

  monthlySpend: 0.00,
  credits: 8.27,
  autoRecharge: true,
  autoRechargeThreshold: 5,
  autoRechargeAmount: 25,
  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",

  invoices: [
    { id: "INV-2026-042", date: "Apr 30, 2026", desc: "Monthly inference & GPU usage", amount: "$184.20", status: "Paid" },
    { id: "INV-2026-031", date: "Mar 31, 2026", desc: "Monthly inference & GPU usage", amount: "$92.40",  status: "Paid" },
    { id: "INV-2026-024", date: "Feb 29, 2026", desc: "Credit purchase ×3",            amount: "$300.00", status: "Paid" },
    { id: "INV-2026-014", date: "Feb 17, 2026", desc: "Onboarding setup credit",        amount: "$10.00",  status: "Paid" },
    { id: "INV-2026-051", date: "May 14, 2026", desc: "Partial month inference",       amount: "$8.27",   status: "Open" },
  ],

  projects: [
    { id: "p1", name: "Default Project", default: true, current: true, created: "Feb 17, 2026", cost: 0.00, collab: 1 },
    { id: "p2", name: "research-finetune", created: "Mar 04, 2026", cost: 412.16, collab: 3 },
    { id: "p3", name: "prod-inference",    created: "Apr 12, 2026", cost: 1284.04, collab: 5 },
    { id: "p4", name: "ml-experiments",    created: "May 02, 2026", cost: 28.18,  collab: 2 },
  ],

  members: [
    { id: "m1", name: "Abhishek Sinha",  email: "2bsinha@gmail.com",         role: "Owner",  lastActive: "Just now",  status: "Active" },
    { id: "m2", name: "Priya Nair",       email: "priya@helixlabs.dev",       role: "Admin",  lastActive: "2h ago",    status: "Active" },
    { id: "m3", name: "Karan Mehta",      email: "karan.m@helixlabs.dev",     role: "Member", lastActive: "Yesterday", status: "Active" },
    { id: "m4", name: "Sasha Volkov",     email: "sasha@partner.io",          role: "Member", lastActive: "4 days",    status: "Pending" },
    { id: "m5", name: "Lin Wei",          email: "lin.wei@helixlabs.dev",     role: "Member", lastActive: "Mar 19",    status: "Active" },
  ],

  apiKeys: [
    { id: "k1", name: "prod-server",         key: "hx-live-9aF3kQ2pLm8nXvR0wY1zT5bN", created: "Apr 12, 2026", lastUsed: "12 min ago",  revealed: false },
    { id: "k2", name: "ci-pipeline",         key: "hx-live-7sJ4dV1cBe5xQ2nR8tY0pW6m", created: "Apr 02, 2026", lastUsed: "1 hour ago",  revealed: false },
    { id: "k3", name: "local-dev",           key: "hx-test-3kL9pM5wT2vN8xR1qY7zB0sH", created: "Mar 28, 2026", lastUsed: "Yesterday",   revealed: false },
    { id: "k4", name: "research-finetune",   key: "hx-live-5tQ8nR2cM4kL9pV1xW7yZ3bH", created: "Mar 04, 2026", lastUsed: "Feb 21",      revealed: false },
  ],

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

  spendByDay: [12, 18, 24, 16, 22, 38, 41, 29, 26, 33, 48, 52, 44, 38, 30, 35, 42, 50, 55, 47, 36, 28, 32, 40, 48, 54, 62, 58, 49, 44],
  spendByModel: [
    { model: "helix-70b",         requests: "1.8M",  tokens: "412.4M", spend: 284.18 },
    { model: "helix-7b-instruct", requests: "12.4M", tokens: "1.2B",   spend: 192.40 },
    { model: "helix-vision-13b",  requests: "248K",  tokens: "84.1M",  spend: 124.06 },
    { model: "helix-embed-v2",    requests: "8.6M",  tokens: "1.8B",   spend: 102.82 },
    { model: "helix-code-34b",    requests: "412K",  tokens: "138M",   spend: 84.70 },
  ],

  files: [
    { id:"f1", name:"finetune_train_v3.jsonl", purpose:"fine-tune", size:"284.4 MB", created:"Apr 28, 2026" },
    { id:"f2", name:"eval_set.jsonl",          purpose:"evaluation", size:"12.1 MB", created:"Apr 27, 2026" },
    { id:"f3", name:"batch_prompts_may.jsonl", purpose:"batch",     size:"58.7 MB", created:"May 02, 2026" },
    { id:"f4", name:"system_prompts.txt",      purpose:"reference", size:"24 KB",   created:"Mar 19, 2026" },
  ],

  integrations: [
    { name:"GitHub",  desc:"Trigger jobs from repository events",        color:"#0b0d12", connected:true },
    { name:"Slack",   desc:"Get alerts on usage, jobs, and incidents",   color:"#4a154b", connected:true },
    { name:"Datadog", desc:"Forward inference metrics and logs",         color:"#632ca6", connected:false },
    { name:"Snowflake",desc:"Read training data from your warehouse",    color:"#29b5e8", connected:false },
    { name:"AWS S3",  desc:"Sync artifacts to your buckets",             color:"#ff9900", connected:true },
    { name:"PagerDuty",desc:"Page on-call when jobs fail",               color:"#06ac38", connected:false },
  ],

  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" },
  ],

  // ---------- CredAxis domain data ----------
  tenants: [
    { id: "t1", name: "Meridian Lending",    industry: "NBFC",          consultant: "Priya Nair",  assessments: 12, avgScore: 78, status: "Active",     since: "Feb 2026" },
    { id: "t2", name: "Cohort Capital",      industry: "Private Credit",consultant: "Karan Mehta", assessments: 8,  avgScore: 84, status: "Active",     since: "Mar 2026" },
    { id: "t3", name: "Brightline Finserv",  industry: "Fintech",       consultant: "Priya Nair",  assessments: 5,  avgScore: 61, status: "Active",     since: "Mar 2026" },
    { id: "t4", name: "Vantage Microcredit", industry: "Microfinance",  consultant: "—",           assessments: 2,  avgScore: 55, status: "Onboarding", since: "May 2026" },
    { id: "t5", name: "Northarc Holdings",   industry: "Family Office", consultant: "Lin Wei",     assessments: 9,  avgScore: 90, status: "Active",     since: "Feb 2026" },
  ],

  assessments: [
    { id: "a1", subject: "Apex Logistics Pvt Ltd",   tenant: "Meridian Lending",    score: 82, stage: "Approved",       evidence: 34, analyst: "Karan Mehta",  updated: "12 min ago" },
    { id: "a2", subject: "Greenfield Agro Exports",  tenant: "Cohort Capital",      score: 64, stage: "Evidence Review",evidence: 21, analyst: "Priya Nair",   updated: "1 hour ago" },
    { id: "a3", subject: "Stellar Components Inc",   tenant: "Meridian Lending",    score: 71, stage: "AI Scoring",     evidence: 28, analyst: "Lin Wei",      updated: "3 hours ago" },
    { id: "a4", subject: "Trident Hospitality",      tenant: "Brightline Finserv",  score: 48, stage: "Collecting",     evidence: 9,  analyst: "Karan Mehta",  updated: "Yesterday" },
    { id: "a5", subject: "Nova Textiles Group",      tenant: "Northarc Holdings",   score: 91, stage: "Approved",       evidence: 41, analyst: "Priya Nair",   updated: "2 days ago" },
    { id: "a6", subject: "Crestpoint Retail Chain",  tenant: "Cohort Capital",      score: 57, stage: "Flagged",        evidence: 17, analyst: "Lin Wei",      updated: "3 days ago" },
    { id: "a7", subject: "Bluewave Marine Services", tenant: "Meridian Lending",    score: 75, stage: "Evidence Review",evidence: 26, analyst: "Karan Mehta",  updated: "4 days ago" },
    { id: "a8", subject: "Helios Renewables Ltd",    tenant: "Northarc Holdings",   score: 88, stage: "Approved",       evidence: 38, analyst: "Priya Nair",   updated: "May 12" },
  ],

  consultants: [
    { id: "c1", name: "Priya Nair",  email: "priya@credaxis.io",  tenants: 2, active: 14, completed: 31, rating: 4.9, status: "Available" },
    { id: "c2", name: "Karan Mehta", email: "karan@credaxis.io",  tenants: 1, active: 9,  completed: 24, rating: 4.7, status: "Available" },
    { id: "c3", name: "Lin Wei",     email: "lin.wei@credaxis.io",tenants: 1, active: 11, completed: 19, rating: 4.8, status: "On engagement" },
    { id: "c4", name: "Sasha Volkov",email: "sasha@credaxis.io",  tenants: 0, active: 0,  completed: 7,  rating: 4.5, status: "Onboarding" },
  ],

  // Assessments completed per day, last 30 days
  assessmentsByDay: [2, 3, 1, 4, 2, 5, 6, 3, 4, 5, 7, 6, 5, 4, 3, 5, 6, 7, 8, 6, 4, 3, 5, 6, 7, 8, 9, 7, 6, 8],
  // Average score trend, last 30 days
  scoreTrend: [68, 69, 67, 70, 71, 70, 72, 71, 73, 72, 74, 73, 72, 74, 75, 74, 76, 75, 77, 76, 78, 77, 76, 78, 79, 78, 80, 79, 81, 80],
});

// 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,
};

// =================== 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 [paletteOpen, setPaletteOpen] = React.useState(false);
  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);

    // Role-based entry point: superadmin/consultant must select a client first
    const role = window.session?.role;
    if ((role === "superadmin" || role === "consultant") && !window.session.tenantId) {
      setRoute("clients");
    }
  }, [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]);

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

  const screen = (() => {
    switch (route) {
      // Dashboard / product (no sidebar)
      case "dashboard":      return window.session?.role === "consultant" ? <PortfolioScreen/> : <LiveDashboard data={data}/>;
      case "peer-groups":    return <PeerGroupsScreen/>;
      case "assessments":    return <AssessmentsScreen data={data}/>;
      case "tenants":        return <TenantsScreen data={data}/>;  // legacy alias
      case "consultants":    return <ConsultantsScreen data={data}/>;
      case "analytics":      return <AnalyticsScreen data={data}/>;
      case "run-history":    return <RunHistoryScreen/>;
      case "source-authority": return <SourceAuthorityScreen/>;
      case "authority":      return <AuthorityEngineScreen/>;
      case "monitoring":     return <MonitoringScreen/>;
      case "brand-dashboard": return <BrandDashboardScreen/>;
      case "clients":         return <ClientsScreen/>;
      case "client-overview": return <ClientOverviewScreen tenantId={routeParam}/>;
      case "workspaces":      return <WorkspaceListScreen clientId={routeParam}/>;
      case "new-client":      return <WorkspaceSetupScreen initialClientId={routeParam} onDone={(id) => setRoute("client-detail", id)}/>;
      case "client-detail":   return <WorkspaceDashboardScreen workspaceId={routeParam}/>;
      case "workspace-task":  {
        const [wsId, tId] = (routeParam || "").split("__");
        return <WorkspaceTaskPage workspaceId={wsId} taskId={tId}/>;
      }
      case "publishers":     return <PublisherManagementScreen/>;
      case "activity-log":    return <ActivityLogScreen/>;
      case "audit-logs":      return <ActivityLogScreen/>;   // legacy alias
      case "brand-analytics": return <AnalyticsScreen data={data}/>;  // brand-role analytics
      case "new-assessment": return <NewAssessmentScreen/>;
      case "assessment-detail": return <AssessmentDetailScreen assessmentId={routeParam}/>;

      // Settings screens (with sidebar)
      case "billing":        return <BillingScreen data={data} setData={setData} openModal={openModal}/>;
      case "projects":       return <ProjectListScreen data={data} setData={setData} openModal={openModal}/>;
      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 "components":     return <ComponentsScreen/>;
      case "cookies":        return <CookiesScreen/>;
      case "org-general":    return <GeneralOrgScreen data={data}/>;
      case "proj-general":   return <GeneralProjectScreen data={data}/>;
      case "cost-analytics": return <CostAnalyticsScreen data={data}/>;
      case "files":          return <FilesScreen data={data}/>;
      case "integrations":   return <IntegrationsScreen data={data}/>;
      case "ssh-key":        return <SshKeyScreen data={data}/>;
      case "audit-logs":     return <AuditLogsScreen/>;
      case "system-settings": return <SystemSettingsScreen/>;
      case "invoice-usage":  return <BillingScreen data={data} setData={setData} openModal={openModal}/>;
      // Create-flow pages
      case "new-project":    return <NewProjectPage data={data} setData={setData}/>;
      case "invite-member":  return <InviteMemberPage data={data} setData={setData}/>;
      case "new-api-key":    return <NewApiKeyPage data={data} setData={setData}/>;
      case "new-ssh-key":    return <NewSshKeyPage data={data} setData={setData}/>;
      case "upload-file":    return <UploadFilePage data={data} setData={setData}/>;
      // Edit-flow pages
      case "edit-project":   return <EditProjectPage data={data} setData={setData} projectId={routeParam}/>;
      // Public pages — no auth required (token-gated via signed URL)
      case "complete":         return <TaskCompletionPage/>;
      // 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 isSettingsRoute = !!found || [
    "new-project","invite-member","new-api-key","new-ssh-key","upload-file",
    "edit-project","invoice-usage","appearance","cookies","dashboard"
  ].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": "files",
    "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
      ? found.group === "Account"
        ? [{ label: "Settings" }, { label: found.item.label }]
        : [{ label: cfg.brand.productName }, { label: found.item.label }]
      : parentFound
        ? [{ label: "Settings" }, { 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={true} showLinks={false} openPalette={() => setPaletteOpen(true)}/>

      <div className="hx-shell" data-shell="with-sidebar">
        <Sidebar data={data} route={route} onNav={(r) => { setRoute(r); setDrawerOpen(false); }} drawerOpen={drawerOpen} onClose={() => setDrawerOpen(false)}/>

        <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)}/>}
            <ErrorBoundary key={route}>{screen}</ErrorBoundary>
          </div>
        </main>
      </div>

      {/* Modals */}
      <AddCreditsModal open={modal==="addCredits"} onClose={closeModal}
        onConfirm={(amt) => { setData(d => ({...d, credits: d.credits + amt})); window.__toast(`Added $${amt} in credits`); }}/>
      <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={(role) => { setData(d => ({...d, members: d.members.map(m => m.id === modalCtx.id ? {...m, role} : m)})); window.__toast(`${modalCtx.name} is now ${role}`); }}/>
      <RemoveMemberModal open={modal==="removeMember"} onClose={closeModal} member={modalCtx}
        onConfirm={() => { setData(d => ({...d, members: d.members.filter(m => m.id !== modalCtx.id)})); window.__toast(`${modalCtx.name} removed`); }}/>
      <RotateKeyModal open={modal==="rotateKey"} onClose={closeModal} apiKey={modalCtx}
        onConfirm={() => {
          const newKey = `hx-live-${Math.random().toString(36).slice(2,12)}${Math.random().toString(36).slice(2,12)}`;
          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={() => { setData(d => ({...d, apiKeys: d.apiKeys.filter(k => k.id !== modalCtx.id)})); window.__toast(`Key "${modalCtx.name}" revoked`); }}/>

      {/* 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={["#5b5bf5","#0fb67e","#e55a2b","#d63a8a","#1d4ed8","#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>
  );
}


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>
      </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></>} side="bottom">
          <button className="hx-tn-icon hx-tn-search" aria-label="Search" onClick={() => window.__openPalette()}>
            <IconSearch size={16}/>
          </button>
        </Tooltip>
        <Tooltip content="Help" side="bottom">
          <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: IconCard,    title: "Payment received",       body: "April invoice paid · $184.20",    at: "12 min ago", unread: true },
    { id: "n2", icon: IconUsers,   title: "Invitation accepted",    body: "Priya Nair joined as Admin",      at: "2 hr ago",   unread: true },
    { id: "n3", icon: IconKey,     title: "API key rotated",        body: "ci-pipeline · new key generated", at: "Yesterday",  unread: false },
    { id: "n4", icon: IconChart,   title: "Spend alert",            body: "Project prod-inference passed $1,000 this month", 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.session?.clear(); history.replaceState(null, "", "/sign-in"); window.location.reload(); }}><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-2", label: "Invite member",   group: "Actions", icon: IconPlus,     action: () => window.__nav("invite-member") });
    items.push({ id: "act-6", label: "New assessment",  group: "Actions", icon: IconPlus,     action: () => window.__nav("new-assessment") });
    items.push({ id: "act-7", label: "System settings", group: "Actions", icon: IconSettings, action: () => window.__nav("system-settings") });
    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, projects, 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 }) => {
  const cfg = window.APP_CONFIG;
  return (
    <>
      {drawerOpen && <div className="hx-drawer-backdrop" onClick={onClose}/>}
      <aside className={`hx-sidebar ${drawerOpen ? "hx-sidebar-open" : ""}`}>
        <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-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">{data.user.email}</div>
          </div>
        </div>

        {cfg.sidebar.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)}>
                    <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-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: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; }
    body[data-sidebar="compact"] .hx-shell[data-shell="with-sidebar"] { grid-template-columns: 220px 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-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;
    }

    /* ----- 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); }
    /* Prevent intrinsic min-content (e.g. wide SVGs) from blowing out grid cells. */
    .hx-card-grid > * { min-width: 0; }

    /* ================ 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-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-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>);
