/* global React, Icon, StatusChip, PageHeader, Banner, etb */
const { useState: useStateLC } = React;

/* ============================================================
   NBE LCR — Annex III (Liquidity Coverage Ratio), spreadsheet UI + Official·Stress
   Totals sum the Amount column; Factor shown for reference (per NBE template).
   Stress = deposit run-off uplift → net outflows rise → LCR falls vs ≥100% floor.
   ETB millions · internal/synthetic
   ============================================================ */
const LCR_ROWS = [
  { kind: "sec", label: "HQLA (Both in Birr and FCY)" },
  { kind: "subsec", label: "Level 1" },
  { no: "1", label: "Cash", factor: "100%", amt: 1654.47259491, grp: "hqla" },
  { no: "2", label: "Deposits with other banks", factor: "100%", amt: 12720.50494174, grp: "hqla", note: "Deposit on FCY and LCY" },
  { no: "3", label: "Deposits at NBE", factor: "100%", amt: 3302.49787509, grp: "hqla" },
  { no: "4", label: "Unencumbered Reserve Balances at NBE (>5%)", factor: "100%", amt: 3884.43016143, grp: "hqla" },
  { kind: "subsec", label: "Level 2" },
  { no: "5", label: "Federal Government, Development Bank of Ethiopia and NBE Bonds/Bills", factor: "85%", amt: 5254.325235, grp: "hqla", note: "DBE Bond & T-bond" },
  { no: "6", label: "Loans/Lending to other FIs/banks", factor: "75%", amt: 1950, grp: "hqla", note: "Interbank loan" },
  { no: "7", label: "Other loans/advances secured by Level 2 assets", factor: "50%", amt: 0, grp: "hqla" },
  { no: "8", label: "Equity Investment by the Bank", factor: "25%", amt: 118.06550986, grp: "hqla" },
  { kind: "tot", label: "Total Value of HQLA", grp: "hqlaTot" },
  { kind: "sec", label: "Cash Outflows (next 30 days)" },
  { no: "9", label: "Saving Deposits: Saving Deposits by Individuals", factor: "10%", amt: 3258.029243, grp: "out" },
  { no: "10", label: "Saving Deposits by Firms", factor: "15%", amt: 2237.04648672, grp: "out" },
  { no: "11", label: "Other Deposits & Payables: Term Deposits with maturity less than 30 days", factor: "100%", amt: 820.50433059, grp: "out" },
  { no: "12", label: "Deposits in Current Accounts", factor: "20%", amt: 6037.08140637, grp: "out" },
  { no: "13", label: "Payable to Mobile Network Operators (EthioTele and Safaricom wallet / Mobile Money Deposits)", factor: "40%", amt: 50.63307878, grp: "out" },
  { no: "14", label: "Additional Requirements: Contingent commitments - Guarantees & LCs", factor: "20%", amt: 2729.59328266, grp: "out" },
  { no: "15", label: "Other expected / contractual cash outflows [within 30 days]", factor: "100%", amt: 0, grp: "out" },
  { no: "16", label: "Other payable", factor: "50%", amt: 1562.22411534, grp: "out", note: "Left for bank's discretion to set the factor" },
  { no: "17", label: "Term Deposits with maturity greater than 30 days as reporting date", factor: "0%", amt: 0, grp: "out", note: "A 0% factor means such payable are excluded from cash outflow calculations." },
  { kind: "tot", label: "Total Cash Outflows", grp: "outTot", note: "(Sum of weighted outflows)" },
  { kind: "sec", label: "Cash Inflows (next 30 days)" },
  { no: "18", label: "Receivable from other Banks/FIs and NBE", factor: "100%", amt: 5625.94794521, grp: "in" },
  { no: "19", label: "Loans Secured by Level 2 HQLA", factor: "50%", amt: 0, grp: "in" },
  { no: "20", label: "Margin Lending - Advances on LC", factor: "50%", amt: 165.72527646, grp: "in" },
  { no: "21", label: "All other contractual receivables from retail [within 30 days]", factor: "25%", amt: 0, grp: "in" },
  { no: "22", label: "Credit or Liquidity Purpose Facility/receivable from NBE", factor: "0%", amt: 0, grp: "in", note: "A 0% factor means such receivable is excluded from cash inflow calculations." },
  { kind: "tot", label: "Total Cash Inflows", grp: "inTot", note: "(Sum of weighted inflows)" },
];
const sumGrp = g => LCR_ROWS.filter(r => r.grp === g).reduce((a, r) => a + r.amt, 0);
const HQLA = sumGrp("hqla"), OUT = sumGrp("out"), IN = sumGrp("in");
const lcrAt = up => { const out = OUT * (1 + up / 100), net = Math.max(out - IN, out * 0.25); return { out, net, lcr: HQLA / net * 100 }; };
const BASE_LCR = lcrAt(0).lcr;
const LCRSEV = [{ name: "Mild", up: 15 }, { name: "Moderate", up: 30 }, { name: "Severe", up: 60 }, { name: "Extreme", up: 110 }];

