/* Voyage Fleet — Tenant Detail (sticky header + 8 tabs). Exports: TenantDetail. */

const { useState: useS_t, useMemo: useMemo_t } = React;

function HeaderLink({ icon, label, mono }) {
  return (
    <a href="#" onClick={e => e.preventDefault()} style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--jrni-color-primary-1)", fontWeight: 500, fontFamily: mono ? "var(--jrni-font-family-mono)" : "var(--jrni-font-family-sans)", whiteSpace: "nowrap" }}>
      {icon}{label}<Icons.External size={11} />
    </a>
  );
}

function MiniKpi({ label, value, tone, sub }) {
  const c = { neutral: "var(--jrni-color-neutral-1)", success: "var(--jrni-color-semantic-green-1)", warning: "var(--jrni-color-semantic-orange-1)", danger: "var(--jrni-color-semantic-red-1)", info: "var(--jrni-color-semantic-blue-1)" }[tone || "neutral"];
  return (
    <Card padding={14} style={{ flex: "1 1 0", minWidth: 0 }}>
      <div style={{ fontSize: 11, fontWeight: 600, color: "var(--jrni-color-text-soft)", textTransform: "uppercase", letterSpacing: 0.03 }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 700, color: c, marginTop: 4, lineHeight: 1.1 }}>{value}</div>
      {sub ? <div style={{ fontSize: 11.5, color: "var(--jrni-color-text-soft)", marginTop: 2 }}>{sub}</div> : null}
    </Card>
  );
}

/* ── timeline ── */
function TimelineItem({ icon, isBot, title, meta, last }) {
  return (
    <div style={{ display: "flex", gap: 12, position: "relative", paddingBottom: last ? 0 : 18 }}>
      {!last ? <span style={{ position: "absolute", left: 13, top: 28, bottom: 0, width: 1, background: "var(--jrni-color-surface-border)" }} /> : null}
      <span style={{ width: 28, height: 28, borderRadius: 999, flexShrink: 0, display: "grid", placeItems: "center", background: isBot ? "var(--jrni-color-primary-4)" : "var(--jrni-color-neutral-6)", border: `1px solid ${isRalph ? "var(--jrni-color-primary-2)" : "var(--jrni-color-surface-border)"}`, color: isRalph ? "var(--jrni-color-primary-1)" : "var(--jrni-color-neutral-2)", zIndex: 1 }}>{icon}</span>
      <div style={{ flex: 1, paddingTop: 2 }}>
        <div style={{ fontSize: 13.5, color: "var(--jrni-color-neutral-1)", lineHeight: 1.45 }}>{title}</div>
        <div style={{ fontSize: 11.5, color: "var(--jrni-color-text-soft)", marginTop: 2 }}>{meta}</div>
      </div>
    </div>
  );
}

/* ════ TAB: Overview ════ */
function TabOverview({ t, mods }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <div style={{ display: "flex", gap: 12 }}>
        <MiniKpi label="Components" value={t.moduleCount} tone={t.drift === "none" ? "success" : t.drift === "major" ? "danger" : "warning"} sub={t.drift === "none" ? "all current" : `${t.modulesBehind} behind`} />
        <MiniKpi label="Last deploy" value={t.lastDeployDays === 0 ? "Today" : `${t.lastDeployDays}d ago`} tone={t.ci === "failing" ? "danger" : "neutral"} sub={t.ci === "failing" ? "Pipeline failing" : "Pipeline passing"} />
        <MiniKpi label="Open PRs" value={t.openPR ? 2 : 0} tone="info" sub="in tenant repo" />
        <MiniKpi label="Pending migrations" value={t.pendingMigrations} tone={t.pendingMigrations ? "warning" : "neutral"} sub={t.pendingMigrations ? "un-applied" : "all applied"} />
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1.5fr 1fr", gap: 16, alignItems: "start" }}>
        <Card padding={18}>
          <SectionLabel>Activity · last 14 days</SectionLabel>
          <div style={{ marginTop: 8 }}>
            <TimelineItem icon={<Icons.Rocket size={14} />} isBot title={<span>Bumped <code style={mono13}>@voyage/events</code> to 2.4.0 — PR #214 merged, deploy passing.</span>} meta="GitHub Actions · 2 days ago" />
            <TimelineItem icon={<Icons.GitPr size={14} />} title={<span>PR #211 merged — bump @voyage/notifications to 2.2.0.</span>} meta="Marek Singh · 4 days ago" />
            <TimelineItem icon={<Icons.Migration size={14} />} isBot title={<span>Applied migration <code style={mono13}>2026_04_waitlists</code> to D1.</span>} meta="GitHub Actions · 5 days ago" />
            <TimelineItem icon={<Icons.Settings size={14} />} title="Rotated SENDGRID_API_KEY via Wrangler secrets." meta="Lori Owens · 8 days ago" />
            <TimelineItem icon={<Icons.Deploy size={14} />} title="Pages + Workers redeploy (convention bump)." meta="GitHub Actions · 11 days ago" last />
          </div>
        </Card>
        <Card padding={18}>
          <SectionLabel>Current composition</SectionLabel>
          <div style={{ marginTop: 4 }}>
            {mods.map(m => (
              <div key={m.id} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "8px 0", borderBottom: "1px solid var(--jrni-color-surface-border)" }}>
                <span style={{ fontFamily: "var(--jrni-font-family-mono)", fontSize: 12.5, color: "var(--jrni-color-neutral-1)" }}>{m.id}</span>
                <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
                  <Mono color={m.drift === "none" ? "var(--jrni-color-semantic-green-1)" : "var(--jrni-color-semantic-orange-1)"}>{m.current}</Mono>
                  <span style={{ width: 7, height: 7, borderRadius: 999, background: m.drift === "none" ? "var(--jrni-color-semantic-green-1)" : "var(--jrni-color-semantic-orange-1)" }} />
                </span>
              </div>
            ))}
          </div>
          <div style={{ marginTop: 12 }}><Button variant="secondary" size="sm" fullWidth icon={<Icons.Layers size={13} />} onClick={() => navigate("/tenant", { slug: t.slug, tab: "configuration" })}>Edit composition</Button></div>
        </Card>
      </div>
    </div>
  );
}
const mono13 = { fontFamily: "var(--jrni-font-family-mono)", fontSize: 12.5, background: "var(--jrni-color-neutral-6)", padding: "1px 5px", borderRadius: 3 };

