/* Command Centre — admin pipeline oversight. */

const { useState: useOS, useEffect: useOE, useRef: useOR } = React;

function fmtAgo(iso) {
  if (!iso) return "—";
  try {
    const d = (Date.now() - new Date(iso).getTime()) / 1000;
    if (d < 60)    return `${Math.floor(d)}s ago`;
    if (d < 3600)  return `${Math.floor(d / 60)}m ago`;
    if (d < 86400) return `${Math.floor(d / 3600)}h ago`;
    return `${Math.floor(d / 86400)}d ago`;
  } catch { return "—"; }
}
function fmtUntil(iso) {
  if (!iso) return "—";
  try {
    const d = (new Date(iso).getTime() - Date.now()) / 1000;
    if (d <= 0)    return "now";
    if (d < 60)    return `${Math.floor(d)}s`;
    if (d < 3600)  return `${Math.floor(d / 60)}m`;
    return `${Math.floor(d / 3600)}h`;
  } catch { return "—"; }
}
function fmtDuration(ms) {
  if (!ms || ms < 0) return "—";
  const s = Math.round(ms / 1000);
  if (s < 60) return `${s}s`;
  return `${Math.floor(s / 60)}m ${s % 60}s`;
}
function fmtPct(n) { return n != null ? `${Math.round(n)}%` : "—"; }
function fmtBytes(n) {
  const v = Number(n || 0);
  if (v < 1024) return `${v} B`;
  if (v < 1024 * 1024) return `${(v / 1024).toFixed(1)} KB`;
  return `${(v / 1024 / 1024).toFixed(1)} MB`;
}
function shortId(id) { return String(id || "").slice(0, 8) || "—"; }

function Card({ title, sub, action, children }) {
  return (
    <section style={{
      background: "var(--surface)",
      border: "1px solid var(--hair)",
      borderRadius: 8,
      overflow: "hidden",
    }}>
      <header style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        padding: "12px 16px",
        borderBottom: "1px solid var(--hair)",
      }}>
        <div>
          <h3 style={{margin: 0, fontSize: 13.5, fontWeight: 600, color: "var(--ink)", letterSpacing: "-0.005em"}}>{title}</h3>
          {sub && <p style={{margin: "2px 0 0", fontSize: 12, color: "var(--ink-4)"}}>{sub}</p>}
        </div>
        {action}
      </header>
      <div style={{padding: "10px 16px 14px"}}>{children}</div>
    </section>
  );
}