const LCR_HELP = {
  title: "LCR / NSFR — Annex III",
  purpose: "The NBE Liquidity Coverage Ratio return. HQLA over net cash outflows for the next 30 days, against the ≥100% floor. Stress applies a deposit run-off, raising outflows until the ratio approaches breach.",
  first: "Read the LCR against 100%. In Stress, scan the severity columns for the run-off band that breaches.",
  terms: [["HQLA", "High-quality liquid assets (the numerator)."], ["Run-off", "Deposit outflow rate under stress."], ["Net cash outflows", "Outflows − capped inflows (the denominator)."]],
  next: ["Open Stress to see the LCR under each severity.", "Tune the run-off lever; the Custom column recomputes live.", "A breach hands off to Recovery."],
  connections: ["Amounts reconcile to deposits & HQLA sources.", "Factors carry NBE LRM rule evidence.", "Feeds the NBE Liquidity Return at the export gate."],
  guardrail: "Internal liquidity workpaper. Stress is analysis-only; official NBE export stays blocked until evidence and approvals pass.",
};

function LCRSheet({ go, info }) {
  const [view, setView] = useStateLC(window.__lcrView === "NSFR" ? "NSFR" : "LCR");
  const [stress, setStress] = useStateLC(false);
  const [mode, setMode] = useStateLC("Custom");
  const [up, setUp] = useStateLC(60);
  const live = lcrAt(up);
  const breach = live.lcr < 100;
  const uBreach = Math.round((HQLA / 100 + IN) / OUT * 100 - 100);

  const f = (v, dp = stress ? 0 : 2) => v == null ? "" : etb(+v, dp);
  const isOutGrp = r => r.grp === "out" || r.grp === "outTot";
  const span = stress ? 8 : 4;
  return (
    <div className="main-inner">
      <PageHeader eyebrow="Prudential Risk · Liquidity" title="LCR / NSFR" onInfo={() => info(LCR_HELP)}
        right={<span className="center gap8"><div className="seg-ctl">{["LCR", "NSFR"].map(v => <button key={v} className={view === v ? "on" : ""} onClick={() => { setView(v); window.__lcrView = v; }}>{v}</button>)}</div>{view === "LCR" && <button className={"btn btn-sm " + (stress ? "btn-dark" : "btn-ghost")} onClick={() => setStress(!stress)}><Icon name="flask" size={15} /> {stress ? "Stress on" : "Stress test"}</button>}</span>} />

      {view === "NSFR" ? <NSFRSheet go={go} /> : <React.Fragment>

      {stress && <div className="banner banner-indigo mb12" style={{ alignItems: "center", padding: "11px 15px" }}>
        <Icon name="flask" size={18} className="ic" /><div className="grow"><b>Deposit run-off overlay</b> · severity columns added to the official sheet · analysis-only, never merged into the official LCR</div><span className="chip chip-indigo">floor 100%</span>
      </div>}

      <div style={{ display: "flex", gap: 16, alignItems: "flex-start" }}>
        <div className="grow" style={{ minWidth: 0 }}>
          <div className="xl-wrap">
            <table className={"xl" + (stress ? " xl-scroll" : "")}>
              <colgroup>
                <col style={{ width: 42 }} /><col style={{ width: stress ? 300 : 390 }} />
                <col style={{ width: stress ? 72 : 74 }} /><col style={{ width: stress ? 100 : 132 }} />
                {stress ? LCRSEV.map(s => <col key={s.name} style={{ width: 82 }} />) : null}
                {stress ? <col style={{ width: 92 }} /> : <col style={{ width: 300 }} />}
              </colgroup>
              <thead><tr>
                <th className="xl-no"></th>
                <th style={{ textAlign: "left" }}>ANNEX III · Liquidity Coverage Ratio · ETB millions</th>
                <th className="xl-amt">Factor</th>
                <th className="xl-amt">{stress ? "Base amt" : "Amount"}</th>
                {stress
                  ? <React.Fragment>{LCRSEV.map(s => <th key={s.name} className={"xl-amt xl-stress" + (up === s.up ? " on" : "")} style={{ cursor: "pointer" }} onClick={() => setUp(s.up)}>{s.name}</th>)}<th className="xl-amt" style={{ background: "var(--indigo)", color: "#fff", borderLeft: "2px solid var(--indigo)" }}>Custom</th></React.Fragment>
                  : <th style={{ textAlign: "left" }}>Notes / Remarks</th>}
              </tr></thead>
              <tbody>
                {LCR_ROWS.map((r, i) => {
                  if (r.kind === "sec") return <tr key={i} className="xl-green"><td className="xl-no"></td><td colSpan={span}>{r.label}</td></tr>;
                  if (r.kind === "subsec") return <tr key={i} className="xl-sub"><td className="xl-no"></td><td colSpan={span} style={{ fontStyle: "italic" }}>{r.label}</td></tr>;
                  const isOut = isOutGrp(r);
                  if (r.kind === "tot") {
                    const base = r.grp === "hqlaTot" ? HQLA : r.grp === "outTot" ? OUT : IN;
                    return <tr key={i} className="xl-orange"><td className="xl-no"></td><td className="xl-label">{r.label}</td><td className="xl-amt"></td><td className="xl-amt tnum">{f(base)}</td>
                      {stress && <React.Fragment>{LCRSEV.map((s, j) => <td key={j} className={"xl-amt xl-stress tnum" + (up === s.up ? " on" : "")} style={{ color: isOut ? "var(--indigo)" : undefined }}>{f(isOut ? base * (1 + s.up / 100) : base)}</td>)}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: isOut ? "var(--indigo)" : undefined }}>{f(isOut ? base * (1 + up / 100) : base)}</td></React.Fragment>}
                      {!stress && <td>{r.note || ""}</td>}</tr>;
                  }
                  const colVal = u => isOut ? r.amt * (1 + u / 100) : r.amt;
                  return <tr key={i} className="xl-item">
                    <td className="xl-no">{r.no}</td><td className="xl-label">{r.label}</td>
                    <td className="xl-amt tnum" style={{ color: "var(--ink-3)" }}>{r.factor}</td>
                    <td className="xl-amt tnum">{r.amt ? f(r.amt) : <span style={{ opacity: .35 }}>–</span>}</td>
                    {stress && <React.Fragment>{LCRSEV.map((s, j) => <td key={j} className={"xl-amt xl-stress tnum" + (up === s.up ? " on" : "")} style={{ color: (isOut && r.amt) ? "var(--indigo)" : undefined }}>{r.amt ? f(colVal(s.up)) : ""}</td>)}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: (isOut && r.amt) ? "var(--indigo)" : undefined }}>{r.amt ? f(colVal(up)) : ""}</td></React.Fragment>}
                    {!stress && <td>{r.note || ""}</td>}
                  </tr>;
                })}
                <tr className="xl-sub"><td className="xl-no"></td><td className="xl-label">Net Cash Outflows (outflows − capped inflows)</td><td className="xl-amt"></td><td className="xl-amt tnum">{f(lcrAt(0).net)}</td>
                  {stress && <React.Fragment>{LCRSEV.map((s, j) => <td key={j} className={"xl-amt xl-stress tnum" + (up === s.up ? " on" : "")} style={{ color: "var(--indigo)", fontWeight: 600 }}>{f(lcrAt(s.up).net)}</td>)}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: "var(--indigo)", fontWeight: 600 }}>{f(live.net)}</td></React.Fragment>}
                  {!stress && <td>Total Cash Outflows Minus Total Cash Inflows</td>}</tr>
                <tr className="xl-orange"><td className="xl-no"></td><td className="xl-label">LCR = HQLA / Net Cash Outflows · NBE floor 100%</td><td className="xl-amt"></td><td className="xl-amt tnum" style={{ color: stress ? undefined : "var(--green-ink)" }}>{BASE_LCR.toFixed(2)}%</td>
                  {stress && <React.Fragment>{LCRSEV.map((s, j) => { const v = lcrAt(s.up).lcr, br = v < 100; return <td key={j} className={"xl-amt tnum xl-cell hit" + (up === s.up ? " on" : "")} onClick={() => setUp(s.up)} style={{ background: br ? "var(--red-soft)" : undefined, color: br ? "var(--red)" : "var(--indigo)", fontWeight: 800 }}>{v.toFixed(0)}%</td>; })}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: breach ? "var(--red-soft)" : "rgba(79,77,208,.05)", color: breach ? "var(--red)" : "var(--indigo)", fontWeight: 800 }}>{live.lcr.toFixed(0)}%</td></React.Fragment>}
                  {!stress && <td>HQLA / Net Cash Outflows</td>}</tr>
              </tbody>
            </table>
          </div>
          {!stress && <Banner kind="green" icon="checkCirc">LCR {BASE_LCR.toFixed(2)}% is well above the NBE 100% floor. Outflow factors carry NBE LRM references; click <b>Stress</b> to add severity columns and apply a deposit run-off.</Banner>}
          {stress && <div className="mt12"><Banner kind={breach ? "red" : "indigo"} icon={breach ? "alert" : "flask"}>
            {breach ? <span><b>Breach — LCR {live.lcr.toFixed(0)}% falls below the 100% floor.</b> Scenario result; hand to Recovery. Official LCR unchanged.</span>
              : <span>Severity columns scale the outflow run-off. The <b style={{ color: "var(--indigo)" }}>Custom</b> column is your own run-off — tune it in the panel.</span>}
          </Banner></div>}
        </div>

        {stress && <aside className="stress-dock">
          <div className="between mb12"><span className="center gap8"><Icon name="sliders" size={15} style={{ color: "var(--indigo)" }} /><b style={{ fontSize: 14, whiteSpace: "nowrap" }}>Run-off controls</b></span><span className="chip chip-indigo">Analysis-only</span></div>
          <div className="seg-ctl" style={{ width: "100%", marginBottom: 14 }}>{[["Presets", "Presets"], ["Custom", "Custom"], ["Reverse", "Reverse"]].map(([l, k]) => <button key={k} className={mode === k ? "on" : ""} style={{ flex: 1 }} onClick={() => setMode(k)}>{l}</button>)}</div>
          {mode === "Presets" && <div><div className="dock-label">Scenario severity</div><div className="seg-ctl" style={{ width: "100%" }}>{LCRSEV.map((s, i) => <button key={s.name} className={up === s.up ? "on" : ""} style={{ flex: 1, padding: "6px 2px" }} onClick={() => setUp(s.up)}>{s.name === "Moderate" ? "Mod" : s.name}</button>)}</div><div className="muted" style={{ fontSize: 11.5, marginTop: 8 }}>Sets the outflow run-off to NBE's named bands.</div></div>}
          {mode === "Custom" && <div><div className="between" style={{ marginBottom: 5 }}><span style={{ fontSize: 12, fontWeight: 600 }}>Deposit run-off uplift</span><span className="chip chip-indigo" style={{ fontSize: 10.5 }}>{up}%</span></div><input type="range" className="lev-slider" min={0} max={160} step={5} value={up} onChange={e => setUp(+e.target.value)} /><div className="muted" style={{ fontSize: 10.5, marginTop: 3 }}>raises every outflow amount</div></div>}
          {mode === "Reverse" && <div><div className="dock-label">Find the breaking point</div><div className="muted" style={{ fontSize: 11.5, marginBottom: 10 }}>Reverse stress — solves the run-off that pulls LCR to the 100% floor.</div><div className="banner banner-indigo" style={{ padding: "11px 13px" }}><Icon name="flask" size={16} className="ic" /><div>A run-off uplift of <b>≈{uBreach}%</b> takes LCR to 100%.</div></div><button className="btn btn-dark btn-sm" style={{ width: "100%", marginTop: 12 }} onClick={() => setUp(Math.max(0, uBreach))}>Set to breaking point</button></div>}
          <hr className="hr" style={{ margin: "16px 0" }} />
          <div className="dock-label">Live LCR (stressed)</div>
          <div className="big-num tnum" style={{ fontSize: 30, color: breach ? "var(--red)" : "var(--green-ink)", marginTop: 2 }}>{live.lcr.toFixed(0)}%</div>
          <div className="gauge"><span className="gauge-fill" style={{ width: Math.max(2, Math.min(98, live.lcr / 300 * 100)) + "%", background: breach ? "var(--red)" : "var(--green)" }} /><span className="gauge-floor" style={{ left: (100 / 300 * 100) + "%" }} /></div>
          <div className="between" style={{ fontSize: 10.5, color: "var(--ink-3)", marginTop: 3 }}><span>0%</span><span>100% floor</span><span>300%</span></div>
          {breach ? <button className="btn btn-dark btn-sm" style={{ width: "100%", marginTop: 12 }} onClick={() => go("recovery")}>Breach — Send to Recovery <Icon name="arrowR" size={13} /></button>
            : <div className="chip chip-green chip-lg" style={{ marginTop: 12 }}><span className="dot" />Within 100% floor</div>}
          <button className="btn btn-ghost btn-sm" style={{ width: "100%", marginTop: 8 }} onClick={() => setUp(0)}><Icon name="refresh" size={13} /> Clear stress · restore baseline</button>
        </aside>}
      </div>
      </React.Fragment>}
    </div>
  );
}
window.LCRSheet = LCRSheet;

