// Shared primitives + icons for Market-Options
const { useState, useEffect, useRef, useMemo, createContext, useContext, Fragment } = React;

// ===== Icons (line, 16px, currentColor) =====
const Icon = {
  Dashboard: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><rect x="2" y="2" width="5" height="6" rx="1"/><rect x="9" y="2" width="5" height="3" rx="1"/><rect x="9" y="7" width="5" height="7" rx="1"/><rect x="2" y="10" width="5" height="4" rx="1"/></svg>,
  Docs: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M3 2.5h7l3 3V13.5a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1Z"/><path d="M10 2.5V6h3"/><path d="M5 8.5h6M5 11h4"/></svg>,
  Key: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="5.5" cy="8" r="2.5"/><path d="M8 8h6"/><path d="M11.5 8v2M14 8v2.5"/></svg>,
  Chart: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M2 13V3"/><path d="M2 13h12"/><path d="M5 11V8M8 11V5M11 11V7"/></svg>,
  Settings: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="8" cy="8" r="2"/><path d="M8 1.5v1.6M8 12.9v1.6M3.4 3.4l1.1 1.1M11.5 11.5l1.1 1.1M1.5 8h1.6M12.9 8h1.6M3.4 12.6l1.1-1.1M11.5 4.5l1.1-1.1"/></svg>,
  Webhook: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="4" cy="11.5" r="1.6"/><circle cx="12" cy="11.5" r="1.6"/><circle cx="8" cy="4" r="1.6"/><path d="M9.4 5l1.5 4.8M6.6 5l-2 4.6M5.6 11.5h4.8"/></svg>,
  Team: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="6" cy="6" r="2.5"/><path d="M2 13c0-2 1.8-3.5 4-3.5S10 11 10 13"/><circle cx="11.5" cy="6.5" r="2"/><path d="M10.5 9.5c1.7.4 2.8 1.8 2.8 3.5"/></svg>,
  Billing: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><rect x="2" y="4" width="12" height="9" rx="1.5"/><path d="M2 7h12"/><path d="M5 10.5h2"/></svg>,
  Search: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="7" cy="7" r="4.5"/><path d="M10.3 10.3l3 3"/></svg>,
  Copy: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><rect x="5" y="5" width="8" height="9" rx="1"/><path d="M3 11V3a1 1 0 0 1 1-1h7"/></svg>,
  Check: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}><path d="M3 8.5l3 3 7-7"/></svg>,
  X: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/></svg>,
  Plus: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 3v10M3 8h10"/></svg>,
  Eye: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M1.5 8C3 4.5 5.5 3 8 3s5 1.5 6.5 5c-1.5 3.5-4 5-6.5 5s-5-1.5-6.5-5Z"/><circle cx="8" cy="8" r="2"/></svg>,
  EyeOff: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M2 8c.5-1.2 1.3-2.2 2.2-2.9M14 8c-1.5 3.5-4 5-6 5-1 0-2-.3-2.9-.8"/><path d="M6.5 6.5a2 2 0 0 0 3 3"/><path d="M2 2l12 12"/></svg>,
  Trash: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M3 4.5h10"/><path d="M6 4.5V3a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v1.5"/><path d="M4.5 4.5l.6 8.4a1 1 0 0 0 1 .9h3.8a1 1 0 0 0 1-.9l.6-8.4"/></svg>,
  Refresh: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M2.5 8a5.5 5.5 0 0 1 9.5-3.8L13.5 5.5"/><path d="M13.5 2.5V5.5H10.5"/><path d="M13.5 8a5.5 5.5 0 0 1-9.5 3.8L2.5 10.5"/><path d="M2.5 13.5V10.5h3"/></svg>,
  ArrowRight: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M3 8h10M9 4l4 4-4 4"/></svg>,
  Bolt: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M8.5 1.5L3 9h4l-.5 5.5L12 7H8l.5-5.5Z"/></svg>,
  Logout: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M6 14H3.5a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1H6"/><path d="M10.5 11l3-3-3-3"/><path d="M13 8H6"/></svg>,
  Globe: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="8" cy="8" r="6"/><path d="M2 8h12M8 2c2 2 2 10 0 12M8 2c-2 2-2 10 0 12"/></svg>,
  Shield: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M8 1.5l5.5 2v4c0 3.5-2.5 6-5.5 7-3-1-5.5-3.5-5.5-7v-4l5.5-2Z"/></svg>,
  Clock: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><circle cx="8" cy="8" r="6"/><path d="M8 4.5V8l2.5 1.5"/></svg>,
  Stack: (p) => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}><path d="M8 1.5L1.5 5L8 8.5L14.5 5L8 1.5Z"/><path d="M1.5 8L8 11.5L14.5 8M1.5 11L8 14.5L14.5 11"/></svg>,
  Github: (p) => <svg viewBox="0 0 16 16" fill="currentColor" {...p}><path d="M8 1.3C4.3 1.3 1.3 4.3 1.3 8c0 3 1.9 5.5 4.6 6.4.3.1.5-.1.5-.3v-1.2c-1.9.4-2.3-.9-2.3-.9-.3-.8-.8-1-.8-1-.6-.4 0-.4 0-.4.7 0 1.1.7 1.1.7.6 1.1 1.7.8 2.1.6.1-.5.3-.8.5-1-1.5-.2-3.1-.8-3.1-3.4 0-.8.3-1.4.7-1.9-.1-.2-.3-.9.1-1.9 0 0 .6-.2 1.9.7.6-.2 1.2-.2 1.8-.2.6 0 1.2.1 1.8.2 1.3-.9 1.9-.7 1.9-.7.4 1 .1 1.7.1 1.9.4.5.7 1.1.7 1.9 0 2.7-1.6 3.2-3.2 3.4.3.2.5.6.5 1.3v1.9c0 .2.1.4.5.3 2.7-.9 4.6-3.5 4.6-6.4 0-3.7-3-6.7-6.7-6.7Z"/></svg>,
  Google: (p) => <svg viewBox="0 0 16 16" {...p}><path fill="#4285F4" d="M14.5 8.2c0-.5 0-.9-.1-1.4H8v2.7h3.7c-.2.9-.7 1.6-1.5 2.1v1.7h2.4c1.4-1.3 2-3.1 2-5.1Z"/><path fill="#34A853" d="M8 14.7c2 0 3.6-.7 4.8-1.7l-2.4-1.7c-.6.4-1.4.7-2.4.7-1.9 0-3.5-1.3-4.1-3H1.4v1.8C2.6 13 5.1 14.7 8 14.7Z"/><path fill="#FBBC04" d="M3.9 9c-.2-.5-.3-1-.3-1.6 0-.5.1-1.1.3-1.6V4H1.4C.9 5.1.6 6.3.6 7.5s.3 2.3.8 3.4l2.5-1.9Z"/><path fill="#EA4335" d="M8 3.4c1.1 0 2 .4 2.8 1.1L13 2.3C11.7 1 9.9.4 8 .4 5.1.4 2.6 2 1.4 4.4l2.5 1.9c.6-1.7 2.2-2.9 4.1-2.9Z"/></svg>,
  Lightning: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M13 2L4.5 13.5h7L11 22l8.5-11.5h-7L13 2Z"/></svg>,
  ChainLink: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M9 15l6-6"/><path d="M10.5 6.5l1.5-1.5a4 4 0 0 1 5.7 5.7L16 12.4"/><path d="M13.5 17.5L12 19a4 4 0 0 1-5.7-5.7L8 11.6"/></svg>,
  Calendar: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><rect x="3.5" y="5" width="17" height="15" rx="2"/><path d="M3.5 9.5h17M8 3v4M16 3v4"/></svg>,
  Quote: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M3 6h7M3 10h13M3 14h10M3 18h15"/></svg>,
  Magnify: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="11" cy="11" r="6"/><path d="M15.5 15.5l4 4"/></svg>,
};

