// screens-healthdiag.jsx — Project Health Diagnostics surfaces (design v2, locked 7/24).
// Exposes: window.HealthDiagSection ({ projectId, projectName, user }) — per-project panel
//          window.HealthRollupPanel ({ user })                        — module rollup
// Data: window.VaultHealthCompute (loads once, cached on window.__HEALTH_CACHE for the
// session; Recompute Baseline invalidates). Engine strings ARE the playbook join keys.
(function () {
  const { useState, useEffect, useMemo } = React;
  const INK = "#142B44", SEC = "#5A6B80", MUTE = "#8494A6", LINE = "#E2E8F1", HAIR = "#F2F5F9";
  const PANEL = { background: "#fff", border: `1px solid ${LINE}`, borderRadius: 16,
    boxShadow: "0 1px 2px rgba(20,43,68,.04),0 10px 30px -22px rgba(20,43,68,.28)" };
  const MONO = "var(--f-mono)";
  const KICK = { fontFamily: MONO, fontSize: 10.5, letterSpacing: ".14em", textTransform: "uppercase", color: MUTE, fontWeight: 500 };

  // Attribution palette (B6 ratified): amber=ours, purple=client, blue=mixed, green=strength
  const ATTR = {
    "OUR PROCESS": { bg: "#FBF3E3", fg: "#8A5B08", dot: "#D19B23", bd: "#F0DCB4" },
    "CLIENT":      { bg: "#F4EDFB", fg: "#6B3FA0", dot: "#9B6FD0", bd: "#E3D3F5" },
    "MIXED":       { bg: "#E8F1FA", fg: "#1F5A9B", dot: "#2E77C2", bd: "#CFE1F3" },
    "STRENGTH":    { bg: "#EAF6EF", fg: "#1B7A46", dot: "#22A35B", bd: "#CDE9D8" },
  };
  const fmtZ = z => (z >= 0 ? "+" : "") + z.toFixed(1) + "\u03c3";
  const zCol = z => z >= 0 ? "#A9750E" : "#1F5A9B";
  const fmt = v => v == null ? "\u2014" : (Math.abs(v) >= 100 ? Math.round(v).toLocaleString() :
    (Math.abs(v) >= 10 ? v.toFixed(1) : v.toFixed(2)));

  // Ledger display names for the engine keys
  const NAMES = { leadRate: "Lead Rate", approvalRate: "Approval Rate", contactRate: "Contact Rate",
    ccRate: "CC Conv Rate", visitRate: "Visit Rate", offersRate: "Offers Rate",
    callsPerLead: "Calls / Lead", mailingsPerLead: "Mailings / Lead", avgMailPerMo: "Avg Mail / Mo",
    avgCallsPerMo: "Avg Calls / Mo", pctCallDM1: "% Call by DM1", mailingPerApproval: "Mailing / Approval",
    callPerApproval: "Call / Approval", broadcastDrag: "Broadcast Drag", daysSinceApproval: "Days Since Appr",
    listsPerMo: "Appr Lists / Mo", targetsApproved: "Targets Approved", callsPerMailing: "Calls / Mailing",
    touchesPerLead: "Touches / Lead", obRate: "O&B Rate", approvedPerMo: "Approved / Mo", sentPerMo: "Sent / Mo" };

  function useHealthData() {
    const [state, setState] = useState(window.__HEALTH_CACHE || null);
    const [err, setErr] = useState(null);
    useEffect(() => {
      if (state) return;
      let live = true;
      (async () => {
        try {
          const S = await window.VaultHealthCompute.loadSources();
          const useStored = S.baselines && S.baselines.length > 0;
          const out = window.VaultHealthCompute.computeAll(S, { useStoredBaselines: useStored });
          const pb = {}; (S.playbook || []).forEach(r => { pb[r.diagnosis] = r; });
          const stamp = useStored ? (S.baselines[0].computed_at || null) : null;
          const pack = { out, playbook: pb, sources: S, baselineStamp: stamp, usedStored: useStored };
          window.__HEALTH_CACHE = pack;
          if (live) setState(pack);
        } catch (e) { if (live) setErr(e.message || String(e)); }
      })();
      return () => { live = false; };
    }, [state]);
    return { data: state, err, refresh: () => { window.__HEALTH_CACHE = null; setState(null); } };
  }

  function Tag({ kind, small }) {
    const c = ATTR[kind] || ATTR.MIXED;
    return (
      <span style={{ display: "inline-flex", alignItems: "center", gap: 6, borderRadius: 8,
        padding: small ? "3px 7px" : "5px 10px", fontFamily: MONO, fontSize: small ? 9.5 : 10.5,
        letterSpacing: ".08em", fontWeight: 600, background: c.bg, color: c.fg, border: `1px solid ${c.bd}`, whiteSpace: "nowrap" }}>
        <span style={{ width: small ? 6 : 7, height: small ? 6 : 7, borderRadius: "50%", background: c.dot }}/>{kind}
      </span>
    );
  }

  function DiagnosisCard({ name, playbook, evidence }) {
    const [open, setOpen] = useState(false);
    const pb = playbook[name] || {};
    const attr = pb.attribution || "MIXED";
    const green = attr === "STRENGTH";
    return (
      <div style={{ border: `1px solid ${green ? "#CDE9D8" : "#E7ECF3"}`, borderRadius: 13,
        padding: "16px 18px", background: green ? "#F4FBF6" : "#FCFDFE" }}>
        <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16 }}>
          <h3 style={{ margin: 0, fontSize: 15.5, fontWeight: 700, letterSpacing: "-.01em", lineHeight: 1.3, color: INK }}>{name}</h3>
          <Tag kind={attr}/>
        </div>
        {pb.action && <p style={{ margin: "9px 0 0", fontSize: 13.5, lineHeight: 1.5, color: "#4A5C70" }}>{pb.action}</p>}
        {evidence.length > 0 && (
          <div style={{ marginTop: 12 }}>
            <button onClick={() => setOpen(o => !o)} style={{ background: "none", border: "none", padding: 0,
              cursor: "pointer", fontFamily: MONO, fontSize: 11.5, color: "#2E77C2", fontWeight: 500 }}>
              {open ? "\u25be Hide evidence" : "\u25b8 Evidence"} {"\u00b7"} {evidence.length} signals
            </button>
            {open && (
              <div style={{ display: "flex", flexWrap: "wrap", gap: 7, marginTop: 11, paddingTop: 12, borderTop: "1px dashed #E2E8F1" }}>
                {evidence.map(ev => (
                  <span key={ev.k} style={{ display: "inline-flex", alignItems: "center", gap: 6, background: "#F5F8FB",
                    border: "1px solid #E7ECF3", borderRadius: 8, padding: "5px 10px", fontSize: 12, color: "#5A6B80" }}>
                    {NAMES[ev.k] || ev.k} <b style={{ fontVariantNumeric: "tabular-nums", color: zCol(ev.z) }}>{fmtZ(ev.z)}</b>
                  </span>
                ))}
              </div>
            )}
          </div>
        )}
      </div>
    );
  }

  function RetainerHeader({ p }) {
    const r = p.retainer;
    const now = new Date();
    const end = r && r.end_date ? new Date(String(r.end_date).slice(0, 10) + "T00:00:00Z") : null;
    const notif = r && r.notification_cancellation_date ? new Date(String(r.notification_cancellation_date).slice(0, 10) + "T00:00:00Z") : null;
    const inWindow = notif && now >= new Date(notif - 45 * 86400000) && now <= notif;
    const daysLeft = notif ? Math.max(0, Math.round((notif - now) / 86400000)) : null;
    return (
      <div style={{ ...PANEL, padding: "18px 24px", display: "flex", alignItems: "center", gap: 28, flexWrap: "wrap" }}>
        <div style={{ minWidth: 210 }}>
          <div style={KICK}>Retainer Context</div>
          <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: "-.01em", marginTop: 3, color: INK }}>{p.buyerName || p.name}</div>
          <div style={{ fontSize: 12.5, color: "#6B7C93", marginTop: 1 }}>{p.type} {"\u00b7"} {p.director}</div>
        </div>
        <div style={{ height: 38, width: 1, background: "#E7ECF3" }}/>
        <div style={{ display: "flex", gap: 32, flexWrap: "wrap", flex: 1 }}>
          {[["Months (anchor: " + p.anchorKind + ")", p.gates.months == null ? "\u2014" : Math.floor(p.gates.months)],
            ["Monthly Retainer", r && r.retainer_monthly ? "$" + Number(r.retainer_monthly).toLocaleString() : "\u2014"],
            ["Retainer End", end ? end.toISOString().slice(0, 10) : (r ? "open" : "\u2014")],
            ["Tail", r && r.tail_months ? r.tail_months + " mo" : "\u2014"]].map(([l, v]) => (
            <div key={l}>
              <div style={{ ...KICK, fontSize: 10.5, letterSpacing: ".12em" }}>{l}</div>
              <div style={{ fontSize: 20, fontWeight: 600, marginTop: 4, fontVariantNumeric: "tabular-nums", color: INK }}>{v}</div>
            </div>
          ))}
        </div>
        {inWindow && (
          <div style={{ display: "flex", alignItems: "center", gap: 9, background: "#FBF3E3", border: "1px solid #F0DCB4",
            color: "#8A5B08", borderRadius: 11, padding: "9px 14px" }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#D19B23" }}/>
            <div>
              <div style={{ fontFamily: MONO, fontSize: 10, letterSpacing: ".12em", textTransform: "uppercase", fontWeight: 600 }}>Renewal Window Open</div>
              <div style={{ fontSize: 12.5, fontWeight: 600 }}>{daysLeft} days left to notify to cancel</div>
            </div>
          </div>
        )}
      </div>
    );
  }

  function MetricLedger({ p, baselines, firedByMetric, stampLine }) {
    const [openRow, setOpenRow] = useState(null);
    const rows = Object.keys(NAMES).map(k => {
      const raw = p.metrics[k];
      const b = baselines[k] || {};
      const z = p.result ? p.result.z[k] : 0;
      const verdict = raw == null ? "NO DENOM" : (z > 1 ? "HIGH" : z < -1 ? "LOW" : "NORMAL");
      return { k, raw, avg: b.avg, sd: b.sd, z, verdict, fired: firedByMetric[k] || null };
    });
    const vStyle = v => v === "HIGH" ? { bg: "#FBF3E3", fg: "#8A5B08", bd: "#F0DCB4" }
      : v === "LOW" ? { bg: "#E8F1FA", fg: "#1F5A9B", bd: "#CFE1F3" }
      : v === "NO DENOM" ? { bg: "#F7F9FB", fg: "#B4C1D2", bd: "#EAEFF4" }
      : { bg: "#F7F9FB", fg: "#8494A6", bd: "#EAEFF4" };
    return (
      <div style={{ ...PANEL, overflow: "hidden" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "16px 24px 12px", flexWrap: "wrap", gap: 8 }}>
          <span style={KICK}>Metric Ledger {"\u00b7"} rates over trailing {window.VaultHealthCompute.WINDOW_MONTHS} mo</span>
          <span style={{ fontFamily: MONO, fontSize: 11, color: MUTE, background: "#F5F8FB", border: "1px solid #E7ECF3", borderRadius: 8, padding: "7px 11px" }}>{stampLine}</span>
        </div>
        <div className="tbl-wrap">
          <div style={{ display: "grid", gridTemplateColumns: "minmax(200px,2.2fr) 110px 110px 80px 80px 110px", gap: 14,
            padding: "11px 24px", background: "#FBFCFE", borderBottom: "1px solid #EEF2F7",
            fontFamily: MONO, fontSize: 10, letterSpacing: ".1em", textTransform: "uppercase", color: "#A6B2C2" }}>
            <span>Metric</span><span style={{ textAlign: "right" }}>This Project</span>
            <span style={{ textAlign: "right" }}>Baseline Avg</span><span style={{ textAlign: "right" }}>SD</span>
            <span style={{ textAlign: "right" }}>z</span><span style={{ textAlign: "center" }}>Verdict</span>
          </div>
          {rows.map(r => {
            const vs = vStyle(r.verdict);
            const acc = r.fired ? (ATTR[r.fired.attr] || ATTR.MIXED).dot : "transparent";
            const open = openRow === r.k;
            return (
              <div key={r.k} style={{ borderBottom: `1px solid ${HAIR}`, background: r.fired ? "#FDFCF8" : "transparent" }}>
                <button onClick={() => setOpenRow(open ? null : r.k)} style={{ appearance: "none", width: "100%",
                  cursor: "pointer", background: "transparent", border: "none", borderLeft: `3px solid ${acc}`,
                  textAlign: "left", display: "grid", gridTemplateColumns: "minmax(200px,2.2fr) 110px 110px 80px 80px 110px",
                  gap: 14, alignItems: "center", padding: "11px 24px 11px 21px", fontFamily: "inherit" }}>
                  <span style={{ display: "flex", alignItems: "center", gap: 9, minWidth: 0 }}>
                    <span style={{ fontSize: 14, fontWeight: 500, color: INK, whiteSpace: "nowrap" }}>{NAMES[r.k]}</span>
                    {r.fired && <span style={{ fontFamily: MONO, fontSize: 9.5, fontWeight: 600, textTransform: "uppercase",
                      background: (ATTR[r.fired.attr] || ATTR.MIXED).bg, color: (ATTR[r.fired.attr] || ATTR.MIXED).fg,
                      borderRadius: 6, padding: "2px 7px", whiteSpace: "nowrap" }}>{r.fired.tag}</span>}
                  </span>
                  <span style={{ textAlign: "right", fontSize: 14, fontWeight: 600, fontVariantNumeric: "tabular-nums", color: "#1F3247" }}>{fmt(r.raw)}</span>
                  <span style={{ textAlign: "right", fontSize: 13.5, fontVariantNumeric: "tabular-nums", color: MUTE }}>{fmt(r.avg)}</span>
                  <span style={{ textAlign: "right", fontSize: 13.5, fontVariantNumeric: "tabular-nums", color: "#A6B2C2" }}>{fmt(r.sd)}</span>
                  <span style={{ textAlign: "right", fontSize: 14, fontWeight: 600, fontVariantNumeric: "tabular-nums", color: zCol(r.z) }}>{r.raw == null ? "\u2014" : fmtZ(r.z)}</span>
                  <span style={{ textAlign: "center" }}><span style={{ display: "inline-block", fontFamily: MONO, fontSize: 10,
                    letterSpacing: ".08em", fontWeight: 600, background: vs.bg, color: vs.fg, border: `1px solid ${vs.bd}`,
                    borderRadius: 7, padding: "4px 9px" }}>{r.verdict}</span></span>
                </button>
                {open && (
                  <div style={{ padding: "0 24px 14px 21px" }}>
                    <div style={{ background: "#F0F5FB", border: "1px solid #DCE6F1", borderRadius: 10, padding: "12px 15px",
                      fontFamily: MONO, fontSize: 13, color: "#28394C" }}>
                      {r.raw == null
                        ? `${NAMES[r.k]}: no denominator in the window \u2014 z forced to 0`
                        : `${NAMES[r.k]}: ${fmt(r.raw)} vs baseline avg ${fmt(r.avg)} (SD ${fmt(r.sd)}) \u2192 ${fmtZ(r.z)}`}
                    </div>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  function HealthDiagSection({ projectId, projectName, user }) {
    const { data, err, refresh } = useHealthData();
    const [busy, setBusy] = useState(false);
    if (err) return <div style={{ ...PANEL, padding: 18, color: "#A32D2D", marginTop: 16 }}>Diagnostics unavailable: {err}</div>;
    if (!data) return <div style={{ ...PANEL, padding: 18, color: SEC, marginTop: 16 }}>Loading diagnostics{"\u2026"}</div>;
    const p = data.out.projects.find(x => x.projectId === String(projectId) ||
      (projectName && x.name.toLowerCase() === String(projectName).toLowerCase()));
    if (!p) return null;   // not a Scott diagnostics project — section stays absent

    const baselines = data.out.baselines[p.type] || {};
    const stampLine = "Baseline: Scott module " + p.type + " \u00b7 " + ((data.out.cohorts[p.type] || []).length) + " projects \u00b7 " +
      (data.usedStored && data.baselineStamp ? "computed " + String(data.baselineStamp).slice(0, 10) : "computed live (unsaved)");

    // Which ledger rows belong to which firing diagnosis (first match wins)
    const RULE_METRICS = {
      "Bad Research": ["approvalRate", "leadRate", "ccRate"],
      "Review Call Quality / Pitch": ["leadRate", "callsPerLead", "callsPerMailing", "pctCallDM1"],
      "Call Coverage (DM1)": ["pctCallDM1", "leadRate", "callsPerLead", "callPerApproval"],
      "Brochure Quality / Pitch": ["leadRate", "mailingsPerLead", "mailingPerApproval", "approvalRate"],
      "Needs Approval List": ["daysSinceApproval"],
      "Mid-funnel Disengagement": ["ccRate", "visitRate", "offersRate"],
      "Needs Call Love (mail-reliant, add calls)": ["callsPerLead", "broadcastDrag", "leadRate", "ccRate"],
      "Aggressive Client - Invest in Research": ["sentPerMo", "approvedPerMo", "ccRate", "visitRate"],
      "Aggressive Client - Improve Contact Rate": ["contactRate", "ccRate", "offersRate", "leadRate"],
      "Aggressive Client - high conversion from CC to Offer": ["obRate"],
    };
    const prefix = {
      "Process Integrity": ["mailingPerApproval", "callsPerMailing", "touchesPerLead", "callPerApproval"],
      "Requires Call Attention": ["mailingPerApproval", "callPerApproval"],
      "Over-": ["callPerApproval", "approvedPerMo", "callsPerLead"],
      "Clean Process": [], "High-Volume": ["approvedPerMo", "obRate"],
      "Good Return": ["callsPerLead", "mailingsPerLead", "avgMailPerMo", "leadRate"],
      "Picky": ["ccRate", "visitRate", "offersRate"], "Universe is Picked Over": ["ccRate", "approvalRate"],
      "Low Client Engagement": ["ccRate", "visitRate", "offersRate"],
      "Funnel Conversion Soft": ["ccRate", "visitRate", "offersRate", "leadRate"],
      "Low Health": [],
    };
    const metricsOf = d => RULE_METRICS[d] || (Object.entries(prefix).find(([k]) => d.startsWith(k)) || [null, []])[1];
    const fired = p.result ? p.result.fired : [];
    const firedByMetric = {};
    fired.forEach(d => {
      const attr = (data.playbook[d] && data.playbook[d].attribution) || "MIXED";
      metricsOf(d).forEach(k => { if (!firedByMetric[k]) firedByMetric[k] = { tag: d.split(" ")[0] === "Aggressive" ? "Aggressive" : d.split("(")[0].trim().split(" - ")[0], attr }; });
    });
    const signals = p.result ? Object.entries(p.result.z).filter(([, z]) => Math.abs(z) > 1) : [];

    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 20, marginTop: 20 }}>
        <RetainerHeader p={p}/>
        {!p.gates.pass ? (
          <div style={{ border: "1.5px dashed #CBD6E3", borderRadius: 13, padding: "26px 18px", background: "#FBFCFE", textAlign: "center" }}>
            <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: "#3E5063" }}>Not enough data yet</h3>
            <p style={{ margin: "7px auto 0", fontSize: 13, lineHeight: 1.5, color: MUTE, maxWidth: 380 }}>
              Diagnoses resume once the gates are met.</p>
            <div style={{ display: "inline-flex", gap: 10, marginTop: 14, fontFamily: MONO, fontSize: 11, color: "#B4C1D2" }}>
              <span style={{ color: p.gates.monthsOk ? "#22A35B" : "#B4C1D2" }}>{p.gates.months == null ? "?" : Math.floor(p.gates.months)} / 2 mo</span>
              <span>{"\u00b7"}</span>
              <span style={{ color: p.gates.listsReturnedOk ? "#22A35B" : "#B4C1D2" }}>{p.gates.listsReturnedOk ? "1+" : "0"} / 1 list returned</span>
              <span>{"\u00b7"}</span>
              <span style={{ color: p.gates.approachOk ? "#22A35B" : "#B4C1D2" }}>approach {p.gates.approachOk ? "started" : "not started"}</span>
            </div>
          </div>
        ) : (
          <>
            {signals.length > 0 && (
              <div style={{ ...PANEL, padding: "16px 20px" }}>
                <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
                  <span style={KICK}>Abnormal Signals {"\u00b7"} |z| {">"} 1</span>
                  <span style={{ fontFamily: MONO, fontSize: 11, color: "#B4C1D2" }}>{signals.length} of {Object.keys(NAMES).length} metrics</span>
                </div>
                <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                  {signals.map(([k, z]) => (
                    <span key={k} style={{ display: "inline-flex", alignItems: "center", gap: 7, background: "#F5F8FB",
                      border: "1px solid #E7ECF3", borderRadius: 9, padding: "6px 10px" }}>
                      <span style={{ fontSize: 12.5, color: "#3E5063", fontWeight: 500 }}>{NAMES[k] || k}</span>
                      <span style={{ fontSize: 10, color: zCol(z) }}>{z >= 0 ? "\u25b2" : "\u25bc"}</span>
                      <b style={{ fontSize: 12.5, fontWeight: 600, fontVariantNumeric: "tabular-nums", color: zCol(z) }}>{fmtZ(z)}</b>
                    </span>
                  ))}
                </div>
              </div>
            )}
            <div style={{ ...PANEL, padding: "20px 22px" }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6, flexWrap: "wrap", gap: 6 }}>
                <span style={KICK}>Diagnoses {"\u00b7"} {fired.length} Firing</span>
                <span style={{ fontFamily: MONO, fontSize: 11, color: "#B4C1D2" }}>Engine v1 (52/52 validated) {"\u00b7"} baseline: Scott {p.type}</span>
              </div>
              <p style={{ margin: "0 0 16px", fontSize: 12.5, lineHeight: 1.5, color: MUTE }}>
                Each metric is compared to the Scott-module {p.type.toLowerCase()} average; a diagnosis fires when a defined
                pattern of deviations appears {"\u2014"} tap any figure in the ledger to see the math.
              </p>
              <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
                {fired.length === 0 && (
                  <div style={{ border: "1px solid #CDE9D8", borderRadius: 13, padding: 18, background: "#F4FBF6", color: "#1B7A46", fontSize: 13.5 }}>
                    No diagnoses firing {"\u2014"} all patterns within baseline.
                  </div>
                )}
                {fired.map(d => (
                  <DiagnosisCard key={d} name={d} playbook={data.playbook}
                    evidence={metricsOf(d).map(k => ({ k, z: p.result.z[k] })).filter(e => e.z !== 0)}/>
                ))}
              </div>
            </div>
            <MetricLedger p={p} baselines={baselines} firedByMetric={firedByMetric} stampLine={stampLine}/>
          </>
        )}
      </div>
    );
  }

  function HealthRollupPanel({ user }) {
    const { data, err, refresh } = useHealthData();
    const [busy, setBusy] = useState(false);
    const [sel, setSel] = useState(null);
    if (err) return <div style={{ ...PANEL, padding: 18, color: "#A32D2D" }}>Rollup unavailable: {err}</div>;
    if (!data) return <div style={{ ...PANEL, padding: 18, color: SEC }}>Loading rollup{"\u2026"}</div>;
    const graded = data.out.projects.filter(p => p.result);
    // Process quality by analyst (researcher)
    const byA = {};
    graded.forEach(p => {
      const a = p.analyst || "(unassigned)";
      byA[a] = byA[a] || { n: 0, clean: 0 };
      byA[a].n++;
      if (p.result.fired.some(f => f.startsWith("Clean Process"))) byA[a].clean++;
    });
    const rows = Object.entries(byA).sort((a, b) => b[1].n - a[1].n);
    // Frequency board
    const freq = {};
    graded.forEach(p => p.result.fired.forEach(f => {
      freq[f] = freq[f] || { n: 0, projects: [] };
      freq[f].n++; freq[f].projects.push(p);
    }));
    const fRows = Object.entries(freq).sort((a, b) => b[1].n - a[1].n);
    async function recompute() {
      setBusy(true);
      try {
        const r = await window.VaultHealthCompute.recomputeBaselines(user);
        window.__HEALTH_CACHE = null; refresh();
        window.VaultUI && window.VaultUI.toast && window.VaultUI.toast("success", "Baseline recomputed \u2014 " + r.saved + " metric rows saved.");
      } catch (e) { window.VaultUI && window.VaultUI.toast && window.VaultUI.toast("error", "Recompute failed: " + (e.message || e)); }
      finally { setBusy(false); }
    }
    return (
      <div style={{ display: "grid", gridTemplateColumns: "1.35fr 1fr", gap: 24, alignItems: "start", marginTop: 20 }} className="kpi-grid">
        <div style={{ ...PANEL, padding: "22px 24px" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
            <span style={KICK}>Process Quality {"\u00b7"} by Researcher</span>
            <button onClick={recompute} disabled={busy} style={{ fontFamily: "inherit", fontSize: 12, fontWeight: 600,
              padding: "7px 13px", borderRadius: 8, border: "1px solid #D9DDE2", background: "#fff", color: INK,
              cursor: busy ? "default" : "pointer", opacity: busy ? .6 : 1 }}>
              {busy ? "Recomputing\u2026" : "\u21bb Recompute Baseline"}</button>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1.4fr 70px 70px 1fr", gap: 12, marginTop: 16,
            paddingBottom: 9, borderBottom: "1px solid #EEF2F7", fontFamily: MONO, fontSize: 10,
            letterSpacing: ".1em", textTransform: "uppercase", color: "#A6B2C2" }}>
            <span>Researcher</span><span style={{ textAlign: "right" }}>Projects</span>
            <span style={{ textAlign: "right" }}>Clean</span><span>% Clean</span>
          </div>
          {rows.map(([name, v]) => {
            const pct = v.n ? Math.round(v.clean / v.n * 100) : 0;
            const good = pct >= 50;
            return (
              <div key={name} style={{ display: "grid", gridTemplateColumns: "1.4fr 70px 70px 1fr", gap: 12,
                alignItems: "center", padding: "12px 0", borderBottom: `1px solid ${HAIR}` }}>
                <span style={{ fontSize: 14, fontWeight: 500, color: "#28394C" }}>{name}</span>
                <span style={{ textAlign: "right", fontSize: 14, fontVariantNumeric: "tabular-nums", color: "#5A6B80" }}>{v.n}</span>
                <span style={{ textAlign: "right", fontSize: 14, fontVariantNumeric: "tabular-nums", color: "#5A6B80" }}>{v.clean}</span>
                <span style={{ display: "flex", alignItems: "center", gap: 10 }}>
                  <span style={{ flex: 1, height: 8, background: "#EDF1F6", borderRadius: 99, overflow: "hidden" }}>
                    <span style={{ display: "block", height: "100%", borderRadius: 99, width: pct + "%",
                      background: good ? "#45A06B" : "#D19B23" }}/></span>
                  <b style={{ fontSize: 13.5, fontWeight: 600, fontVariantNumeric: "tabular-nums", width: 38,
                    textAlign: "right", color: good ? "#1B7A46" : "#8A5B08" }}>{pct}%</b>
                </span>
              </div>
            );
          })}
        </div>
        <div style={{ ...PANEL, padding: "22px 24px" }}>
          <span style={KICK}>Diagnosis Frequency {"\u00b7"} Firing Now</span>
          <div style={{ display: "flex", flexDirection: "column", gap: 8, marginTop: 16 }}>
            {fRows.map(([name, v]) => {
              const attr = (data.playbook[name] && data.playbook[name].attribution) || "MIXED";
              const c = ATTR[attr] || ATTR.MIXED;
              const seld = sel === name;
              return (
                <div key={name}>
                  <button onClick={() => setSel(seld ? null : name)} style={{ appearance: "none", width: "100%",
                    textAlign: "left", cursor: "pointer", display: "flex", alignItems: "center", gap: 12,
                    border: `1px solid ${seld ? c.bd : "#EAEFF4"}`, background: seld ? c.bg : "#FBFCFE",
                    borderRadius: 11, padding: "11px 13px", fontFamily: "inherit" }}>
                    <span style={{ width: 9, height: 9, borderRadius: "50%", background: c.dot, flexShrink: 0 }}/>
                    <span style={{ flex: 1, fontSize: 13.5, fontWeight: 500, color: "#28394C" }}>{name}</span>
                    <span style={{ fontFamily: MONO, fontSize: 15, fontWeight: 600, fontVariantNumeric: "tabular-nums", color: c.fg }}>{v.n}</span>
                  </button>
                  {seld && (
                    <div style={{ margin: "6px 0 4px", paddingLeft: 11, display: "flex", flexDirection: "column", gap: 6, borderLeft: "2px solid #E2E8F1" }}>
                      {v.projects.map(p => (
                        <button key={p.projectId} onClick={() => window.VaultNav && window.VaultNav("projecthealth", { projectId: p.projectId })}
                          style={{ appearance: "none", textAlign: "left", cursor: "pointer", display: "flex", alignItems: "center",
                            gap: 10, background: "#fff", border: "1px solid #EAEFF4", borderRadius: 9, padding: "8px 10px", fontFamily: "inherit" }}>
                          <span style={{ flexShrink: 0, width: 24, height: 24, borderRadius: "50%", display: "flex",
                            alignItems: "center", justifyContent: "center", fontFamily: MONO, fontSize: 10, fontWeight: 600,
                            background: "#E8F1FA", color: "#1F5A9B" }}>
                            {(p.analyst || "?").split(/\s+/).map(w => w[0]).join("").slice(0, 2).toUpperCase()}</span>
                          <span style={{ fontSize: 12.5, fontWeight: 500, color: "#28394C" }}>{p.name}</span>
                          <span style={{ marginLeft: "auto", fontSize: 11, color: "#B4C1D2" }}>{"\u2192"}</span>
                        </button>
                      ))}
                    </div>
                  )}
                </div>
              );
            })}
            {fRows.length === 0 && <div style={{ fontSize: 13, color: MUTE }}>Nothing firing {"\u2014"} run Recompute Baseline after the outreach re-pull.</div>}
          </div>
        </div>
      </div>
    );
  }

  window.HealthDiagSection = HealthDiagSection;
  window.HealthRollupPanel = HealthRollupPanel;
})();