/* ════ TAB: Modules ════ */
function TabModules({ t, mods }) {
  const [expanded, setExpanded] = useS_t(null);
  return (
    <Card padding={0} style={{ overflow: "hidden" }}>
      <table style={{ width: "100%", borderCollapse: "collapse" }}>
        <thead><tr style={{ background: "var(--jrni-color-surface-card-soft)", borderBottom: "1px solid var(--jrni-color-surface-border)" }}>
          {["Module", "Current pin", "Latest", "Drift", ""].map((h, i) => <th key={i} style={thStyle}>{h}</th>)}
        </tr></thead>
        <tbody>
          {mods.map(m => (
            <React.Fragment key={m.id}>
              <tr style={{ borderBottom: "1px solid var(--jrni-color-surface-border)" }}>
                <td style={{ padding: "11px 14px", fontFamily: "var(--jrni-font-family-mono)", fontSize: 13, fontWeight: 600, color: "var(--jrni-color-neutral-1)" }}>{m.name}</td>
                <td style={{ padding: "0 14px" }}><Mono color="var(--jrni-color-neutral-1)">{m.current}</Mono></td>
                <td style={{ padding: "0 14px" }}><Mono>{m.latest}</Mono></td>
                <td style={{ padding: "0 14px" }}><DriftPill level={m.drift} /></td>
                <td style={{ padding: "0 14px", textAlign: "right", whiteSpace: "nowrap" }}>
                  {m.drift !== "none" ? <button onClick={() => setExpanded(expanded === m.id ? null : m.id)} style={{ background: "none", border: "none", color: "var(--jrni-color-primary-1)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", marginRight: 12 }}>What changed</button> : null}
                  <Button variant={m.drift === "none" ? "secondary" : "primary"} size="sm" icon={<Icons.GitPr size={12} />} disabled={m.drift === "none"}>Bump</Button>
                </td>
              </tr>
              {expanded === m.id ? (
                <tr><td colSpan={5} style={{ padding: "0 14px 14px", background: "var(--jrni-color-surface-card-soft)" }}>
                  <div style={{ borderLeft: "2px solid var(--jrni-color-primary-2)", paddingLeft: 14, marginTop: 10, display: "flex", flexDirection: "column", gap: 6 }}>
                    {[`${m.latest} — fast-pass race fix, retry backoff`, `${m.current} → migration 2026_04 introduces D1 column`, "Worker binding QUEUE_DO added"].map((c, i) => (
                      <div key={i} style={{ display: "flex", gap: 8, fontSize: 12.5, color: "var(--jrni-color-neutral-2)" }}><Icons.Branch size={13} color="var(--jrni-color-text-soft)" />{c}</div>
                    ))}
                  </div>
                </td></tr>
              ) : null}
            </React.Fragment>
          ))}
        </tbody>
      </table>
      {t.customOverlay ? (
        <div style={{ display: "flex", gap: 10, padding: "12px 16px", background: "var(--jrni-color-status-warning-bg)", borderTop: "1px solid var(--jrni-color-surface-border)" }}>
          <Icons.Warning size={16} color="var(--jrni-color-semantic-orange-1)" />
          <div style={{ fontSize: 12.5, color: "var(--jrni-color-neutral-1)" }}><b>Custom overlay detected.</b> This tenant has 2 custom screens + 1 integration beyond its starter template. <a href="#" onClick={e => e.preventDefault()} style={{ color: "var(--jrni-color-primary-1)", fontWeight: 600 }}>View diff →</a></div>
        </div>
      ) : null}
    </Card>
  );
}
const thStyle = { textAlign: "left", padding: "10px 14px", fontSize: 11, fontWeight: 700, color: "var(--jrni-color-text-soft)", textTransform: "uppercase", letterSpacing: 0.04 };

