/* Leap Admin Cockpit — Phase 1 (read-only) over /admin/platform/*.
   No-build React (UMD + Babel), Leap design system, dark near-black nav. */
(function () {
  const { useState, useEffect, useCallback } = React;
  const A = window.AdminAPI;
  // Effective capabilities of the signed-in admin (from /admin/me). Views call
  // can(cap) to hide actions the role can't perform — the server still enforces.
  let CAPS = new Set();
  const can = (cap) => CAPS.has(cap);
  const ADMIN_ROLE_LABEL = { superadmin: "Superadmin", finance: "Finance", support: "Support", readonly: "Read-only" };
  const NAV = { bg: "#17181A", fg: "#C9CDD3", fgStrong: "#F4F5F6", muted: "#71767E", hover: "rgba(255,255,255,.06)", activeBg: "rgba(14,165,183,.18)", activeFg: "#4DD0DE", border: "rgba(255,255,255,.09)" };

  const fmtN = (n) => (n == null ? "—" : Number(n).toLocaleString("en-US"));
  const fmtDate = (d) => { if (!d) return "—"; try { return new Date(d).toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }); } catch (e) { return String(d); } };
  const short = (s, n = 10) => (s ? String(s).slice(0, n) + "…" : "—");
  const fmtUsd = (n) => (n == null ? "—" : "$" + Number(n).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
  const fmtUnit = (n) => (n == null ? "—" : "$" + Number(n).toLocaleString("en-US", { minimumFractionDigits: 3, maximumFractionDigits: 4 }));
  const PLAT_LABEL = { x: "X (Twitter)", facebook: "Facebook", instagram: "Instagram", linkedin: "LinkedIn", linkedin_page: "LinkedIn Page", tiktok: "TikTok", youtube: "YouTube", pinterest: "Pinterest", telegram: "Telegram", anthropic: "Anthropic" };
  const OP_LABEL = { post: "Create post", post_url: "Post w/ URL", read: "Read / analytics", refresh: "Token refresh", media_upload: "Media upload", "assistant.briefing": "Assistant briefing", "assistant.chat": "Assistant chat", "assistant.weekly": "Assistant weekly" };
  const KIND_LABEL = { open: "Opened", card_action: "Card action", assist_click: "Assist click", chat_message: "Chat message", action_confirmed: "Action confirmed", action_dismissed: "Action dismissed", weekly_view: "Weekly view", prefs_saved: "Prefs saved" };
  const fmtBytes = (n) => { if (n == null) return "—"; const u = ["B", "KB", "MB", "GB", "TB"]; let i = 0, v = Number(n); while (v >= 1024 && i < u.length - 1) { v /= 1024; i++; } return v.toFixed(v < 10 && i > 0 ? 1 : 0) + " " + u[i]; };
  const fmtAgo = (d) => { if (!d) return "—"; const s = (Date.now() - new Date(d).getTime()) / 1000; if (s < 60) return Math.floor(s) + "s ago"; if (s < 3600) return Math.floor(s / 60) + "m ago"; if (s < 86400) return Math.floor(s / 3600) + "h ago"; return Math.floor(s / 86400) + "d ago"; };
  const SEV_COLOR = { critical: "var(--danger)", high: "var(--danger)", medium: "var(--warning)", low: "var(--fg3)" };
  const SEC_TYPE_LABEL = { "auth.unauthorized": "Failed auth", "auth.bruteforce": "Brute force", "session.refresh_reuse": "Token reuse (theft)", "authz.forbidden": "Forbidden", "authz.tenant_spoof": "Tenant spoof", "authz.probing": "Authz probing", "abuse.rate_limit": "Rate limited", "abuse.blocked_ip": "Blocked IP hit", "admin.privilege_change": "Admin privilege change" };
  const secType = (t) => SEC_TYPE_LABEL[t] || t;
  const GRANT_LABEL = { free_plan: "Free plan", plan: "Plan grant", percent_off: "% off", credits: "Credits" };
  const grantSummary = (c) => c.grantType === "percent_off" ? `${c.percent}% off` : c.grantType === "credits" ? `${c.creditsAmount} credits` : `${c.planKey || "?"}${c.durationMonths ? " · " + c.durationMonths + "mo" : " · perpetual"}`;

  const STATUS_COLOR = { CONNECTED: "var(--success)", active: "var(--success)", trialing: "var(--accent)", EXPIRED: "var(--warning)", past_due: "var(--warning)", REVOKED: "var(--danger)", canceled: "var(--danger)", ERROR: "var(--danger)" };
  function Pill({ children, color }) {
    return <span style={{ display: "inline-block", padding: "2px 9px", borderRadius: 999, fontSize: 11.5, fontWeight: 600, color: color || "var(--fg2)", background: (color || "var(--fg3)") + "1a" }}>{children}</span>;
  }
  const TYPE_LABEL = { individual: "Individual", team: "Team", agency: "Agency" };
  const CATEGORY = { individual: "Individual", direct: "Direct org", agency: "Agency", client: "Agency client" };
  const orgCategory = (o) => (o.parentId ? "client" : o.type === "agency" ? "agency" : o.type === "team" ? "direct" : "individual");
  const ROLE_LABEL = { superadmin: "Superadmin", support: "Support", finance: "Finance", readonly: "Read-only" };
  const ROLE_KEYS = ["superadmin", "support", "finance", "readonly"];

  // ── generic data hook ──────────────────────────────────────────────────────
  function useData(fn, deps) {
    const [state, setState] = useState({ loading: true, data: null, error: null });
    const run = useCallback(() => { setState((s) => ({ ...s, loading: true })); fn().then((data) => setState({ loading: false, data, error: null })).catch((e) => setState({ loading: false, data: null, error: e })); }, deps || []);
    useEffect(() => { run(); }, [run]);
    return { ...state, reload: run };
  }
  const Loading = () => <div style={{ padding: 30, color: "var(--fg3)" }}>Loading…</div>;
  const ErrBox = ({ e }) => <div style={{ padding: 16, border: "1px solid var(--danger)", background: "var(--danger-bg)", color: "var(--danger)", borderRadius: 12 }}>Error {e.status || ""}: {(e.body || e.message || "").slice(0, 200)}</div>;

  // ── reusable table ─────────────────────────────────────────────────────────
  function Table({ cols, rows, onRow, empty }) {
    if (!rows || !rows.length) return <div style={{ padding: 24, color: "var(--fg4)" }}>{empty || "Nothing to show."}</div>;
    return (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, overflow: "hidden", background: "var(--surface)" }}>
        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13.5 }}>
          <thead><tr style={{ background: "var(--bg-muted)" }}>{cols.map((c, i) => <th key={i} style={{ textAlign: c.right ? "right" : "left", padding: "10px 14px", fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{c.label}</th>)}</tr></thead>
          <tbody>{rows.map((row, ri) => (
            <tr key={ri} onClick={onRow ? () => onRow(row) : undefined} style={{ borderTop: "1px solid var(--border)", cursor: onRow ? "pointer" : "default" }}
              onMouseEnter={(e) => onRow && (e.currentTarget.style.background = "var(--accent-weak)")} onMouseLeave={(e) => onRow && (e.currentTarget.style.background = "transparent")}>
              {cols.map((c, ci) => <td key={ci} style={{ padding: "11px 14px", textAlign: c.right ? "right" : "left", color: "var(--fg1)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: c.max || 320 }}>{c.render ? c.render(row) : row[c.key]}</td>)}
            </tr>
          ))}</tbody>
        </table>
      </div>
    );
  }

  // ── views ──────────────────────────────────────────────────────────────────
  function Overview() {
    const { loading, data, error } = useData(() => A.overview(), []);
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const card = (label, value, sub) => (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: "16px 18px", background: "var(--surface)", minWidth: 150 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{label}</div>
        <div style={{ fontSize: 26, fontWeight: 800, fontFamily: "var(--font-display)", color: "var(--fg1)", marginTop: 4 }}>{value}</div>
        {sub && <div style={{ fontSize: 12, color: "var(--fg3)", marginTop: 4 }}>{sub}</div>}
      </div>
    );
    const cat = data.orgs.byCategory || {};
    return (
      <div style={{ display: "flex", flexWrap: "wrap", gap: 14 }}>
        {card("Organizations", fmtN(data.orgs.total), `${cat.agency || 0} agency · ${cat.direct || 0} direct · ${cat.client || 0} client · ${cat.individual || 0} individual`)}
        {card("Users", fmtN(data.users))}
        {card("Active subscriptions", fmtN(data.activeSubscriptions), Object.entries(data.subscriptionsByPlan || {}).map(([k, v]) => `${k}: ${v}`).join(" · ") || undefined)}
        {card("Connected channels", fmtN(Object.values(data.connectionsByStatus || {}).reduce((a, b) => a + b, 0)), Object.entries(data.connectionsByStatus || {}).map(([k, v]) => `${k}: ${v}`).join(" · ") || "none yet")}
      </div>
    );
  }

  function Orgs({ onOpen }) {
    const [search, setSearch] = useState("");
    const [category, setCategory] = useState("");
    const { loading, data, error } = useData(() => A.orgs({ search, category, limit: 100 }), [search, category]);
    return (
      <div>
        <div style={{ display: "flex", gap: 10, marginBottom: 14 }}>
          <input placeholder="Search by name or id…" value={search} onChange={(e) => setSearch(e.target.value)} style={inp} />
          <select value={category} onChange={(e) => setCategory(e.target.value)} style={{ ...inp, flex: "none", width: 200 }}>
            <option value="">All organizations</option>
            <option value="direct">Direct organizations</option>
            <option value="agency">Agencies</option>
            <option value="client">Agency clients</option>
            <option value="individual">Individuals</option>
          </select>
        </div>
        {loading ? <Loading /> : error ? <ErrBox e={error} /> : (
          <Table onRow={(r) => onOpen(r.id)} cols={[
            { label: "Organization", render: (r) => <b>{r.name}</b> },
            { label: "Category", render: (r) => { const c = orgCategory(r); return <Pill color={c === "client" ? "var(--accent)" : c === "agency" ? "var(--leap-teal)" : undefined}>{CATEGORY[c]}</Pill>; } },
            { label: "Plan", render: (r) => r.planKey ? <Pill color={STATUS_COLOR[r.subStatus]}>{r.planKey}{r.subStatus && r.subStatus !== "active" ? ` · ${r.subStatus}` : ""}</Pill> : <span style={{ color: "var(--fg4)" }}>free</span> },
            { label: "Members", right: true, render: (r) => fmtN(r.members) },
            { label: "Clients", right: true, render: (r) => r.type === "agency" ? fmtN(r.clients) : "—" },
            { label: "Created", render: (r) => fmtDate(r.createdAt) },
          ]} rows={data.orgs} empty="No organizations match." />
        )}
      </div>
    );
  }

  function OrgDetail({ orgId, onBack }) {
    const { loading, data, error, reload } = useData(() => A.org360(orgId), [orgId]);
    const plans = useData(() => (can("billing.write") ? A.plans() : Promise.resolve({ plans: [] })), [orgId]);
    const [busy, setBusy] = useState(null);
    const [planKey, setPlanKey] = useState("");
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const { org, members, clients, subscription, connections, billing, recentAudit, usage } = data;
    const changePlan = async () => { if (!planKey) return; setBusy("plan"); try { await A.setSubscription(org.id, { planKey, status: "active" }); setPlanKey(""); await reload(); } catch (e) { alert("Change plan failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const impersonate = async (userId) => { if (!confirm("Issue an impersonation session for this user? This is audited.")) return; setBusy(userId); try { await A.impersonate(userId); alert("Impersonation session issued (audited)."); } catch (e) { alert("Failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const reconnect = async (id) => { if (!confirm("Quarantine this connection? The account owner must reconnect.")) return; setBusy(id); try { await A.quarantineConnection(id, "Quarantined from admin cockpit"); await reload(); } catch (e) { alert("Failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const section = (title, body) => <div style={{ marginBottom: 20 }}><div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>{title}</div>{body}</div>;
    const canBilling = can("billing.write"), canImperson = can("users.impersonate"), canConn = can("connections.manage");
    return (
      <div style={{ maxWidth: 900 }}>
        <button onClick={onBack} style={{ ...btnGhost, marginBottom: 12 }}>‹ Back to organizations</button>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
          <h2 style={{ margin: 0 }}>{org.name}</h2><Pill>{TYPE_LABEL[org.type] || org.type}</Pill>
          {subscription && <Pill color={STATUS_COLOR[subscription.status]}>{subscription.planKey} · {subscription.status}</Pill>}
        </div>
        <div style={{ fontSize: 12.5, color: "var(--fg3)", marginBottom: 20, fontFamily: "var(--font-mono)" }}>{org.id} · created {fmtDate(org.createdAt)}</div>

        {section("Subscription", subscription
          ? <Table cols={[{ label: "Plan", render: () => subscription.planName || subscription.planKey }, { label: "Status", render: () => <Pill color={STATUS_COLOR[subscription.status]}>{subscription.status}</Pill> }, { label: "Seats", right: true, render: () => fmtN(subscription.seats) }, { label: "Client accounts", right: true, render: () => fmtN(subscription.clientAccounts) }, { label: "Renews", render: () => fmtDate(subscription.currentPeriodEnd) }, { label: "Stripe", render: () => subscription.stripeSubscriptionId ? short(subscription.stripeSubscriptionId, 14) : "—" }]} rows={[{}]} />
          : <div style={{ color: "var(--fg4)" }}>No subscription — free / default plan{billing ? ` · Stripe customer ${short(billing.stripeCustomerId, 14)}` : ""}.</div>)}

        {(canBilling || (canBilling && billing)) && section("Admin actions", (
          <div style={{ border: "1px solid var(--border)", borderRadius: 12, padding: 14, background: "var(--surface)", display: "flex", flexWrap: "wrap", gap: 16, alignItems: "flex-end" }}>
            <div>
              <label style={lbl}>Change / comp plan
                <span style={{ display: "flex", gap: 6, marginTop: 4 }}>
                  <select value={planKey} onChange={(e) => setPlanKey(e.target.value)} style={{ ...inp, width: 160, flex: "none" }}>
                    <option value="">Select plan…</option>
                    {((plans.data && plans.data.plans) || []).map((p) => <option key={p.key} value={p.key}>{p.name || p.key}</option>)}
                  </select>
                  <button onClick={changePlan} disabled={!planKey || busy === "plan"} style={{ ...btnGhost, color: "var(--accent)", borderColor: "var(--accent)" }}>{busy === "plan" ? "…" : "Apply"}</button>
                </span>
              </label>
            </div>
            {billing && billing.stripeCustomerId
              ? <div><label style={lbl}>Billing<a href={"https://dashboard.stripe.com/customers/" + billing.stripeCustomerId} target="_blank" rel="noreferrer" style={{ ...btnGhost, display: "inline-block", marginTop: 4, textDecoration: "none" }}>Open in Stripe ↗</a></label></div>
              : <div style={{ fontSize: 12, color: "var(--fg4)", alignSelf: "center" }}>No Stripe customer — refunds available once billing is live.</div>}
          </div>
        ))}

        {section(`Members (${members.length})`, <Table cols={[{ label: "User", render: (m) => m.email || <span style={{ color: "var(--fg4)" }}>{short(m.userId, 12)}</span> }, { label: "Name", render: (m) => m.name || "—" }, { label: "Role", render: (m) => <Pill>{m.role}</Pill> }, { label: "Joined", render: (m) => fmtDate(m.createdAt) }, { label: "", right: true, render: (m) => canImperson ? <button onClick={() => impersonate(m.userId)} disabled={busy === m.userId} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px" }}>Impersonate</button> : null }]} rows={members} empty="No members." />)}

        {org.type === "agency" && section(`Client accounts (${clients.length})`, <Table onRow={(c) => window.__nav && window.__nav(c.id)} cols={[{ label: "Client", render: (c) => <b>{c.name}</b> }, { label: "Created", render: (c) => fmtDate(c.createdAt) }]} rows={clients} empty="No client accounts." />)}

        {section(`Connected channels (${connections.length})`, <Table cols={[{ label: "Platform", render: (c) => c.platform }, { label: "Account", render: (c) => c.accountName }, { label: "Status", render: (c) => <Pill color={STATUS_COLOR[c.status]}>{c.status}</Pill> }, { label: "Token expires", render: (c) => fmtDate(c.expiresAt) }, { label: "Last sync", render: (c) => fmtDate(c.lastSyncedAt) }, { label: "", right: true, render: (c) => canConn && c.status !== "REVOKED" ? <button onClick={() => reconnect(c.id)} disabled={busy === c.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px", color: "var(--warning)", borderColor: "var(--warning)" }}>Force reconnect</button> : null }]} rows={connections} empty="No connected channels." />)}

        {usage && section(`API usage & cost (last ${usage.days} days)`,
          (usage.totals && usage.totals.calls)
            ? <div>
                <div style={{ display: "flex", gap: 20, marginBottom: 10 }}>
                  <div><span style={{ fontSize: 22, fontWeight: 800, fontFamily: "var(--font-display)" }}>{fmtN(usage.totals.calls)}</span> <span style={{ color: "var(--fg3)", fontSize: 12.5 }}>API calls</span></div>
                  <div><span style={{ fontSize: 22, fontWeight: 800, fontFamily: "var(--font-display)", color: "var(--accent)" }}>{fmtUsd(usage.totals.cost)}</span> <span style={{ color: "var(--fg3)", fontSize: 12.5 }}>metered cost</span></div>
                </div>
                <Table cols={[{ label: "Platform", render: (p) => PLAT_LABEL[p.platform] || p.platform }, { label: "Calls", right: true, render: (p) => fmtN(p.calls) }, { label: "Cost", right: true, render: (p) => p.cost > 0 ? <b>{fmtUsd(p.cost)}</b> : <span style={{ color: "var(--fg4)" }}>free</span> }]} rows={usage.byPlatform} empty="—" />
              </div>
            : <div style={{ color: "var(--fg4)" }}>No metered API usage in this window.</div>)}

        {section(`Recent admin activity (${recentAudit.length})`, <Table cols={[{ label: "Action", render: (a) => a.action }, { label: "Actor", render: (a) => short(a.actorUserId, 12) }, { label: "When", render: (a) => fmtDate(a.createdAt) }]} rows={recentAudit} empty="No admin activity for this org." />)}
      </div>
    );
  }

  function Users() {
    const [search, setSearch] = useState("");
    const { loading, data, error, reload } = useData(() => A.users({ search, limit: 100 }), [search]);
    const [busy, setBusy] = useState(null);
    const canExport = can("dsar.export"), canErase = can("dsar.delete");
    const download = (name, obj) => { const blob = new Blob([JSON.stringify(obj, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = name; a.click(); URL.revokeObjectURL(url); };
    const exportData = async (u) => { setBusy(u.id); try { const r = await A.dsarExport(u.id); download("dsar-" + (u.email || u.id) + ".json", r); } catch (e) { alert("Export failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const anonymize = async (u) => { if (!confirm("Anonymize " + (u.email || u.id) + "? This scrubs their personal data and revokes all sessions. Irreversible.")) return; setBusy(u.id); try { const r = await A.dsarAnonymize(u.id); alert("Anonymized. " + r.sessionsRevoked + " session(s) revoked."); await reload(); } catch (e) { alert("Failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const dsarCol = { label: "GDPR", right: true, render: (u) => (
      <span style={{ display: "flex", gap: 6, justifyContent: "flex-end" }}>
        {canExport && <button onClick={() => exportData(u)} disabled={busy === u.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 9px" }}>Export</button>}
        {canErase && <button onClick={() => anonymize(u)} disabled={busy === u.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 9px", color: "var(--danger)", borderColor: "var(--danger)" }}>Anonymize</button>}
      </span>) };
    const cols = [{ label: "Email", render: (u) => <b>{u.email || "—"}</b> }, { label: "Name", render: (u) => u.name || "—" }, { label: "Verified", render: (u) => u.emailVerified ? <Pill color="var(--success)">yes</Pill> : <Pill color="var(--warning)">no</Pill> }, { label: "Orgs", right: true, render: (u) => fmtN(u.orgs) }, { label: "Joined", render: (u) => fmtDate(u.createdAt) }];
    if (canExport || canErase) cols.push(dsarCol);
    return (<div>
      <input placeholder="Search by email or name…" value={search} onChange={(e) => setSearch(e.target.value)} style={{ ...inp, marginBottom: 14 }} />
      {loading ? <Loading /> : error ? <ErrBox e={error} /> : <Table cols={cols} rows={data.users} empty="No users match." />}
    </div>);
  }

  // Relative time-to-expiry with a severity colour (drives the connections table
  // + credential cards). Already-past → "expired" (danger).
  const expiryInfo = (d) => {
    if (!d) return { label: "—", color: "var(--fg4)" };
    const ms = new Date(d).getTime() - Date.now();
    if (ms <= 0) return { label: "expired", color: "var(--danger)" };
    const days = Math.floor(ms / 86400000), hrs = Math.floor(ms / 3600000);
    if (days >= 2) return { label: "in " + days + "d", color: "var(--fg2)" };
    if (hrs >= 1) return { label: "in " + hrs + "h", color: "var(--warning)" };
    return { label: "in <1h", color: "var(--warning)" };
  };

  const copyText = (t) => { try { navigator.clipboard.writeText(t); } catch (e) { const a = document.createElement("textarea"); a.value = t; document.body.appendChild(a); a.select(); document.execCommand("copy"); a.remove(); } };

  function PlatformCard({ p, render, canCreds, onPushed }) {
    const conn = p.connections.byStatus || {};
    const [open, setOpen] = useState(false);
    const [vals, setVals] = useState({});
    const [busyKey, setBusyKey] = useState(null);
    const inGroup = new Set((render && render.vars || []).filter((v) => v.present).map((v) => v.key));
    const canPush = render && render.enabled && canCreds;
    const block = p.envVars.filter((v) => vals[v.name]).map((v) => `${v.name}=${vals[v.name]}`).join("\n");
    const push = async (key) => { if (!vals[key]) return; if (!confirm(`Push ${key} to the Render env group${render.groupName ? " (" + render.groupName + ")" : ""}? It applies on the next deploy.`)) return; setBusyKey(key); try { await A.pushRenderEnv(key, vals[key]); setVals((s) => ({ ...s, [key]: "" })); if (onPushed) await onPushed(); alert(key + " written to Render."); } catch (e) { alert("Push failed (" + (e.status || "?") + "): " + ((e.body || "").slice(0, 120))); } setBusyKey(null); };
    return (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, background: "var(--surface)", padding: "13px 15px" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 9, gap: 8 }}>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{p.label}</div>
          <Pill color={p.configured ? "var(--success)" : "var(--warning)"}>{p.configured ? "configured" : "missing keys"}</Pill>
        </div>
        {p.envVars.length
          ? <div style={{ display: "flex", flexWrap: "wrap", gap: 5, marginBottom: 9 }}>
              {p.envVars.map((v) => <code key={v.name} title={v.present ? "set in this environment" : "NOT set"} style={{ fontSize: 10.5, padding: "2px 6px", borderRadius: 6, background: v.present ? "rgba(52,199,89,.14)" : "var(--danger-bg)", color: v.present ? "var(--success)" : "var(--danger)" }}>{v.present ? "✓" : "✗"} {v.name}</code>)}
            </div>
          : <div style={{ fontSize: 11.5, color: "var(--fg4)", marginBottom: 9 }}>Per-connection token — no app key.</div>}
        <div style={{ fontSize: 12, color: "var(--fg3)" }}>
          {p.connections.total ? Object.entries(conn).map(([k, v]) => `${v} ${k.toLowerCase()}`).join(" · ") : "no connections"}
          {p.connections.soonestExpiry ? <span> · next expiry {fmtDate(p.connections.soonestExpiry)}</span> : null}
        </div>
        {p.envVars.length ? <button onClick={() => setOpen(!open)} style={{ marginTop: 10, background: "none", border: 0, color: "var(--accent)", cursor: "pointer", fontFamily: "inherit", fontSize: 12, padding: 0 }}>{open ? "Hide keys" : "Manage keys"} {open ? "▴" : "▾"}</button> : null}
        {open && p.envVars.length ? (
          <div style={{ marginTop: 10, borderTop: "1px solid var(--border)", paddingTop: 11 }}>
            <div style={{ fontSize: 10.5, color: "var(--fg4)", marginBottom: 9, lineHeight: 1.5 }}>Scopes: {p.scopes.join(", ") || "—"}<br />Callback: <code style={{ fontSize: 10 }}>&lt;API base&gt;/api/social/oauth/callback/{p.platform}</code></div>
            {p.envVars.map((v) => (
              <div key={v.name} style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 7 }}>
                <input type="password" placeholder={v.name} value={vals[v.name] || ""} onChange={(e) => setVals((s) => ({ ...s, [v.name]: e.target.value }))} style={{ ...inp, fontSize: 12, padding: "6px 9px" }} />
                {canPush ? <button onClick={() => push(v.name)} disabled={busyKey === v.name || !vals[v.name]} title={inGroup.has(v.name) ? "Already in the group — overwrites" : "Push to Render"} style={{ ...btnGhost, fontSize: 11.5, padding: "5px 9px", whiteSpace: "nowrap", color: "var(--accent)", borderColor: "var(--accent)" }}>Push{inGroup.has(v.name) ? " ↻" : ""}</button> : null}
              </div>
            ))}
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 4 }}>
              <button onClick={() => copyText(block)} disabled={!block} style={{ ...btnGhost, fontSize: 11.5, padding: "5px 10px" }}>Copy .env block</button>
              <span style={{ fontSize: 10.5, color: "var(--fg4)" }}>{canPush ? `Push → ${render.groupName || "Render env group"} (applies on next deploy)` : "Render push off — paste the block into Render / your env file"}</span>
            </div>
          </div>
        ) : null}
      </div>
    );
  }

  function Connections() {
    const [status, setStatus] = useState("");
    const platforms = useData(() => A.socialPlatforms(), []);
    const render = useData(() => A.renderStatus(), []);
    const conns = useData(() => A.connections({ status, limit: 100 }), [status]);
    const [busy, setBusy] = useState(null);
    const canConn = can("connections.manage");
    const canCreds = can("social.credentials");
    const both = async () => { await conns.reload(); await platforms.reload(); };
    const refresh = async (id) => { setBusy(id); try { await A.refreshConnection(id); await both(); } catch (e) { alert("Refresh failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const reconnect = async (id) => { if (!confirm("Force reconnect? This revokes the token — the account owner must reconnect.")) return; setBusy(id); try { await A.quarantineConnection(id, "Quarantined from Social Ops"); await both(); } catch (e) { alert("Failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const label = (t) => <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 10 }}>{t}</div>;

    const rd = render.data || {};
    return (<div>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
        {label("Platform credentials")}
        <span style={{ fontSize: 11.5, marginBottom: 10, display: "inline-flex", alignItems: "center", gap: 6 }}>
          <span style={{ color: "var(--fg4)" }}>Render push</span>
          {render.loading ? <span style={{ color: "var(--fg4)" }}>…</span>
            : rd.enabled ? <Pill color="var(--success)">on{rd.groupName ? " · " + rd.groupName : ""}</Pill>
            : <Pill color="var(--fg4)">off</Pill>}
        </span>
      </div>
      {platforms.loading ? <Loading /> : platforms.error ? <ErrBox e={platforms.error} />
        : <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(244px, 1fr))", gap: 12, marginBottom: 28 }}>
            {platforms.data.platforms.map((p) => <PlatformCard key={p.platform} p={p} render={render.data} canCreds={canCreds} onPushed={render.reload} />)}
          </div>}

      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginBottom: 10 }}>
        {label("Connected accounts")}
        <span style={{ fontSize: 11.5, color: "var(--fg4)", marginBottom: 10 }}>↻ Tokens auto-refresh hourly (worker) — “Refresh now” forces one.</span>
      </div>
      <select value={status} onChange={(e) => setStatus(e.target.value)} style={{ ...inp, width: 200, marginBottom: 14 }}>
        <option value="">All statuses</option>{["CONNECTED", "EXPIRED", "REVOKED", "ERROR"].map((s) => <option key={s} value={s}>{s}</option>)}
      </select>
      {conns.loading ? <Loading /> : conns.error ? <ErrBox e={conns.error} />
        : <Table cols={[
            { label: "Platform", render: (c) => PLAT_LABEL[c.platform] || c.platform },
            { label: "Account", render: (c) => <span><b>{c.accountName}</b>{c.accountUsername ? <span style={{ color: "var(--fg4)" }}> @{c.accountUsername}</span> : null}</span> },
            { label: "Organization", render: (c) => c.orgName || "—" },
            { label: "Status", render: (c) => <span><Pill color={STATUS_COLOR[c.status]}>{c.status}</Pill>{c.lastError ? <span title={c.lastError} style={{ marginLeft: 6, color: "var(--danger)", cursor: "help" }}>⚠</span> : null}</span> },
            { label: "Token expires", render: (c) => { const e = expiryInfo(c.expiresAt); return <span style={{ color: e.color }}>{e.label}</span>; } },
            { label: "Auto-refresh", render: (c) => c.refreshable ? <Pill color="var(--success)">auto</Pill> : <span style={{ color: "var(--fg4)", fontSize: 12 }}>manual</span> },
            { label: "", right: true, max: 260, render: (c) => canConn ? <span style={{ display: "inline-flex", gap: 6, justifyContent: "flex-end" }}>
                <button onClick={() => refresh(c.id)} disabled={busy === c.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px" }}>Refresh now</button>
                {c.status !== "REVOKED" && <button onClick={() => reconnect(c.id)} disabled={busy === c.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px", color: "var(--warning)", borderColor: "var(--warning)" }}>Force reconnect</button>}
              </span> : null },
          ]} rows={conns.data.connections} empty="No connected channels yet." />}
    </div>);
  }

  function Audit() {
    const { loading, data, error } = useData(() => A.audit({ limit: 100 }).catch(() => ({ entries: [] })), []);
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const rows = (data && (data.entries || data.audit || data)) || [];
    return <Table cols={[{ label: "Action", render: (a) => a.action }, { label: "Actor", render: (a) => short(a.actorUserId, 12) }, { label: "Target", render: (a) => a.targetType ? `${a.targetType} ${short(a.targetId, 10)}` : "—" }, { label: "When", render: (a) => fmtDate(a.createdAt) }]} rows={Array.isArray(rows) ? rows : []} empty="No audit entries." />;
  }

  function Sparkline({ series, color, fmt }) {
    if (!series || series.length < 2) return <div style={{ color: "var(--fg4)", fontSize: 12 }}>Not enough history yet.</div>;
    const vals = series.map((p) => p.value);
    const max = Math.max(...vals), min = Math.min(...vals), span = max - min || 1;
    const w = 260, hgt = 44;
    const pts = series.map((p, i) => `${(i / (series.length - 1)) * w},${hgt - ((p.value - min) / span) * (hgt - 4) - 2}`).join(" ");
    return (
      <div>
        <svg viewBox={`0 0 ${w} ${hgt}`} width="100%" height={hgt} preserveAspectRatio="none" style={{ display: "block" }}>
          <polyline points={pts} fill="none" stroke={color || "var(--accent)"} strokeWidth="1.5" vectorEffect="non-scaling-stroke" />
        </svg>
        <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--fg4)", marginTop: 2 }}><span>{(fmt || fmtN)(min)}</span><span>{(fmt || fmtN)(max)}</span></div>
      </div>
    );
  }

  function Health() {
    const { loading, data, error, reload } = useData(() => A.healthDeep(), []);
    const sizeSeries = useData(() => A.metricsSeries("db.sizeBytes", 24), []);
    const connSeries = useData(() => A.metricsSeries("db.connections", 24), []);
    const watchdog = useData(() => A.watchdogStatus(24), []);
    const rb = useData(() => A.runbooks(), []);
    const [rbBusy, setRbBusy] = useState(null);
    const runRb = async (key) => { setRbBusy(key); try { const r = await A.runRunbook(key); alert(r.ran ? ("Ran — " + r.note) : ("Not run (" + r.reason + ")" + (r.readyInMs ? " · ready in ~" + Math.ceil(r.readyInMs / 60000) + " min" : ""))); await rb.reload(); } catch (e) { alert("Failed (" + (e.status || "?") + "). Needs the security capability."); } setRbBusy(null); };
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const STAT = { healthy: "var(--success)", degraded: "var(--warning)", down: "var(--danger)" };
    const dot = (ok) => <span style={{ width: 10, height: 10, borderRadius: 999, background: ok ? "var(--success)" : "var(--danger)", display: "inline-block" }} />;
    const cap = data.capacity || {};
    const svc = (name, chk) => (
      <div style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)", display: "flex", alignItems: "center", gap: 10 }}>
        {dot(chk.ok)}
        <div style={{ flex: 1 }}><b>{name}</b>{chk.detail ? <div style={{ fontSize: 11.5, color: "var(--danger)" }}>{String(chk.detail).slice(0, 60)}</div> : null}</div>
        <div style={{ fontSize: 12, color: "var(--fg3)", textAlign: "right" }}>{chk.ok ? "ok" : "down"}{chk.latencyMs != null ? <div style={{ fontSize: 11, color: "var(--fg4)" }}>{chk.latencyMs} ms</div> : null}</div>
      </div>
    );
    const gauge = (label, pct, warn, sub) => (
      <div style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)" }}>
        <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12, marginBottom: 6 }}><span style={{ color: "var(--fg3)" }}>{label}</span><b style={{ color: warn ? "var(--warning)" : "var(--fg1)" }}>{sub}</b></div>
        <div style={{ height: 7, borderRadius: 999, background: "var(--bg-muted)", overflow: "hidden" }}><div style={{ width: Math.min(100, pct) + "%", height: "100%", background: warn ? "var(--warning)" : "var(--accent)" }} /></div>
      </div>
    );
    return (
      <div style={{ maxWidth: 920 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 16 }}>
          <span style={{ padding: "6px 14px", borderRadius: 999, fontWeight: 700, fontSize: 13, color: "#fff", background: STAT[data.status] || "var(--fg3)", textTransform: "uppercase", letterSpacing: ".04em" }}>{data.status}</span>
          <span style={{ color: "var(--fg3)", fontSize: 12.5 }}>checked {fmtAgo(data.checkedAt)}</span>
          <button onClick={reload} style={{ ...btnGhost, marginLeft: "auto", fontSize: 12, padding: "6px 12px" }}>Refresh</button>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 18 }}>
          {svc("Postgres", data.checks.postgres)}
          {svc("Redis", data.checks.redis)}
          {svc("Migrations", data.checks.migrations)}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 18 }}>
          {gauge("DB connections", cap.connectionsPct, cap.connectionsPct >= 80, `${fmtN(cap.connections)} / ${fmtN(cap.maxConnections)}`)}
          {gauge("Database size", Math.min(100, (cap.dbSizeBytes / (8 * 1024 ** 3)) * 100), cap.dbSizeWarn, fmtBytes(cap.dbSizeBytes))}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 18 }}>
          <div style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)" }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>DB size · 24h</div>
            {sizeSeries.data ? <Sparkline series={sizeSeries.data.series} fmt={fmtBytes} /> : <Loading />}
          </div>
          <div style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)" }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>DB connections · 24h</div>
            {connSeries.data ? <Sparkline series={connSeries.data.series} color="var(--leap-teal)" /> : <Loading />}
          </div>
        </div>
        {watchdog.data && (
          <div style={{ marginBottom: 18 }}>
            <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginBottom: 8 }}>
              <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>External watchdog</div>
              <span style={{ fontSize: 11.5, color: "var(--fg4)" }}>{watchdog.data.lastCheckedAt ? "last check " + fmtAgo(watchdog.data.lastCheckedAt) : "not running — start apps/watchdog"}</span>
            </div>
            {watchdog.data.targets.length ? (
              <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(160px, 1fr))", gap: 10 }}>
                {watchdog.data.targets.map((t) => (
                  <div key={t.target} style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "11px 13px", background: "var(--surface)" }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>{dot(t.ok)}<b style={{ textTransform: "capitalize" }}>{t.target}</b></div>
                    <div style={{ fontSize: 11.5, color: "var(--fg3)", marginTop: 5 }}>{t.uptimePct != null ? t.uptimePct + "% up · 24h" : "—"}{t.latencyMs != null ? " · " + t.latencyMs + "ms" : ""}</div>
                  </div>
                ))}
              </div>
            ) : <div style={{ color: "var(--fg4)", fontSize: 12.5 }}>No watchdog checks yet. Run <code>apps/watchdog</code> (a separate process) to populate this.</div>}
          </div>
        )}

        {rb.data && rb.data.runbooks && rb.data.runbooks.length > 0 && (
          <div style={{ marginBottom: 18 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Runbooks <span style={{ fontWeight: 500, textTransform: "none", color: "var(--fg4)" }}>— safe, idempotent, rate-limited self-healing</span></div>
            <Table rows={rb.data.runbooks} cols={[
              { label: "Runbook", render: (x) => <b>{x.label}</b> },
              { label: "What it does", render: (x) => <span style={{ color: "var(--fg3)", fontSize: 12.5 }}>{x.description}</span>, max: 420 },
              { label: "Last run", render: (x) => x.lastRunAt ? fmtAgo(x.lastRunAt) : "never" },
              { label: "", right: true, render: (x) => !can("security.act") ? <span style={{ color: "var(--fg4)", fontSize: 12 }}>—</span> : x.readyInMs > 0 ? <span style={{ color: "var(--fg4)", fontSize: 12 }}>cooldown ~{Math.ceil(x.readyInMs / 60000)}m</span> : <button onClick={() => runRb(x.key)} disabled={rbBusy === x.key} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px", color: "var(--accent)", borderColor: "var(--accent)" }}>{rbBusy === x.key ? "…" : "Run"}</button> },
            ]} />
          </div>
        )}

        <div style={{ color: "var(--fg4)", fontSize: 12.5, lineHeight: 1.6 }}>Live in-app probe + a 24h metric trend (sampled every 5&nbsp;min). An in-infra probe can't witness its own infra being fully down — the <b>source of truth for "it's down" is external</b>: the <b>watchdog</b> above runs as a separate process (auto-raises incidents + notifies on state change); pair it with a hosted uptime monitor (Better Stack / UptimeRobot) on <code>/health</code>, Sentry (<code>SENTRY_DSN</code>), and platform auto-restart (Render health checks / Docker <code>restart</code> policy). Runbooks are the first rung of self-healing; auto-remediation stays gated + rate-limited.</div>
      </div>
    );
  }

  function Security() {
    const [hours, setHours] = useState(24);
    const [sev, setSev] = useState("");
    const summary = useData(() => A.securitySummary(hours), [hours]);
    const events = useData(() => A.securityEvents({ sinceHours: hours, severity: sev, limit: 200 }), [hours, sev]);
    const blocked = useData(() => A.blockedIps(), []);
    const incidents = useData(() => A.incidents({}), []);
    const [busy, setBusy] = useState(null);
    const block = async (ip) => { const reason = prompt("Reason for blocking " + ip + "?", "manual block from Security board"); if (reason == null) return; setBusy(ip); try { await A.blockIp({ ip, reason, hours: 0 }); await blocked.reload(); await summary.reload(); } catch (e) { alert("Block failed (" + (e.status || "?") + "). Superadmin only."); } setBusy(null); };
    const unblock = async (ip) => { if (!confirm("Unblock " + ip + "?")) return; setBusy(ip); try { await A.unblockIp(ip); await blocked.reload(); } catch (e) { alert("Failed"); } setBusy(null); };
    const forceLogout = async (userId) => { if (!confirm("Revoke ALL sessions for " + userId + "?")) return; setBusy(userId); try { const r = await A.forceLogout(userId); alert("Revoked " + r.revoked + " session(s)."); } catch (e) { alert("Failed (" + (e.status || "?") + "). Superadmin only."); } setBusy(null); };
    const resolveInc = async (id) => { try { await A.updateIncident(id, "resolved"); await incidents.reload(); } catch (e) { alert("Failed"); } };
    const s = summary.data;
    const sevPill = (v) => <Pill color={SEV_COLOR[v]}>{v}</Pill>;
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14, gap: 10 }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>SIEM-lite feed from auth / tenant / rate-limit hooks. Detection escalates bursts; response actions need the security capability (support / superadmin) + audited.</div>
          <select value={hours} onChange={(e) => setHours(Number(e.target.value))} style={{ ...inp, width: 140, flex: "none" }}>{[1, 24, 168].map((hh) => <option key={hh} value={hh}>Last {hh >= 168 ? "7 days" : hh + "h"}</option>)}</select>
        </div>
        {s && <div style={{ display: "flex", flexWrap: "wrap", gap: 12, marginBottom: 18 }}>
          {[["Events", fmtN(s.total), null], ["High / critical", fmtN((s.bySeverity.high || 0) + (s.bySeverity.critical || 0)), "var(--danger)"], ["Medium", fmtN(s.bySeverity.medium || 0), "var(--warning)"], ["Blocked IPs", fmtN(s.blockedIps), null]].map(([l, v, c], i) => (
            <div key={i} style={{ border: "1px solid var(--border)", borderRadius: 14, padding: "14px 18px", background: "var(--surface)", minWidth: 130 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{l}</div>
              <div style={{ fontSize: 24, fontWeight: 800, fontFamily: "var(--font-display)", color: c || "var(--fg1)", marginTop: 4 }}>{v}</div>
            </div>
          ))}
        </div>}

        {blocked.data && blocked.data.blocked.length > 0 && <div style={{ marginBottom: 18 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Blocked IPs</div>
          <Table rows={blocked.data.blocked} cols={[
            { label: "IP", render: (b) => <span style={{ fontFamily: "var(--font-mono)" }}>{b.ip}</span> },
            { label: "Reason", render: (b) => b.reason || "—" },
            { label: "Since", render: (b) => fmtAgo(b.createdAt) },
            { label: "Expires", render: (b) => b.expiresAt ? fmtDate(b.expiresAt) : "permanent" },
            { label: "", right: true, render: (b) => can("security.act") ? <button onClick={() => unblock(b.ip)} disabled={busy === b.ip} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px" }}>Unblock</button> : <span style={{ color: "var(--fg4)", fontSize: 12 }}>—</span> },
          ]} />
        </div>}

        {incidents.data && incidents.data.incidents.length > 0 && <div style={{ marginBottom: 18 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Incidents</div>
          <Table rows={incidents.data.incidents} cols={[
            { label: "Kind", render: (x) => <Pill color={x.kind === "security" ? "var(--danger)" : "var(--warning)"}>{x.kind}</Pill> },
            { label: "Title", render: (x) => x.title },
            { label: "Severity", render: (x) => sevPill(x.severity) },
            { label: "Status", render: (x) => <Pill color={x.status === "resolved" ? "var(--success)" : "var(--warning)"}>{x.status}</Pill> },
            { label: "Opened", render: (x) => fmtAgo(x.openedAt) },
            { label: "", right: true, render: (x) => x.status !== "resolved" && can("security.act") ? <button onClick={() => resolveInc(x.id)} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px", color: "var(--success)", borderColor: "var(--success)" }}>Resolve</button> : null },
          ]} />
        </div>}

        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>Event feed</div>
          <select value={sev} onChange={(e) => setSev(e.target.value)} style={{ ...inp, width: 150, flex: "none", padding: "5px 8px" }}><option value="">All severities</option>{["critical", "high", "medium", "low"].map((x) => <option key={x} value={x}>{x}</option>)}</select>
        </div>
        {events.loading ? <Loading /> : events.error ? <ErrBox e={events.error} /> : <Table rows={events.data.events} empty="No security events in this window." cols={[
          { label: "Type", render: (e) => <span style={{ fontWeight: 600 }}>{secType(e.type)}</span> },
          { label: "Severity", render: (e) => sevPill(e.severity) },
          { label: "IP", render: (e) => e.ip ? <span style={{ fontFamily: "var(--font-mono)", fontSize: 12.5 }}>{e.ip}</span> : "—" },
          { label: "User", render: (e) => e.userId ? short(e.userId, 12) : "—" },
          { label: "Path", render: (e) => e.path || "—" },
          { label: "When", render: (e) => fmtAgo(e.createdAt) },
          { label: "", right: true, render: (e) => !can("security.act") ? null : <span style={{ display: "flex", gap: 6, justifyContent: "flex-end" }}>
            {e.ip ? <button onClick={() => block(e.ip)} disabled={busy === e.ip} style={{ ...btnGhost, fontSize: 11.5, padding: "4px 8px", color: "var(--danger)", borderColor: "var(--danger)" }}>Block IP</button> : null}
            {e.userId && e.userId.startsWith("usr_") ? <button onClick={() => forceLogout(e.userId)} disabled={busy === e.userId} style={{ ...btnGhost, fontSize: 11.5, padding: "4px 8px" }}>Force logout</button> : null}
          </span> },
        ]} />}
      </div>
    );
  }

  function Admins() {
    const { loading, data, error, reload } = useData(() => A.adminsList(), []);
    const [adding, setAdding] = useState(false);
    const [q, setQ] = useState("");
    const [busy, setBusy] = useState(null);
    const found = useData(() => (q ? A.users({ search: q, limit: 8 }) : Promise.resolve({ users: [] })), [q]);
    const setRole = async (userId, role) => { setBusy(userId); try { await A.grantAdmin(userId, role); await reload(); } catch (e) { alert("Failed: " + (e.status || "")); } setBusy(null); };
    const revoke = async (userId) => { if (!confirm("Revoke admin access for this user?")) return; setBusy(userId); try { await A.revokeAdmin(userId); await reload(); } catch (e) { alert("Failed"); } setBusy(null); };
    const add = async (u, role) => { try { await A.grantAdmin(u.id, role); setAdding(false); setQ(""); await reload(); } catch (e) { alert("Failed: " + (e.status || "")); } };
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const admins = (data && data.admins) || [];
    return (
      <div style={{ maxWidth: 880 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>Back-office users who can access this cockpit, and their role-rights. Superadmin only.</div>
          <button onClick={() => setAdding((a) => !a)} style={{ ...btnGhost, color: "var(--accent)", borderColor: "var(--accent)" }}>{adding ? "Cancel" : "+ Add admin"}</button>
        </div>
        {adding && (
          <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: 16, marginBottom: 16, background: "var(--surface)" }}>
            <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8 }}>Grant admin access — find a platform user, then pick a role</div>
            <input placeholder="Search users by email or name…" value={q} onChange={(e) => setQ(e.target.value)} style={{ ...inp, marginBottom: 8 }} autoFocus />
            {((found.data && found.data.users) || []).map((u) => (
              <div key={u.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "9px 4px", borderTop: "1px solid var(--border)" }}>
                <div style={{ flex: 1, minWidth: 0 }}><b>{u.email}</b> <span style={{ color: "var(--fg4)" }}>{u.name || ""}</span></div>
                {ROLE_KEYS.map((role) => <button key={role} onClick={() => add(u, role)} style={{ ...btnGhost, fontSize: 12, padding: "5px 9px" }}>{ROLE_LABEL[role]}</button>)}
              </div>
            ))}
            {q && found.data && !((found.data.users) || []).length && <div style={{ color: "var(--fg4)", padding: 8 }}>No users match.</div>}
          </div>
        )}
        <Table rows={admins} empty="No admins granted yet. (The dev user is superadmin via DEV_AUTH — add real admins here.)" cols={[
          { label: "User", render: (a) => <b>{a.email || short(a.userId, 12)}</b> },
          { label: "Name", render: (a) => a.name || "—" },
          { label: "Role", render: (a) => <select value={a.role} disabled={busy === a.userId} onChange={(e) => setRole(a.userId, e.target.value)} style={{ ...inp, padding: "6px 8px", width: 150, flex: "none" }}>{ROLE_KEYS.map((role) => <option key={role} value={role}>{ROLE_LABEL[role]}</option>)}</select> },
          { label: "Added", render: (a) => fmtDate(a.createdAt) },
          { label: "", right: true, render: (a) => <button onClick={() => revoke(a.userId)} disabled={busy === a.userId} style={{ ...btnGhost, color: "var(--danger)", borderColor: "var(--danger)", fontSize: 12, padding: "5px 9px" }}>Revoke</button> },
        ]} />
      </div>
    );
  }

  // ── Usage & cost (Phase 2) ───────────────────────────────────────────────
  function MiniBars({ daily }) {
    if (!daily || !daily.length) return null;
    const max = Math.max(...daily.map((d) => d.cost), 0.0001);
    return (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: "14px 16px", background: "var(--surface)", marginBottom: 18 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 10 }}>Metered cost per day</div>
        <div style={{ display: "flex", alignItems: "flex-end", gap: 3, height: 90 }}>
          {daily.map((d, i) => (
            <div key={i} title={`${d.day} · ${fmtUsd(d.cost)} · ${fmtN(d.calls)} calls`} style={{ flex: 1, minWidth: 2, height: Math.max(2, (d.cost / max) * 88), background: "var(--accent)", opacity: 0.55 + 0.45 * (d.cost / max), borderRadius: "3px 3px 0 0" }} />
          ))}
        </div>
        <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--fg4)", marginTop: 6 }}><span>{daily[0].day}</span><span>{daily[daily.length - 1].day}</span></div>
      </div>
    );
  }

  function Usage() {
    const [days, setDays] = useState(30);
    const { loading, data, error } = useData(() => A.usage({ days }), [days]);
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const { overview, byOrg } = data;
    const t = overview.totals || { calls: 0, units: 0, cost: 0 };
    const card = (label, value, sub, accent) => (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: "16px 18px", background: "var(--surface)", minWidth: 150 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{label}</div>
        <div style={{ fontSize: 26, fontWeight: 800, fontFamily: "var(--font-display)", color: accent || "var(--fg1)", marginTop: 4 }}>{value}</div>
        {sub && <div style={{ fontSize: 12, color: "var(--fg3)", marginTop: 4 }}>{sub}</div>}
      </div>
    );
    const avgDaily = t.cost / (overview.days || 30);
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>Metered platform API calls and their cost. Internal accounting — not billed to customers yet.</div>
          <select value={days} onChange={(e) => setDays(Number(e.target.value))} style={{ ...inp, width: 150, flex: "none" }}>
            {[7, 30, 90].map((d) => <option key={d} value={d}>Last {d} days</option>)}
          </select>
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 14, marginBottom: 18 }}>
          {card("API calls", fmtN(t.calls), `${fmtN(t.units)} billable units`)}
          {card("Metered cost", fmtUsd(t.cost), `${fmtUsd(avgDaily)}/day avg`, "var(--accent)")}
          {card("Projected / mo", fmtUsd(avgDaily * 30), "at current run-rate")}
          {card("Platforms", fmtN((overview.byPlatform || []).length), (overview.byPlatform || []).filter((p) => p.cost > 0).map((p) => PLAT_LABEL[p.platform] || p.platform).join(", ") || "none metered")}
        </div>
        <MiniBars daily={overview.daily} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 18 }}>
          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>By platform</div>
            <Table cols={[{ label: "Platform", render: (p) => PLAT_LABEL[p.platform] || p.platform }, { label: "Calls", right: true, render: (p) => fmtN(p.calls) }, { label: "Cost", right: true, render: (p) => p.cost > 0 ? <b>{fmtUsd(p.cost)}</b> : <span style={{ color: "var(--fg4)" }}>free</span> }]} rows={overview.byPlatform} empty="No usage yet." />
          </div>
          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>By operation</div>
            <Table cols={[{ label: "Operation", render: (o) => OP_LABEL[o.operation] || o.operation }, { label: "Calls", right: true, render: (o) => fmtN(o.calls) }, { label: "Cost", right: true, render: (o) => o.cost > 0 ? <b>{fmtUsd(o.cost)}</b> : <span style={{ color: "var(--fg4)" }}>free</span> }]} rows={overview.byOperation} empty="No usage yet." />
          </div>
        </div>
        <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Top organizations by cost — margin vs. plan price</div>
        <Table cols={[
          { label: "Organization", render: (o) => <b>{o.name}</b> },
          { label: "Category", render: (o) => <Pill color={o.parentId ? "var(--accent)" : o.type === "agency" ? "var(--leap-teal)" : undefined}>{CATEGORY[orgCategory(o)]}</Pill> },
          { label: "Calls", right: true, render: (o) => fmtN(o.calls) },
          { label: "Cost", right: true, render: (o) => <b>{fmtUsd(o.cost)}</b> },
          { label: "Plan / mo", right: true, render: (o) => o.monthlyRevenue != null ? fmtUsd(o.monthlyRevenue) : <span style={{ color: "var(--fg4)" }}>—</span> },
          { label: "Margin", right: true, render: (o) => { if (o.monthlyRevenue == null) return <span style={{ color: "var(--fg4)" }} title="Billed via parent agency / no own plan">n/a</span>; const m = o.monthlyRevenue - o.cost; return <b style={{ color: m >= 0 ? "var(--success)" : "var(--danger)" }}>{fmtUsd(m)}</b>; } },
        ]} rows={byOrg} empty="No metered usage attributed to any org yet." />
        <div style={{ fontSize: 12, color: "var(--fg4)", marginTop: 10, lineHeight: 1.6 }}>Cost is metered API spend (USD) over the window. Plan price is the org's monthly list price; agency clients bill through their parent agency, so they show <b>n/a</b> margin. Prices are set in <b>Cost catalog</b>.</div>
      </div>
    );
  }

  // ── AI assistant (read-only) ─────────────────────────────────────────────
  // Status + metered spend for the Leap Assistant's LLM. The API key is a deploy
  // secret (env var) — it's never read here; we only report configured / not, and
  // the metered Anthropic usage. Rotation stays in the hosting secret store.
  function AI() {
    const [days, setDays] = useState(30);
    const { loading, data, error } = useData(() => A.ai({ days }), [days]);
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const t = data.totals || { calls: 0, units: 0, cost: 0 };
    const on = !!data.configured;
    const avgDaily = t.cost / (data.days || 30);
    const card = (label, value, sub, accent) => (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: "16px 18px", background: "var(--surface)", minWidth: 150 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{label}</div>
        <div style={{ fontSize: 26, fontWeight: 800, fontFamily: "var(--font-display)", color: accent || "var(--fg1)", marginTop: 4 }}>{value}</div>
        {sub && <div style={{ fontSize: 12, color: "var(--fg3)", marginTop: 4 }}>{sub}</div>}
      </div>
    );
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>The Leap Assistant's language model — on-arrival briefing and the in-app copilot &amp; support.</div>
          <select value={days} onChange={(e) => setDays(Number(e.target.value))} style={{ ...inp, width: 150, flex: "none" }}>
            {[7, 30, 90].map((d) => <option key={d} value={d}>Last {d} days</option>)}
          </select>
        </div>
        {/* status banner */}
        <div style={{ display: "flex", alignItems: "center", gap: 12, border: "1px solid var(--border)", borderLeft: "4px solid " + (on ? "var(--success)" : "var(--warning)"), borderRadius: 12, padding: "14px 16px", background: "var(--surface)", marginBottom: 18 }}>
          <Icon name={on ? "sparkles" : "power"} size={20} color={on ? "var(--success)" : "var(--warning)"} />
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 700, fontSize: 14 }}>{on ? "AI assistant active" : "AI assistant not configured"}</div>
            <div style={{ fontSize: 12.5, color: "var(--fg3)", marginTop: 2 }}>
              {on
                ? <React.Fragment>Anthropic key present · model <b style={{ fontFamily: "var(--font-mono)" }}>{data.model}</b></React.Fragment>
                : <React.Fragment>Set <b style={{ fontFamily: "var(--font-mono)" }}>ANTHROPIC_API_KEY</b> in the deploy environment to enable. Without it the assistant serves its deterministic briefing only.</React.Fragment>}
            </div>
          </div>
          <Pill color={on ? "var(--success)" : "var(--warning)"}>{on ? "Enabled" : "Fallback"}</Pill>
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 14, marginBottom: 18 }}>
          {card("LLM calls", fmtN(t.calls), `${fmtN(t.units)} tokens metered`)}
          {card("Metered cost", fmtUsd(t.cost), `${fmtUsd(avgDaily)}/day avg`, "var(--accent)")}
          {card("Projected / mo", fmtUsd(avgDaily * 30), "at current run-rate")}
        </div>
        <MiniBars daily={data.daily} />
        {data.caps && (
          <div style={{ border: "1px solid var(--border)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)", marginBottom: 18, fontSize: 12.5, color: "var(--fg2)" }}>
            <span style={{ fontWeight: 700, color: "var(--fg1)" }}>Daily cap per workspace:</span>{" "}
            {data.caps.callCap > 0 ? fmtN(data.caps.callCap) + " calls" : "unlimited calls"} · {data.caps.tokenCap > 0 ? fmtN(data.caps.tokenCap) + " tokens" : "unlimited tokens"}
            {data.caps.peakToday
              ? <span style={{ color: "var(--fg3)" }}> · busiest workspace today: {fmtN(data.caps.peakToday.calls)} calls / {fmtN(data.caps.peakToday.units)} tokens</span>
              : <span style={{ color: "var(--fg4)" }}> · no assistant usage today</span>}
            <span style={{ color: "var(--fg4)" }}> · resets 00:00 UTC. Set via <span style={{ fontFamily: "var(--font-mono)" }}>ASSISTANT_DAILY_CALL_CAP</span> / <span style={{ fontFamily: "var(--font-mono)" }}>_TOKEN_CAP</span>.</span>
          </div>
        )}
        <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>By operation</div>
        <Table cols={[
          { label: "Operation", render: (o) => OP_LABEL[o.operation] || o.operation },
          { label: "Calls", right: true, render: (o) => fmtN(o.calls) },
          { label: "Tokens", right: true, render: (o) => fmtN(o.units) },
          { label: "Cost", right: true, render: (o) => o.cost > 0 ? <b>{fmtUsd(o.cost)}</b> : <span style={{ color: "var(--fg4)" }}>free</span> },
        ]} rows={data.byOperation} empty="No assistant usage yet." />

        {/* Engagement analytics (Phase 5) */}
        {data.engagement && (() => {
          const eng = data.engagement;
          const bk = eng.byKind || {};
          const kindRows = Object.keys(bk).map((k) => ({ kind: k, n: bk[k] })).sort((a, b) => b.n - a.n);
          return (
            <React.Fragment>
              <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", margin: "22px 0 10px" }}>Engagement</div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 12, marginBottom: 16 }}>
                {card("Opens", fmtN(bk.open || 0), `${fmtN((eng.totals || {}).users || 0)} users`)}
                {card("Chats", fmtN(bk.chat_message || 0))}
                {card("Actions taken", fmtN(eng.confirmed || 0), `${fmtN(eng.dismissed || 0)} dismissed`, "var(--accent)")}
                {card("Confirm rate", eng.confirmRate == null ? "—" : Math.round(eng.confirmRate * 100) + "%")}
              </div>
              <MiniBars daily={(eng.dailyOpens || []).map((r) => ({ day: r.day, cost: r.n, calls: r.n }))} />
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
                <div>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>By interaction</div>
                  <Table cols={[{ label: "Interaction", render: (r) => KIND_LABEL[r.kind] || r.kind }, { label: "Count", right: true, render: (r) => fmtN(r.n) }]} rows={kindRows} empty="No activity yet." />
                </div>
                <div>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Most-actioned topics</div>
                  <Table cols={[{ label: "Topic", render: (r) => r.topic }, { label: "Actions", right: true, render: (r) => fmtN(r.n) }]} rows={eng.topActioned || []} empty="No card actions yet." />
                </div>
              </div>
              {(eng.topAssist || []).length > 0 && (
                <div style={{ marginTop: 14 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Where users ask the copilot for help</div>
                  <Table cols={[{ label: "Topic", render: (r) => r.topic }, { label: "Assist clicks", right: true, render: (r) => fmtN(r.n) }]} rows={eng.topAssist} empty="—" />
                </div>
              )}
            </React.Fragment>
          );
        })()}

        <div style={{ fontSize: 12, color: "var(--fg4)", marginTop: 14, lineHeight: 1.6 }}>The API key is a deploy secret (environment variable) — it is never stored in the app database or shown here. Rotate it in your hosting platform's secret store. Per-token pricing is set in <b>Cost catalog</b> (<span style={{ fontFamily: "var(--font-mono)" }}>anthropic</span>). Engagement is anonymous product telemetry (no message content).</div>
      </div>
    );
  }

  function CostCatalog() {
    const { loading, data, error, reload } = useData(() => A.costRates(), []);
    const [edit, setEdit] = useState(null); // {platform, operation, unitCost, active}
    const [busy, setBusy] = useState(false);
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const rates = (data && data.rates) || [];
    const save = async () => {
      setBusy(true);
      try { await A.saveCostRate({ platform: edit.platform, operation: edit.operation, unitCost: Number(edit.unitCost), active: edit.active }); setEdit(null); await reload(); }
      catch (e) { alert("Save failed (" + (e.status || "?") + "). Editing prices is superadmin-only."); }
      setBusy(false);
    };
    // group by platform
    const byPlat = {};
    rates.forEach((r) => { (byPlat[r.platform] = byPlat[r.platform] || []).push(r); });
    return (
      <div style={{ maxWidth: 900 }}>
        <div style={{ color: "var(--fg3)", fontSize: 13.5, marginBottom: 16 }}>Runtime price list — USD per API call/unit. Metering deducts these as calls happen. Seeded from the X pay-per-use model; edit as platform pricing changes. Editing needs the billing capability (finance / superadmin).</div>
        {Object.keys(byPlat).sort((a, b) => (a === "x" ? -1 : b === "x" ? 1 : a.localeCompare(b))).map((plat) => (
          <div key={plat} style={{ marginBottom: 18 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg2)", marginBottom: 8, display: "flex", alignItems: "center", gap: 8 }}>{PLAT_LABEL[plat] || plat}{plat === "x" && <Pill color="var(--accent)">metered</Pill>}</div>
            <Table rows={byPlat[plat]} cols={[
              { label: "Operation", render: (r) => OP_LABEL[r.operation] || r.operation },
              { label: "Unit cost", right: true, render: (r) => edit && edit.id === r.id
                ? <input type="number" step="0.001" min="0" value={edit.unitCost} onChange={(e) => setEdit({ ...edit, unitCost: e.target.value })} style={{ ...inp, width: 110, flex: "none", padding: "5px 8px", textAlign: "right" }} autoFocus />
                : <span style={{ fontFamily: "var(--font-mono)", color: r.unitCost > 0 ? "var(--fg1)" : "var(--fg4)" }}>{r.unitCost > 0 ? fmtUnit(r.unitCost) : "free"}</span> },
              { label: "Active", render: (r) => edit && edit.id === r.id
                ? <input type="checkbox" checked={edit.active} onChange={(e) => setEdit({ ...edit, active: e.target.checked })} />
                : (r.active ? <Pill color="var(--success)">on</Pill> : <Pill color="var(--fg4)">off</Pill>) },
              { label: "Updated", render: (r) => fmtDate(r.updatedAt) },
              { label: "", right: true, render: (r) => edit && edit.id === r.id
                ? <span style={{ display: "flex", gap: 6, justifyContent: "flex-end" }}><button onClick={save} disabled={busy} style={{ ...btnGhost, color: "var(--accent)", borderColor: "var(--accent)", fontSize: 12, padding: "5px 10px" }}>{busy ? "…" : "Save"}</button><button onClick={() => setEdit(null)} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px" }}>Cancel</button></span>
                : !can("billing.write") ? <span style={{ color: "var(--fg4)", fontSize: 12 }}>—</span>
                : <button onClick={() => setEdit({ id: r.id, platform: r.platform, operation: r.operation, unitCost: r.unitCost, active: r.active })} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px" }}>Edit</button> },
            ]} />
          </div>
        ))}
      </div>
    );
  }

  // ── Promo / free-account codes (Phase 4) ─────────────────────────────────
  function Promo() {
    const codes = useData(() => A.promoCodes(), []);
    const [creating, setCreating] = useState(false);
    const [openCode, setOpenCode] = useState(null); // codeId whose redemptions are shown
    const reds = useData(() => (openCode ? A.promoRedemptions(openCode) : Promise.resolve({ redemptions: [] })), [openCode]);
    const [busy, setBusy] = useState(null);
    const [form, setForm] = useState({ code: "", grantType: "free_plan", planKey: "pro", durationMonths: "3", maxRedemptions: "", perUserLimit: "1", campaign: "", percent: "", creditsAmount: "" });
    const toggle = async (c) => { setBusy(c.id); try { await A.setPromoActive(c.id, !c.active); await codes.reload(); } catch (e) { alert("Failed (" + (e.status || "?") + "). Superadmin only."); } setBusy(null); };
    const revoke = async (id) => { if (!confirm("Revoke this grant? Any promo subscription is cleared.")) return; setBusy(id); try { const r = await A.revokeRedemption(id); await reds.reload(); await codes.reload(); if (r.cleared) alert("Grant revoked and promo subscription cleared."); } catch (e) { alert("Failed (" + (e.status || "?") + ")."); } setBusy(null); };
    const submit = async () => {
      const body = { code: form.code, grantType: form.grantType, campaign: form.campaign || undefined, perUserLimit: form.perUserLimit || 1, maxRedemptions: form.maxRedemptions || null };
      if (form.grantType === "free_plan" || form.grantType === "plan") { body.planKey = form.planKey; body.durationMonths = form.durationMonths || null; }
      if (form.grantType === "percent_off") { body.percent = form.percent; body.durationMonths = form.durationMonths || null; }
      if (form.grantType === "credits") { body.creditsAmount = form.creditsAmount; }
      setBusy("new");
      try { await A.createPromoCode(body); setCreating(false); setForm({ ...form, code: "", campaign: "" }); await codes.reload(); }
      catch (e) { alert(e.status === 409 ? "That code already exists." : "Create failed (" + (e.status || "?") + "). Superadmin only."); }
      setBusy(null);
    };
    const isPlan = form.grantType === "free_plan" || form.grantType === "plan";
    return (
      <div style={{ maxWidth: 1000 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>Free-account & promo codes. Redeeming grants an entitlement through the normal engine (plan grants create a promo subscription). Create/disable needs the billing capability + audited.</div>
          {can("billing.write") && <button onClick={() => setCreating((v) => !v)} style={{ ...btnGhost, color: "var(--accent)", borderColor: "var(--accent)" }}>{creating ? "Cancel" : "+ New code"}</button>}
        </div>
        {creating && (
          <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: 16, marginBottom: 16, background: "var(--surface)" }}>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
              <label style={lbl}>Code<input value={form.code} onChange={(e) => setForm({ ...form, code: e.target.value.toUpperCase() })} placeholder="LAUNCH2026" style={inp} /></label>
              <label style={lbl}>Grant type<select value={form.grantType} onChange={(e) => setForm({ ...form, grantType: e.target.value })} style={inp}>{["free_plan", "plan", "percent_off", "credits"].map((g) => <option key={g} value={g}>{GRANT_LABEL[g]}</option>)}</select></label>
              <label style={lbl}>Campaign<input value={form.campaign} onChange={(e) => setForm({ ...form, campaign: e.target.value })} placeholder="Product Hunt" style={inp} /></label>
              {isPlan && <label style={lbl}>Plan key<input value={form.planKey} onChange={(e) => setForm({ ...form, planKey: e.target.value })} placeholder="pro" style={inp} /></label>}
              {form.grantType === "percent_off" && <label style={lbl}>Percent off<input type="number" value={form.percent} onChange={(e) => setForm({ ...form, percent: e.target.value })} placeholder="50" style={inp} /></label>}
              {form.grantType === "credits" && <label style={lbl}>Credits<input type="number" value={form.creditsAmount} onChange={(e) => setForm({ ...form, creditsAmount: e.target.value })} placeholder="2500" style={inp} /></label>}
              {form.grantType !== "credits" && <label style={lbl}>Duration (months, blank = perpetual)<input type="number" value={form.durationMonths} onChange={(e) => setForm({ ...form, durationMonths: e.target.value })} placeholder="3" style={inp} /></label>}
              <label style={lbl}>Max redemptions (blank = ∞)<input type="number" value={form.maxRedemptions} onChange={(e) => setForm({ ...form, maxRedemptions: e.target.value })} placeholder="500" style={inp} /></label>
              <label style={lbl}>Per-user limit<input type="number" value={form.perUserLimit} onChange={(e) => setForm({ ...form, perUserLimit: e.target.value })} style={inp} /></label>
            </div>
            <div style={{ marginTop: 12 }}><button onClick={submit} disabled={busy === "new" || !form.code} style={{ ...btnGhost, color: "var(--accent)", borderColor: "var(--accent)" }}>{busy === "new" ? "Creating…" : "Create code"}</button>
              {(form.grantType === "percent_off" || form.grantType === "credits") && <span style={{ color: "var(--fg4)", fontSize: 12, marginLeft: 10 }}>Recorded now; applied at Stripe checkout / credit ledger (deferred).</span>}</div>
          </div>
        )}
        {codes.loading ? <Loading /> : codes.error ? <ErrBox e={codes.error} /> : (
          <Table rows={codes.data.codes} empty="No promo codes yet." cols={[
            { label: "Code", render: (c) => <b style={{ fontFamily: "var(--font-mono)" }}>{c.code}</b> },
            { label: "Grant", render: (c) => <span><Pill color={c.grantType === "credits" ? "var(--leap-teal)" : "var(--accent)"}>{GRANT_LABEL[c.grantType]}</Pill> <span style={{ color: "var(--fg3)", fontSize: 12.5 }}>{grantSummary(c)}</span></span> },
            { label: "Campaign", render: (c) => c.campaign || "—" },
            { label: "Redeemed", right: true, render: (c) => <button onClick={() => setOpenCode(openCode === c.id ? null : c.id)} style={{ ...btnGhost, fontSize: 12, padding: "4px 9px" }}>{fmtN(c.redemptions)}{c.maxRedemptions ? " / " + c.maxRedemptions : ""}</button> },
            { label: "Expires", render: (c) => c.expiresAt ? fmtDate(c.expiresAt) : "—" },
            { label: "Status", render: (c) => c.active ? <Pill color="var(--success)">active</Pill> : <Pill color="var(--fg4)">disabled</Pill> },
            { label: "", right: true, render: (c) => !can("billing.write") ? <span style={{ color: "var(--fg4)", fontSize: 12 }}>—</span> : <button onClick={() => toggle(c)} disabled={busy === c.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px", color: c.active ? "var(--danger)" : "var(--success)", borderColor: c.active ? "var(--danger)" : "var(--success)" }}>{c.active ? "Disable" : "Enable"}</button> },
          ]} />
        )}
        {openCode && (
          <div style={{ marginTop: 16 }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>Redemptions</div>
            {reds.loading ? <Loading /> : <Table rows={(reds.data && reds.data.redemptions) || []} empty="No redemptions yet." cols={[
              { label: "User", render: (x) => short(x.userId, 14) },
              { label: "Org", render: (x) => short(x.orgId, 14) },
              { label: "Grant", render: (x) => <span>{GRANT_LABEL[x.grantType]}{x.planKey ? " · " + x.planKey : ""}</span> },
              { label: "Redeemed", render: (x) => fmtAgo(x.grantedAt) },
              { label: "Expires", render: (x) => x.expiresAt ? fmtDate(x.expiresAt) : "perpetual" },
              { label: "Status", render: (x) => x.revokedAt ? <Pill color="var(--danger)">revoked</Pill> : <Pill color="var(--success)">active</Pill> },
              { label: "", right: true, render: (x) => x.revokedAt || !can("billing.write") ? null : <button onClick={() => revoke(x.id)} disabled={busy === x.id} style={{ ...btnGhost, fontSize: 12, padding: "5px 10px", color: "var(--danger)", borderColor: "var(--danger)" }}>Revoke</button> },
            ]} />}
          </div>
        )}
      </div>
    );
  }

  // ── Landing A/B tests (marketing) ────────────────────────────────────────
  // Per-variant views, waitlist signups, and conversion rate for the coming-soon
  // page. Variants are assigned client-side (sticky cookie) on www.leap-social.com;
  // the public API records a 'view' per session and a 'signup' on waitlist submit.
  function Marketing() {
    const [days, setDays] = useState(30);
    const { loading, data, error } = useData(() => A.ab({ days }), [days]);
    if (loading) return <Loading />; if (error) return <ErrBox e={error} />;
    const variants = data.variants || [];
    const t = data.totals || { views: 0, signups: 0 };
    const overallCr = t.views > 0 ? t.signups / t.views : null;
    const pct = (x) => x == null ? "—" : (x * 100).toFixed(1) + "%";
    // Best converting variant with a meaningful sample (guards against a 1-view fluke).
    const ranked = variants.filter((v) => v.views >= 20 && v.conversionRate != null).sort((a, b) => b.conversionRate - a.conversionRate);
    const leader = ranked[0];
    const card = (label, value, sub, accent) => (
      <div style={{ border: "1px solid var(--border)", borderRadius: 14, padding: "16px 18px", background: "var(--surface)", minWidth: 150 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{label}</div>
        <div style={{ fontSize: 26, fontWeight: 800, fontFamily: "var(--font-display)", color: accent || "var(--fg1)", marginTop: 4 }}>{value}</div>
        {sub && <div style={{ fontSize: 12, color: "var(--fg3)", marginTop: 4 }}>{sub}</div>}
      </div>
    );
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>Coming-soon landing page — variant performance. Edit variants in <span style={{ fontFamily: "var(--font-mono)" }}>www/index.html</span>.</div>
          <select value={days} onChange={(e) => setDays(Number(e.target.value))} style={{ ...inp, width: 150, flex: "none" }}>
            {[7, 30, 90].map((d) => <option key={d} value={d}>Last {d} days</option>)}
          </select>
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 14, marginBottom: 18 }}>
          {card("Views", fmtN(t.views), `${variants.length} variant${variants.length === 1 ? "" : "s"} live`)}
          {card("Signups", fmtN(t.signups), "waitlist conversions", "var(--accent)")}
          {card("Conversion", pct(overallCr), "signups ÷ views")}
          {card("Leader", leader ? leader.variant.toUpperCase() : "—", leader ? `${pct(leader.conversionRate)} · ${fmtN(leader.views)} views` : "need ≥20 views")}
        </div>
        <div style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 8 }}>By variant</div>
        <Table cols={[
          { label: "Variant", render: (v) => <b style={{ textTransform: "uppercase" }}>{v.variant}{leader && v.variant === leader.variant ? <Pill color="var(--success)">best</Pill> : null}</b> },
          { label: "Views", right: true, render: (v) => fmtN(v.views) },
          { label: "Signups", right: true, render: (v) => fmtN(v.signups) },
          { label: "Conversion", right: true, render: (v) => v.conversionRate == null ? <span style={{ color: "var(--fg4)" }}>—</span> : <b style={{ color: leader && v.variant === leader.variant ? "var(--success)" : "var(--fg1)" }}>{pct(v.conversionRate)}</b> },
        ]} rows={variants} empty="No landing traffic yet — once the page is live, views and signups appear here." />
        <div style={{ fontSize: 12, color: "var(--fg4)", marginTop: 10, lineHeight: 1.6 }}>A visitor is assigned one variant (sticky for a year) and counted once per session. Conversion is waitlist signups ÷ views over the window. Give each variant a few hundred views before trusting the winner.</div>
      </div>
    );
  }

  // ── Early-access waitlist (marketing) ────────────────────────────────────
  // The captured signup list from the landing page — searchable, paged, and CSV
  // exportable. Sourced from social.WaitlistSignup (leap_admin read).
  function Waitlist() {
    const [search, setSearch] = useState("");
    const [q, setQ] = useState("");
    const [offset, setOffset] = useState(0);
    const [busy, setBusy] = useState(false);
    const LIMIT = 100;
    const { loading, data, error } = useData(() => A.waitlist({ search: q, limit: LIMIT, offset }), [q, offset]);
    const rows = (data && data.rows) || [];
    const total = (data && data.total) || 0;
    const download = async () => {
      setBusy(true);
      try {
        const blob = await A.waitlistCsv();
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a"); a.href = url; a.download = "leap-waitlist.csv";
        document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url);
      } catch (e) { /* ignore */ } finally { setBusy(false); }
    };
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14, gap: 12, flexWrap: "wrap" }}>
          <div style={{ color: "var(--fg3)", fontSize: 13.5 }}>Early-access signups from the landing page — <b>{fmtN(total)}</b> total.</div>
          <div style={{ display: "flex", gap: 8 }}>
            <form onSubmit={(e) => { e.preventDefault(); setOffset(0); setQ(search.trim()); }}>
              <input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search email…" style={{ ...inp, width: 200, flex: "none" }} />
            </form>
            <button onClick={download} disabled={busy || total === 0} style={{ ...btnGhost, color: "var(--accent)", borderColor: "var(--accent)" }}>{busy ? "…" : "Export CSV"}</button>
          </div>
        </div>
        {loading ? <Loading /> : error ? <ErrBox e={error} /> : (
          <React.Fragment>
            <Table cols={[
              { label: "Email", render: (r) => <b>{r.email}</b> },
              { label: "Variant", render: (r) => r.variant ? <Pill>{r.variant}</Pill> : <span style={{ color: "var(--fg4)" }}>—</span> },
              { label: "Source", render: (r) => <span style={{ color: "var(--fg3)", fontSize: 12.5 }}>{r.source || "—"}</span> },
              { label: "Signed up", render: (r) => fmtDate(r.createdAt) },
            ]} rows={rows} empty="No signups yet — they'll appear here as people join from the landing page." />
            {total > LIMIT && (
              <div style={{ display: "flex", gap: 10, alignItems: "center", marginTop: 12 }}>
                <button onClick={() => setOffset(Math.max(0, offset - LIMIT))} disabled={offset === 0} style={{ ...btnGhost, fontSize: 12 }}>‹ Prev</button>
                <span style={{ fontSize: 12.5, color: "var(--fg3)" }}>{offset + 1}–{Math.min(offset + LIMIT, total)} of {fmtN(total)}</span>
                <button onClick={() => setOffset(offset + LIMIT)} disabled={offset + LIMIT >= total} style={{ ...btnGhost, fontSize: 12 }}>Next ›</button>
              </div>
            )}
          </React.Fragment>
        )}
      </div>
    );
  }

  // ── shell ──────────────────────────────────────────────────────────────────
  const inp = { flex: 1, fontFamily: "inherit", fontSize: 13.5, padding: "9px 12px", borderRadius: 10, border: "1px solid var(--border-strong)", color: "var(--fg1)", background: "var(--surface)", boxSizing: "border-box", width: "100%" };
  const btnGhost = { border: "1px solid var(--border-strong)", background: "var(--surface)", borderRadius: 10, padding: "7px 12px", cursor: "pointer", fontFamily: "inherit", fontSize: 13, color: "var(--fg2)" };
  const lbl = { display: "flex", flexDirection: "column", gap: 5, fontSize: 11.5, fontWeight: 600, color: "var(--fg3)" };

  const VIEWS = [["overview", "layout-dashboard", "Overview"], ["orgs", "building-2", "Organizations"], ["users", "user", "Platform users"], ["admins", "shield", "Admins & access"], ["connections", "share-2", "Social ops"], ["usage", "gauge", "Usage & cost"], ["ai", "sparkles", "AI assistant"], ["marketing", "flask-conical", "Landing A/B"], ["waitlist", "mail", "Waitlist"], ["costs", "tag", "Cost catalog"], ["promo", "ticket", "Promo codes"], ["security", "shield-alert", "Security"], ["health", "activity", "Health"], ["audit", "scroll-text", "Audit"]];

  function Shell({ user, me, onSwitchRole, onLogout }) {
    const [view, setView] = useState("overview");
    const [orgId, setOrgId] = useState(null);
    window.__nav = (id) => { setOrgId(id); setView("orgs"); };
    const openOrg = (id) => { setOrgId(id); setView("org"); };
    // A role switch can strand you on a view your new role can't see (e.g. admins).
    const effView = view === "admins" && !can("admins.manage") ? "overview" : view;
    const title = effView === "org" ? "Organization" : (VIEWS.find((v) => v[0] === effView) || [, , "Overview"])[2];

    const navItem = ([id, icon, label]) => {
      const on = view === id || (id === "orgs" && view === "org");
      return <button key={id} onClick={() => { setView(id); }} style={{ display: "flex", alignItems: "center", gap: 11, padding: "10px 12px", borderRadius: 10, width: "100%", textAlign: "left", border: 0, cursor: "pointer", fontFamily: "inherit", fontSize: 14, fontWeight: on ? 600 : 500, color: on ? NAV.activeFg : NAV.fg, background: on ? NAV.activeBg : "transparent" }}
        onMouseEnter={(e) => { if (!on) e.currentTarget.style.background = NAV.hover; }} onMouseLeave={(e) => { if (!on) e.currentTarget.style.background = "transparent"; }}>
        <Icon name={icon} size={18} />{label}</button>;
    };

    return (
      <div style={{ display: "flex", height: "100vh" }}>
        <nav style={{ width: 224, flex: "none", background: NAV.bg, borderRight: "1px solid " + NAV.border, display: "flex", flexDirection: "column", padding: "18px 14px", gap: 3, boxSizing: "border-box" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "4px 6px 6px" }}>
            <img src="leap-logo-inverse.svg" alt="Leap" style={{ height: 26 }} />
          </div>
          <div style={{ fontSize: 11, fontWeight: 700, color: NAV.muted, letterSpacing: ".08em", textTransform: "uppercase", padding: "0 8px 14px" }}>Admin cockpit</div>
          {VIEWS.filter(([id]) => id !== "admins" || can("admins.manage")).map(navItem)}
          <div style={{ flex: 1 }} />
          <div style={{ borderTop: "1px solid " + NAV.border, paddingTop: 12, marginTop: 8 }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "0 8px 8px", gap: 8 }}>
              <span style={{ fontSize: 12, color: NAV.fg, overflow: "hidden", textOverflow: "ellipsis" }}>{user.email || "admin"}</span>
              {me && me.role && <span style={{ fontSize: 10.5, fontWeight: 700, color: NAV.activeFg, background: NAV.activeBg, borderRadius: 999, padding: "2px 8px", whiteSpace: "nowrap" }}>{ADMIN_ROLE_LABEL[me.role] || me.role}</span>}
            </div>
            {me && me.dev && (
              <div style={{ padding: "0 8px 8px" }}>
                <label style={{ fontSize: 10, color: NAV.muted, textTransform: "uppercase", letterSpacing: ".06em" }}>Simulate role (dev)</label>
                <select value={A.simRole() || "superadmin"} onChange={(e) => onSwitchRole(e.target.value === "superadmin" ? "" : e.target.value)}
                  style={{ width: "100%", marginTop: 4, fontFamily: "inherit", fontSize: 12.5, padding: "6px 8px", borderRadius: 8, border: "1px solid " + NAV.border, background: "transparent", color: NAV.fgStrong }}>
                  {["superadmin", "finance", "support", "readonly"].map((rr) => <option key={rr} value={rr} style={{ color: "#000" }}>{ADMIN_ROLE_LABEL[rr]}</option>)}
                </select>
              </div>
            )}
            <button onClick={onLogout} style={{ ...btnGhost, width: "100%", background: "transparent", borderColor: NAV.border, color: NAV.fg }}>Sign out</button>
          </div>
        </nav>
        <main style={{ flex: 1, overflow: "auto", background: "var(--bg)" }}>
          <div key={me && me.role} style={{ padding: "22px 28px", maxWidth: 1200 }}>
            <h1 style={{ margin: "0 0 20px" }}>{title}</h1>
            {effView === "overview" && <Overview />}
            {(effView === "orgs") && <Orgs onOpen={openOrg} />}
            {effView === "org" && <OrgDetail orgId={orgId} onBack={() => setView("orgs")} />}
            {effView === "users" && <Users />}
            {effView === "admins" && <Admins />}
            {effView === "connections" && <Connections />}
            {effView === "usage" && <Usage />}
            {effView === "ai" && <AI />}
            {effView === "marketing" && <Marketing />}
            {effView === "waitlist" && <Waitlist />}
            {effView === "costs" && <CostCatalog />}
            {effView === "promo" && <Promo />}
            {effView === "security" && <Security />}
            {effView === "audit" && <Audit />}
            {effView === "health" && <Health />}
          </div>
        </main>
      </div>
    );
  }

  function Login({ onIn }) {
    const [busy, setBusy] = useState(false); const [err, setErr] = useState(null);
    const dev = async () => { setBusy(true); setErr(null); try { await A.devLogin(); onIn(); } catch (e) { setErr(e); setBusy(false); } };
    return (
      <div style={{ display: "grid", placeItems: "center", height: "100vh", background: "var(--bg)" }}>
        <div style={{ width: 380, border: "1px solid var(--border)", borderRadius: 18, padding: 28, background: "var(--surface)", textAlign: "center", boxShadow: "var(--shadow-lg)" }}>
          <img src="leap-mark.svg" alt="" style={{ height: 40, marginBottom: 10 }} />
          <h2 style={{ margin: "0 0 4px" }}>Leap Admin</h2>
          <div style={{ color: "var(--fg3)", fontSize: 13.5, marginBottom: 18 }}>Back-office cockpit</div>
          <button onClick={() => A.googleStart()} style={{ width: "100%", padding: "11px", borderRadius: 12, border: 0, cursor: "pointer", fontFamily: "inherit", fontSize: 14, fontWeight: 600, color: "#fff", background: "var(--gradient-core)" }}>Sign in with Google</button>
          <button onClick={dev} disabled={busy} style={{ width: "100%", padding: "9px", marginTop: 10, borderRadius: 12, border: "1px solid var(--border)", cursor: "pointer", fontFamily: "inherit", fontSize: 12.5, fontWeight: 500, color: "var(--fg3)", background: "transparent" }}>{busy ? "Signing in…" : "Dev login (local only)"}</button>
          {err && <div style={{ color: "var(--danger)", fontSize: 12.5, marginTop: 10 }}>Sign-in failed ({err.status || "?"}).</div>}
        </div>
      </div>
    );
  }

  // Signed in via SSO but not yet a platform admin — surface the user id so the first
  // admin can be bootstrapped (add it to ADMIN_BOOTSTRAP_USER_IDS, then redeploy).
  function NotAdmin({ user, onLogout }) {
    const [copied, setCopied] = useState(false);
    const id = (user && (user.id || user.sub)) || "";
    const email = (user && user.email) || "";
    const copy = () => { try { navigator.clipboard.writeText(id); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch (e) {} };
    return (
      <div style={{ display: "grid", placeItems: "center", height: "100vh", background: "var(--bg)" }}>
        <div style={{ width: 470, border: "1px solid var(--border)", borderRadius: 18, padding: 28, background: "var(--surface)", boxShadow: "var(--shadow-lg)" }}>
          <img src="leap-mark.svg" alt="" style={{ height: 36, marginBottom: 12 }} />
          <h2 style={{ margin: "0 0 6px" }}>Not a platform admin yet</h2>
          <div style={{ color: "var(--fg2)", fontSize: 13.5, lineHeight: 1.5 }}>You're signed in{email ? " as " + email : ""}, but this account isn't a platform admin.</div>
          <div style={{ marginTop: 14, fontSize: 12.5, color: "var(--fg3)", lineHeight: 1.5 }}>To grant the first admin, add this user id to <code>ADMIN_BOOTSTRAP_USER_IDS</code> on the leap-admin service, then redeploy:</div>
          <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
            <input readOnly value={id} onFocus={(e) => e.target.select()} style={{ flex: 1, fontFamily: "var(--font-mono, monospace)", fontSize: 12, padding: "9px 10px", borderRadius: 10, border: "1px solid var(--border)", background: "var(--bg)", color: "var(--fg1)" }} />
            <button onClick={copy} style={{ padding: "9px 12px", borderRadius: 10, border: "1px solid var(--border)", background: "transparent", cursor: "pointer", fontSize: 12.5, fontWeight: 600, color: "var(--fg2)" }}>{copied ? "Copied" : "Copy"}</button>
          </div>
          <button onClick={onLogout} style={{ marginTop: 16, padding: "8px 12px", borderRadius: 10, border: "1px solid var(--border)", background: "transparent", cursor: "pointer", fontSize: 12.5, color: "var(--fg3)" }}>Sign out</button>
        </div>
      </div>
    );
  }

  function App() {
    const [state, setState] = useState({ status: "loading", user: null, me: null });
    const loadMe = useCallback(async () => {
      try { const me = await A.me(); CAPS = new Set(me.capabilities || []); return me; }
      catch { CAPS = new Set(); return null; }
    }, []);
    const check = useCallback(() => {
      A.session()
        // Signed in, but /admin/me 403s for a non-admin → show the bootstrap screen
        // instead of a broken shell.
        .then(async (s) => { const me = await loadMe(); setState({ status: me ? "in" : "notadmin", user: s.user || {}, me }); })
        .catch(() => { CAPS = new Set(); setState({ status: "out", user: null, me: null }); });
    }, [loadMe]);
    // Switch the simulated role (dev): update the header, refetch capabilities, remount.
    const switchRole = useCallback(async (role) => { A.setSimRole(role); const me = await loadMe(); setState((s) => ({ ...s, me })); }, [loadMe]);
    useEffect(() => { check(); }, [check]);
    if (state.status === "loading") return <div style={{ display: "grid", placeItems: "center", height: "100vh", color: "var(--fg3)" }}>Loading…</div>;
    if (state.status === "out") return <Login onIn={check} />;
    if (state.status === "notadmin") return <NotAdmin user={state.user} onLogout={() => { A.setSimRole(""); A.logout().then(check); }} />;
    return <Shell user={state.user} me={state.me} onSwitchRole={switchRole} onLogout={() => { A.setSimRole(""); A.logout().then(check); }} />;
  }

  ReactDOM.createRoot(document.getElementById("root")).render(<App />);
})();