// ===== Brand =====
function BrandMark({ size = 28 }) {
  return (
    <div className="brand-mark" style={{ width: size, height: size, borderRadius: size * 0.25, fontSize: size * 0.5 }}>
      <svg viewBox="0 0 16 16" width={size * 0.65} height={size * 0.65} fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="square">
        <path d="M2 11.5L6 7l3 3 5-7" />
        <path d="M14 6.5V3.5h-3" />
      </svg>
    </div>
  );
}
function Brand({ small }) {
  return (
    <div className="brand">
      <BrandMark size={small ? 22 : 28} />
      <span className="brand-name">Market-Options</span>
      {!small && <span className="brand-env">v1</span>}
    </div>
  );
}

// ===== Sidebar =====
function Sidebar({ active, onNavigate, route }) {
  const items = [
    { id: "dashboard", label: "Dashboard", icon: <Icon.Dashboard className="ico" /> },
    { id: "docs", label: "API Reference", icon: <Icon.Docs className="ico" />, badge: "v1" },
    { id: "keys", label: "API Keys", icon: <Icon.Key className="ico" /> },
    { id: "analytics", label: "Usage", icon: <Icon.Chart className="ico" /> },
    { id: "billing", label: "Billing", icon: <Icon.Billing className="ico" /> },
  ];
  const spUser = (window.Auth && Auth.getUser()) || null;
  // Defensive null-checks: a partially-stored user object (e.g. mid-rotation
  // race, or a backend response missing the email) used to crash the whole
  // sidebar on spUser.email.split — fall back to safe placeholders instead.
  const spEmailRaw = spUser && typeof spUser.email === "string" ? spUser.email : "";
  const spName = spUser
    ? (spUser.name || (spEmailRaw && spEmailRaw.split("@")[0]) || "User")
    : "Guest";
  const spEmail = spEmailRaw || (spUser ? "—" : "Not signed in");
  const spInitials = (function () {
    const src = (spUser && (spUser.name || spEmailRaw)) || "Guest";
    const parts = String(src).split(/[\s@._-]+/).filter(Boolean);
    const a = (parts[0] && parts[0][0]) || "";
    const b = (parts[1] && parts[1][0]) || "";
    return (a + b).toUpperCase() || "G";
  })();

  return (
    <aside className="sidebar">
      <Brand />
      <div className="nav-group-label">Build</div>
      {items.map((it) => (
        <div
          key={it.id}
          className={`nav-item ${active === it.id ? "active" : ""}`}
          onClick={() => onNavigate(it.id)}
        >
          {it.icon}
          <span>{it.label}</span>
          {it.badge && <span className="nav-badge">{it.badge}</span>}
        </div>
      ))}
      <div className="sidebar-foot">
        <div className="avatar">{spInitials}</div>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="user-name">{spName}</div>
          <div className="user-email">{spEmail}</div>
        </div>
        <button
          className="icon-btn"
          title="Sign out"
          onClick={() => { if (window.Auth) Auth.clear(); onNavigate("landing"); }}
        >
          <Icon.Logout style={{ width: 14, height: 14 }} />
        </button>
      </div>
    </aside>
  );
}