/* ════ TAB: Deployments ════ */
function TabDeployments({ t }) {
  const deploys = useMemo_t(() => Array.from({ length: 8 }, (_, i) => {
    const kinds = ["Workers + Pages", "D1 migration", "Workers", "Pages", "All"];
    const sha = Math.floor(Math.random() * 1e7).toString(16).slice(0, 7);
    const status = i === 0 && t.ci === "failing" ? "failing" : i === 1 ? "running" : "passing";
    return { sha, kind: kinds[i % kinds.length], status, actor: i % 3 === 0 ? "GitHub Actions" : "Lori Owens", dur: `${(1 + Math.random() * 4).toFixed(1)}m`, when: i === 0 ? "Today 08:51" : `${i + 1}d ago`, msg: ["Rev to v1.4.2", "Bump notifications 2.2.0", "Apply waitlists migration", "Convention redeploy", "Rotate secret"][i % 5] };
  }), [t.slug]);
  const [open, setOpen] = useS_t(0);
  return (
    <Card padding={0} style={{ overflow: "hidden" }}>
      <table style={{ width: "100%", borderCollapse: "collapse" }}>
        <thead><tr style={{ background: "var(--jrni-color-surface-card-soft)", borderBottom: "1px solid var(--jrni-color-surface-border)" }}>
          {["When", "Commit", "Kind", "Status", "Duration", "By", ""].map((h, i) => <th key={i} style={thStyle}>{h}</th>)}
        </tr></thead>
        <tbody>
          {deploys.map((d, i) => (
            <React.Fragment key={i}>
              <tr onClick={() => setOpen(open === i ? -1 : i)} style={{ borderBottom: "1px solid var(--jrni-color-surface-border)", cursor: "pointer" }}>
                <td style={{ padding: "10px 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-1)", whiteSpace: "nowrap" }}>{d.when}</td>
                <td style={{ padding: "0 14px" }}><span style={{ display: "inline-flex", alignItems: "center", gap: 7 }}><Mono color="var(--jrni-color-neutral-1)">{d.sha}</Mono><span style={{ fontSize: 12, color: "var(--jrni-color-text-soft)" }}>{d.msg}</span></span></td>
                <td style={{ padding: "0 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-2)", whiteSpace: "nowrap" }}>{d.kind}</td>
                <td style={{ padding: "0 14px" }}><CiPill status={d.status} small /></td>
                <td style={{ padding: "0 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-2)" }}>{d.dur}</td>
                <td style={{ padding: "0 14px" }}><ActorChip name={d.actor} isBot={d.actor === "GitHub Actions"} size="sm" /></td>
                <td style={{ padding: "0 14px", textAlign: "right" }}><Icons.Chevron dir={open === i ? "up" : "down"} size={13} color="var(--jrni-color-neutral-3)" /></td>
              </tr>
              {open === i ? (
                <tr><td colSpan={7} style={{ padding: "14px 16px", background: "var(--jrni-color-surface-card-soft)" }}>
                  <div style={{ display: "flex", gap: 24, flexWrap: "wrap" }}>
                    <div><div style={subLbl}>Smoke checks</div><div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 13 }}><Icons.Check size={13} color="var(--jrni-color-semantic-green-1)" />12/12 passing</div></div>
                    <div><div style={subLbl}>Console errors</div><div style={{ fontSize: 13, color: d.status === "failing" ? "var(--jrni-color-semantic-red-1)" : "var(--jrni-color-neutral-1)" }}>{d.status === "failing" ? "3 errors" : "0 errors"}</div></div>
                    <div><div style={subLbl}>axe audit</div><div style={{ fontSize: 13 }}>0 violations</div></div>
                    <div style={{ marginLeft: "auto", display: "flex", gap: 8, alignItems: "center" }}>
                      <HeaderLink icon={<Icons.Github size={13} />} label="View Actions run" />
                      <ClaudeCodeLink command={`voyage-fleet rollback ${t.slug} --to ${d.sha}`} label="Roll back to this deploy" />
                    </div>
                  </div>
                </td></tr>
              ) : null}
            </React.Fragment>
          ))}
        </tbody>
      </table>
    </Card>
  );
}
const subLbl = { fontSize: 10.5, fontWeight: 700, color: "var(--jrni-color-text-soft)", textTransform: "uppercase", letterSpacing: 0.04, marginBottom: 4 };