function ViewOps({ onRerun, openEssay }) {
  const [ops, setOps]     = useOS(null);
  const [err, setErr]     = useOS("");
  const [ageS, setAgeS]   = useOS(0);
  const lastFetch         = useOR(null);

  useOE(() => {
    let cancel = false;
    async function tick() {
      const r = await Api.adminOps();
      if (cancel) return;
      if (r.ok) { setOps(r.data); setErr(""); lastFetch.current = Date.now(); }
      else      { setErr(r.status === 404 ? "Admin-only surface." : "Couldn't load ops."); }
    }
    tick();
    const iv = setInterval(tick, 4000);
    const ageIv = setInterval(() => {
      if (lastFetch.current) setAgeS(Math.floor((Date.now() - lastFetch.current) / 1000));
    }, 500);
    return () => { cancel = true; clearInterval(iv); clearInterval(ageIv); };
  }, []);

  if (err) return (
    <div style={{padding: "32px"}}><p className="auth-status is-miss">{err}</p></div>
  );
  if (!ops) return (
    <div style={{padding: "32px", color: "var(--ink-3)", fontSize: 13}}>
      Loading ops…
    </div>
  );

  const queue        = ops.queue || {};
  const jobs24h      = ops.jobs_24h || {};
  const dur          = jobs24h.durations_ms || {};
  const recentJobs   = ops.recent_jobs || [];
  const recentDead   = ops.recent_dead || [];
  const rh           = ops.retrieval_health || {};
  const successRate  = jobs24h.total ? (jobs24h.completed / jobs24h.total) * 100 : null;
  const byHour       = jobs24h.by_hour || [];
  const histMax      = byHour.length ? Math.max(...byHour, 1) : 1;
  const lastRefresh  = lastFetch.current ? new Date(lastFetch.current) : null;

  // Compose a single throughput series that pairs each hour bucket
  // with the labels along the bottom of the chart (24h..now).
  const hourLabels = byHour.length === 24
    ? byHour.map((_, i) => `${24 - i}h`)
    : byHour.map((_, i) => `${i + 1}`);

  return (
    <div style={{height: "100%", overflowY: "auto"}}>
      <div style={{padding: "24px 32px 60px", maxWidth: 1480, margin: "0 auto"}}>

        {/* ── Header ─────────────────────────────────────────── */}
        <header style={{
          display: "flex", alignItems: "flex-start", justifyContent: "space-between",
          gap: 24, flexWrap: "wrap",
        }}>
          <div>
            <h1 style={{fontWeight: 600, fontSize: 26, margin: 0, color: "var(--ink)", letterSpacing: "-0.01em"}}>
              Command Centre
            </h1>
            <p style={{margin: "4px 0 0", color: "var(--ink-3)", fontSize: 13.5}}>
              Pipeline oversight and operational health
            </p>
          </div>
          <div style={{display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap"}}>
            <span style={{
              display: "inline-flex", alignItems: "center", gap: 6,
              padding: "4px 10px", borderRadius: 999,
              background: "var(--accent-bg)", color: "var(--accent)",
              fontSize: 12, fontWeight: 500,
            }}>
              <span className="vdot healthy"/>Production
            </span>
            <HeaderMetric label="Last refresh"
                          value={lastRefresh ? lastRefresh.toLocaleTimeString("en-AU", {hour12: false}) : "—"}
                          sub={lastRefresh ? `${ageS}s ago` : "loading…"}/>
            <HeaderMetric label="Avg analysis time"
                          value={fmtDuration(dur.avg)}
                          sub={`n=${dur.n || 0}`}/>
            <HeaderMetric label="Success rate"
                          value={successRate != null ? fmtPct(successRate) : "—"}
                          sub={`${jobs24h.completed || 0}/${jobs24h.total || 0} · 24h`}
                          tone={successRate != null && successRate >= 95 ? "healthy" : successRate != null && successRate < 80 ? "miss" : "neutral"}/>
          </div>
        </header>

        {/* ── KPI strip ─────────────────────────────────────── */}
        <div style={{display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12, marginTop: 20}}>
          <KPITile icon={<Icon.Folders size={16} stroke={1.5}/>}
                   label="Pending" value={String(queue.pending_count ?? 0)}
                   sub={queue.pending_count ? "Awaiting worker" : "None waiting"}/>
          <KPITile icon={<Icon.Activity size={16} stroke={1.5}/>}
                   label="Running" value={String(queue.running_count ?? 0)}
                   tone={queue.running_count ? "accent" : null}
                   sub={queue.running_count ? "In flight" : "Idle"}/>
          <KPITile icon={<Icon.Check size={16} stroke={1.5}/>}
                   label="Completed · 24h" value={String(jobs24h.completed ?? 0)}
                   tone={jobs24h.completed ? "healthy" : null}
                   sub={successRate != null ? `${fmtPct(successRate)} success` : "—"}/>
          <KPITile icon={<Icon.X size={16} stroke={1.5}/>}
                   label="Failed · 24h" value={String(jobs24h.failed ?? 0)}
                   tone={jobs24h.failed ? "miss" : null}
                   sub={recentDead[0] ? `Last: ${recentDead[0].failed_stage || "—"}` : "None"}/>
        </div>

        {/* ── Row 1: Queue overview ─────────────────────────── */}
        <div style={{marginTop: 14}}>
          <Card title="Queue overview"
                sub={`${queue.running_count || 0} running · ${queue.pending_count || 0} waiting · throughput last 24h`}
                action={<span style={{fontSize: 12, color: "var(--ink-4)"}}>{jobs24h.total || 0} jobs · 24h</span>}>
            {/* Throughput chart */}
            <div style={{marginTop: 4, marginBottom: 14}}>
              <div style={{display: "grid", gridTemplateColumns: `repeat(${Math.max(byHour.length, 1)}, 1fr)`, gap: 2, alignItems: "flex-end", height: 96}}>
                {byHour.map((v, i) => (
                  <div key={i} title={`${v} jobs · ${24 - i}h ago`}
                       style={{height: "100%", display: "flex", alignItems: "flex-end"}}>
                    <div style={{
                      width: "100%",
                      height: (v / histMax * 100) + "%",
                      background: v > 0 ? "var(--accent)" : "transparent",
                      opacity: v > 0 ? 0.75 : 1,
                      borderRadius: "2px 2px 0 0",
                    }}/>
                  </div>
                ))}
                {!byHour.length && (
                  <p style={{margin: 0, fontSize: 12, color: "var(--ink-4)"}}>No throughput data yet.</p>
                )}
              </div>
              {byHour.length > 0 && (
                <div style={{display: "grid", gridTemplateColumns: `repeat(${byHour.length}, 1fr)`, marginTop: 4}}>
                  {hourLabels.map((l, i) => (
                    <div key={i} style={{fontSize: 10, color: "var(--ink-5)", textAlign: "center", fontVariantNumeric: "tabular-nums"}}>
                      {i % 4 === 0 ? l : ""}
                    </div>
                  ))}
                </div>
              )}
            </div>

            {/* Live queue list */}
            <div style={{borderTop: "1px solid var(--hair)", paddingTop: 10}}>
              {(!queue.pending || !queue.pending.length) && !queue.running_count && (
                <p style={{margin: "8px 0", color: "var(--ink-3)", fontSize: 13}}>Queue is empty.</p>
              )}
              {(queue.running || []).map((r) =>
                <QueueRow key={r.job_id} job={r} state="running"/>
              )}
              {(queue.pending || []).map((p) =>
                <QueueRow key={p.job_id} job={p} state="pending"/>
              )}
            </div>

            {/* Duration percentiles */}
            <div style={{display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 8, marginTop: 14, paddingTop: 12, borderTop: "1px solid var(--hair)"}}>
              <Stat label="p50" value={fmtDuration(dur.p50)}/>
              <Stat label="p95" value={fmtDuration(dur.p95)}/>
              <Stat label="avg" value={fmtDuration(dur.avg)}/>
              <Stat label="n"   value={String(dur.n || 0)}/>
            </div>
          </Card>
        </div>

        {/* ── Row 1b: Pipeline terminal ─────────────────────── */}
        <div style={{marginTop: 14}}>
          <PipelineTerminalCard queue={queue} recentJobs={recentJobs}/>
        </div>

        {/* ── Row 2: Publisher health ───────────────────────── */}
        <div style={{marginTop: 14}}>
          <PublisherFleetCard/>
        </div>

        {/* ── Row 3: Companion fleet + system health ────────── */}
        <div style={{display: "grid", gridTemplateColumns: "minmax(0, 1.35fr) minmax(360px, 0.9fr)", gap: 14, marginTop: 14, alignItems: "start"}}>
          <FleetCard/>
          <div style={{display: "grid", gap: 14}}>
            <SystemHealthCard ops={ops} retrievalHealth={rh}/>
            <ShadowTelemetryCard ops={ops} retrievalHealth={rh}/>
          </div>
        </div>

        {/* ── Row 4: Recent runs ────────────────────────────── */}
        <div style={{marginTop: 14}}>
          <Card title="Recent runs" sub={`${recentJobs.length} latest`}>
            <table className="admin-table" style={{marginTop: 4}}>
              <thead>
                <tr>
                  <th>Job</th><th>Assignment</th><th>User</th><th>Status</th><th>Created</th><th>Duration</th><th></th>
                </tr>
              </thead>
              <tbody>
                {recentJobs.map((r) => {
                  const tone = r.status === "completed" ? "healthy"
                            : r.status === "failed"    ? "miss"
                            : r.status === "running"   ? "warn"
                            : "neutral";
                  const canRerun = r.status === "completed" || r.status === "failed";
                  const canOpen  = r.status === "completed" && openEssay && r.job_id;
                  const onOpen   = canOpen ? () => openEssay(r.job_id) : null;
                  return (
                    <tr key={r.job_id} style={canOpen ? {cursor: "pointer"} : null}
                        onClick={canOpen ? (e) => { if (e.target.closest("button")) return; onOpen(); } : undefined}
                        title={canOpen ? "Open this report" : undefined}>
                      <td style={{color: "var(--ink-3)", fontVariantNumeric: "tabular-nums"}}>{(r.job_id || "").slice(0, 8)}</td>
                      <td>{r.assignment_name || <span style={{color: "var(--ink-4)"}}>—</span>}</td>
                      <td style={{color: "var(--ink-3)", fontVariantNumeric: "tabular-nums"}}>{r.user_id ? r.user_id.slice(0, 8) : "—"}</td>
                      <td><span className={"pill " + tone}><span className={"vdot " + tone}/>{r.status}</span></td>
                      <td style={{color: "var(--ink-3)", fontVariantNumeric: "tabular-nums"}}>{fmtAgo(r.created_at)}</td>
                      <td style={{color: "var(--ink-3)", fontVariantNumeric: "tabular-nums", textAlign: "right"}}>{fmtDuration(r.duration_ms)}</td>
                      <td style={{textAlign: "right", whiteSpace: "nowrap"}}>
                        {canOpen && (
                          <button className="btn sm ghost" onClick={onOpen} title="Open this report" style={{marginRight: 4}}>
                            <Icon.External size={11}/> Open
                          </button>
                        )}
                        {canRerun && onRerun && (
                          <button className="btn sm ghost" onClick={() => onRerun(r.job_id)} title="Re-run this job">
                            <Icon.Refresh size={11}/> Re-run
                          </button>
                        )}
                      </td>
                    </tr>
                  );
                })}
                {!recentJobs.length && (
                  <tr><td colSpan={7} style={{color: "var(--ink-3)"}}>No jobs yet.</td></tr>
                )}
              </tbody>
            </table>
          </Card>
        </div>

      </div>
    </div>
  );
}

function PipelineTerminalCard({ queue, recentJobs }) {
  const [selectedJob, setSelectedJob] = useOS("");
  const [rows, setRows] = useOS([]);
  const [meta, setMeta] = useOS({ job_id: "", size: 0, next_offset: 0 });
  const [err, setErr] = useOS("");
  const [following, setFollowing] = useOS(true);
  const [lastPoll, setLastPoll] = useOS(null);
  const offsetRef = useOR("");
  const activeFileRef = useOR("");
  const scrollRef = useOR(null);

  const running = Array.isArray(queue?.running) ? queue.running : [];
  const pending = Array.isArray(queue?.pending) ? queue.pending : [];
  const seenJobs = new Set();
  const jobOptions = [
    ...running.map((j) => ({ job_id: j.job_id, label: `${shortId(j.job_id)} · running · ${j.assignment_name || j.current_stage || "job"}` })),
    ...pending.map((j) => ({ job_id: j.job_id, label: `${shortId(j.job_id)} · queued · ${j.assignment_name || "job"}` })),
    ...(recentJobs || []).map((j) => ({ job_id: j.job_id, label: `${shortId(j.job_id)} · ${j.status || "recent"} · ${j.assignment_name || "job"}` })),
  ].filter((j) => {
    if (!j.job_id || seenJobs.has(j.job_id)) return false;
    seenJobs.add(j.job_id);
    return true;
  }).slice(0, 40);

  useOE(() => {
    offsetRef.current = "";
    activeFileRef.current = "";
    setRows([]);
    setMeta({ job_id: selectedJob || "", size: 0, next_offset: 0 });
    setErr("");
  }, [selectedJob]);

  useOE(() => {
    let cancel = false;
    async function tick() {
      if (!following || !Api.pipelineEvents) return;
      const r = await Api.pipelineEvents({
        jobId: selectedJob,
        offset: offsetRef.current,
        limit: offsetRef.current === "" ? 120 : 80,
      });
      if (cancel) return;
      if (!r.ok) {
        setErr(r.status === 404 ? "No event stream for that job." : "Couldn't load pipeline events.");
        return;
      }
      const data = r.data || {};
      const nextRows = Array.isArray(data.lines) ? data.lines : [];
      const fileKey = `${data.job_id || ""}:${data.file || ""}`;
      setMeta({
        job_id: data.job_id || "",
        file: data.file || "",
        size: data.size || 0,
        offset: data.offset || 0,
        next_offset: data.next_offset || 0,
        truncated: data.truncated === true,
      });
      setErr("");
      setLastPoll(Date.now());
      offsetRef.current = data.next_offset != null ? String(data.next_offset) : offsetRef.current;
      setRows((prev) => {
        if (fileKey !== activeFileRef.current) {
          activeFileRef.current = fileKey;
          return nextRows.slice(-360);
        }
        if (!nextRows.length) return prev;
        const seen = new Set(prev.map((r) => r.offset));
        const merged = [...prev, ...nextRows.filter((r) => !seen.has(r.offset))];
        return merged.slice(-360);
      });
    }
    tick();
    const iv = setInterval(tick, 1250);
    return () => { cancel = true; clearInterval(iv); };
  }, [selectedJob, following]);

  useOE(() => {
    if (!following || !scrollRef.current) return;
    scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [rows.length, following]);

  const clear = () => {
    offsetRef.current = "";
    activeFileRef.current = "";
    setRows([]);
    setErr("");
  };

  const action = (
    <div style={{display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", justifyContent: "flex-end"}}>
      <select
        value={selectedJob}
        onChange={(e) => setSelectedJob(e.target.value)}
        style={{
          maxWidth: 360,
          background: "var(--surface-2)",
          color: "var(--ink)",
          border: "1px solid var(--hair)",
          borderRadius: 6,
          padding: "6px 8px",
          fontSize: 12,
        }}>
        <option value="">Latest stream</option>
        {jobOptions.map((j) => <option key={j.job_id} value={j.job_id}>{j.label}</option>)}
      </select>
      <button className="btn sm ghost" onClick={() => setFollowing((v) => !v)} title={following ? "Pause feed" : "Resume feed"}>
        {following ? <Icon.Pause size={11}/> : <Icon.Activity size={11}/>} {following ? "Pause" : "Follow"}
      </button>
      <button className="btn sm ghost" onClick={clear} title="Reload the current stream">
        <Icon.Refresh size={11}/> Reset
      </button>
    </div>
  );

  return (
    <Card
      title="Pipeline terminal"
      sub={`${meta.job_id ? shortId(meta.job_id) : "latest"} · ${fmtBytes(meta.size)} · ${lastPoll ? `polled ${fmtAgo(new Date(lastPoll).toISOString())}` : "connecting"}`}
      action={action}>
      {err && <p className="auth-status is-miss" style={{margin: "0 0 10px"}}>{err}</p>}
      <div
        ref={scrollRef}
        style={{
          height: 360,
          overflow: "auto",
          background: "#0f1412",
          color: "#dfe7df",
          border: "1px solid rgba(255,255,255,0.08)",
          borderRadius: 8,
          padding: "8px 0",
          fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
          fontSize: 11.5,
          lineHeight: 1.45,
        }}>
        {!rows.length && (
          <div style={{padding: "10px 12px", color: "#8ea093"}}>
            Waiting for JSONL events…
          </div>
        )}
        {rows.map((row, idx) => <PipelineEventRow key={`${row.offset}-${idx}`} row={row}/>)}
      </div>
      <div style={{display: "flex", justifyContent: "space-between", gap: 10, marginTop: 8, fontSize: 11.5, color: "var(--ink-4)", fontVariantNumeric: "tabular-nums"}}>
        <span>{rows.length} rows buffered</span>
        <span>{meta.file || "agent-events"} · offset {meta.next_offset || 0}{meta.truncated ? " · more available" : ""}</span>
      </div>
    </Card>
  );
}

function PipelineEventRow({ row }) {
  const parsed = row?.parsed && typeof row.parsed === "object" ? row.parsed : {};
  const event = row.event || parsed.event || "event";
  const stage = row.stage || parsed.stage || "";
  const ref = row.ref_id || parsed.ref_id || "";
  const agent = row.agent || parsed.agent || "";
  const action = row.action || parsed.action || "";
  const reason = row.reason || parsed.reason || "";
  const ts = row.ts || parsed.ts || "";
  const tone = /fail|error|blocked|reject|miss/i.test(`${event} ${action} ${reason}`) ? "#ffb4a8"
             : /accept|delivered|complete|ok/i.test(`${event} ${action} ${reason}`) ? "#a9dfbf"
             : /fallback|retry|pending|stale/i.test(`${event} ${action} ${reason}`) ? "#ffd98f"
             : "#dfe7df";
  const summary = [stage, event, action, ref, agent].filter(Boolean).join(" · ");
  const time = ts ? new Date(ts).toLocaleTimeString("en-AU", {hour12: false}) : "";
  return (
    <div style={{padding: "5px 12px", borderBottom: "1px solid rgba(255,255,255,0.05)"}}>
      <div style={{display: "grid", gridTemplateColumns: "74px minmax(0, 1fr)", gap: 8}}>
        <span style={{color: "#809188", fontVariantNumeric: "tabular-nums"}}>{time || row.offset}</span>
        <div style={{minWidth: 0}}>
          <div style={{color: tone, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis"}}>
            {summary || row.line}
          </div>
          {reason && (
            <div style={{color: "#b6c5ba", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis"}}>
              {String(reason)}
            </div>
          )}
          <details style={{marginTop: 2}}>
            <summary style={{cursor: "pointer", color: "#7f9388", width: "fit-content"}}>json</summary>
            <pre style={{margin: "4px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", color: "#c8d6cc"}}>
              {row.line}
            </pre>
          </details>
        </div>
      </div>
    </div>
  );
}

function HeaderMetric({ label, value, sub, tone }) {
  const valueColor = tone === "healthy" ? "var(--healthy)" : tone === "ok" ? "var(--ok)" : tone === "miss" ? "var(--miss)" : "var(--ink)";
  return (
    <div style={{textAlign: "right", lineHeight: 1.2}}>
      <div style={{fontSize: 11.5, color: "var(--ink-4)"}}>{label}</div>
      <div style={{fontSize: 16, fontWeight: 600, color: valueColor, letterSpacing: "-0.01em", marginTop: 2, fontVariantNumeric: "tabular-nums"}}>{value}</div>
      {sub && <div style={{fontSize: 11, color: "var(--ink-5)", marginTop: 1}}>{sub}</div>}
    </div>
  );
}

function KPITile({ icon, label, value, sub, tone }) {
  const valueColor = tone === "healthy" ? "var(--healthy)" : tone === "miss" ? "var(--miss)" : tone === "accent" ? "var(--accent)" : "var(--ink)";
  const iconBg = tone === "healthy" ? "var(--healthy-bg)" : tone === "miss" ? "var(--miss-bg)" : tone === "accent" ? "var(--accent-bg)" : "var(--surface-2)";
  const iconColor = tone === "healthy" ? "var(--healthy)" : tone === "miss" ? "var(--miss)" : tone === "accent" ? "var(--accent)" : "var(--ink-3)";
  return (
    <div style={{
      background: "var(--surface)",
      border: "1px solid var(--hair)",
      borderRadius: 8,
      padding: "14px 16px",
      display: "flex",
      gap: 12,
      alignItems: "flex-start",
    }}>
      <div style={{
        width: 32, height: 32, borderRadius: 8,
        background: iconBg, color: iconColor,
        display: "grid", placeItems: "center",
        flexShrink: 0,
      }}>{icon}</div>
      <div style={{minWidth: 0, flex: 1}}>
        <div style={{fontSize: 12, color: "var(--ink-3)"}}>{label}</div>
        <div style={{
          fontWeight: 600, fontSize: 28, color: valueColor,
          letterSpacing: "-0.02em", marginTop: 4, lineHeight: 1.1,
          fontVariantNumeric: "tabular-nums",
        }}>{value}</div>
        <div style={{fontSize: 12, color: "var(--ink-4)", marginTop: 4}}>{sub}</div>
      </div>
    </div>
  );
}

function QueueRow({ job, state }) {
  const isRunning = state === "running";
  return (
    <div style={{padding: "10px 0", borderBottom: "1px solid var(--hair)"}}>
      <div style={{display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 10, marginBottom: 4}}>
        <span style={{fontSize: 13, color: isRunning ? "var(--ink)" : "var(--ink-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1, minWidth: 0}}>
          {job.assignment_name || job.job_id.slice(0, 8)}
        </span>
        <span style={{fontSize: 12, color: isRunning ? "var(--accent)" : "var(--ink-4)", whiteSpace: "nowrap"}}>
          {isRunning ? (job.current_stage ? job.current_stage.replace(/_/g, " ") : "Running") : "Queued"}
        </span>
      </div>
      <div style={{fontSize: 12, color: "var(--ink-4)", fontVariantNumeric: "tabular-nums"}}>
        {job.user_id ? job.user_id.slice(0, 8) : "—"}
        {isRunning && job.elapsed_s ? ` · ${job.elapsed_s}s elapsed` : ""}
        {!isRunning && job.is_admin ? " · admin priority" : ""}
      </div>
    </div>
  );
}

function SystemHealthCard({ ops, retrievalHealth }) {
  const rh = retrievalHealth || {};
  const library = rh.library || {};
  const clientExtension = rh.client_extension || {};
  const runtime = rh.runtime_stack || {};
  const client24h = clientExtension.totals_24h || {};
  const clientInflight = (client24h.pending || 0) + (client24h.claimed || 0);
  const clientBlocked = (client24h.failed || 0) + (client24h.auth_needed || 0) + (client24h.warning || 0);
  const clientBits = [];
  if (client24h.delivered != null) clientBits.push(`${client24h.delivered} delivered`);
  if (clientInflight) clientBits.push(`${clientInflight} in flight`);
  if (clientBlocked) clientBits.push(`${clientBlocked} blocked`);
  const stackFlags = rh.stack_flags || {};
  const components = stackFlags.components || {};
  const componentList = Object.entries(components);
  const enabledComponents = componentList.filter(([, v]) => v && v.enabled !== false).length;
  const disabledComponents = componentList.filter(([, v]) => v && v.enabled === false).map(([k]) => k);
  const profile = stackFlags.profile || stackFlags.mode || stackFlags.active_profile || (stackFlags.error ? "error" : "default");
  const componentEnabled = (key, fallback = false) => components[key] ? components[key].enabled !== false : fallback;
  const sourceCascade = runtime.source_cascade || {};
  const cfRetrieval = runtime.cloudflare_retrieval || {};
  const remoteExtraction = runtime.remote_extraction || {};
  const runtimeClient = runtime.client_extension || {};
  const allowedNodes = Array.isArray(runtimeClient.allowed_nodes) ? runtimeClient.allowed_nodes : [];
  const enrolledUsers = Array.isArray(runtimeClient.enrolled_users) ? runtimeClient.enrolled_users : (clientExtension.enrolled_users || []);
  const ollamaRuntime = runtime.ollama || {};
  const jobLimits = runtime.job_limits || {};
  const cpuShadow = ops.cpu_agent_shadow_24h || {};
  const cpuShadowTotal = Number(cpuShadow.total || 0);
  const orchestratorState = rh.orchestrator || "off";
  const orchestratorShadow = orchestratorState === "shadow";
  const orchestratorAuthoritative = orchestratorState === "authoritative";
  const orchestratorDetail = orchestratorShadow
    ? "shadow · legacy path authoritative"
    : orchestratorAuthoritative
      ? "authoritative"
      : orchestratorState || "Off";
  const orchestratorTone = orchestratorShadow ? "warn" : orchestratorAuthoritative ? "healthy" : "neutral";
  const annasExhausted = rh.annas?.exhausted === true || rh.annas?.ok === false;
  const llmBits = [];
  if (rh.haiku_rescue) llmBits.push("Haiku");
  if (ollamaRuntime.enabled || rh.ollama) llmBits.push(ollamaRuntime.model || "Ollama");
  if (rh.verify_codex_fallback?.enabled) llmBits.push(`Verify ${rh.verify_codex_fallback.model || "Codex"}`);

  return (
    <Card title="Runtime stack"
          sub={`Live /admin/ops · ${profile} profile`}
          action={ops?.ts && <span style={{fontSize: 12, color: "var(--ink-4)"}}>updated {fmtAgo(ops.ts)}</span>}>
      <div style={{display: "flex", flexDirection: "column", gap: 2, marginTop: 2}}>
        <HealthRow label="Stack flags"
                   ok={!stackFlags.error}
                   detail={componentList.length ? `${enabledComponents}/${componentList.length} components enabled` : "Unknown"}/>
        <HealthRow label="Orchestrator"
                   ok={orchestratorAuthoritative || orchestratorShadow}
                   tone={orchestratorTone}
                   detail={orchestratorDetail}/>
        <HealthRow label="Source cascade"
                   ok={sourceCascade.enabled || componentEnabled("source_cascade")}
                   detail={(sourceCascade.enabled || componentEnabled("source_cascade"))
                     ? `on${sourceCascade.ref_concurrency ? ` · refs ${sourceCascade.ref_concurrency}` : ""}${sourceCascade.text_concurrency ? ` · text ${sourceCascade.text_concurrency}` : ""}`
                     : "Off"}/>
        <HealthRow label="Cloudflare retrieval"
                   ok={cfRetrieval.enabled || componentEnabled("cloudflare_firstout") || componentEnabled("cloudflare_worker")}
                   detail={(cfRetrieval.enabled || componentEnabled("cloudflare_firstout") || componentEnabled("cloudflare_worker"))
                     ? (cfRetrieval.enabled
                       ? `${cfRetrieval.authoritative ? "authoritative" : "enabled"}${cfRetrieval.timeout_s ? ` · ${cfRetrieval.timeout_s}s` : ""}`
                       : "stack lanes enabled")
                     : "Off"}/>
        <HealthRow label="CF bypass"
                   ok={rh.cloudflare_bypass?.ok}
                   detail={rh.cloudflare_bypass?.ok ? "Healthy" : "Off"}/>
        <HealthRow label="Remote extraction"
                   ok={remoteExtraction.mode && remoteExtraction.mode !== "off"}
                   detail={remoteExtraction.mode ? `${remoteExtraction.mode}${remoteExtraction.target_count ? ` · ${remoteExtraction.target_count} targets` : ""}` : "Off"}/>
        <HealthRow label="Client fetch lane"
                   ok={runtimeClient.enabled || componentEnabled("client_extension") || enrolledUsers.length > 0}
                   detail={`${allowedNodes.length ? allowedNodes.join(", ") : "nodes unknown"}${enrolledUsers.length ? ` · ${enrolledUsers.length} user${enrolledUsers.length === 1 ? "" : "s"}` : ""}${clientBits.length ? ` · ${clientBits.join(" · ")} 24h` : ""}`}/>
        <HealthRow label="Publisher lanes"
                   ok={componentEnabled("publisher_landing") || componentEnabled("publisher_apis")}
                   detail={[
                     componentEnabled("publisher_landing") ? "landing" : null,
                     componentEnabled("publisher_apis") ? "APIs" : null,
                   ].filter(Boolean).join(" + ") || "Off"}/>
        <HealthRow label="Alexandria library"
                   ok={(library.total_entries || 0) > 0 && componentEnabled("library", true)}
                   detail={library.total_entries != null ? `${library.total_entries} entries · ${library.tier?.verified || 0} verified` : "Unknown"}/>
        <HealthRow label="Anna's Archive"
                   ok={!annasExhausted && !!rh.annas_local?.enabled}
                   detail={`${rh.annas_local?.enabled ? "local" : "local off"} · ${annasExhausted ? "online exhausted" : "online available"}`}/>
        <HealthRow label="LLM lanes"
                   ok={llmBits.length > 0}
                   detail={llmBits.length ? llmBits.join(" · ") : "Off"}/>
        <HealthRow label="CPU-agent shadow"
                   ok={cpuShadowTotal > 0}
                   tone={cpuShadowTotal ? "warn" : "neutral"}
                   detail={cpuShadowTotal
                     ? `${cpuShadowTotal} observations · ${cpuShadow.delivered || 0} delivered`
                     : "No observations yet"}/>
        <HealthRow label="Disabled flags"
                   ok={disabledComponents.length === 0}
                   tone={disabledComponents.length ? "neutral" : "healthy"}
                   detail={disabledComponents.length ? disabledComponents.slice(0, 3).join(", ") + (disabledComponents.length > 3 ? ` +${disabledComponents.length - 3}` : "") : "None"}/>
        <HealthRow label="Job limits"
                   ok={!!jobLimits.max_concurrent_jobs}
                   detail={jobLimits.max_concurrent_jobs ? `${jobLimits.max_concurrent_jobs} concurrent · ${jobLimits.essay_word_limit || "?"} words` : "Unknown"}/>
      </div>
    </Card>
  );
}

function ShadowTelemetryCard({ ops, retrievalHealth }) {
  const rh = retrievalHealth || {};
  const cpuShadow = ops.cpu_agent_shadow_24h || {};
  // Backend nests the rollup under client_fetch + publisher_heartbeat
  // (storage.get_cpu_agent_shadow_status); reading cpuShadow.total/.by_model at
  // the top level silently returned nothing. Merge both lanes.
  const cf = cpuShadow.client_fetch || {};
  const phb = cpuShadow.publisher_heartbeat || {};
  const lanes = [cf, phb];
  const sumK = (k) => lanes.reduce((s, l) => s + Number(l[k] || 0), 0);
  const mergeDict = (k) => lanes.reduce((o, l) => {
    for (const [name, n] of Object.entries(l[k] || {})) o[name] = (o[name] || 0) + Number(n || 0);
    return o;
  }, {});
  const shadowTotal = sumK("total");
  const shadowDelivered = sumK("delivered");
  const byModel = mergeDict("by_model");
  const byPublisher = mergeDict("by_publisher");
  const mismatches = lanes.flatMap((l) => Array.isArray(l.recent_mismatches) ? l.recent_mismatches : []);
  // Rescue-VLM advisory rollup (Phase 7) — fires on the heartbeat lane.
  const rescueTotal = sumK("rescue_total");
  const rescueVision = sumK("rescue_vision");
  // Re-derive from pooled raw counts (mirror categoryRate below) — the rescue
  // shadow accrues on BOTH lanes, so phb-first agree% / OR'd promo would
  // disagree with the pooled totals shown beside them.
  const rescueAgreePct = rescueTotal ? Math.round((sumK("rescue_category_match") / rescueTotal) * 100) : null;
  const rescuePromo = rescueTotal >= 20 && rescueAgreePct != null && rescueAgreePct >= 90;
  const rescueRecent = lanes.flatMap((l) => Array.isArray(l.rescue_recent) ? l.rescue_recent : []);
  const runningStages = ((ops.queue || {}).running || []).flatMap((job) =>
    (job.stages || []).map((stage) => ({
      job_id: job.job_id,
      name: stage.name,
      status: stage.status,
      diagnostics: stage.diagnostics || {},
    }))
  );
  const orchStages = runningStages
    .map((stage) => ({ ...stage, orchestrator: stage.diagnostics.orchestrator }))
    .filter((stage) => stage.orchestrator && typeof stage.orchestrator === "object");
  const latestOrch = orchStages[orchStages.length - 1]?.orchestrator || null;
  const topModels = Object.entries(byModel).slice(0, 3).map(([name, count]) => `${name}: ${count}`);
  const topPublishers = Object.entries(byPublisher).slice(0, 4).map(([name, count]) => `${name}: ${count}`);
  const categoryRate = shadowTotal ? `${Math.round((sumK("category_match") / shadowTotal) * 100)}% category` : null;
  const actionRate = shadowTotal ? `${Math.round((sumK("action_match") / shadowTotal) * 100)}% action` : null;
  const shadowState = rh.orchestrator === "shadow" ? "orchestrator shadow enabled" : `orchestrator ${rh.orchestrator || "off"}`;

  return (
    <Card title="Shadow telemetry"
          sub={shadowState}
          action={<span className={"pill " + (rh.orchestrator === "shadow" ? "warn" : "neutral")}><span className={"vdot " + (rh.orchestrator === "shadow" ? "warn" : "neutral")}/>{rh.orchestrator || "off"}</span>}>
      <div style={{display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 8, marginBottom: 10}}>
        <Stat label="CPU shadows" value={String(shadowTotal)}/>
        <Stat label="delivered" value={String(shadowDelivered)}/>
        <Stat label="match" value={[categoryRate, actionRate].filter(Boolean).join(" · ") || "—"}/>
      </div>
      <div style={{display: "flex", flexDirection: "column", gap: 8}}>
        <ShadowLine label="Models" value={topModels.join(" · ") || "No CPU-agent shadow observations"}/>
        <ShadowLine label="Publishers" value={topPublishers.join(" · ") || "—"}/>
        <ShadowLine
          label="Rescue VLM"
          value={rescueTotal
            ? `${rescueTotal} advisory · ${rescueVision} vision${rescueAgreePct != null ? ` · ${rescueAgreePct}% agree` : ""} · ${rescuePromo ? "promotion-ready" : (rescueTotal < 20 ? "gated (<20 samples)" : "gated (<0.9 agree)")}`
            : "No rescue-lane fall-throughs yet"}/>
        {rescueRecent.slice(0, 2).map((r, idx) => (
          <ShadowLine
            key={`rescue-${r.publisher || idx}-${idx}`}
            label={idx === 0 ? "Rescue verdict" : ""}
            value={`${r.publisher || "unknown"} · deterministic ${r.deterministic_category || "-"} → rescue ${r.rescue_category || "-"}${r.used_vision ? " (vision)" : ""}${r.rescue_confidence != null ? ` · ${r.rescue_confidence}` : ""}`}
          />
        ))}
        {latestOrch ? (
          <ShadowLine
            label="Running orchestrator"
            value={`${latestOrch.accepted || 0}/${latestOrch.total || 0} accepted · ${latestOrch.avg_ms || 0}ms avg · ${latestOrch.avg_candidates || 0} candidates`}
          />
        ) : (
          <ShadowLine label="Running orchestrator" value={rh.orchestrator === "shadow" ? "waiting for next resolve_sources stage" : "shadow mode is off"}/>
        )}
        {mismatches.slice(0, 3).map((m, idx) => (
          <ShadowLine
            key={`${m.queue_id || idx}-${m.publisher || "publisher"}`}
            label={idx === 0 ? "Recent mismatch" : ""}
            value={`${m.publisher || "unknown"} · deterministic ${m.deterministic_category || "-"} / CPU ${m.cpu_category || "-"}${m.cpu_confidence != null ? ` · ${m.cpu_confidence}` : ""}`}
          />
        ))}
      </div>
    </Card>
  );
}

function ShadowLine({ label, value }) {
  return (
    <div style={{display: "grid", gridTemplateColumns: "112px 1fr", gap: 10, alignItems: "baseline", fontSize: 12.5}}>
      <span style={{color: "var(--ink-4)"}}>{label}</span>
      <span style={{color: "var(--ink-2)", overflowWrap: "anywhere"}}>{value}</span>
    </div>
  );
}

function HealthRow({ label, ok, detail, tone }) {
  const rowTone = tone || (ok ? "healthy" : "neutral");
  return (
    <div style={{
      display: "flex", alignItems: "center", justifyContent: "space-between",
      padding: "8px 0", borderBottom: "1px solid var(--hair)",
    }}>
      <div style={{display: "flex", alignItems: "center", gap: 8}}>
        <span className={"vdot " + rowTone}/>
        <span style={{fontSize: 13, color: "var(--ink)"}}>{label}</span>
      </div>
      <span style={{fontSize: 12, color: ok ? "var(--ink-3)" : "var(--ink-4)", textAlign: "right"}}>
        {detail || (ok ? "Healthy" : "Off")}
      </span>
    </div>
  );
}

function WireframeCard({ title, sub, note }) {
  return (
    <section style={{
      background: "var(--surface)",
      border: "1px dashed var(--hair-strong)",
      borderRadius: 8,
      overflow: "hidden",
      opacity: 0.85,
    }}>
      <header style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        padding: "12px 16px",
        borderBottom: "1px dashed var(--hair-strong)",
      }}>
        <div>
          <h3 style={{margin: 0, fontSize: 13.5, fontWeight: 600, color: "var(--ink-2)", letterSpacing: "-0.005em"}}>{title}</h3>
          {sub && <p style={{margin: "2px 0 0", fontSize: 12, color: "var(--ink-4)"}}>{sub}</p>}
        </div>
        <span style={{
          fontSize: 11, padding: "3px 8px",
          borderRadius: 999,
          background: "var(--surface-2)", color: "var(--ink-4)",
          border: "1px solid var(--hair)",
        }}>Wireframe</span>
      </header>
      <div style={{padding: "14px 16px"}}>
        <div style={{display: "flex", flexDirection: "column", gap: 8}}>
          <div style={{height: 10, borderRadius: 4, background: "var(--surface-2)"}}/>
          <div style={{height: 10, width: "82%", borderRadius: 4, background: "var(--surface-2)"}}/>
          <div style={{height: 10, width: "60%", borderRadius: 4, background: "var(--surface-2)"}}/>
        </div>
        <p style={{margin: "12px 0 0", fontSize: 12, color: "var(--ink-4)", lineHeight: 1.5}}>
          {note}
        </p>
      </div>
    </section>
  );
}

function publisherStatusTone(status) {
  if (status === "ok") return "healthy";
  if (status === "stale" || status === "timeout") return "warn";
  if (["captcha", "sso_required", "publisher_access_required", "no_subscription", "probe_blocked", "error"].includes(status)) return "miss";
  return "neutral";
}

function publisherStatusLabel(status) {
  return String(status || "unknown").replace(/_/g, " ");
}

// The deterministic recovery decision the backend stamps on every blocked
// publisher (storage._stamp_recovery_decision). Surfacing it tells an operator
// what the self-heal actuator will actually try, not just that it's blocked.
const RECOVERY_ROUTE_LABEL = {
  sso_refresh: "SSO refresh",
  cf_bypass: "CF bypass",
  cf_bypass_html_diagnostic: "CF bypass (abstract)",
  cf_bypass_html_text: "CF bypass (full text)",
  node_rotation: "Rotate node",
  library_request: "Library request",
  reject_candidate: "Reject candidate",
  inspect: "Inspect lane",
};
function recoveryRouteLabel(route) {
  return RECOVERY_ROUTE_LABEL[route] || publisherStatusLabel(route);
}

// Zero-browser publisher API lanes (Phase 4): when configured, the publisher's
// DOIs resolve via the API regardless of its browser nodes — so a captcha'd
// fleet is non-critical. Surfaced as an "API" badge so a low ok-node count
// doesn't read as an outage.
const API_LANE_LABEL = {
  wiley_tdm: "Wiley TDM",
  elsevier_api: "Elsevier",
  springer_openaccess: "Springer OA",
  springer_fulltext: "Springer FT",
};
function apiLaneLabel(lanes) {
  const arr = Array.isArray(lanes) ? lanes : [];
  if (!arr.length) return "covered";
  return arr.map(l => API_LANE_LABEL[l] || l).join(" + ");
}

function MiniMetric({ label, value, tone }) {
  const color = tone === "healthy" ? "var(--healthy)"
              : tone === "ok" ? "var(--ok)"
              : tone === "warn" ? "var(--warn)"
              : tone === "miss" ? "var(--miss)"
              : "var(--ink)";
  return (
    <div style={{
      padding: "8px 10px",
      border: "1px solid var(--hair)",
      borderRadius: 6,
      background: "var(--surface-2)",
      minWidth: 0,
    }}>
      <div style={{fontSize: 11.5, color: "var(--ink-4)", whiteSpace: "nowrap"}}>{label}</div>
      <div style={{fontSize: 15, fontWeight: 600, color, marginTop: 2, fontVariantNumeric: "tabular-nums"}}>
        {value}
      </div>
    </div>
  );
}

function ExpandButton({ expanded, onClick }) {
  return (
    <button className="btn ghost sm" onClick={onClick} title={expanded ? "Hide details" : "Show details"}>
      <Icon.Caret size={11} style={{transform: expanded ? "rotate(180deg)" : "none"}}/>
      {expanded ? "Hide" : "Details"}
    </button>
  );
}

function PublisherFleetCard() {
  const [data, setData] = useOS(null);
  const [err, setErr]   = useOS("");
  const [expanded, setExpanded] = useOS(false);

  useOE(() => {
    let cancel = false;
    async function tick() {
      const api = Api.publisherHeartbeat || (() => Promise.resolve({ ok: false, status: 501 }));
      const r = await api();
      if (cancel) return;
      if (r.ok)                       { setData(r.data || {}); setErr(""); }
      else if (r.status === 501)      { setErr("endpoint_unavailable"); }
      else if (r.status === 404)      { setErr("endpoint_unavailable"); }
      else                            { setErr("load_failed"); }
    }
    tick();
    const iv = setInterval(tick, 30000);
    return () => { cancel = true; clearInterval(iv); };
  }, []);

  if (err === "endpoint_unavailable") {
    return <WireframeCard
      title="Publisher readiness"
      sub="VM-native publisher auth canaries"
      note="Awaiting backend /admin/publisher-heartbeat. Expected shape: publishers grouped by publisher with ok/stale/blocking nodes."/>;
  }
  if (err) {
    return (
      <section style={{background: "var(--surface)", border: "1px solid var(--hair)", borderRadius: 8, padding: "16px"}}>
        <p style={{margin: 0, fontSize: 13, color: "var(--ink-3)"}}>Couldn't load publisher fleet.</p>
      </section>
    );
  }
  if (!data) {
    return (
      <Card title="Publisher readiness" sub="Loading…"><p style={{margin: "8px 0", fontSize: 13, color: "var(--ink-3)"}}>Loading publisher canaries…</p></Card>
    );
  }

  const publishers = Array.isArray(data.publishers) ? data.publishers : [];
  const okCountOf = p => (Array.isArray(p.ok_nodes) ? p.ok_nodes.length : 0);
  // Readiness = can >=1 HEALTHY fetch node serve this publisher right now. That's the thing
  // that matters: the per-node mix (some captcha/sso while others are ok) is routed around,
  // so a publisher with ok nodes is fetchable even while other nodes block it.
  const isReady = p => (p.delivery_ready != null ? !!p.delivery_ready : okCountOf(p) > 0);
  const FRAGILE_MAX = 2;

  // Real fetch-node counts. The old card summed each publisher's per-node array
  // (p.nodes.length) — that's node x publisher CELLS (~19 publishers x ~10 nodes ~ 188),
  // not nodes, and read as a meaningless "188/188". Use the backend's node-id lists, but
  // (a) exclude the GPU/host nodes (vm1/talos — fetch-quarantined, never a fetch lane) and
  // (b) DEDUPE across the lists: they are publisher-aggregated, so one node id can appear in
  // more than one list (e.g. fresh for one publisher, stale for another).
  const fetchIds = lst => (Array.isArray(lst) ? lst : []).filter(id => !nonFetchRoleNode({ node_id: id }));
  const activeFetch = fetchIds(data.active_node_ids);
  const allFetch = new Set([...activeFetch, ...fetchIds(data.fresh_node_ids), ...fetchIds(data.stale_node_ids)]);
  const activeNodes = activeFetch.length;
  const totalNodes  = allFetch.size;
  const staleNodes  = Math.max(0, totalNodes - activeNodes);

  const ready   = publishers.filter(isReady);
  const blocked = publishers.filter(p => !isReady(p));
  const fragile = ready.filter(p => { const n = okCountOf(p); return n > 0 && n <= FRAGILE_MAX; });
  const readySorted = [...ready].sort((a, b) =>
    okCountOf(b) - okCountOf(a) || String(a.publisher).localeCompare(String(b.publisher)));
  const blockedSorted = [...blocked].sort((a, b) =>
    String(a.publisher).localeCompare(String(b.publisher)));

  const nodeSummary = totalNodes
    ? `${totalNodes} fetch node${totalNodes === 1 ? "" : "s"} · ${activeNodes} active${staleNodes ? ` · ${staleNodes} stale` : ""}`
    : "no fetch nodes reporting";
  const sub = publishers.length
    ? `${ready.length}/${publishers.length} ready · ${blocked.length} blocked${fragile.length ? ` · ${fragile.length} fragile` : ""} · ${nodeSummary}`
    : "No publisher canaries reporting";

  const groupLabel = (text) => (
    <div style={{fontSize: 11, letterSpacing: 0.4, textTransform: "uppercase",
                 color: "var(--ink-4)", margin: "14px 0 6px"}}>{text}</div>
  );

  return (
    <Card title="Publisher readiness"
          sub={sub}
          action={<div style={{display: "flex", alignItems: "center", gap: 8}}>
            {data.generated_at && <span style={{fontSize: 12, color: "var(--ink-4)"}}>updated {fmtAgo(data.generated_at)}</span>}
            <ExpandButton expanded={expanded} onClick={() => setExpanded(v => !v)}/>
          </div>}>
      <div style={{display: "grid", gridTemplateColumns: "repeat(4, minmax(0, 1fr))", gap: 8}}>
        <MiniMetric label="Ready to fetch" value={`${ready.length}/${publishers.length || 0}`} tone={blocked.length ? "warn" : "healthy"}/>
        <MiniMetric label="Blocked" value={String(blocked.length)} tone={blocked.length ? "miss" : "healthy"}/>
        <MiniMetric label="Fragile" value={String(fragile.length)} tone={fragile.length ? "warn" : "healthy"}/>
        <MiniMetric label="Fetch nodes" value={String(activeNodes || totalNodes)} tone={!totalNodes ? "miss" : staleNodes ? "warn" : "healthy"}/>
      </div>

      {blockedSorted.length > 0 && (
        <div>
          {groupLabel(`Blocked — no healthy node (${blockedSorted.length})`)}
          <div style={{display: "flex", flexDirection: "column", gap: 6}}>
            {blockedSorted.map(p => {
              const cov = p.covered_by_api_lane && p.covered_by_api_lane.configured;
              const recover = p.recovery_route && p.recovery_route !== "none"
                && p.recommended_action !== "claim";
              return (
                <div key={p.publisher} style={{display: "flex", alignItems: "center", flexWrap: "wrap", gap: 7}}>
                  <span className="vdot miss"/>
                  <span style={{fontSize: 13, color: "var(--ink)", fontWeight: 500, textTransform: "capitalize"}}>
                    {p.publisher || "unknown"}
                  </span>
                  <span className="pill miss" style={{fontSize: 11}}>
                    <span className="vdot miss"/>{publisherStatusLabel(p.status)}
                  </span>
                  {cov && (
                    <span className="pill healthy" style={{fontSize: 10.5}}
                          title={"Resolves via a zero-browser publisher API — the browser block is non-critical for its DOIs. Lane(s): "
                            + (p.covered_by_api_lane.configured_lanes || []).join(", ")}>
                      <span className="vdot healthy"/>API serves it: {apiLaneLabel(p.covered_by_api_lane.configured_lanes)}
                    </span>
                  )}
                  {recover && (
                    <span style={{fontSize: 11.5, color: "var(--ink-4)"}}>→ {recoveryRouteLabel(p.recovery_route)}</span>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      )}

      {readySorted.length > 0 && (
        <div>
          {groupLabel(`Ready to fetch (${readySorted.length})`)}
          <div style={{display: "flex", flexWrap: "wrap", gap: 6}}>
            {readySorted.map(p => {
              const n = okCountOf(p);
              const frag = n > 0 && n <= FRAGILE_MAX;
              const tone = frag ? "warn" : "healthy";
              const okList = (Array.isArray(p.ok_nodes) ? p.ok_nodes : []).join(", ");
              return (
                <span key={p.publisher} className={"pill " + tone} style={{fontSize: 11}}
                      title={`${n} healthy node${n === 1 ? "" : "s"}${okList ? ": " + okList : ""}${frag ? " — fragile (one failure from blocked)" : ""}`}>
                  <span className={"vdot " + tone}/>
                  <span style={{textTransform: "capitalize"}}>{p.publisher || "unknown"}</span>
                  <span style={{opacity: 0.7, fontVariantNumeric: "tabular-nums"}}> · {n}{frag ? " · fragile" : ""}</span>
                </span>
              );
            })}
          </div>
        </div>
      )}

      {!publishers.length && (
        <p style={{margin: "8px 0", color: "var(--ink-3)", fontSize: 13}}>
          Backend returned no publisher canaries. Check the publisher heartbeat proof timer.
        </p>
      )}
      {expanded && (
        <div style={{marginTop: 12, borderTop: "1px solid var(--hair)", paddingTop: 2}}>
          {[...blockedSorted, ...readySorted].map(p => <PublisherFleetRow key={p.publisher} publisher={p}/>)}
        </div>
      )}
    </Card>
  );
}

function PublisherFleetRow({ publisher }) {
  const status = publisher.status || "unknown";
  const tone = publisherStatusTone(status);
  const okNodes = Array.isArray(publisher.ok_nodes) ? publisher.ok_nodes : [];
  const staleNodes = Array.isArray(publisher.stale_nodes) ? publisher.stale_nodes : [];
  const blockingNodes = Array.isArray(publisher.blocking_nodes) ? publisher.blocking_nodes : [];
  // Only fetch-lane nodes count toward a publisher's health — exclude the GPU/host
  // nodes (vm1/talos) so a stale vm1 canary doesn't show as a chip or inflate the
  // "X/Y ok" denominator (e.g. dukeupress now reads 0/7, not 0/9 with vm1 stale).
  const nodeItems = (Array.isArray(publisher.nodes) ? publisher.nodes : []).filter(n => !nonFetchRoleNode(n));
  const latestDetail = blockingNodes[0]?.detail
    || nodeItems.find(n => n.status && n.status !== "ok")?.detail
    || nodeItems.find(n => n.detail)?.detail
    || "";

  return (
    <div style={{padding: "10px 0", borderBottom: "1px solid var(--hair)"}}>
      <div style={{display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 10}}>
        <div style={{display: "flex", alignItems: "center", gap: 8, minWidth: 0, flex: 1}}>
          <span className={"vdot " + tone}/>
          <span style={{fontSize: 13, color: "var(--ink)", fontWeight: 500, textTransform: "capitalize"}}>
            {publisher.publisher || "unknown"}
          </span>
          <span className={"pill " + tone} style={{fontSize: 11}}>
            <span className={"vdot " + tone}/>{publisherStatusLabel(status)}
          </span>
          {publisher.covered_by_api_lane && publisher.covered_by_api_lane.configured && (
            <span className="pill healthy" style={{fontSize: 10.5}}
                  title={"Resolves via a zero-browser publisher API — browser captchas are non-critical. Lane(s): "
                    + (publisher.covered_by_api_lane.configured_lanes || []).join(", ")}>
              <span className="vdot healthy"/>API: {apiLaneLabel(publisher.covered_by_api_lane.configured_lanes)}
            </span>
          )}
        </div>
        <span style={{fontSize: 12, color: "var(--ink-3)", whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums"}}>
          {okNodes.length}/{nodeItems.length || publisher.fresh_nodes || 0} ok
        </span>
      </div>
      <div style={{fontSize: 12, color: "var(--ink-4)", marginTop: 3, fontVariantNumeric: "tabular-nums"}}>
        checked {fmtAgo(publisher.last_checked_at)}
        {publisher.last_ok_at ? ` · last ok ${fmtAgo(publisher.last_ok_at)}` : ""}
        {publisher.fresh_nodes != null ? ` · ${publisher.fresh_nodes} fresh` : ""}
      </div>
      <div style={{display: "flex", flexWrap: "wrap", gap: 5, marginTop: 7}}>
        {nodeItems.map(n => {
          const ntone = n.stale ? "warn" : publisherStatusTone(n.status);
          const chipBg = ntone === "neutral" ? "var(--surface-2)" : `var(--${ntone}-bg)`;
          const chipColor = ntone === "neutral" ? "var(--ink-3)" : `var(--${ntone})`;
          const chipBorder = ntone === "neutral" ? "var(--hair)" : `color-mix(in oklch, var(--${ntone}) 35%, transparent)`;
          return (
            <span key={`${publisher.publisher}-${n.node_id}`}
                  title={n.detail || publisherStatusLabel(n.status)}
                  style={{
                    display: "inline-flex", alignItems: "center", gap: 5,
                    fontSize: 11, padding: "2px 7px", borderRadius: 999,
                    background: chipBg,
                    color: chipColor,
                    border: `1px solid ${chipBorder}`,
                  }}>
              <span className={"vdot " + ntone}/>{n.node_id || "node"}{n.stale ? " stale" : ""}
            </span>
          );
        })}
        {staleNodes.length > 0 && !nodeItems.length && (
          <span style={{fontSize: 11.5, color: "var(--ink-4)"}}>stale: {staleNodes.join(", ")}</span>
        )}
      </div>
      {latestDetail && (
        <div style={{fontSize: 11.5, color: "var(--ink-5)", marginTop: 5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>
          {latestDetail}
        </div>
      )}
      {status !== "ok" && publisher.recommended_action
        && publisher.recovery_route && publisher.recovery_route !== "none"
        && publisher.recommended_action !== "claim" && (() => {
        // use_backend_fallback is a STEADY serving route (the publisher is
        // delivery-ready via the backend), not an active recovery — label it
        // "Served via" in a neutral tone so it doesn't read as a failure.
        // reject_candidate is a hard miss; anything else is an active recovery.
        const served = publisher.recommended_action === "use_backend_fallback";
        const tone = served ? "neutral"
                   : publisher.recovery_route === "reject_candidate" ? "miss" : "warn";
        return (
          <div style={{display: "flex", alignItems: "center", flexWrap: "wrap", gap: 7, marginTop: 6}}>
            <span className={"pill " + tone} style={{fontSize: 10.5}}>
              <span className={"vdot " + tone}/>
              {served ? "Served via" : "Recovery"}: {recoveryRouteLabel(publisher.recovery_route)}
            </span>
            {publisher.delivery_ready === false && (
              <span style={{fontSize: 11, color: "var(--ink-4)"}}>delivery blocked</span>
            )}
            {publisher.source_analysis_ready === false && (
              <span style={{fontSize: 11, color: "var(--ink-4)"}}>
                {served ? "abstract only · full text via supplement" : "analysis blocked"}
              </span>
            )}
          </div>
        );
      })()}
    </div>
  );
}

function nonFetchRoleNode(node) {
  // Not part of the publisher fetch lane: talos (runs the backend) and vm1/vm1-w2
  // (the GPU/LLM node — fetch-quarantined by design, excluded from the recovery
  // actuator's --nodes). Must not be counted as fetch nodes or flagged as a
  // down/blocked fetch lane; a stale vm1 canary shouldn't drag a publisher's count.
  const id = String(node.node_id || node.hostname || "").toLowerCase();
  return id === "talos" || id === "vm1" || id === "vm1-w2";
}
function nodeRoleLabel(node) {
  const id = String(node.node_id || node.hostname || "").toLowerCase();
  if (id === "talos") return "Host only";
  if (id === "vm1" || id === "vm1-w2") return "GPU node";
  return "";
}
// Back-compat boolean alias (now means "any non-fetch-lane node").
const isHostOnlyCompanionNode = nonFetchRoleNode;

function companionNodeDisplayState(node) {
  const roleLabel = nodeRoleLabel(node);
  if (roleLabel) return { tone: "neutral", label: roleLabel };
  const state = node.service_state || "down";
  const health = String(node.health_status || "").toLowerCase();
  const degraded = ["cdp_degraded", "cdp_no_dom", "degraded"].includes(health);
  const backoff = node.backoff_active === true
    || (node.backoff_until && Date.parse(node.backoff_until) > Date.now());
  if (state === "down") return { tone: "miss", label: "Down" };
  // The node is still heartbeating, but the backend has demoted it from the
  // claim lane (CDP/DOM broken, or in recovery backoff). Never show these green
  // — a green "Active" pill on a quarantined node is what hid the Phase 5 self-heal.
  if (degraded) return { tone: "miss", label: "Degraded" };
  if (backoff) return { tone: "warn", label: "Backing off" };
  if (state === "stale") return { tone: "warn", label: "Stale" };
  return { tone: "healthy", label: "Active" };
}

function FleetCard() {
  const [data, setData] = useOS(null);
  const [err, setErr]   = useOS("");
  const [expanded, setExpanded] = useOS(false);

  useOE(() => {
    let cancel = false;
    async function tick() {
      const r = await Api.adminFleet();
      if (cancel) return;
      if (r.ok)                       { setData(r.data); setErr(""); }
      else if (r.status === 501)      { setErr("endpoint_unavailable"); }
      else if (r.status === 404)      { setErr("endpoint_unavailable"); }
      else                            { setErr("load_failed"); }
    }
    tick();
    const iv = setInterval(tick, 10000);
    return () => { cancel = true; clearInterval(iv); };
  }, []);

  if (err === "endpoint_unavailable") {
    return <WireframeCard
      title="Companion fleet"
      sub="Per-node health and recent activity"
      note="Awaiting backend /admin/fleet. Expected shape: nodes: [{ node_id, hostname, ip_class, service_state, last_heartbeat_at, last_claim_at, delivered_24h, failed_24h, extension_version }]."/>;
  }
  if (err) {
    return (
      <section style={{background: "var(--surface)", border: "1px solid var(--hair)", borderRadius: 8, padding: "16px"}}>
        <p style={{margin: 0, fontSize: 13, color: "var(--ink-3)"}}>Couldn't load fleet.</p>
      </section>
    );
  }
  if (!data) {
    return (
      <Card title="Companion fleet" sub="Loading…"><p style={{margin: "8px 0", fontSize: 13, color: "var(--ink-3)"}}>Loading nodes…</p></Card>
    );
  }

  const nodes = data.nodes || [];
  const fetchNodes = nodes.filter(n => !isHostOnlyCompanionNode(n));
  const hostOnly = nodes.filter(isHostOnlyCompanionNode);
  // Key on the DISPLAY state (service_state + health_status + backoff), not raw
  // service_state — a heartbeating-but-degraded/quarantined node must read as an
  // issue, not "active".
  const disp = fetchNodes.map(n => companionNodeDisplayState(n));
  const healthy  = disp.filter(d => d.tone === "healthy").length;
  const down     = disp.filter(d => d.label === "Down").length;
  const degraded = disp.filter(d => d.label === "Degraded").length;
  const backingOff = disp.filter(d => d.label === "Backing off").length;
  const stale    = disp.filter(d => d.label === "Stale").length;
  const claimed24 = fetchNodes.reduce((sum, n) => sum + Number(n.claimed_24h || 0), 0);
  const delivered24 = fetchNodes.reduce((sum, n) => sum + Number(n.delivered_24h || 0), 0);
  const failed24 = fetchNodes.reduce((sum, n) => sum + Number(n.failed_24h || 0), 0);
  const issueNodes = fetchNodes.filter(n => companionNodeDisplayState(n).tone !== "healthy");
  const fleetTone = disp.some(d => d.tone === "miss") ? "miss" : disp.some(d => d.tone === "warn") ? "warn" : "healthy";
  const sub    = nodes.length
    ? `${fetchNodes.length} fetch nodes · ${healthy} healthy${down ? ` · ${down} down` : ""}${degraded ? ` · ${degraded} degraded` : ""}${backingOff ? ` · ${backingOff} backing off` : ""}${stale ? ` · ${stale} stale` : ""}${hostOnly.length ? ` · ${hostOnly.length} non-fetch` : ""}`
    : "No nodes reporting";

  return (
    <Card title="Companion fleet"
          sub={sub}
          action={<div style={{display: "flex", alignItems: "center", gap: 8}}>
            {data.last_updated && <span style={{fontSize: 12, color: "var(--ink-4)"}}>updated {fmtAgo(data.last_updated)}</span>}
            <ExpandButton expanded={expanded} onClick={() => setExpanded(v => !v)}/>
          </div>}>
      <div style={{display: "grid", gridTemplateColumns: "repeat(4, minmax(0, 1fr))", gap: 8}}>
        <MiniMetric label="Fetch nodes" value={`${healthy}/${fetchNodes.length || 0}`} tone={fleetTone}/>
        <MiniMetric label="Delivered" value={String(delivered24)} tone="healthy"/>
        <MiniMetric label="Claimed" value={String(claimed24)} tone={claimed24 ? "neutral" : "warn"}/>
        <MiniMetric label="Failed" value={String(failed24)} tone={failed24 ? "miss" : "healthy"}/>
      </div>
      <div style={{display: "flex", flexWrap: "wrap", gap: 6, marginTop: 10}}>
        {issueNodes.map(n => {
          const state = companionNodeDisplayState(n);
          return (
            <span key={n.node_id || n.hostname} className={"pill " + state.tone} style={{fontSize: 11}}>
              <span className={"vdot " + state.tone}/>{n.node_id || n.hostname}: {state.label}
            </span>
          );
        })}
        {hostOnly.map(n => (
          <span key={n.node_id || n.hostname} className="pill neutral" style={{fontSize: 11}}>
            <span className="vdot neutral"/>{n.node_id || n.hostname}: {nodeRoleLabel(n).toLowerCase()}
          </span>
        ))}
        {!issueNodes.length && !hostOnly.length && nodes.length > 0 && (
          <span style={{fontSize: 12.5, color: "var(--ink-3)"}}>All fetch companions are active.</span>
        )}
      </div>
      {expanded && (
        <div style={{marginTop: 10, borderTop: "1px solid var(--hair)", paddingTop: 2}}>
          {nodes.map(n => <FleetRow key={n.node_id || n.hostname} node={n}/>)}
        </div>
      )}
      {!nodes.length && (
        <p style={{margin: "8px 0", color: "var(--ink-3)", fontSize: 13}}>
          Backend returned no nodes. Check that companion instances are heartbeating.
        </p>
      )}
    </Card>
  );
}

function FleetRow({ node }) {
  const state = companionNodeDisplayState(node);
  const tone = state.tone;
  const stateLabel = state.label;
  const hostOnly = isHostOnlyCompanionNode(node);
  return (
    <div style={{padding: "10px 0", borderBottom: "1px solid var(--hair)"}}>
      <div style={{display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 10}}>
        <div style={{display: "flex", alignItems: "center", gap: 8, minWidth: 0, flex: 1}}>
          <span className={"vdot " + tone}/>
          <span style={{fontSize: 13, color: "var(--ink)", fontWeight: 500}}>
            {node.node_id || node.hostname || "unknown"}
          </span>
          {node.hostname && node.node_id && node.hostname !== node.node_id && (
            <span style={{fontSize: 12, color: "var(--ink-4)", fontVariantNumeric: "tabular-nums"}}>
              {node.hostname}
            </span>
          )}
          {node.ip_class && (
            <span style={{
              fontSize: 11, padding: "1px 6px", borderRadius: 4,
              background: "var(--surface-2)", color: "var(--ink-3)",
              border: "1px solid var(--hair)",
            }}>
              {node.ip_class}
            </span>
          )}
          <span className={"pill " + tone} style={{fontSize: 11}}>
            <span className={"vdot " + tone}/>{stateLabel}
          </span>
        </div>
        <span style={{fontSize: 12, color: "var(--ink-3)", whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums"}}>
          {hostOnly ? "Excluded from fetch lane" : `${node.claimed_24h ?? 0} claimed · ${node.delivered_24h ?? 0} delivered · 24h`}
        </span>
      </div>
      <div style={{fontSize: 12, color: "var(--ink-4)", marginTop: 3, fontVariantNumeric: "tabular-nums"}}>
        {node.extension_version ? `v${node.extension_version} · ` : ""}
        last heartbeat {fmtAgo(node.last_heartbeat_at)}
        {!hostOnly && node.last_success_at ? ` · last ok ${fmtAgo(node.last_success_at)}` : ""}
        {node.last_claim_at ? ` · last claim ${fmtAgo(node.last_claim_at)}` : ""}
        {!hostOnly && node.failed_24h ? ` · ${node.failed_24h} failed` : ""}
        {!hostOnly && Array.isArray(node.publishers_authed) && node.publishers_authed.length
          ? ` · ${node.publishers_authed.length} publishers authed` : ""}
      </div>
      {!hostOnly && tone !== "healthy" && (node.health_status || node.consecutive_failures || node.backoff_until) && (
        <div style={{fontSize: 11.5, color: tone === "neutral" ? "var(--ink-3)" : `var(--${tone})`, marginTop: 4, fontVariantNumeric: "tabular-nums"}}>
          {node.health_status ? publisherStatusLabel(node.health_status) : "demoted"}
          {node.consecutive_failures ? ` · ${node.consecutive_failures} consecutive fails` : ""}
          {node.backoff_until && Date.parse(node.backoff_until) > Date.now()
            ? ` · backoff ${fmtUntil(node.backoff_until)}` : ""}
          {node.last_error_code ? ` · ${node.last_error_code}` : ""}
        </div>
      )}
    </div>
  );
}

function Stat({ label, value }) {
  return (
    <div style={{padding: "8px 10px", background: "var(--surface-2)", border: "1px solid var(--hair)", borderRadius: 6}}>
      <div style={{fontSize: 11.5, color: "var(--ink-4)"}}>{label}</div>
      <div style={{fontSize: 13, color: "var(--ink)", marginTop: 2, fontVariantNumeric: "tabular-nums"}}>{value}</div>
    </div>
  );
}

Object.assign(window, { ViewOps });