// ===== Top bar inside content =====
function TopBar({ crumbs = [], status = "operational" }) {
  return (
    <div className="topbar">
      <div className="crumbs">
        {crumbs.map((c, i) => (
          <Fragment key={i}>
            {i > 0 && <span className="sep">/</span>}
            <span className={i === crumbs.length - 1 ? "cur" : ""}>{c}</span>
          </Fragment>
        ))}
      </div>
      <span className="status-pill">
        <span className={`dot ${status === "degraded" ? "amber" : status === "down" ? "red" : ""}`} />
        Market-Options API · operational
      </span>
    </div>
  );
}

// ===== Code block with tabs + copy =====
function CodeBlock({ tabs, defaultTab, height }) {
  const [active, setActive] = useState(defaultTab || tabs[0].id);
  const [copied, setCopied] = useState(false);
  const cur = tabs.find((t) => t.id === active) || tabs[0];
  const copy = () => {
    const text = cur.code
      .replace(/<[^>]+>/g, "")
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&amp;/g, "&");
    if (navigator.clipboard) navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 1400);
  };
  return (
    <div className="code-card">
      <div className="code-tabs">
        {tabs.map((t) => (
          <div
            key={t.id}
            className={`code-tab ${active === t.id ? "active" : ""}`}
            onClick={() => setActive(t.id)}
          >
            <span className="lang-dot" />
            {t.label}
          </div>
        ))}
        <div className="code-toolbar">
          <button className="icon-btn" onClick={copy} title="Copy">
            {copied ? <Icon.Check style={{ width: 13, height: 13, color: "var(--green)" }} /> : <Icon.Copy style={{ width: 13, height: 13 }} />}
          </button>
        </div>
      </div>
      <pre className="code-body" style={height ? { maxHeight: height } : null}><code dangerouslySetInnerHTML={{ __html: cur.code }} /></pre>
    </div>
  );
}