/* ============================================================
   NSFR — Available vs Required Stable Funding (≥ 100%)
   Faithful ASF/RSF factor tables · stress = wholesale rollover decline
   ============================================================ */
const NSFR_ASF = [
  { kind: "sec", label: "Available Stable Funding (ASF)" },
  { no: "1", label: "Regulatory capital (Tier 1 + Tier 2)", amt: 12439, factor: 100 },
  { no: "2", label: "Other capital instruments & liabilities ≥ 1 year", amt: 3200, factor: 100 },
  { no: "3", label: "Stable retail & SME deposits (< 1 year)", amt: 64200, factor: 95 },
  { no: "4", label: "Less stable retail & SME deposits (< 1 year)", amt: 28900, factor: 90 },
  { no: "5", label: "Operational deposits", amt: 9400, factor: 50 },
  { no: "6", label: "Wholesale funding from non-financials (< 1 year)", amt: 21400, factor: 50, wholesale: true },
  { no: "7", label: "Wholesale funding from financials (< 6 months)", amt: 8200, factor: 0, wholesale: true },
  { no: "8", label: "Other liabilities (no stable funding value)", amt: 4100, factor: 0 },
  { kind: "tot", label: "Total Available Stable Funding", grp: "asf" },
];
const NSFR_RSF = [
  { kind: "sec", label: "Required Stable Funding (RSF)" },
  { no: "9", label: "Cash & NBE reserves", amt: 18420, factor: 0 },
  { no: "10", label: "Level 1 HQLA — govt securities (T-bills, bonds)", amt: 9650, factor: 5 },
  { no: "11", label: "Level 2A HQLA", amt: 3200, factor: 15 },
  { no: "12", label: "Loans to financial institutions (< 6 months)", amt: 4300, factor: 10 },
  { no: "13", label: "Performing loans (< 1 year)", amt: 38000, factor: 50 },
  { no: "14", label: "Performing residential mortgages", amt: 11000, factor: 65 },
  { no: "15", label: "Other performing loans (> 1 year)", amt: 54000, factor: 85 },
  { no: "16", label: "Defaulted loans, fixed & other assets", amt: 8900, factor: 100 },
  { kind: "tot", label: "Total Required Stable Funding", grp: "rsf" },
];
const wsum = (rows, factorOf) => rows.filter(r => r.no).reduce((a, r) => a + r.amt * (factorOf ? factorOf(r) : r.factor) / 100, 0);
const ASF0 = wsum(NSFR_ASF), RSF0 = wsum(NSFR_RSF);
const NSFRSEV = [{ name: "Mild", drop: 15 }, { name: "Moderate", drop: 30 }, { name: "Severe", drop: 55 }, { name: "Extreme", drop: 80 }];
// stress: wholesale ASF factors decline (funding flightiness) → ASF falls
const asfAt = drop => wsum(NSFR_ASF, r => r.wholesale ? r.factor * (1 - drop / 100) : r.factor);
const nsfrAt = drop => asfAt(drop) / RSF0 * 100;
const BASE_NSFR = ASF0 / RSF0 * 100;

