// Admin-only data store + screens (Accounts, Data Sets, Approvals).
// Plain in-memory state held on window so different screens share it.

(function initAdminStore() {
  if (window.VAULT_ADMIN_STORE) return;

  // Seed login activity (synthetic but plausible — 60d trailing)
  const now = new Date("2026-05-11T09:30:00");
  const day = 24*60*60*1000;
  const mk = (email, daysAgo, hour, min, ip, geo, device, ok = true) => ({
    id: `la_${email.split("@")[0]}_${daysAgo}_${hour}${min}`,
    email, ts: new Date(now - daysAgo*day).setHours(hour, min, 0, 0),
    ip, geo, device, success: ok,
  });

  const loginActivity = [
    mk("bscott@harveyllc.com",  0, 8, 12, "73.140.22.18",  "Seattle, WA",  "Mac · Chrome 139"),
    mk("bscott@harveyllc.com",  1, 7, 51, "73.140.22.18",  "Seattle, WA",  "Mac · Chrome 139"),
    mk("bscott@harveyllc.com",  2, 9,  4, "73.140.22.18",  "Seattle, WA",  "Mac · Chrome 139"),
    mk("bscott@harveyllc.com",  3,17, 22, "10.0.4.117",    "VPN (HQ)",     "Mac · Safari 18"),
    mk("bscott@harveyllc.com",  6, 6, 31, "73.140.22.18",  "Seattle, WA",  "iPhone · Safari"),
    mk("jscheftz@harveyllc.com",0, 9,  2, "98.42.183.7",   "Irvine, CA",   "Mac · Chrome 139"),
    mk("jscheftz@harveyllc.com",0,14, 45, "98.42.183.7",   "Irvine, CA",   "Mac · Chrome 139"),
    mk("jscheftz@harveyllc.com",1, 8, 41, "98.42.183.7",   "Irvine, CA",   "Mac · Chrome 139"),
    mk("jscheftz@harveyllc.com",2, 8, 33, "98.42.183.7",   "Irvine, CA",   "Mac · Chrome 139"),
    mk("jscheftz@harveyllc.com",4,11, 12, "98.42.183.7",   "Irvine, CA",   "iPhone · Safari"),
    mk("jscheftz@harveyllc.com",7,21, 18, "73.140.22.18",  "Seattle, WA",  "Mac · Chrome", false),
    mk("dperrin@harveyllc.com", 0, 7, 55, "75.16.84.221",  "Chicago, IL",  "Mac · Chrome 139"),
    mk("dperrin@harveyllc.com", 1, 7, 49, "75.16.84.221",  "Chicago, IL",  "Mac · Chrome 139"),
    mk("rhartley@harveyllc.com",0, 8, 14, "204.45.78.9",   "Boston, MA",   "Windows · Edge 124"),
    mk("rhartley@harveyllc.com",2, 8, 26, "204.45.78.9",   "Boston, MA",   "Windows · Edge 124"),
    mk("kkaneko@harveyllc.com", 0,10,  5, "61.215.18.44",  "Tokyo, JP",    "Mac · Chrome 139"),
    mk("kkaneko@harveyllc.com", 1,10, 12, "61.215.18.44",  "Tokyo, JP",    "Mac · Chrome 139"),
    mk("smulholland@harveyllc.com",0, 7, 24, "75.16.84.221","Chicago, IL", "Mac · Chrome 139"),
    mk("dharvey@harveyllc.com", 0, 6, 41, "73.140.22.18",  "Seattle, WA",  "Mac · Safari 18"),
    mk("dharvey@harveyllc.com", 1, 6, 38, "73.140.22.18",  "Seattle, WA",  "Mac · Safari 18"),
    mk("dharvey@harveyllc.com", 8, 9, 11, "199.83.221.7",  "London, UK",   "iPad · Safari"),
  ];

  // Active accounts (derived primarily from VAULT_ACCOUNTS, plus a longer roster)
  // Real login accounts only — mirrors window.VAULT_ACCOUNTS (the two credential profiles
  // the login screen actually accepts). Hard-coded fields here add the audit metadata
  // (createdISO, lastIp, status) that the Accounts table renders.
  const accounts = [
    { email: "bscott@harveyllc.com",   name: "Brian Scott",    access: "admin", status: "active", createdISO: "2024-08-15", lastIp: "73.140.22.18", role: "Managing Director · Industrials" },
    { email: "jscheftz@harveyllc.com", name: "Jordan Scheftz", access: "user",  status: "active", createdISO: "2024-09-02", lastIp: "98.42.183.7",  role: "Associate" },
  ];

  // Pending account-creation requests (awaiting admin approval)
  const pendingAccounts = [
    { id: "pa_001", email: "rgomez@harveyllc.com",   name: "Rafael Gomez",    requestedAccess: "user",  requestedBy: "rhartley@harveyllc.com", requestedISO: "2026-05-09", note: "New hire — Healthcare module, starting 5/18." },
    { id: "pa_002", email: "lchen@harveyllc.com",    name: "Lily Chen",       requestedAccess: "user",  requestedBy: "kkaneko@harveyllc.com",  requestedISO: "2026-05-07", note: "Analyst transfer from London office." },
    { id: "pa_003", email: "mkowalski@harveyllc.com",name: "Marek Kowalski",  requestedAccess: "admin", requestedBy: "jpark@harveyllc.com",     requestedISO: "2026-05-04", note: "IT Director — needs admin for data-set management." },
    { id: "pa_004", email: "vortega@harveyllc.com",  name: "Valeria Ortega",  requestedAccess: "user",  requestedBy: "smulholland@harveyllc.com", requestedISO: "2026-05-02", note: "Returning intern, summer 2026." },
  ];

  // Source data sets used to populate the model
  const dataSets = [
    { id: "ds_org",       section: "Org Chart",     filename: "Harvey-Org-2026-05.csv",        rows: 123, sizeKB: 24,    uploadedISO: "2026-05-08", uploadedBy: "Brian Scott",   schema: ["Name","Email","Title","Role","Module","Sub-Team","Supervisor"], status: "active",  notes: "May 2026 reorg applied; sub-team leaders folded into team rolls." },
    { id: "ds_outreach",  section: "Outreach",      filename: "Outreach-Q1Q2-2026.xlsx",       rows: 18420, sizeKB: 612, uploadedISO: "2026-05-07", uploadedBy: "Jennifer Park", schema: ["Date","UserId","Type","Target","Notes","Module"], status: "active",  notes: "Daily push from CRM @ 06:00 PT." },
    { id: "ds_leadgen",   section: "Lead Gen",      filename: "LeadGen-Master-2026.xlsx",       rows: 2840,  sizeKB: 184, uploadedISO: "2026-05-06", uploadedBy: "Brian Scott",   schema: ["LeadId","Source","UserId","Stage","Score","CreatedAt"], status: "active",  notes: "Stage enum: cold → warm → qualified → engaged." },
    { id: "ds_offers",    section: "Offers / IOI",  filename: "Offers-IOI-2026-Q2.xlsx",        rows: 64,    sizeKB: 28,  uploadedISO: "2026-05-05", uploadedBy: "Brian Scott",   schema: ["Target","Buyer","Type","Amount","Status","Module","CreatedAt"], status: "active",  notes: "Confidential — buy-side and sell-side IOIs." },
    { id: "ds_pipeline",  section: "Deal Pipeline", filename: "Pipeline-2026-05-08.pdf",        rows: 30,    sizeKB: 412, uploadedISO: "2026-05-08", uploadedBy: "Jordan Scheftz",schema: ["Target","Status","NextAction","Module","Owner"], status: "active",  notes: "Parsed from weekly Monday PDF." },
    { id: "ds_clients",   section: "Projects",      filename: "Active-Engagements-2026-05.pdf", rows: 47,    sizeKB: 218, uploadedISO: "2026-05-08", uploadedBy: "Jennifer Park", schema: ["Client","Project","Type","Retainer","TEV","Owner"], status: "active",  notes: "Includes Retained, Platform, Add-On." },
    { id: "ds_closed",    section: "Closed Deals",  filename: "Closed-Deals-2025-FY.xlsx",      rows: 36,    sizeKB: 144, uploadedISO: "2026-04-30", uploadedBy: "Jennifer Park", schema: ["Date","Client","Buyer","Type","TEV","Fee","Owner"], status: "active",  notes: "FY2025 + YTD 2026; locked after audit." },
    { id: "ds_newclients",section: "New Client Tracker", filename: "New-Clients-Funnel.xlsx",   rows: 88,    sizeKB: 96,  uploadedISO: "2026-05-03", uploadedBy: "Brian Scott",   schema: ["Prospect","Source","Stage","Owner","NextStep","NextDate"], status: "active",  notes: "Stages: Identified → Intro → Pitch → Engaged." },
    { id: "ds_badges",    section: "Badges",        filename: "Achievement-Rules.json",         rows: 24,    sizeKB: 8,   uploadedISO: "2026-04-12", uploadedBy: "Brian Scott",   schema: ["Badge","Tier","Family","Trigger","Threshold"], status: "active",  notes: "Tiered families — Caller, Rainmaker, Closer." },
    { id: "ds_old_org",   section: "Org Chart",     filename: "Harvey-Org-2025-12.csv",         rows: 119,   sizeKB: 23,  uploadedISO: "2025-12-15", uploadedBy: "Brian Scott",   schema: ["Name","Email","Title","Role","Module"], status: "archived", notes: "Superseded by 2026-05 reorg." },
  ];

  // Pending edits awaiting admin approval (from "user" role edits on Projects + Closed Deals)
  const pendingEdits = [
    { id: "pe_001", kind: "project",  target: "Project Beacon",     entity: "PRJ-114",
      field: "TEV", before: "$280M", after: "$315M",
      submittedBy: "jscheftz@harveyllc.com", submittedISO: "2026-05-10T14:22:00",
      reason: "Updated TEV after revised LOI from BlackArrow Capital received 5/10." },
    { id: "pe_002", kind: "project",  target: "Project Lattice",    entity: "PRJ-098",
      field: "Status", before: "Diligence", after: "Documentation",
      submittedBy: "dperrin@harveyllc.com", submittedISO: "2026-05-10T11:05:00",
      reason: "Buyer signed APA draft; advanced to documentation phase." },
    { id: "pe_003", kind: "closed",   target: "Vista Specialty Foods", entity: "CD-2026-007",
      field: "Fee", before: "$2.10M", after: "$2.38M",
      submittedBy: "jscheftz@harveyllc.com", submittedISO: "2026-05-09T16:48:00",
      reason: "Earnout trigger hit Q1, fee accrual updated per engagement letter §4.2." },
    { id: "pe_004", kind: "project",  target: "Project Heron",      entity: "PRJ-127",
      field: "Owner", before: "Megan Sullivan", after: "Adam Friedman",
      submittedBy: "afriedman@harveyllc.com", submittedISO: "2026-05-09T09:14:00",
      reason: "Lead reassignment per 5/8 module review — Friedman has buyer relationship." },
    { id: "pe_005", kind: "closed",   target: "Northwind Logistics", entity: "CD-2026-005",
      field: "Buyer", before: "Confidential", after: "Arclight Industrial Partners",
      submittedBy: "dperrin@harveyllc.com", submittedISO: "2026-05-08T17:30:00",
      reason: "Buyer cleared for disclosure post-announcement 5/8." },
    { id: "pe_006", kind: "project",  target: "Project Alder",      entity: "PRJ-131",
      field: "Retainer", before: "$45K", after: "$60K",
      submittedBy: "edutra@harveyllc.com", submittedISO: "2026-05-08T10:02:00",
      reason: "Scope expanded to include carve-out analysis; retainer adjusted." },
  ];

  // Approval history (already processed)
  const approvalHistory = [
    { id: "ph_001", target: "Project Cobalt", field: "TEV", decision: "approved", by: "bscott@harveyllc.com", decidedISO: "2026-05-07T15:11:00" },
    { id: "ph_002", target: "Cascade Beverages", field: "Buyer", decision: "approved", by: "bscott@harveyllc.com", decidedISO: "2026-05-06T11:30:00" },
    { id: "ph_003", target: "Project Sienna", field: "Owner", decision: "rejected", by: "dharvey@harveyllc.com", decidedISO: "2026-05-05T09:48:00" },
    { id: "ph_004", target: "Project Quill", field: "Status", decision: "approved", by: "bscott@harveyllc.com", decidedISO: "2026-05-04T16:22:00" },
  ];

  // Tabs that the admin can hide from "user" role (under construction)
  // Keys correspond to view ids in app.jsx
  const hiddenTabs = new Set(["badges"]); // pre-hide Badges as a demo

  const store = {
    accounts, pendingAccounts,
    loginActivity,
    dataSets,
    pendingEdits, approvalHistory,
    hiddenTabs,
  };
  window.VAULT_ADMIN_STORE = store;

  // expose recorder for login.jsx
  window.VAULT_RECORD_LOGIN = (email) => {
    store.loginActivity.unshift({
      id: "la_live_" + Date.now(),
      email,
      ts: Date.now(),
      ip: "73.140.22.18",
      geo: "Seattle, WA",
      device: "Mac · Chrome 139",
      success: true,
    });
  };
})();