/* ════ TAB: Migrations ════ */
function TabMigrations({ t }) {
  const applied = Array.from({ length: 6 }, (_, i) => ({ id: `2026_0${5 - Math.floor(i/2)}_${["waitlists","fastpass","reporting_idx","kv_rename","do_binding","r2_assets"][i]}`, when: `${i + 2}d ago`, by: i % 2 ? "GitHub Actions" : "Marek Singh", mod: ["events","queuing","reporting","notifications","queuing","booking"][i], status: "applied" }));
  const [mf, setMf] = useS_t({ status: "all", time: "all" });
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
        <Segmented value={mf.status} onChange={v => setMf(s => ({ ...s, status: v }))} options={[{ value: "all", label: "All" }, { value: "applied", label: "Applied" }, { value: "pending", label: "Pending" }, { value: "failed", label: "Failed" }, { value: "held", label: "Held" }]} />
        <MultiFilter label="Source module" value="" onChange={() => {}} options={t.modules.map(m => ({ value: m, label: m }))} />
        <Segmented value={mf.time} onChange={v => setMf(s => ({ ...s, time: v }))} options={[{ value: "all", label: "Any time" }, { value: "7", label: "7d" }, { value: "30", label: "30d" }]} />
      </div>
      {t.pendingMigrations > 0 ? (
        <Card padding={0} style={{ overflow: "hidden", borderColor: "var(--jrni-color-semantic-orange-1)" }}>
          <div style={{ padding: "10px 16px", background: "var(--jrni-color-status-warning-bg)", borderBottom: "1px solid var(--jrni-color-surface-border)", display: "flex", alignItems: "center", gap: 8 }}>
            <Icons.Warning size={15} color="var(--jrni-color-semantic-orange-1)" />
            <span style={{ fontSize: 13, fontWeight: 600, color: "var(--jrni-color-neutral-1)" }}>{t.pendingMigrations} pending migration{t.pendingMigrations > 1 ? "s" : ""}</span>
          </div>
          {Array.from({ length: t.pendingMigrations }, (_, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 16px", borderBottom: i < t.pendingMigrations - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
              <Mono color="var(--jrni-color-neutral-1)">2026_05_{["waitlist_v2","queue_state","stripe_evt"][i % 3]}</Mono>
              <Pill tone="neutral">{["deploy pending","migration failed","manual hold"][i % 3]}</Pill>
              <span style={{ marginLeft: "auto", fontSize: 12, color: "var(--jrni-color-text-soft)" }}>from @voyage/{t.modules[i % t.modules.length]}</span>
              <Button variant="secondary" size="sm">View SQL</Button>
            </div>
          ))}
        </Card>
      ) : null}
      <Card padding={0} style={{ overflow: "hidden" }}>
        <div style={{ padding: "10px 16px", borderBottom: "1px solid var(--jrni-color-surface-border)", fontSize: 13, fontWeight: 600, color: "var(--jrni-color-neutral-1)" }}>Applied ledger</div>
        <table style={{ width: "100%", borderCollapse: "collapse" }}>
          <thead><tr style={{ background: "var(--jrni-color-surface-card-soft)", borderBottom: "1px solid var(--jrni-color-surface-border)" }}>{["Migration", "Applied", "By", "Source module", "Status", ""].map((h, i) => <th key={i} style={thStyle}>{h}</th>)}</tr></thead>
          <tbody>{applied.map((m, i) => (
            <tr key={i} style={{ borderBottom: i < applied.length - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
              <td style={{ padding: "9px 14px" }}><Mono color="var(--jrni-color-neutral-1)">{m.id}</Mono></td>
              <td style={{ padding: "0 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-2)" }}>{m.when}</td>
              <td style={{ padding: "0 14px" }}><ActorChip name={m.by} isBot={m.by === "GitHub Actions"} size="sm" /></td>
              <td style={{ padding: "0 14px", fontFamily: "var(--jrni-font-family-mono)", fontSize: 12, color: "var(--jrni-color-neutral-2)" }}>@voyage/{m.mod}</td>
              <td style={{ padding: "0 14px" }}><Pill tone="success">Applied</Pill></td>
              <td style={{ padding: "0 14px", textAlign: "right" }}><button style={{ background: "none", border: "none", color: "var(--jrni-color-primary-1)", fontSize: 12.5, fontWeight: 600, cursor: "pointer" }}>SQL</button></td>
            </tr>
          ))}</tbody>
        </table>
      </Card>
    </div>
  );
}

/* ════ TAB: Configuration ════ */
function TabConfiguration({ t, mods, archived }) {
  const bindings = [
    { type: "D1", name: `${t.slug}-primary`, status: "provisioned" },
    { type: "D1", name: `${t.slug}-reporting`, status: "provisioned" },
    { type: "KV", name: `${t.slug}-sessions`, status: "provisioned" },
    { type: "R2", name: `${t.slug}-assets`, status: "provisioned" },
    { type: "Queue", name: `${t.slug}-notifications`, status: "provisioned" },
    { type: "DO", name: `${t.slug}-queue-state`, status: t.drift === "major" ? "pending" : "provisioned" },
  ];
  const envs = [["SENDGRID_API_KEY", "12 days ago"], ["STRIPE_WEBHOOK_SECRET", "16h ago"], ["JWT_SIGNING_KEY", "3 months ago"], ["OAUTH_CLIENT_SECRET", "1 month ago"]];
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <div style={{ display: "flex", gap: 10, padding: "11px 16px", background: "var(--jrni-color-status-info-bg)", borderRadius: 8, alignItems: "center" }}>
        <Icons.Git size={16} color="var(--jrni-color-semantic-blue-1)" />
        <span style={{ fontSize: 12.5, color: "var(--jrni-color-neutral-1)", flex: 1 }}>Every change writes a PR against <Mono color="var(--jrni-color-semantic-blue-1)">{t.repo}</Mono>. Choose <b>Open PR</b> or <b>Auto-merge &amp; deploy</b> for low-risk edits.</span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, alignItems: "start" }}>
        <Card padding={18}>
          <SectionLabel action={<span style={{ display: "inline-flex", alignItems: "center", gap: 12 }}><ClaudeCodeLink path={`ryanbreen/voyage-${t.slug}/wrangler.toml`} label="Edit in code" /><Button variant="tertiary" size="sm" icon={<Icons.Plus size={12} />} disabled={archived}>Add</Button></span>}>Cloudflare bindings</SectionLabel>
          <table style={{ width: "100%", borderCollapse: "collapse", marginTop: 4 }}><tbody>
            {bindings.map((b, i) => (
              <tr key={i} style={{ borderBottom: i < bindings.length - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
                <td style={{ padding: "8px 0", width: 56 }}><span style={{ fontSize: 10.5, fontWeight: 700, padding: "1px 6px", borderRadius: 4, background: "var(--jrni-color-neutral-6)", color: "var(--jrni-color-neutral-2)", fontFamily: "var(--jrni-font-family-mono)" }}>{b.type}</span></td>
                <td style={{ padding: "8px 0", fontFamily: "var(--jrni-font-family-mono)", fontSize: 12.5, color: "var(--jrni-color-neutral-1)" }}>{b.name}</td>
                <td style={{ padding: "8px 0", textAlign: "right" }}>{b.status === "provisioned" ? <Pill tone="success">Provisioned</Pill> : <Pill tone="warning">Pending</Pill>}</td>
              </tr>
            ))}
          </tbody></table>
        </Card>
        <Card padding={18}>
          <SectionLabel action={<ClaudeCodeLink path={`ryanbreen/voyage-${t.slug}/.env.example`} label="Edit in code" />}>Environment variables</SectionLabel>
          <table style={{ width: "100%", borderCollapse: "collapse", marginTop: 4 }}><tbody>
            {envs.map((e, i) => (
              <tr key={i} style={{ borderBottom: i < envs.length - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
                <td style={{ padding: "8px 0", fontFamily: "var(--jrni-font-family-mono)", fontSize: 12.5, color: "var(--jrni-color-neutral-1)" }}>{e[0]}</td>
                <td style={{ padding: "8px 0", fontFamily: "var(--jrni-font-family-mono)", fontSize: 12.5, color: "var(--jrni-color-neutral-3)" }}>••••••••</td>
                <td style={{ padding: "8px 0", fontSize: 11.5, color: "var(--jrni-color-text-soft)", textAlign: "right" }}>rotated {e[1]}</td>
                <td style={{ padding: "8px 0 8px 12px", textAlign: "right" }}><button style={{ background: "none", border: "none", color: "var(--jrni-color-primary-1)", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>Rotate</button></td>
              </tr>
            ))}
          </tbody></table>
        </Card>
        <Card padding={18}>
          <SectionLabel action={<ClaudeCodeLink path={`ryanbreen/voyage-${t.slug}/brand-tokens.json`} label="Edit in code" />}>Brand tokens</SectionLabel>
          <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 6 }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}><span style={{ fontSize: 13, color: "var(--jrni-color-neutral-2)" }}>Primary color</span><span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}><span style={{ width: 18, height: 18, borderRadius: 4, background: "#5142C7", border: "1px solid var(--jrni-color-surface-border)" }} /><Mono color="var(--jrni-color-neutral-1)">#5142C7</Mono></span></div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}><span style={{ fontSize: 13, color: "var(--jrni-color-neutral-2)" }}>Logo URL</span><Mono>cdn/{t.slug}/logo.svg</Mono></div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}><span style={{ fontSize: 13, color: "var(--jrni-color-neutral-2)" }}>Typography</span><span style={{ fontSize: 13, color: "var(--jrni-color-neutral-1)" }}>DM Sans (default)</span></div>
          </div>
        </Card>
        <Card padding={18}>
          <SectionLabel action={<ClaudeCodeLink path={`ryanbreen/voyage-${t.slug}/integrations.yaml`} label="Edit in code" />}>Integration credentials</SectionLabel>
          <div style={{ display: "flex", flexDirection: "column", gap: 0, marginTop: 4 }}>
            {[["Google", "success", "2h ago"], ["Salesforce", t.modules.includes("int-salesforce") ? "success" : "neutral", "1d ago"], ["Stripe", t.modules.includes("int-stripe") ? "success" : "neutral", "16h ago"]].map((c, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "9px 0", borderBottom: i < 2 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
                <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 13, color: "var(--jrni-color-neutral-1)" }}><Icons.Plug size={14} color="var(--jrni-color-text-soft)" />{c[0]}</span>
                {c[1] === "success" ? <span style={{ fontSize: 12, color: "var(--jrni-color-text-soft)" }}>refreshed {c[2]} · <span style={{ color: "var(--jrni-color-semantic-green-1)", fontWeight: 600 }}>connected</span></span> : <span style={{ fontSize: 12, color: "var(--jrni-color-neutral-3)" }}>not connected</span>}
              </div>
            ))}
          </div>
        </Card>
        <Card padding={18}>
          <SectionLabel action={<ClaudeCodeLink path={`ryanbreen/voyage-${t.slug}/wrangler.toml`} label="Edit in code" />}>Custom domains</SectionLabel>
          <div style={{ display: "flex", flexDirection: "column", gap: 0, marginTop: 4 }}>
            {[[`${t.slug === "aperture" ? "aperture.com" : t.slug + ".com"}`, "active", "valid"], [`book.${t.slug}.com`, t.modules.includes("booking") ? "active" : "pending", t.modules.includes("booking") ? "valid" : "pending"]].map((d, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, padding: "9px 0", borderBottom: i < 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
                <Mono color="var(--jrni-color-neutral-1)">{d[0]}</Mono>
                <Icons.Chevron dir="right" size={11} color="var(--jrni-color-neutral-3)" />
                <Mono>cf-pages-host</Mono>
                <span style={{ marginLeft: "auto", display: "inline-flex", gap: 5 }}>
                  <Pill tone={d[1] === "active" ? "success" : "warning"}>TLS {d[1] === "active" ? "active" : "pending"}</Pill>
                  <Pill tone={d[2] === "valid" ? "success" : "warning"}>DNS {d[2]}</Pill>
                </span>
              </div>
            ))}
          </div>
          <div style={{ marginTop: 12 }}><ClaudeCodeLink command={`voyage-fleet dns revalidate ${t.slug}`} label="Re-validate DNS in Claude Code" /></div>
        </Card>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <span style={{ fontSize: 12, color: "var(--jrni-color-text-soft)" }}>Config changes file a PR against <Mono>{t.repo}</Mono>.</span>
        <div style={{ flex: 1 }} />
        <Button variant="secondary" icon={<Icons.External size={13} />} onClick={() => navigate("/tenant", { slug: t.slug, tab: "configuration", full: "1" })}>Open full-page editor</Button>
        <Button variant="primary" icon={<Icons.GitPr size={13} />} disabled={archived}>Open PR with changes</Button>
      </div>
    </div>
  );
}