function NSFRSheet({ go }) {
  const [stress, setStress] = useStateLC(false);
  const [drop, setDrop] = useStateLC(55);
  const live = nsfrAt(drop), breach = live < 100;
  const dBreach = Math.round((1 - (RSF0 - wsum(NSFR_ASF, r => r.wholesale ? 0 : r.factor)) / wsum(NSFR_ASF.filter(r => r.wholesale))) * 100);
  const f = v => v == null ? "" : etb(Math.round(v));
  const span = stress ? 9 : 5;
  const rows = NSFR_ASF.concat(NSFR_RSF);

  return (
    <React.Fragment>
      <div className="between mb12">
        <div className="muted" style={{ fontSize: 12.5 }}>NSFR = Available Stable Funding / Required Stable Funding · NBE floor 100%</div>
        <button className={"btn btn-sm " + (stress ? "btn-dark" : "btn-ghost")} onClick={() => setStress(!stress)}><Icon name="flask" size={15} /> {stress ? "Stress on" : "Stress test"}</button>
      </div>
      {stress && <div className="banner banner-indigo mb12" style={{ alignItems: "center", padding: "11px 15px" }}>
        <Icon name="flask" size={18} className="ic" /><div className="grow"><b>Wholesale-funding rollover overlay</b> · severity columns reduce the ASF factor on flighty wholesale funding · analysis-only</div><span className="chip chip-indigo">floor 100%</span>
      </div>}

      <div style={{ display: "flex", gap: 16, alignItems: "flex-start" }}>
        <div className="grow" style={{ minWidth: 0 }}>
          <div className="xl-wrap">
            <table className={"xl" + (stress ? " xl-scroll" : "")}>
              <colgroup><col style={{ width: 42 }} /><col style={{ width: stress ? 300 : "100%" }} /><col style={{ width: 110 }} /><col style={{ width: 80 }} /><col style={{ width: 110 }} />{stress ? NSFRSEV.map(s => <col key={s.name} style={{ width: 82 }} />) : null}{stress ? <col style={{ width: 92 }} /> : null}</colgroup>
              <thead><tr>
                <th className="xl-no"></th><th style={{ textAlign: "left" }}>NSFR · ETB millions</th>
                <th className="xl-amt">Amount</th><th className="xl-amt">Factor</th><th className="xl-amt">Weighted</th>
                {stress && <React.Fragment>{NSFRSEV.map(s => <th key={s.name} className={"xl-amt xl-stress" + (drop === s.drop ? " on" : "")} style={{ cursor: "pointer" }} onClick={() => setDrop(s.drop)}>{s.name}</th>)}<th className="xl-amt" style={{ background: "var(--indigo)", color: "#fff", borderLeft: "2px solid var(--indigo)" }}>Custom</th></React.Fragment>}
              </tr></thead>
              <tbody>
                {rows.map((r, i) => {
                  if (r.kind === "sec") return <tr key={i} className="xl-green"><td className="xl-no"></td><td colSpan={span}>{r.label}</td></tr>;
                  if (r.kind === "tot") { const base = r.grp === "asf" ? ASF0 : RSF0; return <tr key={i} className="xl-orange"><td className="xl-no"></td><td className="xl-label">{r.label}</td><td className="xl-amt"></td><td className="xl-amt"></td><td className="xl-amt tnum">{f(base)}</td>{stress && <React.Fragment>{NSFRSEV.map((s, j) => <td key={j} className={"xl-amt xl-stress tnum" + (drop === s.drop ? " on" : "")} style={{ color: r.grp === "asf" ? "var(--indigo)" : undefined }}>{f(r.grp === "asf" ? asfAt(s.drop) : RSF0)}</td>)}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: r.grp === "asf" ? "var(--indigo)" : undefined }}>{f(r.grp === "asf" ? asfAt(drop) : RSF0)}</td></React.Fragment>}</tr>; }
                  const w = r.amt * r.factor / 100;
                  const wAt = d => r.wholesale ? r.amt * (r.factor * (1 - d / 100)) / 100 : w;
                  return <tr key={i} className="xl-item">
                    <td className="xl-no">{r.no}</td><td className="xl-label">{r.label}</td>
                    <td className="xl-amt tnum">{f(r.amt)}</td><td className="xl-amt tnum" style={{ color: "var(--ink-3)" }}>{r.factor}%</td><td className="xl-amt tnum" style={{ fontWeight: 600 }}>{f(w)}</td>
                    {stress && <React.Fragment>{NSFRSEV.map((s, j) => <td key={j} className={"xl-amt xl-stress tnum" + (drop === s.drop ? " on" : "")} style={{ color: r.wholesale ? "var(--indigo)" : undefined }}>{f(wAt(s.drop))}</td>)}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: r.wholesale ? "var(--indigo)" : undefined }}>{f(wAt(drop))}</td></React.Fragment>}
                  </tr>;
                })}
                <tr className="xl-orange"><td className="xl-no"></td><td className="xl-label">NSFR = ASF / RSF · NBE floor 100%</td><td className="xl-amt"></td><td className="xl-amt"></td><td className="xl-amt tnum" style={{ color: stress ? undefined : "var(--green-ink)" }}>{BASE_NSFR.toFixed(0)}%</td>
                  {stress && <React.Fragment>{NSFRSEV.map((s, j) => { const v = nsfrAt(s.drop), br = v < 100; return <td key={j} className={"xl-amt tnum xl-cell hit" + (drop === s.drop ? " on" : "")} onClick={() => setDrop(s.drop)} style={{ background: br ? "var(--red-soft)" : undefined, color: br ? "var(--red)" : "var(--indigo)", fontWeight: 800 }}>{v.toFixed(0)}%</td>; })}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: breach ? "var(--red-soft)" : "rgba(79,77,208,.05)", color: breach ? "var(--red)" : "var(--indigo)", fontWeight: 800 }}>{live.toFixed(0)}%</td></React.Fragment>}</tr>
              </tbody>
            </table>
          </div>
          {!stress && <Banner kind="green" icon="checkCirc">NSFR {BASE_NSFR.toFixed(0)}% is above the NBE 100% floor. ASF/RSF factors follow the NBE liquidity framework; click <b>Stress</b> to model a wholesale-funding rollover decline.</Banner>}
          {stress && <div className="mt12"><Banner kind={breach ? "red" : "indigo"} icon={breach ? "alert" : "flask"}>{breach ? <span><b>Breach — NSFR {live.toFixed(0)}% below the 100% floor.</b> Hand to Recovery. Official NSFR unchanged.</span> : <span>Severity columns compress the ASF factor on flighty wholesale funding. The <b style={{ color: "var(--indigo)" }}>Custom</b> column is your own decline.</span>}</Banner></div>}
        </div>

        {stress && <aside className="stress-dock">
          <div className="between mb12"><span className="center gap8"><Icon name="sliders" size={15} style={{ color: "var(--indigo)" }} /><b style={{ fontSize: 14, whiteSpace: "nowrap" }}>Funding controls</b></span><span className="chip chip-indigo">Analysis-only</span></div>
          <div><div className="dock-label">Wholesale rollover decline</div><div className="seg-ctl" style={{ width: "100%", marginBottom: 10 }}>{NSFRSEV.map((s, i) => <button key={s.name} className={drop === s.drop ? "on" : ""} style={{ flex: 1, padding: "6px 2px" }} onClick={() => setDrop(s.drop)}>{s.name === "Moderate" ? "Mod" : s.name}</button>)}</div>
            <div className="between" style={{ marginBottom: 5 }}><span style={{ fontSize: 12, fontWeight: 600 }}>ASF factor decline</span><span className="chip chip-indigo" style={{ fontSize: 10.5 }}>{drop}%</span></div>
            <input type="range" className="lev-slider" min={0} max={100} step={5} value={drop} onChange={e => setDrop(+e.target.value)} />
            <div className="muted" style={{ fontSize: 10.5, marginTop: 3 }}>cuts the stable-funding value of wholesale funding</div></div>
          <hr className="hr" style={{ margin: "16px 0" }} />
          <div className="dock-label">Live NSFR (stressed)</div>
          <div className="big-num tnum" style={{ fontSize: 30, color: breach ? "var(--red)" : "var(--green-ink)", marginTop: 2 }}>{live.toFixed(0)}%</div>
          <div className="gauge"><span className="gauge-fill" style={{ width: Math.max(2, Math.min(98, live / 200 * 100)) + "%", background: breach ? "var(--red)" : "var(--green)" }} /><span className="gauge-floor" style={{ left: "50%" }} /></div>
          <div className="between" style={{ fontSize: 10.5, color: "var(--ink-3)", marginTop: 3 }}><span>0%</span><span>100% floor</span><span>200%</span></div>
          {breach ? <button className="btn btn-dark btn-sm" style={{ width: "100%", marginTop: 12 }} onClick={() => go("recovery")}>Breach — Send to Recovery <Icon name="arrowR" size={13} /></button> : <div className="chip chip-green chip-lg" style={{ marginTop: 12 }}><span className="dot" />Within 100% floor</div>}
          <button className="btn btn-ghost btn-sm" style={{ width: "100%", marginTop: 8 }} onClick={() => setDrop(0)}><Icon name="refresh" size={13} /> Clear stress · restore baseline</button>
        </aside>}
      </div>
    </React.Fragment>
  );
}