// ============================================================================
// ACCOUNTS SCREEN
// ============================================================================
function AccountsScreen() {
  const store = window.VAULT_ADMIN_STORE;
  const [, force] = React.useReducer(x => x + 1, 0);
  const [tab, setTab] = React.useState("active"); // active | pending | activity
  const [filter, setFilter] = React.useState("");
  const [showPwId, setShowPwId] = React.useState(null);

  const fmtTime = (ts) => {
    const d = new Date(ts);
    const now = Date.now();
    const m = Math.round((now - ts) / 60000);
    if (m < 60) return m + "m ago";
    if (m < 60*24) return Math.round(m/60) + "h ago";
    const days = Math.round(m/(60*24));
    if (days < 7) return days + "d ago";
    return d.toLocaleDateString("en-US", { month:"short", day:"numeric" });
  };

  // Pretend each account has a generated password (visible only when toggled)
  const pwFor = (email) => {
    // demo — clear creds for the two real logins, opaque hash for the rest
    const known = (window.VAULT_ACCOUNTS || []).find(a => a.email === email);
    if (known) return known.password;
    return "•••" + email.split("@")[0].slice(0, 4) + "••" + email.length;
  };

  const approve = (id) => {
    const req = store.pendingAccounts.find(p => p.id === id);
    if (!req) return;
    store.accounts.push({
      email: req.email, name: req.name, access: req.requestedAccess,
      status: "active",
      createdISO: new Date().toISOString().slice(0,10),
      lastIp: "—", role: "Pending role assignment",
    });
    store.pendingAccounts = store.pendingAccounts.filter(p => p.id !== id);
    force();
  };
  const reject = (id) => {
    store.pendingAccounts = store.pendingAccounts.filter(p => p.id !== id);
    force();
  };
  const toggleAccess = (email) => {
    const a = store.accounts.find(x => x.email === email);
    if (!a) return;
    a.access = a.access === "admin" ? "user" : "admin";
    force();
  };
  const toggleStatus = (email) => {
    const a = store.accounts.find(x => x.email === email);
    if (!a) return;
    a.status = a.status === "active" ? "suspended" : "active";
    force();
  };

  const activeFiltered = store.accounts.filter(a =>
    !filter || (a.name + " " + a.email + " " + a.role).toLowerCase().includes(filter.toLowerCase())
  );
  const activityFiltered = store.loginActivity.filter(l =>
    !filter || (l.email + " " + l.geo + " " + l.device).toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div className="col" style={{ gap: 16, padding: 24 }}>
      {/* Header strip */}
      <div className="row" style={{ gap: 12, flexWrap: "wrap" }}>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Total accounts</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{store.accounts.length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Admins</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{store.accounts.filter(a => a.access === "admin").length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Pending approval</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2, color: store.pendingAccounts.length > 0 ? "var(--warn)" : "var(--ink)" }}>{store.pendingAccounts.length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Logins · 7d</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>
            {store.loginActivity.filter(l => Date.now() - l.ts < 7*24*60*60*1000).length}
          </div>
        </div>
      </div>

      {/* Tab bar */}
      <div className="card" style={{ padding: 0, overflow: "hidden" }}>
        <div className="row" style={{ padding: "10px 14px", gap: 12, borderBottom: "1px solid var(--line)" }}>
          <div className="seg">
            <button className={tab === "active" ? "on" : ""} onClick={() => setTab("active")}>
              Active accounts · {store.accounts.length}
            </button>
            <button className={tab === "pending" ? "on" : ""} onClick={() => setTab("pending")}>
              Pending · {store.pendingAccounts.length}
            </button>
            <button className={tab === "activity" ? "on" : ""} onClick={() => setTab("activity")}>
              Login activity
            </button>
          </div>
          <div style={{ flex: 1 }}/>
          <input value={filter} onChange={e => setFilter(e.target.value)} placeholder="Filter…" style={{
            padding: "6px 10px", border: "1px solid var(--line)", borderRadius: 8,
            background: "var(--surface)", color: "var(--ink)", fontSize: 12.5, width: 220,
          }}/>
          {tab === "active" && <button className="btn primary sm"><Icon.plus/> New account</button>}
        </div>

        {tab === "active" && (
          <div style={{ overflowX: "auto" }}>
            <table className="dt">
              <thead><tr>
                <th>User</th><th>Email</th><th>Role</th><th>Password</th>
                <th>Access</th><th>Status</th><th className="r">Created</th><th></th>
              </tr></thead>
              <tbody>
                {activeFiltered.map(a => (
                  <tr key={a.email}>
                    <td>
                      <span className="row" style={{ gap: 8 }}>
                        <span className="avatar" data-tone={(a.email.charCodeAt(0) % 5 === 0 ? "a" : a.email.charCodeAt(0) % 5 === 1 ? "b" : a.email.charCodeAt(0) % 5 === 2 ? "c" : a.email.charCodeAt(0) % 5 === 3 ? "d" : "e")}>{a.name.split(" ").map(s=>s[0]).join("").slice(0,2)}</span>
                        <span style={{ fontWeight: 500 }}>{a.name}</span>
                      </span>
                    </td>
                    <td className="mono small">{a.email}</td>
                    <td className="muted small">{a.role}</td>
                    <td>
                      <span className="row" style={{ gap: 6 }}>
                        <span className="mono small" style={{
                          fontFamily: showPwId === a.email ? "var(--f-mono)" : "var(--f-mono)",
                          letterSpacing: showPwId === a.email ? ".02em" : ".15em",
                          color: showPwId === a.email ? "var(--ink)" : "var(--muted-2)",
                        }}>
                          {showPwId === a.email ? pwFor(a.email) : "••••••••••"}
                        </span>
                        <button className="btn ghost sm" style={{ padding: 3 }}
                          onClick={() => setShowPwId(showPwId === a.email ? null : a.email)}
                          title={showPwId === a.email ? "Hide" : "Reveal"}>
                          {showPwId === a.email ? (
                            <svg width="13" height="13" viewBox="0 0 16 16" fill="none"><path d="M2 8s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4zM3 3l10 10" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></svg>
                          ) : (
                            <svg width="13" height="13" viewBox="0 0 16 16" fill="none"><path d="M2 8s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4z" stroke="currentColor" strokeWidth="1.3"/><circle cx="8" cy="8" r="2" stroke="currentColor" strokeWidth="1.3"/></svg>
                          )}
                        </button>
                      </span>
                    </td>
                    <td>
                      <button className={"pill " + (a.access === "admin" ? "accent" : "")} onClick={() => toggleAccess(a.email)} style={{ cursor: "pointer", border: 0 }}>
                        <span className="dot"/>{a.access}
                      </button>
                    </td>
                    <td>
                      <span className={"pill " + (a.status === "active" ? "ok" : "risk")}>
                        <span className="dot"/>{a.status}
                      </span>
                    </td>
                    <td className="r mono small muted">{a.createdISO}</td>
                    <td className="r">
                      <button className="btn ghost sm" onClick={() => toggleStatus(a.email)}>
                        {a.status === "active" ? "Suspend" : "Reactivate"}
                      </button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        {tab === "pending" && (
          <div className="col" style={{ padding: 16, gap: 12 }}>
            {store.pendingAccounts.length === 0 && (
              <div className="muted" style={{ padding: 40, textAlign: "center" }}>No pending requests.</div>
            )}
            {store.pendingAccounts.map(req => (
              <div key={req.id} className="card" style={{
                padding: 16, display: "grid",
                gridTemplateColumns: "auto 1fr auto", gap: 16, alignItems: "center",
              }}>
                <span className="avatar lg" data-tone="d">{req.name.split(" ").map(s=>s[0]).join("").slice(0,2)}</span>
                <div>
                  <div className="row" style={{ gap: 8, marginBottom: 2 }}>
                    <span style={{ fontWeight: 600, fontSize: 14 }}>{req.name}</span>
                    <span className={"pill " + (req.requestedAccess === "admin" ? "accent" : "")}><span className="dot"/>{req.requestedAccess}</span>
                  </div>
                  <div className="mono small muted">{req.email}</div>
                  <div className="muted small" style={{ marginTop: 6, lineHeight: 1.5 }}>{req.note}</div>
                  <div className="row" style={{ gap: 14, marginTop: 6, color: "var(--muted)" }}>
                    <span className="small">Requested by <span style={{ color: "var(--ink-2)" }}>{req.requestedBy.split("@")[0]}</span></span>
                    <span className="small">·</span>
                    <span className="small">{req.requestedISO}</span>
                  </div>
                </div>
                <div className="col" style={{ gap: 6 }}>
                  <button className="btn primary sm" onClick={() => approve(req.id)}>Approve</button>
                  <button className="btn sm" onClick={() => reject(req.id)}>Reject</button>
                </div>
              </div>
            ))}
          </div>
        )}

        {tab === "activity" && (
          <div style={{ overflowX: "auto" }}>
            <table className="dt">
              <thead><tr>
                <th>Account</th><th>When</th><th>IP</th><th>Location</th><th>Device</th><th className="r">Result</th>
              </tr></thead>
              <tbody>
                {activityFiltered.map(l => (
                  <tr key={l.id}>
                    <td className="mono small">{l.email}</td>
                    <td>{fmtTime(l.ts)}</td>
                    <td className="mono small">{l.ip}</td>
                    <td>{l.geo}</td>
                    <td className="muted small">{l.device}</td>
                    <td className="r">
                      <span className={"pill " + (l.success ? "ok" : "risk")}>
                        <span className="dot"/>{l.success ? "OK" : "Failed"}
                      </span>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </div>
  );
}

// ============================================================================
// DATA SETS SCREEN
// ============================================================================
function DataSetsScreen() {
  const store = window.VAULT_ADMIN_STORE;
  const [, force] = React.useReducer(x => x + 1, 0);
  const [editing, setEditing] = React.useState(null);   // ds id
  const [previewing, setPreviewing] = React.useState(null);
  const [adding, setAdding] = React.useState(null);     // section name when adding

  const sections = [...new Set(store.dataSets.map(d => d.section))];

  const rename = (id, newName) => {
    const d = store.dataSets.find(x => x.id === id);
    if (d) { d.filename = newName; force(); }
  };
  const renameSection = (id, newSection) => {
    const d = store.dataSets.find(x => x.id === id);
    if (d) { d.section = newSection; force(); }
  };
  const archive = (id) => {
    const d = store.dataSets.find(x => x.id === id);
    if (d) { d.status = d.status === "active" ? "archived" : "active"; force(); }
  };
  const swap = (id) => {
    // simulate file swap — bump timestamp + row count
    const d = store.dataSets.find(x => x.id === id);
    if (d) {
      d.uploadedISO = new Date().toISOString().slice(0,10);
      d.uploadedBy = "Brian Scott";
      d.rows = d.rows + Math.floor(Math.random() * 12);
      force();
    }
  };

  return (
    <div className="col" style={{ gap: 16, padding: 24 }}>
      <div className="row" style={{ gap: 12, flexWrap: "wrap" }}>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Data sets</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{store.dataSets.length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Total rows</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>
            {store.dataSets.reduce((s, d) => s + d.rows, 0).toLocaleString()}
          </div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Sections covered</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{sections.length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Last upload</div>
          <div className="display" style={{ fontSize: 16, marginTop: 4 }}>
            {[...store.dataSets].sort((a,b) => b.uploadedISO.localeCompare(a.uploadedISO))[0]?.uploadedISO || "—"}
          </div>
        </div>
      </div>

      {sections.map(section => {
        const sectionDS = store.dataSets.filter(d => d.section === section);
        return (
          <div key={section} className="card">
            <div className="row" style={{ padding: "12px 16px", borderBottom: "1px solid var(--line-2)" }}>
              <span className="micro">{section}</span>
              <span className="tag" style={{ marginLeft: 8 }}>{sectionDS.length}</span>
              <div style={{ flex: 1 }}/>
              <button className="btn ghost sm" onClick={() => setAdding(section)}><Icon.plus/> Add data set</button>
            </div>
            {sectionDS.map(d => (
              <div key={d.id} style={{
                padding: "14px 16px",
                borderBottom: "1px solid var(--line-2)",
                display: "grid",
                gridTemplateColumns: "auto 1fr auto",
                gap: 14, alignItems: "center",
              }}>
                <span style={{
                  width: 38, height: 38, borderRadius: 8,
                  background: d.status === "active" ? "var(--accent-soft)" : "var(--surface-2)",
                  color: d.status === "active" ? "var(--accent)" : "var(--muted)",
                  display: "inline-flex", alignItems: "center", justifyContent: "center",
                }}>
                  <FileIcon ext={d.filename.split(".").pop()}/>
                </span>
                <div style={{ minWidth: 0 }}>
                  <div className="row" style={{ gap: 8 }}>
                    {editing === d.id ? (
                      <input
                        autoFocus
                        defaultValue={d.filename}
                        onBlur={(e) => { rename(d.id, e.target.value); setEditing(null); }}
                        onKeyDown={(e) => { if (e.key === "Enter") { rename(d.id, e.target.value); setEditing(null); } if (e.key === "Escape") setEditing(null); }}
                        style={{ fontSize: 13.5, fontWeight: 500, padding: "3px 6px",
                          border: "1px solid var(--accent)", borderRadius: 5,
                          background: "var(--surface)", color: "var(--ink)", width: 380,
                          fontFamily: "var(--f-mono)" }}
                      />
                    ) : (
                      <span className="mono" style={{ fontSize: 13.5, fontWeight: 500, color: "var(--ink)" }}>{d.filename}</span>
                    )}
                    <span className={"pill " + (d.status === "active" ? "ok" : "")} style={{ fontSize: 9.5 }}>
                      <span className="dot"/>{d.status}
                    </span>
                  </div>
                  <div className="row" style={{ gap: 16, marginTop: 4, color: "var(--muted)" }}>
                    <span className="small">{d.rows.toLocaleString()} rows</span>
                    <span className="small">· {d.sizeKB} KB</span>
                    <span className="small">· Uploaded {d.uploadedISO} by {d.uploadedBy}</span>
                  </div>
                  <div className="row" style={{ gap: 6, marginTop: 6, flexWrap: "wrap" }}>
                    {d.schema.map(s => <span key={s} className="tag mono" style={{ fontSize: 10 }}>{s}</span>)}
                  </div>
                  {d.notes && <div className="muted small" style={{ marginTop: 6, fontStyle: "italic" }}>{d.notes}</div>}
                </div>
                <div className="col" style={{ gap: 4 }}>
                  <button className="btn sm" onClick={() => setPreviewing(d.id)}>Preview</button>
                  <button className="btn sm" onClick={() => swap(d.id)}>Swap file</button>
                  <button className="btn sm" onClick={() => setEditing(d.id)}>Rename</button>
                  <button className="btn ghost sm" onClick={() => archive(d.id)}>{d.status === "active" ? "Archive" : "Restore"}</button>
                </div>
              </div>
            ))}
          </div>
        );
      })}

      {previewing && (
        <DataSetPreviewModal ds={store.dataSets.find(d => d.id === previewing)} onClose={() => setPreviewing(null)} onRenameSection={renameSection}/>
      )}

      {adding && (
        <AddDataSetModal
          defaultSection={adding}
          existingSections={sections}
          onClose={() => setAdding(null)}
          onAdd={(ds) => {
            store.dataSets.push(ds);
            setAdding(null);
            force();
          }}
        />
      )}
    </div>
  );
}

function AddDataSetModal({ defaultSection, existingSections, onClose, onAdd }) {
  const [filename, setFilename] = React.useState("");
  const [section, setSection] = React.useState(defaultSection || "");
  const [rows, setRows] = React.useState("");
  const [schema, setSchema] = React.useState("");
  const [notes, setNotes] = React.useState("");
  const [dragOver, setDragOver] = React.useState(false);

  const canSave = filename.trim().length > 0 && section.trim().length > 0;

  const handleFile = (file) => {
    if (!file) return;
    setFilename(file.name);
    // Rough row estimate based on file size (~80 bytes/row)
    if (!rows) setRows(String(Math.max(1, Math.round(file.size / 80))));
  };

  const submit = () => {
    if (!canSave) return;
    const id = "ds_" + filename.toLowerCase().replace(/[^a-z0-9]+/g, "_").slice(0, 20) + "_" + Date.now().toString(36).slice(-4);
    onAdd({
      id,
      section: section.trim(),
      filename: filename.trim(),
      rows: parseInt(rows, 10) || 0,
      sizeKB: Math.max(1, Math.round(((parseInt(rows, 10) || 0) * 80) / 1024)),
      uploadedISO: new Date().toISOString().slice(0, 10),
      uploadedBy: "Brian Scott",
      schema: schema.split(",").map(s => s.trim()).filter(Boolean),
      status: "active",
      notes: notes.trim() || undefined,
    });
  };

  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, zIndex: 50,
      background: "rgba(20,19,15,.4)",
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <div onClick={e => e.stopPropagation()} className="card" style={{
        width: 560, maxWidth: "92vw", maxHeight: "86vh", overflow: "auto",
        display: "flex", flexDirection: "column",
      }}>
        <div className="row" style={{ padding: "14px 18px", borderBottom: "1px solid var(--line)" }}>
          <div style={{ flex: 1 }}>
            <div className="micro">New data set</div>
            <div className="display" style={{ fontSize: 18, fontWeight: 600, marginTop: 2 }}>Add file to {section || "section"}</div>
          </div>
          <button className="btn ghost sm" onClick={onClose} style={{ padding: 4 }}>
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
          </button>
        </div>

        <div style={{ padding: 18, display: "flex", flexDirection: "column", gap: 14 }}>
          {/* Drop zone */}
          <label
            onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
            onDragLeave={() => setDragOver(false)}
            onDrop={(e) => { e.preventDefault(); setDragOver(false); handleFile(e.dataTransfer.files?.[0]); }}
            style={{
              border: `1.5px dashed ${dragOver ? "var(--accent)" : "var(--line)"}`,
              background: dragOver ? "var(--accent-soft)" : "var(--surface-2)",
              borderRadius: 8, padding: "20px 16px",
              textAlign: "center", cursor: "pointer",
              transition: "all .15s",
            }}>
            <input type="file" style={{ display: "none" }} onChange={(e) => handleFile(e.target.files?.[0])}/>
            <div className="display" style={{ fontSize: 14, fontWeight: 600, color: "var(--ink)" }}>
              {filename ? filename : "Drop file or click to browse"}
            </div>
            <div className="muted small" style={{ marginTop: 4 }}>
              {filename ? "Click to choose a different file" : "CSV, XLSX, JSON, PDF — any source file"}
            </div>
          </label>

          <div className="row" style={{ gap: 12 }}>
            <label style={{ flex: 1 }}>
              <div className="micro" style={{ marginBottom: 4 }}>Section</div>
              <input list="ds-sections" value={section} onChange={e => setSection(e.target.value)} placeholder="e.g. Pipeline" style={{
                width: "100%", padding: "7px 10px", fontSize: 13,
                border: "1px solid var(--line)", borderRadius: 6,
                background: "var(--surface)", color: "var(--ink)",
              }}/>
              <datalist id="ds-sections">
                {existingSections.map(s => <option key={s} value={s}/>)}
              </datalist>
            </label>
            <label style={{ width: 130 }}>
              <div className="micro" style={{ marginBottom: 4 }}>Rows</div>
              <input value={rows} onChange={e => setRows(e.target.value.replace(/[^0-9]/g, ""))} placeholder="0" style={{
                width: "100%", padding: "7px 10px", fontSize: 13,
                border: "1px solid var(--line)", borderRadius: 6,
                background: "var(--surface)", color: "var(--ink)",
                fontFamily: "var(--f-mono)",
              }}/>
            </label>
          </div>

          <label>
            <div className="micro" style={{ marginBottom: 4 }}>Schema columns <span className="muted">(comma-separated)</span></div>
            <input value={schema} onChange={e => setSchema(e.target.value)} placeholder="LeadId, Company, Stage, Amount" className="mono" style={{
              width: "100%", padding: "7px 10px", fontSize: 13,
              border: "1px solid var(--line)", borderRadius: 6,
              background: "var(--surface)", color: "var(--ink)",
            }}/>
          </label>

          <label>
            <div className="micro" style={{ marginBottom: 4 }}>Notes <span className="muted">(optional)</span></div>
            <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={2} placeholder="Source, refresh cadence, gotchas…" style={{
              width: "100%", padding: "7px 10px", fontSize: 13,
              border: "1px solid var(--line)", borderRadius: 6,
              background: "var(--surface)", color: "var(--ink)",
              fontFamily: "inherit", resize: "vertical",
            }}/>
          </label>
        </div>

        <div className="row" style={{ padding: "12px 18px", borderTop: "1px solid var(--line)", gap: 8, justifyContent: "flex-end" }}>
          <button className="btn ghost sm" onClick={onClose}>Cancel</button>
          <button className="btn primary sm" disabled={!canSave} onClick={submit} style={{ opacity: canSave ? 1 : 0.5 }}>Add data set</button>
        </div>
      </div>
    </div>
  );
}

function FileIcon({ ext }) {
  const e = (ext || "").toLowerCase();
  const colorBy = { csv: "var(--ok)", xlsx: "var(--ok)", pdf: "var(--risk)", json: "var(--info)" }[e] || "currentColor";
  return (
    <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
      <path d="M4 2h8l4 4v12H4z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/>
      <path d="M12 2v4h4" stroke="currentColor" strokeWidth="1.3"/>
      <text x="10" y="15" textAnchor="middle" fontSize="5.5" fontWeight="700" fill={colorBy} fontFamily="ui-monospace">{e.slice(0,4).toUpperCase()}</text>
    </svg>
  );
}

function DataSetPreviewModal({ ds, onClose, onRenameSection }) {
  if (!ds) return null;
  // Mock rows
  const rows = Array.from({ length: 8 }).map((_, i) => {
    const row = {};
    ds.schema.forEach((s, j) => {
      row[s] = ["ID","RowId","UserId","LeadId"].includes(s) ? `${ds.id.toUpperCase()}-${(1001+i).toString()}`
            : s.toLowerCase().includes("date") || s.toLowerCase().includes("at") ? `2026-05-${String(1+((i*3)%9)).padStart(2,"0")}`
            : s.toLowerCase().includes("amount") || s === "TEV" || s === "Fee" ? `$${(1.2 + i*0.4).toFixed(2)}M`
            : s.toLowerCase().includes("status") || s.toLowerCase().includes("stage") ? ["Active","Engaged","Cold","Pending"][i % 4]
            : `${s} val ${i+1}`;
    });
    return row;
  });
  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, zIndex: 50,
      background: "rgba(20,19,15,.4)",
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <div onClick={e => e.stopPropagation()} className="card" style={{
        width: 820, maxWidth: "92vw", maxHeight: "86vh", overflow: "hidden",
        display: "flex", flexDirection: "column",
      }}>
        <div className="row" style={{ padding: "14px 18px", borderBottom: "1px solid var(--line)" }}>
          <div style={{ flex: 1 }}>
            <div className="micro">Preview · {ds.section}</div>
            <div className="mono" style={{ fontSize: 14, fontWeight: 600, marginTop: 2 }}>{ds.filename}</div>
          </div>
          <button className="btn ghost sm" onClick={onClose} style={{ padding: 4 }}>
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
          </button>
        </div>
        <div style={{ padding: 18, overflow: "auto" }}>
          <div className="row" style={{ gap: 12, marginBottom: 14 }}>
            <label style={{ flex: 1 }}>
              <div className="micro" style={{ marginBottom: 4 }}>Section</div>
              <input defaultValue={ds.section} onBlur={e => onRenameSection(ds.id, e.target.value)} style={{
                width: "100%", padding: "7px 10px", fontSize: 13,
                border: "1px solid var(--line)", borderRadius: 6,
                background: "var(--surface)", color: "var(--ink)",
              }}/>
            </label>
            <label style={{ flex: 1 }}>
              <div className="micro" style={{ marginBottom: 4 }}>Filename</div>
              <input defaultValue={ds.filename} onBlur={e => { ds.filename = e.target.value; }} className="mono" style={{
                width: "100%", padding: "7px 10px", fontSize: 13,
                border: "1px solid var(--line)", borderRadius: 6,
                background: "var(--surface)", color: "var(--ink)",
              }}/>
            </label>
          </div>
          <table className="dt" style={{ fontSize: 11.5 }}>
            <thead><tr>{ds.schema.map(s => <th key={s}>{s}</th>)}</tr></thead>
            <tbody>{rows.map((r, i) => (
              <tr key={i}>{ds.schema.map(s => <td key={s} className="mono small">{r[s]}</td>)}</tr>
            ))}</tbody>
          </table>
          <div className="muted small" style={{ marginTop: 10, textAlign: "right" }}>
            Showing 8 of {ds.rows.toLocaleString()} rows · sample
          </div>
        </div>
      </div>
    </div>
  );
}

// ============================================================================
// APPROVALS SCREEN
// ============================================================================
function ApprovalsScreen() {
  const store = window.VAULT_ADMIN_STORE;
  const [, force] = React.useReducer(x => x + 1, 0);
  const [tab, setTab] = React.useState("pending");
  const [filter, setFilter] = React.useState("all"); // all | project | closed

  const decide = (id, decision, by = "bscott@harveyllc.com") => {
    const edit = store.pendingEdits.find(e => e.id === id);
    if (!edit) return;
    store.approvalHistory.unshift({
      id: "ph_" + Date.now(),
      target: edit.target, field: edit.field, decision, by, decidedISO: new Date().toISOString(),
    });
    store.pendingEdits = store.pendingEdits.filter(e => e.id !== id);
    force();
  };

  const filtered = store.pendingEdits.filter(e => filter === "all" || e.kind === filter);

  return (
    <div className="col" style={{ gap: 16, padding: 24 }}>
      {/* Strip */}
      <div className="row" style={{ gap: 12, flexWrap: "wrap" }}>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Awaiting decision</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2, color: store.pendingEdits.length > 0 ? "var(--warn)" : "var(--ink)" }}>{store.pendingEdits.length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Project edits</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{store.pendingEdits.filter(e => e.kind === "project").length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Closed-deal edits</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{store.pendingEdits.filter(e => e.kind === "closed").length}</div>
        </div>
        <div className="card card-pad" style={{ padding: "12px 16px", flex: 1, minWidth: 180 }}>
          <div className="micro">Decided · 30d</div>
          <div className="display num" style={{ fontSize: 28, marginTop: 2 }}>{store.approvalHistory.length}</div>
        </div>
      </div>

      <div className="card" style={{ padding: 0 }}>
        <div className="row" style={{ padding: "10px 14px", gap: 12, borderBottom: "1px solid var(--line)" }}>
          <div className="seg">
            <button className={tab === "pending" ? "on" : ""} onClick={() => setTab("pending")}>Pending · {store.pendingEdits.length}</button>
            <button className={tab === "history" ? "on" : ""} onClick={() => setTab("history")}>History</button>
          </div>
          {tab === "pending" && (
            <div className="seg">
              <button className={filter === "all" ? "on" : ""} onClick={() => setFilter("all")}>All</button>
              <button className={filter === "project" ? "on" : ""} onClick={() => setFilter("project")}>Projects</button>
              <button className={filter === "closed" ? "on" : ""} onClick={() => setFilter("closed")}>Closed Deals</button>
            </div>
          )}
        </div>

        {tab === "pending" && (
          <div className="col" style={{ padding: 16, gap: 12 }}>
            {filtered.length === 0 && (
              <div className="muted" style={{ padding: 40, textAlign: "center" }}>No pending approvals.</div>
            )}
            {filtered.map(e => (
              <div key={e.id} className="card" style={{ padding: 16 }}>
                <div className="row" style={{ gap: 10, marginBottom: 10 }}>
                  <span className={"pill " + (e.kind === "closed" ? "ok" : "info")}>
                    <span className="dot"/>{e.kind === "closed" ? "Closed Deal" : "Project"}
                  </span>
                  <span style={{ fontWeight: 600, fontSize: 14 }}>{e.target}</span>
                  <span className="mono small muted">{e.entity}</span>
                  <div style={{ flex: 1 }}/>
                  <span className="muted small">{new Date(e.submittedISO).toLocaleString("en-US", { month:"short", day:"numeric", hour:"numeric", minute:"2-digit" })}</span>
                </div>

                <div style={{
                  display: "grid", gridTemplateColumns: "120px 1fr auto 1fr",
                  gap: 12, alignItems: "center",
                  padding: "10px 12px",
                  background: "var(--canvas)",
                  border: "1px solid var(--line-2)", borderRadius: 8,
                }}>
                  <span className="micro">{e.field}</span>
                  <span className="mono small" style={{
                    padding: "4px 8px", borderRadius: 5,
                    background: "var(--risk-soft)",
                    color: "var(--risk)",
                    textDecoration: "line-through",
                  }}>{e.before}</span>
                  <span style={{ color: "var(--muted-2)" }}>→</span>
                  <span className="mono small" style={{
                    padding: "4px 8px", borderRadius: 5,
                    background: "var(--ok-soft)",
                    color: "var(--ok)",
                    fontWeight: 600,
                  }}>{e.after}</span>
                </div>

                <div className="row" style={{ gap: 8, marginTop: 10, alignItems: "flex-start" }}>
                  <span className="avatar" data-tone={e.submittedBy[0] === "j" ? "c" : "a"}>{e.submittedBy.split("@")[0].slice(0,2).toUpperCase()}</span>
                  <div style={{ flex: 1 }}>
                    <div className="small"><span style={{ color: "var(--ink-2)", fontWeight: 500 }}>{e.submittedBy.split("@")[0]}</span> · <span className="muted">{e.reason}</span></div>
                  </div>
                  <button className="btn sm" onClick={() => decide(e.id, "rejected")}>Reject</button>
                  <button className="btn primary sm" onClick={() => decide(e.id, "approved")}>Approve</button>
                </div>
              </div>
            ))}
          </div>
        )}

        {tab === "history" && (
          <div style={{ overflowX: "auto" }}>
            <table className="dt">
              <thead><tr>
                <th>Target</th><th>Field</th><th>Decision</th><th>By</th><th className="r">When</th>
              </tr></thead>
              <tbody>
                {store.approvalHistory.map(h => (
                  <tr key={h.id}>
                    <td style={{ fontWeight: 500 }}>{h.target}</td>
                    <td className="muted">{h.field}</td>
                    <td>
                      <span className={"pill " + (h.decision === "approved" ? "ok" : "risk")}>
                        <span className="dot"/>{h.decision}
                      </span>
                    </td>
                    <td className="mono small">{h.by}</td>
                    <td className="r muted small">{new Date(h.decidedISO).toLocaleString("en-US", { month:"short", day:"numeric", hour:"numeric", minute:"2-digit" })}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </div>
  );
}

// ============================================================================
// HIDE-TABS PANEL (lives inside settings popover from sidebar; exposes hidden set)
// ============================================================================
function TabVisibilityPanel({ onClose }) {
  const store = window.VAULT_ADMIN_STORE;
  const [, force] = React.useReducer(x => x + 1, 0);
  const userTabs = [
    { id: "overview",     label: "Overview" },
    { id: "team",         label: "Team" },
    { id: "leaderboards", label: "Leaderboards" },
    { id: "projects",     label: "Projects" },
    { id: "pipeline",     label: "Deal Pipeline" },
    { id: "closed",       label: "Closed Deals" },
    { id: "newclients",   label: "New Client Tracker" },
    { id: "badges",       label: "Badges" },
  ];
  const toggle = (id) => {
    if (store.hiddenTabs.has(id)) store.hiddenTabs.delete(id);
    else store.hiddenTabs.add(id);
    force();
    window.dispatchEvent(new CustomEvent("vault:hidden-tabs-changed"));
  };
  return (
    <div className="card" style={{
      position: "absolute", bottom: 70, left: 12, right: 12,
      padding: 14, zIndex: 30,
      boxShadow: "var(--shadow-md)",
    }}>
      <div className="row" style={{ marginBottom: 10 }}>
        <span className="micro">Hide user tabs · under construction</span>
        <div style={{ flex: 1 }}/>
        <button className="btn ghost sm" style={{ padding: 3 }} onClick={onClose}>
          <svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
        </button>
      </div>
      <div className="muted small" style={{ marginBottom: 10 }}>
        Hidden tabs disappear from the User-role sidebar but stay visible to admins.
      </div>
      <div className="col" style={{ gap: 4 }}>
        {userTabs.map(t => {
          const hidden = store.hiddenTabs.has(t.id);
          return (
            <label key={t.id} className="row" style={{ padding: "5px 6px", borderRadius: 5, cursor: "pointer", fontSize: 12.5 }}
              onMouseEnter={e => e.currentTarget.style.background = "var(--canvas)"}
              onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
              <span className="stretch">{t.label}</span>
              <span style={{
                width: 28, height: 16, borderRadius: 999,
                background: hidden ? "var(--warn)" : "var(--surface-2)",
                border: "1px solid " + (hidden ? "var(--warn)" : "var(--line)"),
                position: "relative", transition: "background .15s",
              }} onClick={() => toggle(t.id)}>
                <span style={{
                  position: "absolute", top: 1, left: hidden ? 13 : 1,
                  width: 12, height: 12, borderRadius: "50%",
                  background: "#fff", transition: "left .15s",
                }}/>
              </span>
            </label>
          );
        })}
      </div>
    </div>
  );
}

window.AccountsScreen = AccountsScreen;
window.DataSetsScreen = DataSetsScreen;
window.ApprovalsScreen = ApprovalsScreen;
window.TabVisibilityPanel = TabVisibilityPanel;