/* ════ TAB: Drift ════ */
function TabDrift({ t, mods }) {
  if (t.drift === "none") return <Empty icon={<Icons.Check size={20} />} title="No drift — all components current" body="Every pinned @voyage/* component is at its latest published version. No bumps or migrations pending." />;
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <Card padding={18} style={{ background: "var(--jrni-color-primary-4)", borderColor: "var(--jrni-color-primary-2)" }}>
        <div style={{ display: "flex", gap: 24, flexWrap: "wrap", alignItems: "center" }}>
          <div><div style={subLbl}>Components behind</div><div style={{ fontSize: 24, fontWeight: 700, color: "var(--jrni-color-neutral-1)" }}>{t.modulesBehind} / {t.moduleCount}</div></div>
          <div><div style={subLbl}>Below latest minor</div><div style={{ fontSize: 24, fontWeight: 700, color: "var(--jrni-color-semantic-orange-1)" }}>{t.modulesOutdatedMinor + t.modulesOutdatedMajor}</div></div>
          <div><div style={subLbl}>Pending migrations</div><div style={{ fontSize: 24, fontWeight: 700, color: "var(--jrni-color-neutral-1)" }}>{t.pendingMigrations}</div></div>
          <div><div style={subLbl}>Rollout effort</div><div style={{ marginTop: 4 }}><Pill tone={t.effort === "large" ? "danger" : t.effort === "medium" ? "warning" : "success"}>{t.effort}</Pill></div></div>
          <div style={{ marginLeft: "auto", display: "flex", flexDirection: "column", gap: 6, alignItems: "flex-end" }}>
            <ClaudeCodeLink command={`voyage-fleet rev ${t.slug} --to latest`} label="Rev components in Claude Code" variant="button" tone="primary" />
            <span style={{ fontSize: 11, color: "var(--jrni-color-text-soft)" }}>or open per-component PRs in the Components tab</span>
          </div>
        </div>
      </Card>
      <Card padding={0} style={{ overflow: "hidden" }}>
        <div style={{ padding: "10px 16px", borderBottom: "1px solid var(--jrni-color-surface-border)", fontSize: 13, fontWeight: 600 }}>Per-module drift detail</div>
        {mods.filter(m => m.drift !== "none").map((m, i, arr) => (
          <div key={m.id} style={{ padding: "12px 16px", borderBottom: i < arr.length - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <span style={{ fontFamily: "var(--jrni-font-family-mono)", fontSize: 13, fontWeight: 600, color: "var(--jrni-color-neutral-1)" }}>{m.name}</span>
              <Mono>{m.current}</Mono><Icons.Chevron dir="right" size={11} /><Mono color="var(--jrni-color-semantic-green-1)">{m.latest}</Mono>
              <span style={{ marginLeft: "auto" }}><DriftPill level={m.drift} /></span>
            </div>
            <div style={{ marginTop: 6, fontSize: 12.5, color: "var(--jrni-color-text-soft)", paddingLeft: 2 }}>Changelog: capacity rounding fix · waitlist D1 column · retry backoff (2 intervening versions)</div>
          </div>
        ))}
      </Card>
    </div>
  );
}

/* ════ TAB: CI/CD ════ */
function TabCICD({ t }) {
  const runs = Array.from({ length: 6 }, (_, i) => ({ status: i === 0 && t.ci === "failing" ? "failing" : i === 1 && t.ci === "running" ? "running" : "passing", wf: ["deploy.yml", "ci.yml", "deploy.yml", "migrate.yml", "ci.yml", "deploy.yml"][i], dur: `${(2 + Math.random() * 5).toFixed(1)}m`, when: i === 0 ? "Today 08:51" : `${i}d ago` }));
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {t.ci === "failing" ? (
        <Card padding={16} style={{ borderColor: "var(--jrni-color-semantic-red-1)", background: "var(--jrni-color-status-danger-bg)" }}>
          <div style={{ display: "flex", gap: 10 }}>
            <Icons.Warning size={18} color="var(--jrni-color-semantic-red-1)" />
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--jrni-color-neutral-1)" }}>Latest run failing — test suite, 3 consecutive failures</div>
              <div style={{ fontSize: 12.5, color: "var(--jrni-color-neutral-2)", marginTop: 3 }}>Worker deploy step exited 1 — binding <Mono color="var(--jrni-color-semantic-red-1)">QUEUE_DO</Mono> not found. Request ID <Mono>req_8fk2a9d</Mono>.</div>
              <div style={{ display: "flex", gap: 8, marginTop: 10 }}><ClaudeCodeLink command={`voyage-fleet ci rerun ${t.slug}`} label="Re-run failed jobs" /><Button variant="secondary" size="sm" icon={<Icons.Github size={12} />}>View logs</Button></div>
            </div>
          </div>
        </Card>
      ) : null}
      <Card padding={0} style={{ overflow: "hidden" }}>
        <table style={{ width: "100%", borderCollapse: "collapse" }}>
          <thead><tr style={{ background: "var(--jrni-color-surface-card-soft)", borderBottom: "1px solid var(--jrni-color-surface-border)" }}>{["Workflow", "Status", "Duration", "When", ""].map((h, i) => <th key={i} style={thStyle}>{h}</th>)}</tr></thead>
          <tbody>{runs.map((r, i) => (
            <tr key={i} style={{ borderBottom: i < runs.length - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
              <td style={{ padding: "10px 14px", fontFamily: "var(--jrni-font-family-mono)", fontSize: 12.5, color: "var(--jrni-color-neutral-1)" }}>.github/workflows/{r.wf}</td>
              <td style={{ padding: "0 14px" }}><CiPill status={r.status} small /></td>
              <td style={{ padding: "0 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-2)" }}>{r.dur}</td>
              <td style={{ padding: "0 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-2)" }}>{r.when}</td>
              <td style={{ padding: "0 14px", textAlign: "right" }}><HeaderLink icon={<Icons.Github size={13} />} label="Logs" /></td>
            </tr>
          ))}</tbody>
        </table>
      </Card>
    </div>
  );
}

/* ════ TAB: Activity / Audit ════ */
function TabActivity({ t }) {
  const rows = window.FLEET.AUDIT.filter(a => a.target === t.slug).slice(0, 10);
  const shown = rows.length ? rows : window.FLEET.AUDIT.slice(0, 8).map(a => ({ ...a, target: t.slug }));
  const [af, setAf] = useS_t({ actor: "all", time: "all" });
  const filtered = shown.filter(a => af.actor === "all" || (af.actor === "bot" ? a.isBot : !a.isBot));
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
      <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
        <MultiFilter label="Action type" value="" onChange={() => {}} options={["deploy", "migration", "config-change", "secret-rotation", "rev-tenant", "rollback"].map(a => ({ value: a, label: a }))} />
        <Segmented value={af.actor} onChange={v => setAf(s => ({ ...s, actor: v }))} options={[{ value: "all", label: "All" }, { value: "operator", label: "Operators" }, { value: "bot", label: "Automation" }]} />
        <Segmented value={af.time} onChange={v => setAf(s => ({ ...s, time: v }))} options={[{ value: "all", label: "Any time" }, { value: "24", label: "24h" }, { value: "7", label: "7d" }]} />
      </div>
      <Card padding={0} style={{ overflow: "hidden" }}>
        <table style={{ width: "100%", borderCollapse: "collapse" }}>
          <thead><tr style={{ background: "var(--jrni-color-surface-card-soft)", borderBottom: "1px solid var(--jrni-color-surface-border)" }}>{["When", "Actor", "Action", "Outcome", "Request ID"].map((h, i) => <th key={i} style={thStyle}>{h}</th>)}</tr></thead>
          <tbody>{filtered.map((a, i) => (
            <tr key={i} style={{ borderBottom: i < filtered.length - 1 ? "1px solid var(--jrni-color-surface-border)" : "none" }}>
              <td style={{ padding: "9px 14px", fontSize: 12.5, color: "var(--jrni-color-neutral-2)", whiteSpace: "nowrap" }}>{a.time}</td>
              <td style={{ padding: "0 14px" }}><ActorChip name={a.actor} isBot={a.isBot} size="sm" /></td>
              <td style={{ padding: "0 14px" }}><Pill tone={a.sev === "destructive" ? "danger" : a.sev === "change" ? "warning" : "neutral"}>{a.action}</Pill></td>
              <td style={{ padding: "0 14px" }}>{a.outcome === "success" ? <Pill tone="success">Success</Pill> : <Pill tone="danger">Failure</Pill>}</td>
              <td style={{ padding: "0 14px" }}><Mono>{a.reqId}</Mono></td>
            </tr>
          ))}</tbody>
        </table>
      </Card>
    </div>
  );
}