// Syntax highlighter — placeholder-based to avoid markup collisions
function escapeHtml(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
function highlight(raw, rules) {
  // 1. Extract by-pattern tokens into placeholders so later rules don't see inside them.
  const slots = [];
  let work = raw;
  for (const r of rules) {
    work = work.replace(r.re, (m, ...args) => {
      // args last two are offset, string; the captures are args[0..n-1]
      const groups = args.slice(0, -2);
      const inner = r.capture ? groups[r.capture - 1] : m;
      slots.push({ cls: r.cls, text: inner, full: m });
      return `\x00${slots.length - 1}\x00`;
    });
  }
  // 2. HTML-escape what's left.
  work = escapeHtml(work);
  // 3. Swap placeholders for spans, escaping their inner text too.
  return work.replace(/\x00(\d+)\x00/g, (_, i) => {
    const s = slots[+i];
    // If the rule had a capture group, surround only the captured part with the span.
    if (s.full === s.text) {
      return `<span class="${s.cls}">${escapeHtml(s.text)}</span>`;
    }
    // Otherwise replace the captured substring within the full match.
    const idx = s.full.indexOf(s.text);
    const before = escapeHtml(s.full.slice(0, idx));
    const after = escapeHtml(s.full.slice(idx + s.text.length));
    return `${before}<span class="${s.cls}">${escapeHtml(s.text)}</span>${after}`;
  });
}

const H = {
  json: (raw) => highlight(raw, [
    { re: /"[^"]*"(?=\s*:)/g, cls: "tok-key" },           // object keys
    { re: /"[^"]*"/g, cls: "tok-str" },                    // string values
    { re: /\b(true|false|null)\b/g, cls: "tok-bool" },     // bool/null
    // numbers — the lookbehind/lookahead skip digits inside \x00 placeholders
    { re: /(?<![\x00\d])-?\d+\.?\d*(?![\d\x00])/g, cls: "tok-num" },
  ]),
  curl: (raw) => highlight(raw, [
    { re: /"[^"]*"/g, cls: "tok-str" },                    // strings first
    { re: /^curl\b/m, cls: "tok-method" },                 // curl keyword
    { re: /\b(GET|POST|DELETE|PUT)\b/g, cls: "tok-method" },
    { re: /https?:\/\/[^\s"]+/g, cls: "tok-path" },        // URLs
    { re: /(?:^|\s)(--?[a-zA-Z-]+)/g, cls: "tok-flag", capture: 1 }, // flags
  ]),
  js: (raw) => highlight(raw, [
    { re: /\/\/[^\n]*/g, cls: "tok-cmt" },
    { re: /"[^"]*"|'[^']*'|`[^`]*`/g, cls: "tok-str" },
    { re: /\b(const|let|var|async|await|function|return|new|import|from|export)\b/g, cls: "tok-bool" },
    { re: /\b(fetch|console|log)\b/g, cls: "tok-method" },
  ]),
  py: (raw) => highlight(raw, [
    { re: /#[^\n]*/g, cls: "tok-cmt" },
    { re: /"[^"]*"|'[^']*'/g, cls: "tok-str" },
    { re: /\b(import|from|def|return|if|else|None|True|False)\b/g, cls: "tok-bool" },
  ]),
};

// ===== Mini spark/area chart =====
function Sparkline({ data, color = "var(--green)", height = 36, fill = true }) {
  const w = 200, h = height;
  const max = Math.max(...data), min = Math.min(...data);
  const range = max - min || 1;
  const pts = data.map((v, i) => {
    const x = (i / (data.length - 1)) * w;
    const y = h - ((v - min) / range) * (h - 4) - 2;
    return [x, y];
  });
  const path = pts.map((p, i) => `${i === 0 ? "M" : "L"}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(" ");
  const area = `${path} L${w},${h} L0,${h} Z`;
  return (
    <svg className="spark" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none">
      {fill && <path d={area} fill={color} opacity="0.14" />}
      <path d={path} stroke={color} strokeWidth="1.6" fill="none" />
    </svg>
  );
}

// ===== Tag =====
function Tag({ kind = "muted", children }) {
  return <span className={`tag ${kind}`}>{children}</span>;
}

// ===== Loading / error states (console screens) =====
function LoadingScreen({ crumbs }) {
  return (
    <>
      <TopBar crumbs={crumbs || []} />
      <div className="page" style={{ display: "grid", placeItems: "center", minHeight: 420 }}>
        <div className="mono text-sm muted">Loading…</div>
      </div>
    </>
  );
}

function ErrorScreen({ crumbs, message, onRetry }) {
  return (
    <>
      <TopBar crumbs={crumbs || []} status="degraded" />
      <div className="page" style={{ display: "grid", placeItems: "center", minHeight: 420 }}>
        <div style={{ textAlign: "center", maxWidth: 380 }}>
          <div style={{
            width: 40, height: 40, borderRadius: 10, margin: "0 auto 14px",
            background: "rgba(229,72,77,0.12)", color: "var(--red)",
            display: "grid", placeItems: "center",
          }}>
            <Icon.X style={{ width: 18, height: 18 }} />
          </div>
          <div style={{ fontWeight: 500, marginBottom: 6 }}>Something went wrong</div>
          <div className="text-sm muted" style={{ marginBottom: 16 }}>{message || "Please try again."}</div>
          {onRetry && <button className="btn btn-primary" onClick={onRetry}>
            <Icon.Refresh style={{ width: 13, height: 13 }} /> Retry
          </button>}
        </div>
      </div>
    </>
  );
}

// Ensures a sparkline always has at least two data points.
function sparkData(arr) {
  const a = (arr || []).map((n) => Number(n) || 0);
  if (a.length === 0) return [0, 0];
  if (a.length === 1) return [0, a[0]];
  return a;
}

// ===== Re-auth (step-up password) modal + guard hook =====
// Sensitive actions (API keys, subscriptions) require a fresh password check.
function ReauthModal({ title, message, onAuthed, onClose }) {
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const overlay = {
    position: "fixed", inset: 0, zIndex: 70,
    background: "oklch(0.08 0.012 240 / 0.7)", backdropFilter: "blur(6px)",
    display: "grid", placeItems: "center",
  };

  const submit = (e) => {
    if (e) e.preventDefault();
    if (loading) return;
    if (!password) { setError("Enter your password to continue."); return; }
    setLoading(true);
    setError("");
    API.reauth(password)
      .then(() => { setLoading(false); onAuthed(); })
      .catch((err) => {
        setError((err && err.message) || "Could not confirm your password.");
        setLoading(false);
      });
  };

  return (
    <div style={overlay} onClick={onClose}>
      <form className="card" style={{ width: 420, boxShadow: "var(--shadow-lg)" }}
        onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="card-head">
          <span style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <Icon.Shield style={{ width: 15, height: 15, color: "var(--cyan)" }} />
            {title || "Confirm it's you"}
          </span>
          <button type="button" className="icon-btn" style={{ marginLeft: "auto" }} onClick={onClose}>
            <Icon.X style={{ width: 13, height: 13 }} />
          </button>
        </div>
        <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          <p className="text-sm muted" style={{ margin: 0, lineHeight: 1.55 }}>
            {message || "This is a sensitive action. Re-enter your password to continue."}
          </p>
          <div className="field">
            <label>Password</label>
            <input className="input" type="password" value={password} autoFocus
              placeholder="Your account password"
              onChange={(e) => setPassword(e.target.value)} />
          </div>
          {error && (
            <div style={{
              padding: "9px 12px", borderRadius: 7, fontSize: 12.5,
              background: "rgba(229,72,77,0.12)", border: "1px solid var(--red)",
              color: "var(--red)",
            }}>{error}</div>
          )}
          <div className="row gap-10" style={{ justifyContent: "flex-end", marginTop: 2 }}>
            <button type="button" className="btn" onClick={onClose}>Cancel</button>
            <button type="submit" className="btn btn-primary" disabled={loading}>
              <Icon.Shield style={{ width: 13, height: 13 }} />
              {loading ? "Confirming…" : "Confirm"}
            </button>
          </div>
        </div>
      </form>
    </div>
  );
}

// Returns { guard, reauthModal }. Call guard(fn, opts) to run a sensitive
// action: if a fresh step-up token exists it runs immediately, otherwise the
// password modal appears first. Render {reauthModal} somewhere in the screen.
function useReauthGuard() {
  const [pending, setPending] = useState(null);
  const guard = (fn, opts) => {
    if (window.Auth && Auth.hasReauth()) { fn(); return; }
    setPending({ fn: fn, title: opts && opts.title, message: opts && opts.message });
  };
  const reauthModal = pending ? (
    <ReauthModal
      title={pending.title}
      message={pending.message}
      onClose={() => setPending(null)}
      onAuthed={() => { const fn = pending.fn; setPending(null); fn(); }}
    />
  ) : null;
  return { guard: guard, reauthModal: reauthModal };
}

/**
 * Copy `text` to the clipboard. Prefers the async Clipboard API (HTTPS only)
 * and falls back to a hidden textarea + execCommand so legacy browsers and
 * iframes without the Clipboard API still work. Resolves on success, rejects
 * on any failure — callers handle both states so the UI never lies about
 * whether the copy happened.
 */
function copyToClipboard(text) {
  if (!text || typeof text !== "string") {
    return Promise.reject(new Error("nothing to copy"));
  }
  if (navigator.clipboard && navigator.clipboard.writeText) {
    return navigator.clipboard.writeText(text);
  }
  return new Promise(function (resolve, reject) {
    try {
      var ta = document.createElement("textarea");
      ta.value = text;
      ta.setAttribute("readonly", "");
      ta.style.position = "fixed";
      ta.style.top = "0";
      ta.style.left = "-9999px";
      document.body.appendChild(ta);
      ta.focus();
      ta.select();
      var ok = document.execCommand("copy");
      document.body.removeChild(ta);
      if (ok) resolve();
      else reject(new Error("execCommand copy returned false"));
    } catch (err) {
      reject(err);
    }
  });
}

// Globally expose
Object.assign(window, {
  Icon, BrandMark, Brand, Sidebar, TopBar, CodeBlock, H, Sparkline, Tag,
  LoadingScreen, ErrorScreen, sparkData, ReauthModal, useReauthGuard,
  copyToClipboard,
});
