// ForecastPage (Stage 11) — window.ForecastPage
// Forecast & Scenario Analysis. All actuals come from data.plHistory (the
// app's canonical 24-month P&L); remaining months are extrapolated from the
// trailing 3-month run rate. Below-EBITDA items (D&A / interest / tax) and the
// cash base come from data.txns + data.cash. No fabricated figures: where a
// number can't be derived we show "—".

(function () {
  const h = React.createElement;
  const { useState } = React;
  const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

  function fmtShort(n) {
    if (n === null || n === undefined || isNaN(n)) return "—";
    const a = Math.abs(n), s = n < 0 ? "-" : "";
    if (a >= 1e6) return s + "$" + (a / 1e6).toFixed(1) + "M";
    if (a >= 1e3) return s + "$" + (a / 1e3).toFixed(0) + "K";
    return s + "$" + a.toFixed(0);
  }
  const avg = (arr) => (arr.length ? arr.reduce((s, v) => s + v, 0) / arr.length : 0);

  // Trailing-12-month below-EBITDA items + operating cost, from the GL. Mirrors
  // the IncomeStatement rollup math (abs-by-section, signs applied here).
  function ltmRollup(txns, profile) {
    const tax = window.PerduraTaxonomy;
    let maxD = 0;
    for (const t of txns || []) { if (t.posted_date) { const d = +new Date(t.posted_date); if (d > maxD) maxD = d; } }
    if (!maxD) maxD = Date.now();
    const cut = maxD - 365 * 86400000;
    const sec = {};
    for (const t of txns || []) {
      const cat = t.canonical_category; if (!cat) continue;
      const d = t.posted_date ? +new Date(t.posted_date) : 0; if (!d || d <= cut || d > maxD) continue;
      let s = null; try { s = tax && tax.sectionForCategory ? tax.sectionForCategory(cat, profile) : null; } catch (e) { s = null; }
      if (!s) continue;
      sec[s] = (sec[s] || 0) + Math.abs(Number(t.amount) || 0);
    }
    const g = (k) => sec[k] || 0;
    const revenue = g("revenue") - g("contra_revenue") + g("other_income");
    const cogs = g("cogs"), opex = g("opex"), da = g("da"), otherOp = g("other_op_exp");
    const interest = g("interest"), taxx = g("tax"), otherExp = g("other_expense");
    const oi = (revenue - cogs) - opex - da - otherOp;
    return { revenue, cogs, opex, da, interest, tax: taxx, otherExp, oi, ebitda: oi + da, ni: oi - interest - taxx - otherExp, hasData: revenue > 0 || cogs > 0 || opex > 0 };
  }

  // ── Section-2 chart: actual bars (solid) + projected bars (dashed) + prior line
  function ProjBars(K, actual, projected, prior, labels) {
    const all = actual.concat(projected).concat(prior || []);
    if (!all.length) return h("div", { style: { padding: 16, fontSize: 11, color: K.MUTE } }, "No data.");
    const W = 820, H = 200, padT = 20, padB = 28, padX = 16, plotH = H - padT - padB;
    const maxV = Math.max(0, ...all), minV = Math.min(0, ...all), range = (maxV - minV) || 1;
    const y = (v) => padT + ((maxV - v) / range) * plotH;
    const n = actual.length + projected.length, colW = (W - padX * 2) / n, bw = Math.min(38, colW * 0.62);
    const cx = (i) => padX + colW * i + colW / 2;
    const bars = [];
    const draw = (v, i, proj) => {
      const top = v >= 0 ? y(v) : y(0), hh = Math.max(1, Math.abs(y(v) - y(0)));
      bars.push(h("g", { key: "b" + i },
        h("rect", { x: cx(i) - bw / 2, y: top, width: bw, height: hh, rx: 2, fill: proj ? "#93B4F5" : "#1C4ED8", opacity: proj ? 0.7 : 0.95, stroke: proj ? "#1C4ED8" : "none", strokeWidth: proj ? 1 : 0, strokeDasharray: proj ? "3 2" : "0" }),
        h("text", { x: cx(i), y: top - 5, textAnchor: "middle", fontSize: "9", fontWeight: "700", fill: v < 0 ? "#DC2626" : "#0F1520", fontFamily: "Inter, system-ui, sans-serif" }, fmtShort(v)),
        labels[i] ? h("text", { x: cx(i), y: H - 12, textAnchor: "middle", fontSize: "9", fill: "#94A3B8" }, labels[i]) : null));
    };
    actual.forEach((v, i) => draw(v, i, false));
    projected.forEach((v, i) => draw(v, actual.length + i, true));
    const priorPts = (prior && prior.length) ? prior.map((v, i) => [cx(i), y(v)]) : null;
    return h("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", style: { display: "block", width: "100%", height: "auto", maxHeight: H + "px" }, preserveAspectRatio: "xMidYMid meet" },
      h("line", { x1: padX, x2: W - padX, y1: y(0), y2: y(0), stroke: K.LINE }),
      bars,
      priorPts ? h("polyline", { points: priorPts.map((p) => p.join(",")).join(" "), fill: "none", stroke: "#94A3B8", strokeWidth: 1.8, strokeDasharray: "4 3" }) : null,
      priorPts ? priorPts.map((p, i) => h("circle", { key: "pp" + i, cx: p[0], cy: p[1], r: 2, fill: "#94A3B8" })) : null);
  }

  // ── Section-5 chart: multi-line cash projection with threshold + key labels
  function RunwayChart(K, series, labels, threshold) {
    const allV = series.flatMap((s) => s.values).concat(threshold != null ? [threshold] : []).concat([0]);
    const W = 820, H = 200, padT = 16, padB = 28, padX = 44, plotH = H - padT - padB, plotW = W - padX - 16;
    const maxV = Math.max(...allV), minV = Math.min(...allV), range = (maxV - minV) || 1;
    const n = labels.length;
    const x = (i) => padX + (n <= 1 ? 0 : (plotW / (n - 1)) * i);
    const y = (v) => padT + (1 - (v - minV) / range) * plotH;
    const keyMarks = [3, 6, 9, 12];
    return h("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", style: { display: "block", width: "100%", height: "auto", maxHeight: H + "px" }, preserveAspectRatio: "xMidYMid meet" },
      h("line", { x1: padX, x2: W - 16, y1: y(0), y2: y(0), stroke: K.LINE }),
      threshold != null ? h("line", { x1: padX, x2: W - 16, y1: y(threshold), y2: y(threshold), stroke: "#D97706", strokeWidth: 1.3, strokeDasharray: "6 4" }) : null,
      threshold != null ? h("text", { x: W - 18, y: y(threshold) - 5, textAnchor: "end", fontSize: "9", fontWeight: "700", fill: "#D97706" }, "min cash · " + fmtShort(threshold)) : null,
      series.map((s, si) => {
        const pts = s.values.map((v, i) => [x(i), y(v)]);
        const path = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
        return h("g", { key: "s" + si },
          h("path", { d: path, fill: "none", stroke: s.color, strokeWidth: 2.4, strokeLinecap: "round", strokeLinejoin: "round" }),
          keyMarks.filter((m) => m < n).map((m) => h("g", { key: "k" + m },
            h("circle", { cx: x(m), cy: y(s.values[m]), r: 3, fill: s.color }),
            h("text", { x: x(m), y: y(s.values[m]) - 8, textAnchor: "middle", fontSize: "9", fontWeight: "700", fill: s.color, fontFamily: "Inter, system-ui, sans-serif" }, fmtShort(s.values[m])))));
      }),
      labels.map((l, i) => (i % 2 === 0 ? h("text", { key: "x" + i, x: x(i), y: H - 12, textAnchor: "middle", fontSize: "8.5", fill: "#94A3B8" }, l) : null)));
  }

  function ScenarioCard(K, cfg, base, onEdit) {
    const sRev = base.fyRev * (1 + cfg.rev / 100);
    const sEb = base.fyEb * (1 + cfg.eb / 100);
    const startCash = base.startCash;
    const monthlyDelta = sEb / 12;
    const cashYE = startCash + sEb;
    const runway = monthlyDelta >= 0 ? "Cash-generative" : (startCash > 0 ? (startCash / -monthlyDelta).toFixed(0) + " mo" : "0 mo");
    const num = (v, onv) => h("input", { type: "number", value: v, onChange: (e) => onv(Number(e.target.value)), style: { width: 52, fontSize: 13, fontWeight: 800, color: cfg.accent, border: "1px solid " + K.LINE, borderRadius: 6, padding: "2px 4px", background: "#fff", textAlign: "right", fontVariantNumeric: "tabular-nums" } });
    const row = (label, node) => h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, padding: "5px 0", borderBottom: "1px solid " + K.LINE } },
      h("span", { style: { fontSize: 12, color: K.MUTE, fontWeight: 600 } }, label), node);
    return h("div", { style: Object.assign({}, K.CARD, { borderTop: "4px solid " + cfg.accent, padding: 16, display: "flex", flexDirection: "column", gap: 2 }) },
      h("div", { style: { fontSize: 13, fontWeight: 800, color: cfg.accent, textTransform: "uppercase", letterSpacing: 0.5, marginBottom: 8 } }, cfg.icon + " " + cfg.name),
      row("Revenue Δ", h("div", { style: { display: "flex", alignItems: "center", gap: 3 } }, num(cfg.rev, (v) => onEdit("rev", v)), h("span", { style: { fontSize: 12, color: K.MUTE } }, "%"))),
      row("EBITDA Δ", h("div", { style: { display: "flex", alignItems: "center", gap: 3 } }, num(cfg.eb, (v) => onEdit("eb", v)), h("span", { style: { fontSize: 12, color: K.MUTE } }, "%"))),
      row("FY Revenue", h("b", { style: { fontSize: 13, color: K.INK, fontVariantNumeric: "tabular-nums" } }, fmtShort(sRev))),
      row("FY EBITDA", h("b", { style: { fontSize: 13, color: sEb < 0 ? K.NEG : K.INK, fontVariantNumeric: "tabular-nums" } }, fmtShort(sEb))),
      row("Cash @ YE", h("b", { style: { fontSize: 13, color: cashYE < 0 ? K.NEG : K.INK, fontVariantNumeric: "tabular-nums" } }, fmtShort(cashYE))),
      h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", paddingTop: 8 } },
        h("span", { style: { fontSize: 12, color: K.MUTE, fontWeight: 600 } }, "Runway"),
        h("b", { style: { fontSize: 14, color: cfg.accent } }, runway)));
  }

  function Page(props) {
    const K = window.PerduraPageKit;
    if (!K) return h("div", { className: "pc-page" }, "Loading…");
    const { data, companyProfile } = props;
    const plH = (data && data.plHistory) || (data && data.pl) || { labels: [], years: [], revenue: [], ebitda: [] };
    const labels = plH.labels || [], years = plH.years || [];
    const revA = (plH.revenue || []).map(Number), ebA = (plH.ebitda || []).map(Number);
    const N = revA.length;

    if (N < 3) {
      return h("div", { className: "pc-page" },
        h(K.SectionHead, { eye: "PLANNING & FORECAST", title: "Forecast & Scenario Analysis", sub: "Awaiting underlying data" }),
        h(K.Card, { title: "Not enough history" }, h("div", { style: { padding: 8, fontSize: 13, color: K.MUTE } }, "A forecast needs at least 3 months of P&L history. Connect QuickBooks or Xero under Settings → Configuration.")));
    }

    // G1 — period selector sets the forecast "as-of" vintage (anchor month).
    const anchor = K.anchorFromPlH(plH);
    const ps = K.usePeriodState("forecast", "ytd");
    const fRange = K.resolvePeriod(ps.mode, anchor, ps.custom);
    const fSpan = K.plIdxRange(plH, fRange);
    const hiIdx = fSpan[1] >= 0 ? fSpan[1] : N - 1;
    // Anchor to the latest month carrying revenue at/before the selected period end.
    let curIdx = hiIdx;
    for (let i = hiIdx; i >= 0; i--) { if (revA[i] > 0) { curIdx = i; break; } }
    const fyStart = (companyProfile && companyProfile.fiscal_year_start_month) || 1;
    const curMonthNum = (MONTHS.indexOf(labels[curIdx]) + 1) || 12;
    const elapsed = (((curMonthNum - fyStart + 12) % 12)) + 1; // months from FY start through current, inclusive
    const remaining = Math.max(0, 12 - elapsed);

    const fyIdxs = [];
    for (let j = 0; j < elapsed; j++) { const idx = curIdx - (elapsed - 1) + j; if (idx >= 0) fyIdxs.push(idx); }
    const fyActualRev = fyIdxs.reduce((s, i) => s + (revA[i] || 0), 0);
    const fyActualEb = fyIdxs.reduce((s, i) => s + (ebA[i] || 0), 0);
    const last3 = (arr) => arr.slice(Math.max(0, curIdx - 2), curIdx + 1);
    const avg3rev = avg(last3(revA)), avg3eb = avg(last3(ebA));
    const fyRev = fyActualRev + avg3rev * remaining;
    const fyEb = fyActualEb + avg3eb * remaining;

    // Days until fiscal year end.
    const today = new Date();
    const fyEndMonth = ((fyStart + 10) % 12) + 1; // month preceding FY start (1-indexed)
    let fyEnd = new Date(today.getFullYear(), fyEndMonth, 0);
    if (fyEnd < today) fyEnd = new Date(today.getFullYear() + 1, fyEndMonth, 0);
    const daysToYE = Math.max(0, Math.round((fyEnd - today) / 86400000));

    const ltm = ltmRollup(data ? data.txns : [], companyProfile);
    const fixedBelow = ltm.da + ltm.interest + ltm.tax + ltm.otherExp; // annualized, treated as fixed
    const startCash = (data && data.cash && Number.isFinite(data.cash.endCash)) ? data.cash.endCash : 0;

    // KPI strip
    const kpis = [
      { label: "FY Revenue Forecast", value: fmtShort(fyRev), sub: elapsed + " mo actual · " + remaining + " mo run-rate", valueColor: "gold" },
      { label: "FY EBITDA Forecast", value: fmtShort(fyEb), sub: fyRev ? (fyEb / fyRev * 100).toFixed(1) + "% margin" : "—", valueColor: fyEb < 0 ? "red" : "green" },
      { label: "Forecast Accuracy", value: "—", sub: "no prior forecast on record", valueColor: "teal" },
      { label: "Days Until Year End", value: String(daysToYE), sub: "FY ends " + fyEnd.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }), valueColor: "navy" },
    ];

    // Section-2 projection arrays (12 FY months).
    const projRev = [], projActual = [], projProjected = [], projPrior = [], projLabels = [];
    for (let j = 0; j < 12; j++) {
      const idx = curIdx - (elapsed - 1) + j;
      const mNum = ((fyStart - 1 + j) % 12);
      projLabels.push(MONTHS[mNum]);
      if (j < elapsed && idx >= 0) projActual.push(revA[idx] || 0);
      else projProjected.push(avg3rev);
      projPrior.push(idx - 12 >= 0 ? (revA[idx - 12] || 0) : (idx >= 0 && idx < N ? (revA[idx] || 0) : avg3rev));
    }

    const proj12 = projActual.concat(projProjected); // 12 FY months: actual then projected

    // Scenario state.
    const [scn, setScn] = useState({ bull: { rev: 15, eb: 22 }, base: { rev: 8, eb: 12 }, bear: { rev: -5, eb: -18 } });
    const editScn = (key) => (field, v) => setScn((p) => Object.assign({}, p, { [key]: Object.assign({}, p[key], { [field]: v }) }));
    const baseForScn = { fyRev, fyEb, startCash };
    const cfgs = [
      { key: "bull", name: "Bull Case", icon: "🐂", accent: "#059669" },
      { key: "base", name: "Base Case", icon: "📊", accent: "#1C4ED8" },
      { key: "bear", name: "Bear Case", icon: "🐻", accent: "#DC2626" },
    ];

    // Section-4 sensitivity grid.
    const baseMargin = fyRev ? fyEb / fyRev : 0;
    const growths = [-0.05, 0, 0.05, 0.10, 0.15];
    const marginPP = [-0.02, -0.01, 0, 0.01, 0.02];
    const cellNI = (g, m) => fyRev * (1 + g) * (baseMargin + m) - fixedBelow;
    let maxAbsNI = 1;
    growths.forEach((g) => marginPP.forEach((m) => { maxAbsNI = Math.max(maxAbsNI, Math.abs(cellNI(g, m))); }));
    const cellBg = (v) => {
      const t = Math.max(-1, Math.min(1, v / maxAbsNI));
      if (t >= 0) { const a = 0.08 + t * 0.5; return `rgba(5,150,105,${a.toFixed(3)})`; }
      const a = 0.08 + (-t) * 0.5; return `rgba(220,38,38,${a.toFixed(3)})`;
    };

    // Section-5 runway lines.
    const runwaySeries = cfgs.map((c) => {
      const sEb = fyEb * (1 + scn[c.key].eb / 100);
      const delta = sEb / 12;
      const vals = []; for (let t = 0; t <= 12; t++) vals.push(startCash + delta * t);
      return { name: c.name, color: c.accent, values: vals };
    });
    const runwayLabels = []; for (let t = 0; t <= 12; t++) runwayLabels.push("M" + t);
    const monthlyOpCost = Math.max(0, avg3rev - avg3eb); // operating cost run-rate (rev − ebitda)
    const baseDelta = (fyEb * (1 + scn.base.eb / 100)) / 12;
    const baseRunwayMonths = baseDelta >= 0 ? null : (startCash > 0 ? startCash / -baseDelta : 0);
    let runwayDateTxt;
    if (baseRunwayMonths == null) runwayDateTxt = "is indefinite at base case — the business is cash-generative.";
    else { const d = new Date(today.getTime() + baseRunwayMonths * 30.4 * 86400000); runwayDateTxt = "extends to " + d.toLocaleDateString(undefined, { month: "short", year: "numeric" }) + " (" + baseRunwayMonths.toFixed(0) + " months) at base case."; }

    // Section-6 commentary.
    const trajUp = avg3rev >= avg(revA.slice(Math.max(0, curIdx - 5), Math.max(1, curIdx - 2)));
    const commentary = [
      { icon: "▣", text: "Current trajectory points to a <b>" + fmtShort(fyRev) + "</b> full-year revenue forecast (<b>" + (fyRev ? (fyEb / fyRev * 100).toFixed(0) : "—") + "%</b> EBITDA margin), with " + elapsed + " months actual and " + remaining + " projected." },
      { icon: "◆", text: "The forecast is driven by the <b>trailing 3-month run rate</b> of " + fmtShort(avg3rev) + "/mo revenue — " + (trajUp ? "accelerating" : "moderating") + " versus earlier in the year." },
      { icon: "▲", text: "Primary upside: the <b>bull case</b> (+" + scn.bull.rev + "% revenue) lifts full-year EBITDA to <b>" + fmtShort(fyEb * (1 + scn.bull.eb / 100)) + "</b>." },
      { icon: "▼", text: "Primary downside: the <b>bear case</b> (" + scn.bear.rev + "% revenue) compresses EBITDA to <b>" + fmtShort(fyEb * (1 + scn.bear.eb / 100)) + "</b>" + (baseRunwayMonths != null ? " and shortens runway materially." : ".") },
      { icon: "➜", text: "<b>Recommended action:</b> " + (fyEb < 0 ? "Forecast EBITDA is negative — prioritize the cost levers in the bear-case plan now." : baseDelta < 0 ? "Run-rate burn persists — secure financing or accelerate collections before the runway date above." : "Trajectory is healthy — lock the base plan and reinvest upside selectively.") },
    ];

    const fcTakeaway = "Full-year revenue is tracking to <b>" + fmtShort(fyRev) + "</b>" + (fyRev ? " at a <b>" + (fyEb / fyRev * 100).toFixed(0) + "%</b> EBITDA margin" : "") +
      ", with " + elapsed + " months actual and " + remaining + " projected from the trailing 3-month run-rate of " + fmtShort(avg3rev) + "/mo. " +
      "Bull case lifts EBITDA to <b>" + fmtShort(fyEb * (1 + scn.bull.eb / 100)) + "</b>; bear case compresses it to <b>" + fmtShort(fyEb * (1 + scn.bear.eb / 100)) + "</b>.";

    return h(K.Shell, { hero: { eyebrow: "PLANNING & FORECAST", title: "Forecast & Scenario Analysis", subtitle: "Run-rate forecast from trailing 3-month average · as of " + (labels[curIdx] || "") + " " + (years[curIdx] || ""), controls: h(K.PeriodControls, Object.assign({}, ps, { showCompare: false })) } },

      h("div", { className: "pa-kpi-strip pa-kpi-strip-4" }, kpis.map((k, i) => h(K.Kpi, Object.assign({ key: i, animDelay: i * 0.05, onClick: () => window.__perduraSetPage && window.__perduraSetPage("cash_flow_statement") }, k)))),

      h("div", { style: { fontSize: 10, color: "#6475a0", padding: "2px 2px 10px", display: "flex", alignItems: "center", gap: 6 } },
        h("span", null, "💡"), "Click any metric to navigate to the relevant detail page"),

      h(K.Card, {
        title: "REVENUE — ACTUALS + FULL-YEAR PROJECTION",
        sub: "Solid = actual · hatched = run-rate projection · grey dashed = prior year",
        right: h("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", fontSize: 10, color: "#475569", fontWeight: 600 } },
          h("span", { style: { display: "inline-flex", alignItems: "center", gap: 5 } }, h("span", { style: { width: 12, height: 9, background: "#1C4ED8", borderRadius: 2 } }), "Actual"),
          h("span", { style: { display: "inline-flex", alignItems: "center", gap: 5 } }, h("span", { style: { width: 12, height: 9, background: "#bcd2f7", border: "1px solid #1C4ED8", borderRadius: 2 } }), "Projected"),
          h("span", { style: { display: "inline-flex", alignItems: "center", gap: 5 } }, h("span", { style: { width: 14, height: 0, borderTop: "2px dashed #94a3b8" } }), "Prior Year")),
      },
        h(K.MultiSeriesBarChart, { months: projLabels, height: 180, series: [
          { type: "bar", color: "#1C4ED8", data: proj12, hatchIdx: elapsed },
          { type: "dashed-line", color: "#94a3b8", data: projPrior },
        ] }),
        h(K.KeyTakeaway, { text: fcTakeaway })),

      h(K.SectionHead, { title: "Three-Scenario Analysis", sub: "Each scenario applies an editable % adjustment to the base run-rate. Click a value to edit." }),
      h("div", { className: "pc-kpi-grid pc-kpi-grid-3" }, cfgs.map((c) => h("div", { key: c.key }, ScenarioCard(K, Object.assign({}, scn[c.key], c), baseForScn, editScn(c.key))))),

      h(K.Card, { title: "Sensitivity Analysis — Net Income", sub: "Rows: revenue growth · Columns: EBITDA-margin shift · base case outlined in navy" },
        h("div", { style: { overflowX: "auto" } },
          h("table", { className: "pc-table", style: { fontSize: 12, width: "100%", borderCollapse: "separate", borderSpacing: 0 } },
            h("thead", null, h("tr", null,
              h("th", { style: { textAlign: "left", padding: "8px 10px", color: K.MUTE, fontSize: 10.5 } }, "REV ↓ / MARGIN →"),
              marginPP.map((m, i) => h("th", { key: i, style: { textAlign: "right", padding: "8px 10px", color: K.MUTE, fontSize: 10.5 } }, (m >= 0 ? "+" : "") + (m * 100).toFixed(0) + "pp")))),
            h("tbody", null, growths.map((g, ri) => h("tr", { key: ri },
              h("td", { style: { textAlign: "left", padding: "8px 10px", fontWeight: 700, color: K.INK } }, (g >= 0 ? "+" : "") + (g * 100).toFixed(0) + "%"),
              marginPP.map((m, ci) => { const v = cellNI(g, m); const isBase = ri === 1 && ci === 2; return h("td", { key: ci, style: { textAlign: "right", padding: "8px 10px", fontVariantNumeric: "tabular-nums", fontWeight: isBase ? 800 : 600, color: v < 0 ? "#7F1D1D" : "#064E3B", background: cellBg(v), border: isBase ? "2px solid #0F2044" : "1px solid #fff" } }, fmtShort(v)); }))))))),

      h("div", { className: "pa-grid-64" },
        h(K.Card, { title: "Cash Runway Projection", sub: "12-month cash balance by scenario · dashed = 30-day min cash" },
          RunwayChart(K, runwaySeries, runwayLabels, monthlyOpCost),
          h("div", { style: { marginTop: 8, fontSize: 12.5, color: K.INK, fontWeight: 600 } }, "At base case, cash runway " + runwayDateTxt)),
        h(K.Card, { title: "Runway summary" },
          (() => {
            const bearDelta = (fyEb * (1 + scn.bear.eb / 100)) / 12;
            const bearRun = bearDelta >= 0 ? null : (startCash > 0 ? startCash / -bearDelta : 0);
            const rows = [
              ["Current cash", fmtShort(startCash)],
              ["Monthly op. cost", fmtShort(monthlyOpCost)],
              ["Base runway", baseRunwayMonths == null ? "Cash-generative" : baseRunwayMonths.toFixed(0) + " mo"],
              ["Bear runway", bearRun == null ? "Cash-generative" : bearRun.toFixed(0) + " mo"],
            ];
            return h("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, rows.map((r, i) => h("div", { key: i, title: "View cash flow statement", onClick: () => window.__perduraSetPage && window.__perduraSetPage("cash_flow_statement"), style: { display: "flex", justifyContent: "space-between", fontSize: 12.5, cursor: "pointer" } }, h("span", { style: { color: "#6475a0" } }, r[0]), h("b", { style: { color: "#0d2040" } }, r[1]))));
          })())),

      h(K.Commentary, { title: "CFO Forecast Intelligence", items: commentary }));
  }

  window.ForecastPage = Page;
})();