/* ════ Tenant Detail shell ════ */
function TenantDetail({ params }) {
  const t = window.FLEET.tenantBySlug(params.slug) || window.FLEET.TENANTS[0];
  const mods = useMemo_t(() => window.FLEET.tenantModules(t), [t.slug]);
  const tab = params.tab || "overview";
  const setTab = id => navigate("/tenant", { slug: t.slug, tab: id });
  const tabs = [
    { id: "overview", label: "Overview" }, { id: "modules", label: "Modules", count: mods.length },
    { id: "deployments", label: "Deployments" }, { id: "migrations", label: "Migrations", count: t.pendingMigrations || undefined },
    { id: "configuration", label: "Configuration" }, { id: "drift", label: "Drift" },
    { id: "cicd", label: "CI/CD" }, { id: "activity", label: "Activity" },
  ];
  const statusTone = { healthy: "success", drift: "warning", "failed-deploy": "danger", paused: "neutral" }[t.status];
  const archived = t.status === "archived";
  const booting = useBoot(params);
  const elasticityLabel = { "region-pinned": "Region-pinned", "cold-standby": "Cold standby", "active-active": "Active-active", "customer-hosted": "Customer-hosted" }[t.elasticity] || "Region-pinned";
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      {/* sticky header */}
      <div style={{ flexShrink: 0, background: "white", borderBottom: "1px solid var(--jrni-color-surface-border)", padding: "16px 24px 0" }}>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 20, flexWrap: "wrap" }}>
          <div style={{ flex: "1 1 260px", minWidth: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <h1 style={{ margin: 0, fontSize: 22, fontWeight: 700, color: "var(--jrni-color-neutral-1)", fontFamily: "var(--jrni-font-family-mono)", letterSpacing: -0.3, whiteSpace: "nowrap" }}>{t.slug}</h1>
              <TenantStatusPill status={t.status} />
              {t.customOverlay ? <Pill tone="primary"><Icons.Layers size={11} />Custom overlay</Pill> : null}
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 5, flexWrap: "wrap" }}>
              <span style={{ fontSize: 14, color: "var(--jrni-color-text-soft)" }}>{t.name}</span>
              <span style={{ fontSize: 11, fontWeight: 600, padding: "1px 7px", borderRadius: 4, textTransform: "uppercase", background: t.env === "prod" ? "var(--jrni-color-primary-3)" : "var(--jrni-color-neutral-5)", color: t.env === "prod" ? "var(--jrni-color-primary-1)" : "var(--jrni-color-neutral-2)" }}>{t.env}</span>
              <span style={{ fontSize: 12.5, color: "var(--jrni-color-neutral-2)", display: "inline-flex", alignItems: "center", gap: 5 }}><Icons.Region size={13} color="var(--jrni-color-text-soft)" />{t.region}</span>
              <Tooltip label={`Topology: ${elasticityLabel}. Elevating topology routes to a code-agent affordance + an elevation pipeline (v2).`} width={250}><span style={{ fontSize: 11, fontWeight: 600, padding: "1px 8px", borderRadius: 999, background: "var(--jrni-color-neutral-6)", border: "1px solid var(--jrni-color-surface-border)", color: "var(--jrni-color-neutral-2)", display: "inline-flex", alignItems: "center", gap: 4 }}><Icons.Server size={11} />{elasticityLabel}</span></Tooltip>
              <span style={{ fontSize: 12.5, color: "var(--jrni-color-neutral-2)" }}>Started from {t.seamName}</span>
            </div>
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <CompositionCell tenant={t} />
            </div>
            <div style={{ display: "flex", gap: 14 }}>
              <HeaderLink icon={<Icons.Globe size={13} />} label="Live URL" />
              <HeaderLink icon={<Icons.Github size={13} />} label={t.repo} mono />
              <HeaderLink icon={<Icons.Cloudflare size={13} />} label="Cloudflare" />
            </div>
          </div>
          <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
            {archived
              ? <Tooltip label="Tenant is archived. Unarchive to enable Claude Code handoffs." width={240}><span style={{ display: "inline-flex" }}><Button variant="primary" icon={<Icons.Terminal size={13} />} disabled>Rev components</Button></span></Tooltip>
              : <ClaudeCodeLink command={`voyage-fleet rev ${t.slug} --to latest`} label="Rev components" variant="button" tone="primary" />}
            {archived ? null : <ClaudeCodeLink command={`voyage-fleet deploy ${t.slug}`} label="Deploy" variant="button" />}
            {archived ? null : <ClaudeCodeLink command={`voyage-fleet rollback ${t.slug}`} label="Rollback" variant="button" />}
            <TenantRowActions tenant={t} inHeader />
          </div>
        </div>
        <div style={{ marginTop: 14 }}>
          <Tabs tabs={tabs} active={tab} onChange={setTab} padding="0" />
        </div>
      </div>
      {/* content */}
      <div style={{ flex: 1, overflowY: "auto", padding: "20px 24px 28px" }}>
        {archived ? (
          <div style={{ display: "flex", gap: 10, padding: "12px 16px", marginBottom: 16, background: "var(--jrni-color-neutral-6)", border: "1px solid var(--jrni-color-surface-border-strong)", borderRadius: 8, alignItems: "center" }}>
            <Icons.Lock size={16} color="var(--jrni-color-text-soft)" />
            <span style={{ fontSize: 13, color: "var(--jrni-color-neutral-1)", flex: 1 }}>This tenant was archived on <b>{t.archivedOn}</b> by {t.archivedBy}. Deployments are paused; data is retained but read-only.</span>
          </div>
        ) : null}
        {booting ? (
          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <div style={{ display: "flex", gap: 12 }}>{Array.from({ length: 4 }, (_, i) => <SkeletonTile key={i} />)}</div>
            <div style={{ border: "1px solid var(--jrni-color-surface-border)", borderRadius: 8, background: "white", overflow: "hidden" }}><SkeletonRows n={6} /></div>
          </div>
        ) : (
        <React.Fragment>
        {tab === "overview" && <TabOverview t={t} mods={mods} />}
        {tab === "modules" && <TabModules t={t} mods={mods} />}
        {tab === "deployments" && <TabDeployments t={t} />}
        {tab === "migrations" && <TabMigrations t={t} />}
        {tab === "configuration" && <TabConfiguration t={t} mods={mods} archived={archived} />}
        {tab === "drift" && <TabDrift t={t} mods={mods} />}
        {tab === "cicd" && <TabCICD t={t} />}
        {tab === "activity" && <TabActivity t={t} />}
        </React.Fragment>
        )}
      </div>
    </div>
  );
}

window.TenantDetail = TenantDetail;
