// Board Report — executive, board-ready, print-quality financial pack.
//
// window.BoardReportsPage. Renders a comprehensive 8-section board report from
// the live financial data (data.pl / plHistory / cash / aging / txns) plus the
// intelligence layer (window.PerduraSynthesis) and KPI benchmarks (kpi_library).
// Designed to print as a clean A4/Letter PDF (navy headers, accent rules, no
// gradients). Everything is computed from real data; views that depend on data we
// do not have (full balance sheet, segmented cash-flow statement, historical
// balances) show honest "awaiting data" notes rather than fabricated figures.
//
// Props (from app.jsx): { scopedCompanyId, companyProfile, data, isLive, setPage }

(function () {
  const R = React;
  const { useState, useEffect, useMemo } = R;

  // ── design tokens ───────────────────────────────────────────────────────────
  const NAVY = "#0F2044", ACCENT = "#1C4ED8", ACCENT_SOFT = "#EEF2FF";
  const BORDER = "#E4E8F0", INK = "#1A2233", INK2 = "#475569", MUTED = "#6B7A99";
  const GREEN = "#059669", AMBER = "#D97706", RED = "#DC2626", SLATE = "#94A3B8";
  const SHELF = "#F7F8FB";
  const SERIES = [ACCENT, "#0891B2", AMBER, "#7C3AED", "#DB2777", "#059669", "#64748B"];
  const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
  const MONTH_ABBR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];

  const N = (v) => { const n = Number(v); return isFinite(n) ? n : 0; };
  const arrOf = (a) => Array.isArray(a) ? a.map(N) : [];
  const sumRange = (a, s, e) => { let t = 0; for (let i = Math.max(0, s); i < e; i++) t += N(a[i]); return t; };
  const money = (v, opts) => {
    if (v == null || !isFinite(v)) return "—";
    if (window.PerduraFormat && window.PerduraFormat.money) return window.PerduraFormat.money(v, opts);
    const n = Math.round(v); return (n < 0 ? "-$" : "$") + Math.abs(n).toLocaleString();
  };
  const pctStr = (v, d) => (v == null || !isFinite(v)) ? "—" : v.toFixed(d == null ? 1 : d) + "%";
  const variance = (cur, prior) => ({ abs: (cur == null || prior == null) ? null : cur - prior, pct: (prior ? (cur - prior) / Math.abs(prior) * 100 : null) });
  // status for "higher is better" variance
  const statusUp = (p) => p == null ? "neutral" : p >= 2 ? "green" : p >= -2 ? "amber" : "red";
  const STAT = { green: { fg: GREEN, bg: "#DCFCE7", label: "On track" }, amber: { fg: AMBER, bg: "#FEF3C7", label: "Watch" }, red: { fg: RED, bg: "#FEE2E2", label: "Concern" }, neutral: { fg: INK2, bg: "#F1F5F9", label: "—" } };

  // category → PL bucket (mirrors src/data-live.js bucketForCategory)
  const LEGACY_BUCKET = { Revenue: "revenue", COGS: "cogs", Opex: "opex", AR: "ar", AP: "ap", Cash: "cash", "Contra-revenue": "revenue", "Interest Expense": "opex", "Interest Income": "revenue", "Tax Expense": "opex", "Other Income/Expense": "opex" };
  const SECTION_BUCKET = { revenue: "revenue", contra_revenue: "revenue", other_income: "revenue", cogs: "cogs", opex: "opex", da: "opex", other_op_exp: "opex", interest: "opex", tax: "opex", other_expense: "opex" };
  function bucketFor(category, profile) {
    if (!category) return null;
    if (LEGACY_BUCKET[category]) return LEGACY_BUCKET[category];
    const tax = window.PerduraTaxonomy;
    if (!tax || !tax.sectionForCategory) return null;
    const sec = tax.sectionForCategory(category, profile);
    return SECTION_BUCKET[sec] || null;
  }

  // ── chart primitives (inline SVG, print-safe solid colors) ───────────────────
  function LineTrend({ series, labels, height, benchmark, benchmarkLabel, fmt }) {
    const H = height || 150, W = 640, padL = 46, padR = 14, padT = 12, padB = 24;
    const all = series.flatMap((s) => s.data).filter((v) => isFinite(v));
    let max = Math.max(...all, benchmark != null ? benchmark : -Infinity), min = Math.min(...all, benchmark != null ? benchmark : Infinity, 0);
    if (!isFinite(max)) max = 1; if (!isFinite(min)) min = 0; if (max === min) max = min + 1;
    const x = (i, n) => padL + (n <= 1 ? 0 : i / (n - 1) * (W - padL - padR));
    const y = (v) => padT + (1 - (v - min) / (max - min)) * (H - padT - padB);
    const f = fmt || ((v) => money(v, { compact: true }));
    return R.createElement("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", height: H, preserveAspectRatio: "none", style: { display: "block" } },
      [0, 0.5, 1].map((t, k) => { const v = min + t * (max - min); const yy = y(v); return R.createElement("g", { key: k },
        R.createElement("line", { x1: padL, x2: W - padR, y1: yy, y2: yy, stroke: "#EEF1F6", strokeWidth: 1 }),
        R.createElement("text", { x: padL - 6, y: yy + 3, textAnchor: "end", fontSize: 9, fill: MUTED }, f(v))); }),
      benchmark != null ? R.createElement("g", null,
        R.createElement("line", { x1: padL, x2: W - padR, y1: y(benchmark), y2: y(benchmark), stroke: RED, strokeWidth: 1.4, strokeDasharray: "5 4" }),
        R.createElement("text", { x: W - padR, y: y(benchmark) - 4, textAnchor: "end", fontSize: 9, fill: RED, fontWeight: 600 }, benchmarkLabel || ("" + benchmark))) : null,
      series.map((s, si) => R.createElement("polyline", { key: si, fill: "none", stroke: s.color || SERIES[si % SERIES.length], strokeWidth: s.dashed ? 1.6 : 2.2, strokeDasharray: s.dashed ? "5 4" : "none", points: s.data.map((v, i) => `${x(i, s.data.length)},${y(N(v))}`).join(" ") })),
      (labels || []).map((l, i) => (l && i % Math.ceil((labels.length) / 12) === 0) ? R.createElement("text", { key: i, x: x(i, labels.length), y: H - 6, textAnchor: "middle", fontSize: 9, fill: MUTED }, l) : null));
  }

  function Waterfall({ steps, height }) {
    const H = height || 200, W = 640, padT = 16, padB = 30, padL = 8, padR = 8;
    let cum = 0; const tops = []; let maxTop = 0;
    steps.forEach((s) => { if (s.total) { tops.push({ base: 0, top: s.value }); maxTop = Math.max(maxTop, s.value); } else { const start = cum; cum += s.value; tops.push({ base: Math.min(start, cum), top: Math.max(start, cum) }); maxTop = Math.max(maxTop, Math.max(start, cum)); } });
    maxTop = maxTop || 1;
    const bw = (W - padL - padR) / steps.length * 0.62;
    const gap = (W - padL - padR) / steps.length;
    const y = (v) => padT + (1 - v / maxTop) * (H - padT - padB);
    return R.createElement("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", height: H, preserveAspectRatio: "none", style: { display: "block" } },
      steps.map((s, i) => { const t = tops[i]; const cx = padL + gap * i + (gap - bw) / 2; const yTop = y(t.top), yBase = y(t.base);
        const color = s.total ? NAVY : s.value >= 0 ? ACCENT : RED;
        return R.createElement("g", { key: i },
          R.createElement("rect", { x: cx, y: yTop, width: bw, height: Math.max(2, yBase - yTop), fill: color, rx: 2 }),
          R.createElement("text", { x: cx + bw / 2, y: yTop - 5, textAnchor: "middle", fontSize: 9.5, fontWeight: 600, fill: INK }, money(s.value, { compact: true })),
          R.createElement("text", { x: cx + bw / 2, y: H - 14, textAnchor: "middle", fontSize: 9, fill: INK2 }, s.label)); }));
  }

  function Donut({ slices, size }) {
    const S = size || 150, r = S / 2 - 6, cx = S / 2, cy = S / 2, inner = r * 0.58;
    const total = slices.reduce((s, x) => s + Math.abs(x.value), 0) || 1;
    let a0 = -Math.PI / 2;
    const arc = (val) => { const a1 = a0 + (Math.abs(val) / total) * Math.PI * 2; const p = [cx + r * Math.cos(a0), cy + r * Math.sin(a0), cx + r * Math.cos(a1), cy + r * Math.sin(a1), cx + inner * Math.cos(a1), cy + inner * Math.sin(a1), cx + inner * Math.cos(a0), cy + inner * Math.sin(a0)]; const large = (a1 - a0) > Math.PI ? 1 : 0; a0 = a1; return `M ${p[0]} ${p[1]} A ${r} ${r} 0 ${large} 1 ${p[2]} ${p[3]} L ${p[4]} ${p[5]} A ${inner} ${inner} 0 ${large} 0 ${p[6]} ${p[7]} Z`; };
    return R.createElement("svg", { viewBox: `0 0 ${S} ${S}`, width: S, height: S },
      slices.map((s, i) => R.createElement("path", { key: i, d: arc(s.value), fill: s.color || SERIES[i % SERIES.length] })));
  }

  function Sparkline({ data, color, w, h }) {
    const W = w || 84, H = h || 24, a = arrOf(data);
    if (a.length < 2) return R.createElement("div", { style: { width: W, height: H } });
    const max = Math.max(...a), min = Math.min(...a), rng = (max - min) || 1;
    const pts = a.map((v, i) => `${i / (a.length - 1) * W},${H - ((v - min) / rng) * (H - 4) - 2}`).join(" ");
    return R.createElement("svg", { width: W, height: H, style: { display: "block" } }, R.createElement("polyline", { fill: "none", stroke: color || ACCENT, strokeWidth: 1.5, points: pts }));
  }

  // ── shared layout atoms ──────────────────────────────────────────────────────
  function SectionHeader({ n, title, sub }) {
    return R.createElement("div", { style: { borderLeft: `4px solid ${ACCENT}`, paddingLeft: 12, margin: "0 0 16px" } },
      R.createElement("div", { style: { fontSize: 11, fontWeight: 700, color: ACCENT, letterSpacing: 0.6, textTransform: "uppercase" } }, n != null ? `Section ${n}` : ""),
      R.createElement("div", { style: { fontSize: 19, fontWeight: 700, color: NAVY, letterSpacing: -0.3 } }, title),
      sub ? R.createElement("div", { style: { fontSize: 12.5, color: MUTED, marginTop: 2 } }, sub) : null);
  }
  const cardStyle = { background: "#fff", border: `1px solid ${BORDER}`, borderRadius: 10, padding: 18, boxShadow: "0 1px 3px rgba(15,32,68,0.05)" };
  function Card({ children, style, onClick, title }) { return R.createElement("div", { className: "board-card", onClick: onClick || undefined, title: title || undefined, style: { ...cardStyle, ...(style || {}), ...(onClick ? { cursor: "pointer" } : {}) } }, children); }
  function Section({ n, title, sub, children, brk }) {
    return R.createElement("section", { className: "board-section" + (brk ? " board-pagebreak" : "") , style: { marginBottom: 34 } },
      R.createElement(SectionHeader, { n, title, sub }), children);
  }
  function KpiTile({ label, value, prior, varc, status, note }) {
    const s = STAT[status || "neutral"];
    // Every tile is clickable → most relevant statement (cash metrics → cash
    // flow, everything else → income statement).
    const dest = /cash|runway|burn/.test(String(label || "").toLowerCase()) ? "cash_flow_statement" : "income_statement";
    return R.createElement(Card, { style: { padding: 15 }, title: "Open detail page", onClick: () => window.__perduraSetPage && window.__perduraSetPage(dest) },
      R.createElement("div", { style: { fontSize: 11.5, color: MUTED, fontWeight: 600, marginBottom: 6 } }, label),
      R.createElement("div", { style: { fontSize: 23, fontWeight: 700, color: NAVY, letterSpacing: -0.5 } }, value),
      R.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 7, flexWrap: "wrap" } },
        prior ? R.createElement("span", { style: { fontSize: 11, color: MUTED } }, "PY " + prior) : null,
        varc && varc.pct != null ? R.createElement("span", { style: { fontSize: 11.5, fontWeight: 700, color: s.fg } }, (varc.pct >= 0 ? "▲ " : "▼ ") + pctStr(Math.abs(varc.pct), 1)) : null,
        R.createElement("span", { style: { fontSize: 9.5, fontWeight: 700, color: s.fg, background: s.bg, borderRadius: 999, padding: "2px 8px", textTransform: "uppercase", letterSpacing: 0.3 } }, s.label)),
      note ? R.createElement("div", { style: { fontSize: 10.5, color: MUTED, marginTop: 5 } }, note) : null);
  }
  function Note({ children }) {
    return R.createElement("div", { style: { fontSize: 11.5, color: MUTED, lineHeight: 1.5, border: `1px dashed ${BORDER}`, borderRadius: 8, padding: "10px 14px", background: "#FCFDFF" } }, children);
  }
  function Commentary({ children }) {
    return R.createElement("div", { style: { fontSize: 12.5, color: INK2, lineHeight: 1.65, background: ACCENT_SOFT, borderRadius: 8, padding: "12px 16px", marginTop: 14 } },
      R.createElement("span", { style: { fontWeight: 800, color: ACCENT, textTransform: "uppercase", fontSize: 10.5, letterSpacing: 0.5, marginRight: 6 } }, "CFO Commentary"), children);
  }

  // numeric table
  const tbl = { width: "100%", borderCollapse: "collapse", fontSize: 12 };
  const th = { padding: "9px 10px", background: NAVY, color: "#fff", fontWeight: 600, fontSize: 11, textAlign: "right", whiteSpace: "nowrap" };
  const thL = { ...th, textAlign: "left" };
  const td = { padding: "8px 10px", borderBottom: `1px solid #EEF1F6`, textAlign: "right", color: INK2, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" };
  const tdL = { ...td, textAlign: "left", color: INK, fontWeight: 500 };
  const zebra = (i) => ({ background: i % 2 ? "#FAFBFD" : "#fff" });

  // ── stateless helpers ─────────────────────────────────────────────────────────
  function titleCase(s) { return String(s || "").replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); }
  function agingTotal(data, side) {
    const a = data && data.aging && data.aging[side];
    if (!a || !Array.isArray(a.buckets)) return null;
    if (a.hasSubledger === false) return null;
    return a.buckets.reduce((s, b) => s + N(b.value), 0);
  }
  function subOrNull(a, b) { return (a == null || b == null) ? null : a - b; }
  function bsRow(label, value, bold) {
    return R.createElement("tr", null,
      R.createElement("td", { style: { ...tdL, fontWeight: bold ? 700 : 500, color: bold ? NAVY : INK } }, label),
      R.createElement("td", { style: { ...td, fontWeight: bold ? 700 : 500 } }, value == null ? "—" : money(value, { compact: true })));
  }
  function legendRow(color, label, val, denom) {
    return R.createElement("div", { key: label, style: { display: "flex", alignItems: "center", gap: 7 } },
      R.createElement("span", { style: { width: 10, height: 10, borderRadius: 2, background: color } }),
      R.createElement("span", { style: { fontWeight: 600 } }, label), R.createElement("span", null, money(val, { compact: true }) + " · " + pctStr(denom ? val / denom * 100 : 0, 0)));
  }
  function topTable(rows, denom) {
    const total = denom || rows.reduce((s, r) => s + r.value, 0);
    return R.createElement("table", { style: tbl }, R.createElement("tbody", null, rows.map((r, i) =>
      R.createElement("tr", { key: i, style: zebra(i) }, R.createElement("td", { style: tdL }, r.name),
        R.createElement("td", { style: td }, money(r.value, { compact: true })), R.createElement("td", { style: { ...td, color: MUTED } }, pctStr(total ? r.value / total * 100 : 0, 0))))));
  }
  function expenseTable(rows, rev) {
    return R.createElement("table", { style: tbl }, R.createElement("tbody", null, rows.map((r, i) =>
      R.createElement("tr", { key: i, style: zebra(i) }, R.createElement("td", { style: tdL }, r.name),
        R.createElement("td", { style: td }, money(r.value, { compact: true })), R.createElement("td", { style: { ...td, color: MUTED } }, pctStr(rev ? r.value / rev * 100 : 0, 1))))));
  }
  function renderConcentration(data) {
    const an = data && data.analytics;
    const top = an && (an.customers || an.topCustomers);
    if (Array.isArray(top) && top.length) {
      return topTable(top.slice(0, 5).map((c) => ({ name: c.name || c.customer_name || c.label || "(customer)", value: N(c.value || c.revenue || c.total) })), null);
    }
    return R.createElement(Note, null, "Customer concentration requires customer-level revenue (sales subledger). Connect a sales feed to rank top customers and concentration.");
  }
  function renderAttention(attention, max) {
    if (attention == null) return R.createElement("div", { style: { fontSize: 12, color: MUTED } }, "Loading intelligence…");
    const items = (attention.prioritized || attention.items || attention.signals || []).slice(0, max || 3);
    if (attention.error || !items.length) return R.createElement(Note, null, "No board-level items flagged by the intelligence layer for this period, or the synthesis surface is not yet populated for this company.");
    return R.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, items.map((it, i) => {
      const sev = (it.severity || "info").toLowerCase();
      const col = sev === "high" ? RED : sev === "medium" ? AMBER : ACCENT;
      return R.createElement(Card, { key: i, style: { padding: "12px 14px", borderLeft: `3px solid ${col}` } },
        R.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: NAVY } }, it.headline || it.title || "Item"),
        it.supporting_detail || it.detail ? R.createElement("div", { style: { fontSize: 12, color: INK2, marginTop: 3, lineHeight: 1.5 } }, it.supporting_detail || it.detail) : null,
        it.estimated_dollar_impact != null ? R.createElement("div", { style: { fontSize: 11.5, color: col, fontWeight: 600, marginTop: 4 } }, "Impact: " + money(it.estimated_dollar_impact, { compact: true })) : null);
    }));
  }
  function renderRecommendations(attention) {
    const items = attention && !attention.error ? (attention.prioritized || attention.items || []).slice(0, 5) : [];
    if (!items.length) return R.createElement(Note, null, "Prioritized recommendations populate from the intelligence layer (what-matters-now synthesis). None are available for this company/period yet — the financial sections above still stand on their own.");
    return R.createElement(Card, { style: { padding: 0, overflow: "hidden" } }, R.createElement("table", { style: tbl },
      R.createElement("thead", null, R.createElement("tr", null,
        R.createElement("th", { style: thL }, "Issue"), R.createElement("th", { style: th }, "Impact"), R.createElement("th", { style: thL }, "Recommended action"), R.createElement("th", { style: thL }, "Timeline"))),
      R.createElement("tbody", null, items.map((it, i) => R.createElement("tr", { key: i, style: zebra(i) },
        R.createElement("td", { style: tdL }, it.headline || it.title || "—"),
        R.createElement("td", { style: { ...td, color: NAVY, fontWeight: 600 } }, it.estimated_dollar_impact != null ? money(it.estimated_dollar_impact, { compact: true }) : "—"),
        R.createElement("td", { style: tdL }, it.recommended_action || it.action || "Review with management"),
        R.createElement("td", { style: tdL }, it.timeline || ((it.severity || "").toLowerCase() === "high" ? "This quarter" : "Next quarter")))))));
  }

  // ── Print-instructions modal ────────────────────────────────────────────────
  // Shown before opening the native print dialog. Explains the per-browser
  // "Save as PDF" step so a non-technical owner isn't lost in the print sheet.
  // Exposed on window so other pages (e.g. the CIM builder) can reuse it.
  function PrintInstructionsModal(p) {
    const accent = p.accent || "#0E7C7B";
    const line = (t) => R.createElement("div", { style: { display: "flex", gap: 8, marginTop: 6 } },
      R.createElement("span", { style: { color: accent, fontWeight: 700 } }, "•"), R.createElement("span", null, t));
    return R.createElement("div", { className: "no-print", style: { position: "fixed", inset: 0, background: "rgba(0,0,0,.5)", zIndex: 100000, display: "flex", alignItems: "center", justifyContent: "center", padding: 20 }, onClick: p.onClose },
      R.createElement("div", { onClick: (e) => e.stopPropagation(), style: { background: "#fff", borderRadius: 16, padding: 32, width: 440, maxWidth: "92vw", boxShadow: "0 24px 64px rgba(0,0,0,.3)" } },
        R.createElement("div", { style: { fontSize: 18, fontWeight: 800, color: "#0d2040", marginBottom: 8 } }, "Export " + (p.docType || "Document") + " as PDF"),
        R.createElement("div", { style: { fontSize: 13, color: "#6475a0", marginBottom: 14, lineHeight: 1.6 } }, "Your browser will open the print dialog. To save as a PDF instead of printing:"),
        R.createElement("div", { style: { fontSize: 12.5, color: "#1a2540", lineHeight: 1.5, background: "#f7f8fb", border: "1px solid #e9ecef", borderRadius: 10, padding: "12px 14px", marginBottom: 20 } },
          line("Chrome / Edge — choose “Save as PDF” in the Destination dropdown"),
          line("Safari — click the “PDF” button at the bottom-left, then “Save as PDF”"),
          line("Firefox — choose “Microsoft Print to PDF” (or “Save to PDF”)")),
        R.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } },
          R.createElement("button", { onClick: p.onClose, style: { padding: "10px 20px", border: "1px solid #e9ecef", borderRadius: 8, background: "#fff", cursor: "pointer", fontSize: 13, fontWeight: 600 } }, "Cancel"),
          R.createElement("button", { onClick: p.onPrint, style: { padding: "10px 20px", background: accent, color: "#fff", border: "none", borderRadius: 8, cursor: "pointer", fontWeight: 700, fontSize: 13 } }, "Open Print Dialog →"))));
  }
  if (typeof window !== "undefined" && !window.PrintInstructionsModal) window.PrintInstructionsModal = PrintInstructionsModal;

  // ── the page ──────────────────────────────────────────────────────────────────
  function BoardReportsPage(props) {
    const profile = props.companyProfile || {};
    const data = props.data || {};
    const companyId = props.scopedCompanyId;
    const [attention, setAttention] = useState(null); // synthesis what_matters_now
    const [benchmarks, setBenchmarks] = useState({}); // kpi_key -> {benchmark_p50, direction}
    // Editable CFO commentary + manual action items, persisted per company so a
    // CFO can refine the AI-drafted narrative and track follow-ups across sessions.
    const lsKey = (s) => `perdura_board_${companyId || "anon"}_${s}`;
    const [commentary, setCommentary] = useState(() => { try { return localStorage.getItem(lsKey("commentary")) || ""; } catch (_) { return ""; } });
    const [actionItems, setActionItems] = useState(() => {
      try { const raw = localStorage.getItem(lsKey("actions")); const a = raw ? JSON.parse(raw) : null; return Array.isArray(a) && a.length ? a : [{ item: "", owner: "", due: "", status: "Open" }]; }
      catch (_) { return [{ item: "", owner: "", due: "", status: "Open" }]; }
    });
    const [generating, setGenerating] = useState(false);
    const [showPrintModal, setShowPrintModal] = useState(false);
    useEffect(() => { try { localStorage.setItem(lsKey("commentary"), commentary); } catch (_) {} }, [commentary, companyId]);
    useEffect(() => { try { localStorage.setItem(lsKey("actions"), JSON.stringify(actionItems)); } catch (_) {} }, [actionItems, companyId]);

    const flags = (window.PerduraProfileEngine && window.PerduraProfileEngine.flags) ? window.PerduraProfileEngine.flags(profile) : { revenueLabel: "Revenue", archetype: "generic" };
    const isNonprofit = flags.archetype === "nonprofit";
    const surplusLabel = isNonprofit ? "Operating Surplus" : "EBITDA";

    useEffect(() => {
      let cancelled = false;
      if (window.PerduraSynthesis && companyId) {
        window.PerduraSynthesis.fetch(companyId, "what_matters_now", {}).then((res) => { if (!cancelled) setAttention(res || { error: true }); }).catch(() => { if (!cancelled) setAttention({ error: true }); });
      } else setAttention({ error: true });
      const db = window.supabaseClient;
      if (db && profile.archetype) {
        db.from("kpi_library").select("kpi_key,kpi_name,benchmark_p50,direction,unit").eq("archetype", profile.archetype).is("industry", null).then(({ data: rows }) => {
          if (cancelled || !rows) return;
          const m = {}; rows.forEach((r) => { m[r.kpi_key] = r; }); setBenchmarks(m);
        }, () => {});
      }
      return () => { cancelled = true; };
    }, [companyId, profile.archetype]);

    // ── financial model (TTM vs prior TTM, YTD, monthly) ──────────────────────
    const M = useMemo(() => {
      const plh = data.plHistory || data.pl || {};
      const rev = arrOf(plh.revenue), cogs = arrOf(plh.cogs), opex = arrOf(plh.opex), gp = arrOf(plh.gp), eb = arrOf(plh.ebitda);
      const labels = plh.labels || [], years = plh.years || [];
      const L = rev.length;
      const has24 = L >= 24;
      const ttm = (a) => ({ cur: sumRange(a, L - 12, L), prior: has24 ? sumRange(a, L - 24, L - 12) : null });
      const anchorLabel = labels[L - 1] || "";
      const anchorYear = years[L - 1] || null;
      const anchorMonthNum = (MONTHS.indexOf(anchorLabel) + 1) || 12;
      const fyStart = N(profile.fiscal_year_start_month) || 1;
      const ytdN = ((anchorMonthNum - fyStart + 12) % 12) + 1;
      const ytd = (a) => ({ cur: sumRange(a, L - ytdN, L), prior: (L >= 12 + ytdN) ? sumRange(a, L - 12 - ytdN, L - 12) : null });
      const last12 = (a) => a.slice(-12);
      const prior12 = (a) => has24 ? a.slice(-24, -12) : null;
      const gmSeries = last12(rev).map((r, i) => r ? last12(gp)[i] / r * 100 : 0);
      const ebmSeries = last12(rev).map((r, i) => r ? last12(eb)[i] / r * 100 : 0);
      const rTTM = ttm(rev), cTTM = ttm(cogs), oTTM = ttm(opex), gTTM = ttm(gp), eTTM = ttm(eb);
      const monthLabels = last12(labels).map((l) => MONTH_ABBR[MONTHS.indexOf(l)] || l);
      const r12 = last12(rev);
      const mom = r12.length >= 2 && r12[r12.length - 2] ? (r12[r12.length - 1] - r12[r12.length - 2]) / r12[r12.length - 2] * 100 : null;
      const yoy = rTTM.prior ? (rTTM.cur - rTTM.prior) / rTTM.prior * 100 : null;
      const last3 = sumRange(rev, L - 3, L), prev3 = sumRange(rev, L - 6, L - 3);
      const trend3 = prev3 ? (last3 - prev3) / prev3 * 100 : null;
      return { rev, cogs, opex, gp, eb, labels, years, L, has24, anchorLabel, anchorYear, anchorMonthNum, ytdN,
        rTTM, cTTM, oTTM, gTTM, eTTM, rYTD: ytd(rev), gYTD: ytd(gp), eYTD: ytd(eb),
        last12, prior12, gmSeries, ebmSeries, monthLabels, mom, yoy, trend3 };
    }, [data, profile.fiscal_year_start_month]);

    const periodLabel = M.anchorLabel ? `Trailing 12 Months ended ${MONTH_ABBR[MONTHS.indexOf(M.anchorLabel)] || M.anchorLabel} ${M.anchorYear || ""}` : "Trailing 12 Months";
    const today = new Date();
    const fmtDate = (d) => `${MONTH_ABBR[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
    const nextDue = new Date(today.getFullYear(), today.getMonth() + 3, 1);

    const gmCur = M.rTTM.cur ? M.gTTM.cur / M.rTTM.cur * 100 : null;
    const gmPrior = M.rTTM.prior ? M.gTTM.prior / M.rTTM.prior * 100 : null;
    const ebmCur = M.rTTM.cur ? M.eTTM.cur / M.rTTM.cur * 100 : null;
    const cash = data.cash || {};
    const cashNow = isFinite(cash.startCash) ? cash.startCash : (isFinite(cash.endCash) ? cash.endCash : null);

    // ── AI commentary (real metrics + synthesis) and YTD budget helpers ────────
    // "Generate" composes 3–5 plain-English bullets from the period's actual
    // numbers plus the cfo-synthesis "what matters now" surface, then drops them
    // into the editable textarea for the CFO to refine before printing.
    function buildBullets(attn) {
      const b = [];
      if (M.yoy != null) b.push(`• Revenue ${M.yoy >= 0 ? "grew" : "declined"} ${pctStr(Math.abs(M.yoy), 1)} YoY to ${money(M.rTTM.cur)} (trailing 12 months).`);
      if (gmCur != null) b.push(`• Gross margin ${gmPrior != null ? (gmCur >= gmPrior ? "expanded to" : "compressed to") : "stands at"} ${pctStr(gmCur)}${gmPrior != null ? ` (from ${pctStr(gmPrior)} prior year)` : ""}.`);
      if (ebmCur != null) b.push(`• ${surplusLabel} margin is ${pctStr(ebmCur)} on ${money(M.rTTM.cur, { compact: true })} of ${(flags.revenueLabel || "revenue").toLowerCase()}.`);
      if (cashNow != null) b.push(`• Cash position of ${money(cashNow)}${cash.runwayDays != null ? ` — roughly ${Math.round(cash.runwayDays)} days of runway` : ""}.`);
      const items = attn && !attn.error ? (attn.prioritized || attn.items || attn.signals || []) : [];
      items.slice(0, 2).forEach((it) => { const h = it.headline || it.title; if (h) b.push(`• ${h}${it.estimated_dollar_impact != null ? ` (${money(it.estimated_dollar_impact, { compact: true })} estimated impact)` : ""}.`); });
      return b.join("\n");
    }
    async function generateCommentary() {
      setGenerating(true);
      try {
        let attn = attention;
        if (window.PerduraSynthesis && companyId) {
          const res = await window.PerduraSynthesis.fetch(companyId, "what_matters_now", {}).catch(() => null);
          if (res && !res.error) { attn = res; setAttention(res); }
        }
        setCommentary(buildBullets(attn));
      } finally { setGenerating(false); }
    }

    // Real YTD opex (EBITDA = GP − opex, so opex = GP − EBITDA). Budget is read
    // defensively from data.budget; when absent/unrecognized we show "—" rather
    // than fabricate a target.
    const opexYTD = { cur: subOrNull(M.gYTD.cur, M.eYTD.cur), prior: subOrNull(M.gYTD.prior, M.eYTD.prior) };
    function ytdBudget(key) {
      const bg = data.budget;
      if (!bg || Array.isArray(bg)) return null;
      const rec = bg[key];
      if (rec == null) return null;
      const v = typeof rec === "number" ? rec : N(rec.ytd != null ? rec.ytd : rec.total);
      return (v == null || !isFinite(v) || v === 0) ? null : v;
    }

    const breakdown = useMemo(() => {
      const txns = Array.isArray(data.txns) ? data.txns : [];
      const rev = {}, cogs = {}, opex = {};
      for (const t of txns) {
        const b = bucketFor(t.canonical_category, profile);
        const amt = Math.abs(N(t.amount));
        const key = t.canonical_category || t.account_name || "(uncategorized)";
        if (b === "revenue") rev[key] = (rev[key] || 0) + amt;
        else if (b === "cogs") cogs[key] = (cogs[key] || 0) + amt;
        else if (b === "opex") opex[key] = (opex[key] || 0) + amt;
      }
      const rank = (o) => Object.entries(o).map(([k, v]) => ({ name: k, value: v })).sort((a, b) => b.value - a.value);
      return { rev: rank(rev), cogs: rank(cogs), opex: rank(opex), hasTxns: txns.length > 0 };
    }, [data.txns, profile]);

    const dsoOf = () => { const ar = agingTotal(data, "ar"); return (ar != null && M.rTTM.cur) ? ar / M.rTTM.cur * 365 : null; };
    const dpoOf = () => { const ap = agingTotal(data, "ap"); return (ap != null && M.cTTM.cur) ? ap / M.cTTM.cur * 365 : null; };

    function mkKpi(label, value, benchKey, unit, spark) {
      const b = benchKey && benchmarks[benchKey];
      const p50 = b ? N(b.benchmark_p50) : null;
      const dir = b ? b.direction : "higher_is_better";
      let status = "neutral";
      if (value != null && p50 != null && isFinite(value)) {
        if (dir === "lower_is_better") status = value <= p50 ? "green" : value <= p50 * 1.2 ? "amber" : "red";
        else status = value >= p50 ? "green" : value >= p50 * 0.8 ? "amber" : "red";
      }
      const arrow = spark && spark.length >= 2 ? (spark[spark.length - 1] >= spark[spark.length - 2] ? "up" : "down") : "flat";
      const fmtVal = unit === "percent" ? pctStr(value) : unit === "days" ? (value != null && isFinite(value) ? Math.round(value) + " d" : "—") : money(value, { compact: true });
      const fmtBench = p50 == null ? "—" : unit === "percent" ? pctStr(p50, 0) : unit === "days" ? Math.round(p50) + " d" : money(p50, { compact: true });
      return { label, valueStr: fmtVal, benchStr: fmtBench, status, arrow, spark };
    }

    function renderScorecard() {
      const groups = [
        { name: "Profitability", rows: [
          mkKpi("Gross Margin %", gmCur, "gross_margin_pct", "percent", M.gmSeries),
          mkKpi(surplusLabel + " Margin %", ebmCur, "ebitda_margin", "percent", M.ebmSeries),
          mkKpi("Net Margin %", ebmCur, "net_margin", "percent", M.ebmSeries),
        ] },
        { name: "Liquidity", rows: [
          mkKpi("Days of Cash", cash.runwayDays, "days_of_cash", "days", null),
          mkKpi("Cash on Hand", cashNow, null, "currency", null),
        ] },
        { name: "Efficiency", rows: [
          mkKpi("DSO", dsoOf(), "dso", "days", null),
          mkKpi("DPO", dpoOf(), "days_payable_outstanding", "days", null),
        ] },
        { name: "Growth", rows: [
          mkKpi("Revenue YoY %", M.yoy, null, "percent", null),
          mkKpi("Revenue 3-mo Trend %", M.trend3, null, "percent", null),
        ] },
      ];
      return R.createElement(Card, { style: { padding: 0, overflow: "hidden" } }, R.createElement("div", { style: { overflowX: "auto" } },
        R.createElement("table", { style: tbl },
          R.createElement("thead", null, R.createElement("tr", null,
            R.createElement("th", { style: thL }, "KPI"), R.createElement("th", { style: th }, "Current"), R.createElement("th", { style: th }, "Benchmark"), R.createElement("th", { style: { ...th, textAlign: "center" } }, "Status"), R.createElement("th", { style: { ...th, textAlign: "center" } }, "Trend"), R.createElement("th", { style: { ...th, textAlign: "center" } }, "12-mo"))),
          R.createElement("tbody", null, groups.flatMap((g, gi) => [
            R.createElement("tr", { key: "g" + gi }, R.createElement("td", { colSpan: 6, style: { padding: "7px 10px", background: "#F1F5F9", fontSize: 10.5, fontWeight: 700, color: NAVY, textTransform: "uppercase", letterSpacing: 0.5 } }, g.name)),
            ...g.rows.map((r, ri) => R.createElement("tr", { key: gi + "-" + ri, style: zebra(ri) },
              R.createElement("td", { style: tdL }, r.label),
              R.createElement("td", { style: { ...td, fontWeight: 600, color: NAVY } }, r.valueStr),
              R.createElement("td", { style: td }, r.benchStr),
              R.createElement("td", { style: { ...td, textAlign: "center" } }, R.createElement("span", { title: STAT[r.status].label }, r.status === "green" ? "🟢" : r.status === "amber" ? "🟡" : r.status === "red" ? "🔴" : "⚪")),
              R.createElement("td", { style: { ...td, textAlign: "center", color: r.arrow === "up" ? GREEN : r.arrow === "down" ? RED : MUTED, fontWeight: 700 } }, r.arrow === "up" ? "▲" : r.arrow === "down" ? "▼" : "—"),
              R.createElement("td", { style: { ...td, textAlign: "center" } }, r.spark ? R.createElement(Sparkline, { data: r.spark, color: r.status === "red" ? RED : ACCENT }) : "—"))),
          ])))));
    }

    // ── PRINT CSS ──────────────────────────────────────────────────────────────
    // NOTE: the previous version used `body * { visibility: hidden }` +
    // `.board-root { position: absolute; width: 100% }` to isolate the report for
    // print. That collapsed the report into a narrow column because the absolute
    // box resolved its width against a narrow offset container. Replaced with a
    // robust approach: hide the app chrome (the shell already does this for
    // .pc-sidebar/.pc-topbar in @media print) and the toolbar, then let the report
    // flow as a normal FULL-WIDTH block. No absolute positioning, no visibility
    // hack — so the page no longer collapses to a sidebar-width column.
    const printCss = `
      .board-print-footer { display: none; }
      @media print {
        @page { size: Letter; margin: 14mm 15mm; }
        html, body { background: #fff !important; }
        .pc-sidebar, .pc-topbar, .no-print, .board-no-print { display: none !important; }
        .pc-app, .pc-main, .pc-content { display: block !important; margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; }
        /* neutralize the on-screen fixed overlay so print flows normally full-width */
        .board-overlay { position: static !important; overflow: visible !important; inset: auto !important; top: auto !important; left: auto !important; width: auto !important; height: auto !important; z-index: auto !important; }
        .board-report-wrapper, .board-root {
          width: 100% !important; max-width: 100% !important; margin: 0 !important; padding: 0 !important;
          background: #fff !important; -webkit-print-color-adjust: exact; print-color-adjust: exact;
        }
        .board-card { box-shadow: none !important; border: 1px solid #D6DCE6 !important; }
        .board-commentary, .board-section input, .board-section select { border: none !important; background: transparent !important; -webkit-appearance: none; appearance: none; resize: none !important; color: #1A2233 !important; }
        .board-section { break-inside: avoid; page-break-inside: avoid; }
        .board-pagebreak, .page-break { break-before: page; page-break-before: always; }
        .board-cover { break-after: page; page-break-after: always; }
        .board-print-footer { display: block; position: fixed; bottom: 6mm; left: 0; right: 0; font-size: 9px; color: #6B7A99; text-align: center; }
      }`;
    const doPrint = () => window.print();
    const goBack = () => { if (props.setPage) props.setPage("health"); };

    // Brief pre-print modal explaining how to "Save as PDF" in each browser. Shown
    // when the user clicks Print; confirming opens the native print dialog.
    const printModal = showPrintModal ? R.createElement(window.PrintInstructionsModal || PrintInstructionsModal, {
      docType: "Board Report", accent: ACCENT,
      onClose: () => setShowPrintModal(false),
      onPrint: () => { setShowPrintModal(false); setTimeout(() => window.print(), 60); },
    }) : null;

    // ── render ───────────────────────────────────────────────────────────────
    // Fixed full-screen white overlay (escapes the app shell / sidebar entirely),
    // with the document centered inside. Print CSS neutralizes the fixed overlay.
    return R.createElement("div", { className: "board-overlay", style: { position: "fixed", top: 0, left: 0, width: "100vw", height: "100vh", overflowY: "auto", background: "white", zIndex: 99999, fontFamily: "Inter, system-ui, sans-serif", color: "#0F1520", boxSizing: "border-box" } },
      R.createElement("style", null, printCss),
      R.createElement("div", { className: "board-root board-report-wrapper", style: { width: "100%", maxWidth: 1000, margin: "0 auto", padding: "24px 40px 60px", boxSizing: "border-box", fontFamily: "Inter, system-ui, sans-serif" } },
      R.createElement("div", { className: "board-no-print no-print", style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10, marginBottom: 14 } },
        R.createElement("button", { onClick: goBack, style: { background: "transparent", color: INK2, border: "1px solid " + BORDER, borderRadius: 7, padding: "9px 16px", fontWeight: 600, fontSize: 13.5, cursor: "pointer" } }, "←  Back to dashboard"),
        R.createElement("div", { style: { display: "flex", alignItems: "center", gap: 12 } },
          R.createElement("span", { style: { fontSize: 10.5, color: "#A8B4CC", fontFamily: "ui-monospace, monospace" } }, "build 20260610-fullscreen"),
          R.createElement("button", { onClick: generateCommentary, disabled: generating, style: { background: "#0E7C7B", color: "#fff", border: "none", borderRadius: 7, padding: "9px 18px", fontWeight: 600, fontSize: 13.5, cursor: generating ? "default" : "pointer", opacity: generating ? 0.7 : 1 } }, generating ? "Generating…" : "✨ Generate Commentary"),
          R.createElement("button", { onClick: () => setShowPrintModal(true), style: { background: ACCENT, color: "#fff", border: "none", borderRadius: 7, padding: "9px 18px", fontWeight: 600, fontSize: 13.5, cursor: "pointer" } }, "🖨  Print / Export PDF"))),
      printModal,

      // COVER
      R.createElement("div", { className: "board-cover", style: { background: NAVY, color: "#fff", borderRadius: 12, padding: "56px 48px", marginBottom: 34, minHeight: 360, display: "flex", flexDirection: "column", justifyContent: "space-between" } },
        R.createElement("div", null,
          R.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, marginBottom: 40 } },
            R.createElement("div", { style: { width: 46, height: 46, borderRadius: 10, background: ACCENT, display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 800, fontSize: 20 } }, (profile.name || "C").slice(0, 1).toUpperCase()),
            R.createElement("div", { style: { fontSize: 15, fontWeight: 600, letterSpacing: 0.5, opacity: 0.85 } }, "PerduraCFO")),
          R.createElement("div", { style: { fontSize: 13, fontWeight: 600, color: "#8FB3FF", letterSpacing: 1.5, textTransform: "uppercase", marginBottom: 10 } }, "Board Financial Report"),
          R.createElement("div", { style: { fontSize: 40, fontWeight: 800, letterSpacing: -1, lineHeight: 1.1 } }, profile.name || "Company"),
          R.createElement("div", { style: { fontSize: 17, color: "#C7D5F0", marginTop: 10 } }, periodLabel)),
        R.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", flexWrap: "wrap", gap: 16, borderTop: "1px solid rgba(255,255,255,0.16)", paddingTop: 20 } },
          R.createElement("div", null,
            R.createElement("div", { style: { fontSize: 11, opacity: 0.7 } }, "Prepared by"), R.createElement("div", { style: { fontSize: 14, fontWeight: 600 } }, "PerduraCFO"),
            R.createElement("div", { style: { fontSize: 11, opacity: 0.7, marginTop: 10 } }, "Date"), R.createElement("div", { style: { fontSize: 14, fontWeight: 600 } }, fmtDate(today))),
          (profile.business_type || profile.industry) ? R.createElement("div", { style: { background: "rgba(255,255,255,0.12)", borderRadius: 999, padding: "7px 16px", fontSize: 13, fontWeight: 600 } },
            [titleCase(profile.business_type || flags.archetype), profile.industry].filter(Boolean).join(" — ")) : null)),

      // SECTION 1 — Executive Summary
      R.createElement(Section, { n: 1, title: "Executive Summary", sub: periodLabel + " vs. prior year" },
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12, marginBottom: 16 } },
          R.createElement(KpiTile, { label: flags.revenueLabel || "Revenue", value: money(M.rTTM.cur, { compact: true }), prior: M.rTTM.prior != null ? money(M.rTTM.prior, { compact: true }) : null, varc: variance(M.rTTM.cur, M.rTTM.prior), status: statusUp(M.yoy) }),
          R.createElement(KpiTile, { label: "Gross Margin %", value: pctStr(gmCur), prior: gmPrior != null ? pctStr(gmPrior) : null, varc: gmPrior != null ? { pct: gmCur - gmPrior } : null, status: statusUp(gmPrior != null ? gmCur - gmPrior : null) }),
          R.createElement(KpiTile, { label: surplusLabel, value: money(M.eTTM.cur, { compact: true }), prior: M.eTTM.prior != null ? money(M.eTTM.prior, { compact: true }) : null, varc: variance(M.eTTM.cur, M.eTTM.prior), status: statusUp(variance(M.eTTM.cur, M.eTTM.prior).pct) }),
          R.createElement(KpiTile, { label: "Cash Position", value: money(cashNow, { compact: true }), status: cash.runwayDays != null ? (cash.runwayDays > 180 ? "green" : cash.runwayDays > 90 ? "amber" : "red") : "neutral", note: cash.runwayDays != null ? Math.round(cash.runwayDays) + " days runway" : null })),
        R.createElement(Card, null,
          R.createElement("div", { style: { fontSize: 13.5, color: INK, lineHeight: 1.7 } },
            `This period ${profile.name || "the company"} generated ${money(M.rTTM.cur)} in ${(flags.revenueLabel || "revenue").toLowerCase()}, `,
            M.yoy != null ? `${M.yoy >= 0 ? "up" : "down"} ${pctStr(Math.abs(M.yoy), 1)} versus the prior year. ` : "with prior-year comparison pending sufficient history. ",
            `${surplusLabel} of ${money(M.eTTM.cur)} represents a ${pctStr(ebmCur)} margin, `,
            gmPrior != null ? `and gross margin ${gmCur >= gmPrior ? "improved" : "softened"} to ${pctStr(gmCur)}. ` : `with gross margin at ${pctStr(gmCur)}. `,
            cash.runwayDays != null ? `Cash stands at ${money(cashNow)} (${Math.round(cash.runwayDays)} days runway); ${cash.runwayDays > 180 ? "liquidity is comfortable heading into next period." : "management is monitoring liquidity closely."}` : "")),
        R.createElement("div", { style: { marginTop: 16 } },
          R.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Top items requiring board attention"),
          renderAttention(attention, 3))),

      // SECTION 2 — Income Statement Summary
      R.createElement(Section, { n: 2, title: "Income Statement Summary", brk: true },
        R.createElement(Card, { style: { padding: 0, overflow: "hidden" } }, R.createElement("div", { style: { overflowX: "auto" } },
          R.createElement("table", { style: tbl },
            R.createElement("thead", null, R.createElement("tr", null,
              R.createElement("th", { style: thL }, "Line"), R.createElement("th", { style: th }, "Current"), R.createElement("th", { style: th }, "Prior Yr"), R.createElement("th", { style: th }, "Var $"), R.createElement("th", { style: th }, "Var %"), R.createElement("th", { style: th }, "YTD"), R.createElement("th", { style: th }, "Prior YTD"), R.createElement("th", { style: th }, "YTD Var %"))),
            R.createElement("tbody", null, [
              { l: flags.revenueLabel || "Revenue", t: M.rTTM, y: M.rYTD, bold: true },
              { l: "Gross Profit", t: M.gTTM, y: M.gYTD },
              { l: "Gross Margin %", pctRow: true, cur: gmCur, prior: gmPrior },
              { l: surplusLabel, t: M.eTTM, y: M.eYTD, bold: true },
              { l: surplusLabel + " Margin %", pctRow: true, cur: ebmCur, prior: M.rTTM.prior ? M.eTTM.prior / M.rTTM.prior * 100 : null },
              { l: "Net Income*", t: M.eTTM, y: M.eYTD },
            ].map((row, i) => {
              if (row.pctRow) { const v = row.prior != null ? row.cur - row.prior : null; return R.createElement("tr", { key: i, style: zebra(i) },
                R.createElement("td", { style: tdL }, row.l), R.createElement("td", { style: td }, pctStr(row.cur)), R.createElement("td", { style: td }, pctStr(row.prior)),
                R.createElement("td", { style: td }, v != null ? (v >= 0 ? "+" : "") + pctStr(v) + "pp" : "—"), R.createElement("td", { style: td }, "—"), R.createElement("td", { style: td }, "—"), R.createElement("td", { style: td }, "—"), R.createElement("td", { style: td }, "—")); }
              const vc = variance(row.t.cur, row.t.prior); const yv = variance(row.y.cur, row.y.prior);
              const cellL = { ...tdL, fontWeight: row.bold ? 700 : 500, color: row.bold ? NAVY : INK };
              return R.createElement("tr", { key: i, style: zebra(i) },
                R.createElement("td", { style: cellL }, row.l),
                R.createElement("td", { style: { ...td, fontWeight: row.bold ? 700 : 500 } }, money(row.t.cur, { compact: true })),
                R.createElement("td", { style: td }, row.t.prior != null ? money(row.t.prior, { compact: true }) : "—"),
                R.createElement("td", { style: { ...td, color: vc.abs == null ? INK2 : vc.abs >= 0 ? GREEN : RED } }, vc.abs != null ? money(vc.abs, { compact: true }) : "—"),
                R.createElement("td", { style: { ...td, color: vc.pct == null ? INK2 : vc.pct >= 0 ? GREEN : RED } }, vc.pct != null ? pctStr(vc.pct) : "—"),
                R.createElement("td", { style: td }, money(row.y.cur, { compact: true })),
                R.createElement("td", { style: td }, row.y.prior != null ? money(row.y.prior, { compact: true }) : "—"),
                R.createElement("td", { style: { ...td, color: yv.pct == null ? INK2 : yv.pct >= 0 ? GREEN : RED } }, yv.pct != null ? pctStr(yv.pct) : "—"));
            })))),
          R.createElement("div", { style: { fontSize: 10, color: MUTED, padding: "6px 12px" } }, "*Net income equals operating surplus; interest, tax and depreciation are not separately captured in the source ledger.")),
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginTop: 16 } },
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Revenue → Net Income waterfall"),
            R.createElement(Waterfall, { steps: [
              { label: "Revenue", value: M.rTTM.cur, total: true },
              { label: "− COGS", value: -M.cTTM.cur },
              { label: "Gross Profit", value: M.gTTM.cur, total: true },
              { label: "− Opex", value: -M.oTTM.cur },
              { label: surplusLabel, value: M.eTTM.cur, total: true },
            ] })),
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Gross margin % — trailing 12 months"),
            R.createElement(LineTrend, { series: [{ data: M.gmSeries, color: ACCENT }], labels: M.monthLabels, fmt: (v) => pctStr(v, 0) }),
            R.createElement("div", { style: { fontSize: 10, color: MUTED, marginTop: 4 } }, "Source: monthly GL roll-up"))),
        R.createElement(Commentary, null,
          `${surplusLabel} margin of ${pctStr(ebmCur)} on ${money(M.rTTM.cur, { compact: true })} of ${(flags.revenueLabel || "revenue").toLowerCase()}. `,
          gmPrior != null ? `Gross margin ${gmCur >= gmPrior ? "expanded" : "compressed"} ${pctStr(Math.abs(gmCur - gmPrior), 1)}pp year-over-year, ${gmCur >= gmPrior ? "reflecting pricing or mix gains." : "warranting a look at input costs and pricing."}` : "Prior-year margin comparison will populate once 24 months of history is available.")),

      // SECTION 3 — Revenue Analysis
      R.createElement(Section, { n: 3, title: "Revenue Analysis", brk: true },
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, marginBottom: 16 } },
          R.createElement(KpiTile, { label: "Revenue YoY", value: M.yoy != null ? pctStr(M.yoy) : "—", status: statusUp(M.yoy) }),
          R.createElement(KpiTile, { label: "Revenue MoM", value: M.mom != null ? pctStr(M.mom) : "—", status: statusUp(M.mom) }),
          R.createElement(KpiTile, { label: "3-Month Trend", value: M.trend3 != null ? pctStr(M.trend3) : "—", status: statusUp(M.trend3) })),
        R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Monthly revenue — current vs prior year"),
          R.createElement(LineTrend, { series: [
            { data: M.last12(M.rev), color: ACCENT },
            M.has24 ? { data: M.prior12(M.rev), color: SLATE, dashed: true } : null,
          ].filter(Boolean), labels: M.monthLabels }),
          R.createElement("div", { style: { display: "flex", gap: 16, fontSize: 11, color: MUTED, marginTop: 6 } },
            R.createElement("span", null, R.createElement("span", { style: { color: ACCENT, fontWeight: 700 } }, "—"), " Current year"),
            M.has24 ? R.createElement("span", null, R.createElement("span", { style: { color: SLATE, fontWeight: 700 } }, "– –"), " Prior year") : null)),
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginTop: 16 } },
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 10 } }, "Top revenue sources"),
            breakdown.rev.length ? topTable(breakdown.rev.slice(0, 5), M.rTTM.cur) : R.createElement(Note, null, "Awaiting categorized revenue transactions to break down revenue by stream.")),
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 10 } }, "Customer concentration"),
            renderConcentration(data))),
        R.createElement(Commentary, null,
          M.yoy != null ? `Revenue is ${M.yoy >= 0 ? "growing" : "contracting"} ${pctStr(Math.abs(M.yoy), 1)} year-over-year with a ${M.trend3 != null && M.trend3 >= 0 ? "positive" : "negative"} 3-month trend. ` : "",
          breakdown.rev.length ? `The top source represents ${pctStr(breakdown.rev[0].value / (breakdown.rev.reduce((s, x) => s + x.value, 0) || 1) * 100, 0)} of categorized revenue.` : "Revenue stream detail unlocks with categorized GL data.")),

      // SECTION 4 — Cost & Expense Analysis
      R.createElement(Section, { n: 4, title: "Cost & Expense Analysis", brk: true },
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.3fr 1fr", gap: 16 } },
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 10 } }, "Operating expenses — top 10 (% of revenue)"),
            breakdown.opex.length ? expenseTable(breakdown.opex.slice(0, 10), M.rTTM.cur) : R.createElement(Note, null, "Awaiting categorized expense transactions.")),
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 10 } }, "Expenditure mix"),
            (breakdown.cogs.length || breakdown.opex.length || M.cTTM.cur || M.oTTM.cur) ? R.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 10 } },
              R.createElement(Donut, { slices: [{ label: "COGS", value: M.cTTM.cur, color: ACCENT }, { label: "Opex", value: M.oTTM.cur, color: AMBER }, { label: surplusLabel, value: Math.max(0, M.eTTM.cur), color: GREEN }] }),
              R.createElement("div", { style: { fontSize: 11.5, color: INK2, lineHeight: 1.8 } },
                legendRow(ACCENT, "COGS", M.cTTM.cur, M.rTTM.cur), legendRow(AMBER, "Opex", M.oTTM.cur, M.rTTM.cur), legendRow(GREEN, surplusLabel, Math.max(0, M.eTTM.cur), M.rTTM.cur))) : R.createElement(Note, null, "Awaiting expense data."))),
        R.createElement(Card, { style: { marginTop: 16 } }, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 6 } }, "COGS as % of revenue — trailing 12 months"),
          R.createElement(LineTrend, { height: 120, series: [{ data: M.last12(M.rev).map((r, i) => r ? M.last12(M.cogs)[i] / r * 100 : 0), color: AMBER }], labels: M.monthLabels, fmt: (v) => pctStr(v, 0) })),
        R.createElement(Commentary, null,
          `COGS ran ${pctStr(M.rTTM.cur ? M.cTTM.cur / M.rTTM.cur * 100 : null)} of revenue and operating expenses ${pctStr(M.rTTM.cur ? M.oTTM.cur / M.rTTM.cur * 100 : null)}. `,
          breakdown.opex.length ? `The largest expense line, ${breakdown.opex[0].name}, is ${pctStr(M.rTTM.cur ? breakdown.opex[0].value / M.rTTM.cur * 100 : null)} of revenue.` : "")),

      // SECTION 5 — Cash Flow & Liquidity
      R.createElement(Section, { n: 5, title: "Cash Flow & Liquidity", brk: true },
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, marginBottom: 16 } },
          R.createElement(KpiTile, { label: "Cash on Hand", value: money(cashNow, { compact: true }) }),
          R.createElement(KpiTile, { label: "Days of Runway", value: cash.runwayDays != null ? Math.round(cash.runwayDays) + " days" : "—", status: cash.runwayDays != null ? (cash.runwayDays > 180 ? "green" : cash.runwayDays > 90 ? "amber" : "red") : "neutral" }),
          R.createElement(KpiTile, { label: "Net Burn / Day", value: cash.burnPerDay != null ? money(cash.burnPerDay, { compact: true }) : "—", status: cash.burnPerDay == null ? "neutral" : cash.burnPerDay >= 0 ? "green" : "amber", note: cash.burnPerDay != null ? (cash.burnPerDay >= 0 ? "cash building" : "cash draining") : null })),
        R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "90-day cash projection"),
          Array.isArray(cash.proj) && cash.proj.length ? R.createElement(LineTrend, { series: [{ data: cash.proj, color: ACCENT }], labels: cash.proj.map((_, i) => i % 15 === 0 ? "D" + i : ""), benchmark: 0, benchmarkLabel: "zero cash" }) : R.createElement(Note, null, "Awaiting a cash projection — needs dated GL cash movements.")),
        R.createElement(Card, { style: { marginTop: 16 } }, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Cash flow summary"),
          R.createElement(Note, null, "Segmented operating / investing / financing cash flows and 3-month and 12-month cash positions require the cash-flow statement feed and stored period-end balances. The figures above are derived from the live cash projection and net daily burn.")),
        R.createElement(Commentary, null,
          cash.burnPerDay != null && cashNow != null ? `Cash of ${money(cashNow)} is ${cash.burnPerDay >= 0 ? "building" : "drawing down"} at roughly ${money(Math.abs(cash.burnPerDay))} per day` + (cash.runwayDays != null ? `, implying ${Math.round(cash.runwayDays)} days of runway at the current rate.` : ".") : "Cash drivers will populate once dated cash movements are available.")),

      // SECTION 6 — Balance Sheet Highlights
      R.createElement(Section, { n: 6, title: "Balance Sheet Highlights", brk: true },
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 } },
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 10 } }, "Position from available subledgers"),
            R.createElement("table", { style: tbl }, R.createElement("tbody", null,
              bsRow("Cash", cashNow), bsRow("Accounts Receivable", agingTotal(data, "ar")), bsRow("Accounts Payable", agingTotal(data, "ap")),
              bsRow("Net (AR − AP)", subOrNull(agingTotal(data, "ar"), agingTotal(data, "ap")), true)))),
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 10 } }, "Key ratios"),
            R.createElement(Note, null, "Current ratio, quick ratio and debt/equity require full current-asset and current-liability balances from the balance-sheet feed. Inventory, prepaids, debt and equity are not in the current source — connect the GL balance sheet to complete this section."))),
        R.createElement(Commentary, null,
          `Receivables of ${money(agingTotal(data, "ar"), { compact: true })} against payables of ${money(agingTotal(data, "ap"), { compact: true })} indicate a ${(agingTotal(data, "ar") || 0) >= (agingTotal(data, "ap") || 0) ? "net positive" : "net negative"} short-term working-capital position. A full balance sheet unlocks the complete ratio set.`)),

      // SECTION 7 — KPI Scorecard
      R.createElement(Section, { n: 7, title: "KPI Scorecard", sub: "Archetype-appropriate metrics vs. benchmark", brk: true },
        renderScorecard()),

      // SECTION 8 — Outlook & Recommendations
      R.createElement(Section, { n: 8, title: "Outlook & Recommendations", brk: true },
        renderRecommendations(attention),
        R.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginTop: 16 } },
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Risks to watch"),
            R.createElement("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12.5, color: INK2, lineHeight: 1.7 } },
              [].concat(
                cash.runwayDays != null && cash.runwayDays < 120 ? [R.createElement("li", { key: "r1" }, `Liquidity: ${Math.round(cash.runwayDays)} days of runway — monitor burn and collections.`)] : [],
                gmPrior != null && gmCur < gmPrior ? [R.createElement("li", { key: "r2" }, "Margin: gross margin compressed year-over-year.")] : [],
                [R.createElement("li", { key: "r3" }, agingTotal(data, "ar") ? `${money(agingTotal(data, "ar"), { compact: true })} in receivables outstanding — collection discipline protects cash.` : "Receivables data not yet connected.")]))),
          R.createElement(Card, null, R.createElement("div", { style: { fontSize: 12.5, fontWeight: 700, color: NAVY, marginBottom: 8 } }, "Opportunities to capture"),
            R.createElement("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12.5, color: INK2, lineHeight: 1.7 } },
              R.createElement("li", { key: "o1" }, M.yoy != null && M.yoy > 0 ? "Sustain top-line momentum; reinvest in the highest-return revenue streams." : "Stabilize revenue through pricing and retention initiatives."),
              R.createElement("li", { key: "o2" }, "Tighten working capital — accelerate collections and align payables to terms."),
              R.createElement("li", { key: "o3" }, "Use the KPI scorecard to set next-quarter targets against benchmark.")))),
        R.createElement("div", { style: { marginTop: 16, textAlign: "center", fontSize: 12.5, color: NAVY, fontWeight: 600, background: ACCENT_SOFT, borderRadius: 8, padding: "12px 16px" } },
          "Next board report due: " + fmtDate(nextDue))),

      // SECTION 9 — YTD Budget vs Actual
      R.createElement(Section, { n: 9, title: "YTD Budget vs Actual", sub: "Fiscal year to date", brk: true },
        R.createElement(Card, { style: { padding: 0, overflow: "hidden" } }, R.createElement("div", { style: { overflowX: "auto" } },
          R.createElement("table", { style: tbl },
            R.createElement("thead", null, R.createElement("tr", null,
              R.createElement("th", { style: thL }, "Metric"), R.createElement("th", { style: th }, "Budget"), R.createElement("th", { style: th }, "Actual"), R.createElement("th", { style: th }, "Variance"), R.createElement("th", { style: th }, "Var %"))),
            R.createElement("tbody", null, [
              { l: flags.revenueLabel || "Revenue", actual: M.rYTD.cur, bkey: "revenue", bold: true },
              { l: "Gross Profit", actual: M.gYTD.cur, bkey: "gross_profit" },
              { l: "Operating Expenses", actual: opexYTD.cur, bkey: "opex" },
              { l: "Net Income", actual: M.eYTD.cur, bkey: "net_income", bold: true },
            ].map((row, i) => {
              const bud = ytdBudget(row.bkey);
              const vAbs = (bud == null || row.actual == null) ? null : row.actual - bud;
              const vPct = (bud && vAbs != null) ? vAbs / Math.abs(bud) * 100 : null;
              const cellL = { ...tdL, fontWeight: row.bold ? 700 : 500, color: row.bold ? NAVY : INK };
              return R.createElement("tr", { key: i, style: zebra(i) },
                R.createElement("td", { style: cellL }, row.l),
                R.createElement("td", { style: td }, bud == null ? "—" : money(bud, { compact: true })),
                R.createElement("td", { style: { ...td, fontWeight: row.bold ? 700 : 500 } }, row.actual == null ? "—" : money(row.actual, { compact: true })),
                R.createElement("td", { style: { ...td, color: vAbs == null ? INK2 : vAbs >= 0 ? GREEN : RED } }, vAbs == null ? "—" : money(vAbs, { compact: true })),
                R.createElement("td", { style: { ...td, color: vPct == null ? INK2 : vPct >= 0 ? GREEN : RED } }, vPct == null ? "—" : pctStr(vPct)));
            })))),
        (data.budget && !Array.isArray(data.budget) && Object.keys(data.budget).length) ? null
          : R.createElement("div", { style: { marginTop: 10 } }, R.createElement(Note, null, "Budget columns populate once a budget is loaded for this company. Actuals above are live year-to-date figures from the general ledger.")))),

      // SECTION 10 — CFO Commentary (AI-drafted, editable before printing)
      R.createElement(Section, { n: 10, title: "CFO Commentary", sub: "AI-drafted from this period's numbers — editable before printing", brk: true },
        R.createElement(Card, null,
          R.createElement("div", { className: "board-no-print no-print", style: { display: "flex", justifyContent: "flex-end", marginBottom: 10 } },
            R.createElement("button", { onClick: generateCommentary, disabled: generating, style: { background: "#0E7C7B", color: "#fff", border: "none", borderRadius: 7, padding: "7px 14px", fontWeight: 600, fontSize: 12.5, cursor: generating ? "default" : "pointer", opacity: generating ? 0.7 : 1 } }, generating ? "Generating…" : "✨ Generate Commentary")),
          R.createElement("textarea", { className: "board-commentary", value: commentary, onChange: (e) => setCommentary(e.target.value), placeholder: "Click “Generate Commentary” to draft 3–5 plain-English bullets from this period's numbers, then edit as needed before printing…", style: { width: "100%", minHeight: 150, resize: "vertical", border: `1px solid ${BORDER}`, borderRadius: 8, padding: "12px 14px", fontSize: 13, lineHeight: 1.6, color: INK, fontFamily: "inherit", boxSizing: "border-box", background: "#fff" } }))),

      // SECTION 11 — Action Items (manual, owner-assigned)
      R.createElement(Section, { n: 11, title: "Action Items", sub: "Owner-assigned follow-ups from this board meeting", brk: true },
        R.createElement(Card, { style: { padding: 0, overflow: "hidden" } }, R.createElement("table", { style: tbl },
          R.createElement("thead", null, R.createElement("tr", null,
            R.createElement("th", { style: { ...thL, width: 34 } }, "#"), R.createElement("th", { style: thL }, "Item"), R.createElement("th", { style: thL }, "Owner"), R.createElement("th", { style: thL }, "Due Date"), R.createElement("th", { style: thL }, "Status"), R.createElement("th", { style: { ...th, width: 34 }, className: "board-no-print no-print" }, ""))),
          R.createElement("tbody", null, actionItems.map((it, i) => {
            const upd = (field, val) => setActionItems((prev) => prev.map((r, j) => j === i ? { ...r, [field]: val } : r));
            const inp = { width: "100%", border: "none", background: "transparent", fontSize: 12, color: INK, fontFamily: "inherit", padding: "2px 0", boxSizing: "border-box" };
            return R.createElement("tr", { key: i, style: zebra(i) },
              R.createElement("td", { style: { ...td, textAlign: "center", color: MUTED } }, i + 1),
              R.createElement("td", { style: tdL }, R.createElement("input", { value: it.item || "", onChange: (e) => upd("item", e.target.value), placeholder: "Describe the action…", style: inp })),
              R.createElement("td", { style: tdL }, R.createElement("input", { value: it.owner || "", onChange: (e) => upd("owner", e.target.value), placeholder: "Owner", style: inp })),
              R.createElement("td", { style: tdL }, R.createElement("input", { type: "date", value: it.due || "", onChange: (e) => upd("due", e.target.value), style: inp })),
              R.createElement("td", { style: tdL }, R.createElement("select", { value: it.status || "Open", onChange: (e) => upd("status", e.target.value), style: { ...inp, cursor: "pointer" } },
                ["Open", "In Progress", "Blocked", "Done"].map((s) => R.createElement("option", { key: s, value: s }, s)))),
              R.createElement("td", { style: { ...td, textAlign: "center" }, className: "board-no-print no-print" },
                R.createElement("button", { onClick: () => setActionItems((prev) => prev.length > 1 ? prev.filter((_, j) => j !== i) : prev), title: "Remove", style: { border: "none", background: "transparent", color: MUTED, cursor: "pointer", fontSize: 14 } }, "✕")));
          }))),
        R.createElement("button", { className: "board-no-print no-print", onClick: () => setActionItems((prev) => [...prev, { item: "", owner: "", due: "", status: "Open" }]), style: { marginTop: 10, background: "transparent", color: ACCENT, border: `1px dashed ${ACCENT}`, borderRadius: 7, padding: "7px 14px", fontWeight: 600, fontSize: 12.5, cursor: "pointer" } }, "+ Add action item"))),

      R.createElement("div", { className: "board-print-footer" }, `${profile.name || "Company"}  ·  Board Financial Report  ·  ${periodLabel}  ·  Confidential`)));
  }

  window.BoardReportsPage = BoardReportsPage;
})();
