// DebtSchedulePage — window.DebtSchedulePage
//
// The debt schedule for financing the general ledger does not record as debt.
//
// WHY THIS PAGE EXISTS. When a merchant cash advance or revenue-based financing
// deal is booked wrong, the money arrives in the bank and the offsetting credit
// lands in revenue or in a suspense account instead of in a liability. The result
// is a balance sheet with no obligation on it and an income statement carrying
// borrowed cash as sales. Nothing in a normal debt schedule can show that,
// because a normal debt schedule reads the liability accounts — and by
// construction there is nothing in them to read. This page reads the integrity
// layer's identification instead.
//
// EVERY FIGURE HERE IS THE SIGNAL'S. The page performs no detection and no
// arithmetic of its own beyond summing rows the evidence already separated. It
// reads `hidden_financing_detected` through window.PerduraFinancingEvidence, the
// same reader the P&L distortion banner and the Revenue page flag use, so the
// three surfaces cannot quote different numbers for one finding.
//
// TENANT-AGNOSTIC BY CONSTRUCTION. There is not a funder name, a dollar
// threshold, an account code or a tenant conditional anywhere in this file. The
// only gate is whether the signal fired: a tenant with clean books gets the clean
// state below, and a tenant that starts stacking advances is covered the day the
// signal fires, with no new code here.
//
// THE THREE THINGS THIS PAGE WILL NOT DO
//
//   1. It will not state a remaining balance, an amount repaid, an effective rate
//      or a total cost. The repayment stream is not in the ledger — the evidence
//      says so explicitly via repayment_terms_available — so all four are
//      unknowable. They appear as named, empty columns explaining why, because a
//      schedule that silently omitted them would read as though the advances were
//      never repaid, and one that estimated them would be inventing the most
//      important numbers on the page.
//
//   2. It will not add the revenue-offset portion to the other portion. They are
//      separate facts: only the revenue-offset portion inflated the P&L. A
//      combined "impact" number would overstate the correction against lines that
//      were never inflated.
//
//   3. It will not present funder-relationship evidence as an advance. A funder
//      that appears only as an outgoing fee tells you a financing relationship
//      exists; it does not tell you what was advanced. That block sits apart from
//      every total on the page and says the advance is not in the ledger.

(function () {
  const h = React.createElement;
  const { useState } = React;

  const MONO = "ui-monospace, SFMono-Regular, Menlo, monospace";
  const MUTE = "#6475a0";

  const fmtDay = (d) => {
    if (!d) return "—";
    try { return new Date(d + "T00:00:00").toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }); }
    catch (e) { return d; }
  };

  // "Not computable" is a first-class value on this page, not a blank. It renders
  // as a labelled cell with the reason attached so a reader never has to guess
  // whether the number is zero, missing, or unknowable.
  function NotComputable({ reason, compact }) {
    return h("span", {
      title: reason,
      style: {
        display: "inline-flex", alignItems: "baseline", gap: "0.375rem",
        fontFamily: MONO, fontSize: compact ? "0.6875rem" : "0.75rem", color: MUTE,
      },
    },
      h("span", { style: { fontStyle: "italic" } }, "not computable"),
      h("span", {
        style: {
          fontSize: "0.625rem", color: MUTE, opacity: 0.75, fontStyle: "normal",
          borderBottom: "1px dotted currentColor", cursor: "help",
        },
      }, "why"));
  }

  function Page(props) {
    const K = window.PerduraPageKit;
    const FE = window.PerduraFinancingEvidence;
    if (!K) return h("div", { className: "pc-page" }, "Loading…");
    if (!FE) return h("div", { className: "pc-page" }, "Financing evidence reader not loaded.");

    const { data, setPage } = props;
    const { loading, error, financing, noLedger } = FE.useFinancingEvidence(data);
    const [tab, setTab] = useState("schedule");

    const M = (v) => K.moneyStr(v, { compact: true });
    const Mf = (v) => K.moneyStr(v);

    const hero = {
      eyebrow: "GENERAL LEDGER",
      title: "Debt & Financing Schedule",
      subtitle: financing
        ? (financing.funderCount || 0) + " financing counterpart" + (financing.funderCount === 1 ? "y" : "ies") +
          " · " + Mf(financing.totalAdvanced) + " advanced · identified from posting structure, not from the liability accounts"
        : "Financing identified from the general ledger's posting structure",
    };

    // ── loading / error / clean states ────────────────────────────────────────
    if (loading) {
      return h(K.Shell, { hero },
        h(K.Card, { title: "Reading the ledger" },
          h("div", { style: { padding: "1.5rem", color: MUTE, fontSize: "0.8125rem" } },
            "Evaluating this company's posting structure for unrecorded financing…")));
    }

    if (error) {
      return h(K.Shell, { hero },
        h(K.Card, { title: "Could not evaluate this ledger" },
          h("div", { style: { padding: "1.5rem", fontSize: "0.8125rem", lineHeight: 1.7, color: "var(--text-2)" } },
            h("div", { style: { color: "var(--danger)", fontWeight: 700, marginBottom: "0.5rem" } },
              "This page is not saying the books are clean."),
            "The financing evaluation did not complete, so no opinion could be formed either way. ",
            "That is different from finding nothing, and it is shown as an error rather than as an all-clear on purpose.",
            h("div", { style: { marginTop: "0.75rem", fontFamily: MONO, fontSize: "0.6875rem", color: MUTE } },
              String((error && error.message) || error)))));
    }

    // No ledger read yet. Distinct from the clean state on purpose, and the
    // distinction is the whole point: "we found nothing" is an opinion about the
    // books, and it must never be printed when nothing was examined. Rendering
    // the all-clear here would tell a reader their financing is clean because a
    // fetch had not finished.
    if (noLedger) {
      return h(K.Shell, { hero },
        h(K.Card, { title: "No ledger loaded" },
          h("div", { style: { padding: "1.5rem", fontSize: "0.8125rem", lineHeight: 1.75, color: "var(--text-2)" } },
            "There are no general-ledger transactions in view for this company yet, so the financing check ",
            h("b", null, "has not run"), ". ",
            "This page is not saying the books are clean — it is saying nothing has been examined. ",
            "Connect an accounting source or wait for the sync to finish, and the check runs automatically.")));
    }

    // The honest clean state. Most tenants land here, and they should — the
    // detector fires on the posting fingerprint of unrecorded financing, and most
    // businesses do not have one.
    if (!financing) {
      return h(K.Shell, { hero },
        h(K.Card, { title: "No unrecorded financing identified" },
          h("div", { style: { padding: "1.5rem", fontSize: "0.8125rem", lineHeight: 1.8, color: "var(--text-2)" } },
            h("div", { style: { fontWeight: 700, color: "var(--positive)", marginBottom: "0.625rem" } },
              "✓ This company's ledger shows no sign of financing recorded as something other than debt."),
            h("div", { style: { marginBottom: "0.875rem" } },
              "The check looks for a specific posting fingerprint: large cash receipts from two or more distinct ",
              "non-customer counterparties inside a rolling window, each offset to a revenue, expense or non-cash ",
              "asset account rather than to a liability. Money received but not recorded as money owed. ",
              "No such pattern is present here."),
            h("div", {
              style: {
                padding: "0.875rem 1rem", borderRadius: 8, background: "rgba(13,32,64,.035)",
                border: "1px solid rgba(13,32,64,.08)", fontSize: "0.75rem", color: MUTE, lineHeight: 1.7,
              },
            },
              h("b", { style: { color: "var(--text-2)" } }, "What this does and does not say. "),
              "It says nothing suspicious was found in how receipts are posted. It is not an audit, ",
              "and it cannot see financing that never passed through this ledger at all — an advance ",
              "routed through receivables, or one taken by an entity whose books are not connected here. ",
              "Debt that is correctly recorded as debt is not this page's subject; it appears on the ",
              "balance sheet, where it belongs."))));
    }

    const f = financing;
    const revShare = (f.annualRevenue && f.bookedAsRevenue)
      ? (f.bookedAsRevenue / f.annualRevenue) * 100 : null;

    // ── KPI row ───────────────────────────────────────────────────────────────
    // total_advanced is the gross the funders sent. It is shown as the headline
    // because this is a financing schedule, but the two portions beneath it are
    // never combined into a third "impact" figure.
    const kpis = h("div", {
      className: "pa-kpi-row",
      style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: "0.875rem", marginBottom: "1.375rem" },
    },
      h(K.Kpi, { label: "Total Financing Identified", value: M(f.totalAdvanced), sub: "gross advanced", accent: "var(--danger)" }),
      h(K.Kpi, { label: "Funding Counterparties", value: String(f.funderCount == null ? "—" : f.funderCount), sub: f.windowDays != null ? "over " + f.windowDays + " days" : null, accent: "var(--warning)" }),
      h(K.Kpi, { label: "Booked to Revenue", value: M(f.bookedAsRevenue), sub: revShare != null ? revShare.toFixed(1) + "% of annual revenue" : "overstates the P&L", accent: "var(--danger)" }),
      h(K.Kpi, { label: "Booked Elsewhere", value: M(f.bookedElsewhere), sub: "never touched the P&L", accent: "var(--text-2)" }),
      h(K.Kpi, { label: "Recorded as Debt", value: M(f.recordedDebt), sub: "what the balance sheet shows", accent: "var(--info)" }));

    // ── the standing caveat ───────────────────────────────────────────────────
    const caveat = h("div", {
      style: {
        padding: "1rem 1.125rem", borderRadius: 10, marginBottom: "1.375rem",
        background: "color-mix(in srgb, var(--danger) 7%, var(--surface-1, #ffffff))",
        border: "1px solid color-mix(in srgb, var(--danger) 35%, var(--border))",
        borderLeft: "4px solid var(--danger)",
      },
    },
      h("div", { style: { fontSize: "0.6875rem", fontWeight: 800, letterSpacing: "0.08em", color: "var(--danger)", marginBottom: "0.5rem" } },
        "THIS FINANCING IS NOT ON THE BALANCE SHEET"),
      h("div", { style: { fontSize: "0.8125rem", color: "var(--text-2)", lineHeight: 1.75 } },
        "The advances below arrived as cash and were offset to income or expense accounts instead of to a ",
        "liability. The obligation to repay them therefore appears nowhere in the balance sheet's debt, and ",
        f.bookedAsRevenue != null && f.bookedAsRevenue > 0
          ? h(React.Fragment, null,
              Mf(f.bookedAsRevenue), " of it was posted to revenue accounts, so reported revenue and EBITDA ",
              "are overstated by that amount. ")
          : null,
        "This schedule identifies the money in; correcting it belongs in the source books — journal the ",
        "advances to a financing liability. Do not remap the revenue account in reporting, because that ",
        "account also carries the company's real sales."),
      f.limits ? h("div", {
        style: { marginTop: "0.625rem", fontSize: "0.75rem", color: MUTE, fontStyle: "italic" },
      }, "Limit carried with the finding: ", f.limits) : null);

    // ── tabs ──────────────────────────────────────────────────────────────────
    const TABS = [
      ["schedule", "▤ Debt Schedule"],
      ["advances", "◈ Identified Advances (" + f.advances.length + ")"],
    ];
    if (f.relationships.length) TABS.push(["relationships", "⚑ Funder Relationships (" + f.relationships.length + ")"]);
    TABS.push(["limits", "⊘ What Is Not Computable"]);

    const tabBar = h("div", {
      style: { display: "flex", gap: "0.25rem", borderBottom: "2px solid rgba(13,32,64,.08)", marginBottom: "1.375rem", overflowX: "auto" },
    },
      TABS.map(([id, label]) => h("button", {
        key: id, onClick: () => setTab(id),
        style: {
          padding: "0.625rem 1.125rem", fontSize: "0.75rem", fontWeight: 700, cursor: "pointer",
          border: "none", borderBottom: tab === id ? "3px solid #0d2040" : "3px solid transparent",
          background: "transparent", color: tab === id ? "#0d2040" : MUTE, whiteSpace: "nowrap",
        },
      }, label)));

    const th = {
      textAlign: "left", padding: "0.5rem 0.75rem", fontSize: "0.625rem", fontWeight: 800,
      letterSpacing: "0.06em", color: MUTE, textTransform: "uppercase",
      borderBottom: "2px solid rgba(13,32,64,.1)", whiteSpace: "nowrap",
    };
    const thR = Object.assign({}, th, { textAlign: "right" });
    const td = { padding: "0.625rem 0.75rem", fontSize: "0.75rem", borderBottom: "1px solid rgba(13,32,64,.055)", verticalAlign: "top" };
    const tdR = Object.assign({}, td, { textAlign: "right", fontFamily: MONO, fontVariantNumeric: "tabular-nums" });

    // Corroborating memo token, shown as evidence ON the row it corroborates.
    // Absent is a legitimate state: the structural gates identified the advance,
    // and no supporting free text happened to accompany it. That is a weaker
    // label on the same finding, never a weaker finding.
    const tokenChip = (t) => t
      ? h("code", {
          title: "Financing token found in this transaction's bank memo or counterparty field. Corroboration only — the advance was identified by its posting structure.",
          style: {
            fontFamily: MONO, fontSize: "0.625rem", fontWeight: 700, padding: "0.125rem 0.375rem",
            borderRadius: 4, background: "color-mix(in srgb, var(--warning) 18%, transparent)",
            color: "var(--text-1)", whiteSpace: "nowrap",
          },
        }, t)
      : h("span", { title: "No financing token in the memo. Identified on posting structure alone.", style: { color: MUTE, fontSize: "0.6875rem" } }, "structural only");

    // The repayment cell. Three genuinely different outcomes, rendered as three
    // different things — the whole point of gating the cadence search is that
    // "we found no stream" stays distinguishable from "we did not look" and from
    // "the balance is zero".
    function repaymentCell(rp) {
      if (!rp) {
        return h(NotComputable, { compact: true, reason: "No repayment search result is attached to this funder." });
      }
      if (rp.ambiguous) {
        return h("span", {
          title: "This funder's normalized name is shared by more than one counterparty in the ledger, so outflows cannot be attributed to it without guessing. Searching loosely here is how payroll gets mistaken for merchant-advance repayment.",
          style: { fontFamily: MONO, fontSize: "0.6875rem", color: MUTE, fontStyle: "italic" },
        }, "not attributable");
      }
      if (rp.cadence_identified) {
        return h("span", { style: { display: "inline-block" } },
          h("span", { style: { fontWeight: 700, color: "var(--text-1)" } }, Mf(rp.repayment_total)),
          h("div", { style: { fontSize: "0.5625rem", color: MUTE, fontWeight: 600, marginTop: "0.125rem" } },
            rp.repayment_count + " debits · ~" + rp.median_gap_days + "d cadence"),
          rp.fee_total > 0
            ? h("div", { style: { fontSize: "0.5625rem", color: "var(--warning)", marginTop: "0.0625rem" } }, "+ " + Mf(rp.fee_total) + " fees")
            : null);
      }
      // No cadence. Say so, and show the outflow evidence behind the statement
      // so the reader can see it is a measurement rather than an assumption.
      return h("span", {
        title: "A repayment cadence was searched for against this specific funder and none is present. " +
          (rp.outflow_count
            ? rp.outflow_count + " outflow(s) totalling " + Mf(rp.outflow_total) + " went to this counterparty — too few and too small to be a repayment stream, consistent with wire or servicing fees."
            : "No cash outflow to this counterparty appears in the ledger at all.") +
          " Merchant advances are usually repaid by daily or weekly ACH debits, often netted from receipts before the cash arrives; if that is happening here it is not reaching these books.",
        style: { display: "inline-block", cursor: "help" },
      },
        h("span", { style: { fontFamily: MONO, fontSize: "0.6875rem", color: MUTE, fontStyle: "italic", borderBottom: "1px dotted currentColor" } },
          "no stream in ledger"),
        h("div", { style: { fontSize: "0.5625rem", color: MUTE, marginTop: "0.125rem", fontStyle: "normal" } },
          rp.outflow_count ? rp.outflow_count + " outflow · " + Mf(rp.outflow_total) : "no outflow found"));
    }

    // ── Tab: Debt Schedule (per funder) ───────────────────────────────────────
    function scheduleTab() {
      return h("div", null,
        kpis,
        caveat,
        h(K.Card, {
          title: "Financing by counterparty",
          sub: "Advanced is measured. Repaid and remaining are not in this ledger and are not estimated.",
          padding: 0,
        },
          h("div", { style: { overflowX: "auto" } },
            h("table", { style: { width: "100%", borderCollapse: "collapse", minWidth: 900 } },
              h("thead", null, h("tr", null,
                h("th", { style: th }, "Funding counterparty"),
                h("th", { style: th }, "First advance"),
                h("th", { style: th }, "Last advance"),
                h("th", { style: thR }, "Advances"),
                h("th", { style: thR }, "Advanced"),
                // NOT "Repaid to date". That heading would imply the column is a
                // complete account of repayment; this column reports only what a
                // search of the postings found, which is a different claim.
                h("th", { style: thR }, "Repayments in ledger"),
                h("th", { style: thR }, "Remaining balance"),
                h("th", { style: thR }, "Effective rate"))),
              h("tbody", null,
                f.byFunder.map((row, i) => h("tr", { key: i },
                  h("td", { style: td },
                    h("div", { style: { fontWeight: 700, color: "var(--text-1)" } }, row.counterparty),
                    row.counterparty_raw && row.counterparty_raw.toUpperCase().replace(/[^A-Z0-9 ]/g, " ").replace(/\s+/g, " ").trim() !== row.counterparty
                      ? h("div", { style: { fontSize: "0.625rem", color: MUTE, marginTop: "0.125rem" } }, "ledger: ", row.counterparty_raw)
                      : null,
                    row.tokens.length
                      ? h("div", { style: { marginTop: "0.3125rem", display: "flex", gap: "0.25rem", flexWrap: "wrap" } }, row.tokens.map((t, j) => h("span", { key: j }, tokenChip(t))))
                      : null),
                  h("td", { style: Object.assign({}, td, { fontFamily: MONO, fontSize: "0.6875rem" }) }, fmtDay(row.first_date)),
                  h("td", { style: Object.assign({}, td, { fontFamily: MONO, fontSize: "0.6875rem" }) }, fmtDay(row.last_date)),
                  h("td", { style: tdR }, String(row.count)),
                  h("td", { style: Object.assign({}, tdR, { fontWeight: 700, color: "var(--danger)" }) }, Mf(row.advanced)),
                  h("td", { style: tdR }, repaymentCell(row.repayment)),
                  h("td", { style: tdR }, h(NotComputable, { compact: true, reason: "Remaining balance = advanced − repaid. Repaid is unknown, so remaining cannot be derived. Obtain the funding agreements and the funders' statements." })),
                  h("td", { style: tdR }, h(NotComputable, { compact: true, reason: "An effective rate requires the total repayment amount and the payment schedule. Neither is in the ledger. A rate computed without them would be a fabrication with a percent sign on it." })))),
                h("tr", { style: { background: "rgba(13,32,64,.04)" } },
                  h("td", { style: Object.assign({}, td, { fontWeight: 800 }) }, "Total identified"),
                  h("td", { style: td }, ""),
                  h("td", { style: td }, ""),
                  h("td", { style: Object.assign({}, tdR, { fontWeight: 800 }) }, String(f.advances.length)),
                  h("td", { style: Object.assign({}, tdR, { fontWeight: 800, color: "var(--danger)" }) }, Mf(f.totalAdvanced)),
                  h("td", { style: Object.assign({}, tdR, { fontWeight: 700 }) },
                    f.repaymentStreamsIdentified > 0 ? Mf(f.repayments.reduce((s, r) => s + (r.cadence_identified ? r.repayment_total : 0), 0)) : "—"),
                  h("td", { style: tdR }, "—"),
                  h("td", { style: tdR }, "—")))))),

        // What the repayment search did, stated plainly. This panel is the reason
        // the column above is allowed to say "no stream" rather than "not
        // computable": a search ran, against a named set, and came back empty.
        h("div", {
          style: {
            marginTop: "1rem", padding: "0.9375rem 1.0625rem", borderRadius: 9,
            background: f.repaymentStreamsIdentified > 0
              ? "color-mix(in srgb, var(--warning) 8%, transparent)"
              : "rgba(13,32,64,.035)",
            border: "1px solid " + (f.repaymentStreamsIdentified > 0
              ? "color-mix(in srgb, var(--warning) 30%, var(--border))" : "rgba(13,32,64,.09)"),
            fontSize: "0.75rem", color: "var(--text-2)", lineHeight: 1.75,
          },
        },
          h("b", { style: { color: "var(--text-1)" } }, "The repayment search. "),
          "A repayment cadence — three or more debits to the same counterparty on a 1–35 day rhythm — was searched for against ",
          h("b", null, f.fundersSearchedForRepayment),
          " identified funding counterpart", f.fundersSearchedForRepayment === 1 ? "y" : "ies",
          f.repaymentStreamsIdentified > 0
            ? h(React.Fragment, null, ". ", h("b", null, f.repaymentStreamsIdentified), " repayment stream",
                f.repaymentStreamsIdentified === 1 ? " was" : "s were", " found and ",
                f.repaymentStreamsIdentified === 1 ? "is" : "are", " shown per funder above.")
            : h(React.Fragment, null, " and ", h("b", null, "none was found"),
                ". In total ", h("b", null, Mf(f.outflowToFunders)), " of cash left to those counterparties, against ",
                Mf(f.totalAdvanced), " advanced — the size of servicing fees, not of a repayment stream. ",
                "The repayments are not reaching these books."),
          h("div", { style: { marginTop: "0.5rem", color: MUTE } },
            h("b", null, "Why the search is restricted to identified funders. "),
            "Applied to the open ledger, the same cadence rule matches ordinary business rhythm — payroll, owner draws, ",
            "carrier settlements, fuel cards, payroll tax. On one measured ledger it admitted 142 counterparties, and on a ",
            "company with clean books it paired a weekly payroll run as though it were a merchant advance. ",
            "Restricting the search to counterparties the posting structure has already identified is what keeps that out. ",
            h("b", null, "No withholding percentage is computed anywhere on this page"),
            " — a repayments-to-revenue ratio built on repayments that are not in the ledger would be fiction, in the same way an effective rate would be."),
          f.repayments.some((r) => r.ambiguous)
            ? h("div", { style: { marginTop: "0.5rem", color: MUTE } },
                h("b", null, "One or more funders is marked not attributable. "),
                "Its normalized name is shared with another counterparty in this ledger, so outflows cannot be assigned to it without guessing. ",
                "The search declines rather than attributing loosely.")
            : null),

        // The two portions, side by side and never summed.
        h("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: "1rem", marginTop: "1.375rem" } },
          h(K.Card, { title: "Portion that inflated the P&L" },
            h("div", { style: { padding: "1.125rem" } },
              h("div", { style: { fontFamily: MONO, fontSize: "1.5rem", fontWeight: 800, color: "var(--danger)" } }, Mf(f.bookedAsRevenue)),
              h("div", { style: { fontSize: "0.75rem", color: "var(--text-2)", lineHeight: 1.7, marginTop: "0.5rem" } },
                "Offset to revenue accounts. Reported revenue and EBITDA are overstated by this amount",
                revShare != null ? h(React.Fragment, null, " — ", h("b", null, revShare.toFixed(1) + "%"), " of annual revenue") : null,
                ". This is the figure a restatement subtracts."))),
          h(K.Card, { title: "Portion that did not" },
            h("div", { style: { padding: "1.125rem" } },
              h("div", { style: { fontFamily: MONO, fontSize: "1.5rem", fontWeight: 800, color: "var(--text-2)" } }, Mf(f.bookedElsewhere)),
              h("div", { style: { fontSize: "0.75rem", color: "var(--text-2)", lineHeight: 1.7, marginTop: "0.5rem" } },
                "Offset to expense, suspense or non-cash asset accounts. Still unrecorded debt, but it never ",
                "inflated revenue. ",
                h("b", null, "Not added to the figure on the left"), " — subtracting the combined total would ",
                "overstate the correction against lines that were never inflated.")))),

        f.financingNamedSpend != null && f.financingNamedSpend > 0
          ? h(K.Card, { title: "Separately: financing-named accounts sitting in overhead", sub: "A different finding, shown here because it is adjacent — not a repayment measurement." },
              h("div", { style: { padding: "1.125rem", fontSize: "0.8125rem", color: "var(--text-2)", lineHeight: 1.7 } },
                h("b", { style: { fontFamily: MONO, color: "var(--warning)" } }, Mf(f.financingNamedSpend)),
                " of spend sits in accounts whose own names describe financing activity (loan, factoring or advance ",
                "fees) but which are mapped to operating expense or COGS. That claims the classification is wrong. ",
                h("b", null, "It does not measure repayment of the advances above"), " and must not be read as doing so."))
          : null);
    }

    // ── Tab: Identified Advances (per transaction) ────────────────────────────
    function advancesTab() {
      return h("div", null,
        h(K.Card, {
          title: "Every identified advance",
          sub: "One row per cash receipt the detector identified, with the account its offsetting entry was posted to.",
          padding: 0,
        },
          h("div", { style: { overflowX: "auto" } },
            h("table", { style: { width: "100%", borderCollapse: "collapse", minWidth: 900 } },
              h("thead", null, h("tr", null,
                h("th", { style: th }, "Date"),
                h("th", { style: th }, "Funding counterparty"),
                h("th", { style: thR }, "Amount advanced"),
                h("th", { style: th }, "Miscoded to"),
                h("th", { style: th }, "Category"),
                h("th", { style: th }, "Corroborating memo token"))),
              h("tbody", null,
                f.advances.map((a, i) => h("tr", { key: i },
                  h("td", { style: Object.assign({}, td, { fontFamily: MONO, fontSize: "0.6875rem", whiteSpace: "nowrap" }) }, fmtDay(a.date)),
                  h("td", { style: td },
                    h("div", { style: { fontWeight: 700, color: "var(--text-1)" } }, a.counterparty),
                    a.counterparty_raw ? h("div", { style: { fontSize: "0.625rem", color: MUTE, marginTop: "0.125rem" } }, a.counterparty_raw) : null),
                  h("td", { style: Object.assign({}, tdR, { fontWeight: 700, color: "var(--danger)" }) }, Mf(a.amount)),
                  h("td", { style: td },
                    h("div", { style: { color: "var(--text-1)" } }, a.offset_account_name || "—"),
                    a.offset_account_code ? h("div", { style: { fontSize: "0.625rem", color: MUTE, fontFamily: MONO, marginTop: "0.125rem" } }, "acct ", a.offset_account_code) : null),
                  h("td", { style: td },
                    h("span", {
                      style: {
                        fontSize: "0.6875rem", fontWeight: 700, padding: "0.1875rem 0.4375rem", borderRadius: 4,
                        background: a.offset_is_revenue ? "color-mix(in srgb, var(--danger) 15%, transparent)" : "rgba(13,32,64,.06)",
                        color: a.offset_is_revenue ? "var(--danger)" : "var(--text-2)",
                      },
                    }, a.offset_category || "—"),
                    a.offset_is_revenue
                      ? h("div", { style: { fontSize: "0.625rem", color: "var(--danger)", marginTop: "0.25rem", fontWeight: 700 } }, "inflates reported revenue")
                      : h("div", { style: { fontSize: "0.625rem", color: MUTE, marginTop: "0.25rem" } }, "did not touch the P&L")),
                  h("td", { style: td }, tokenChip(a.memo_token)))))))),

        h("div", {
          style: {
            marginTop: "1rem", padding: "0.875rem 1rem", borderRadius: 8,
            background: "rgba(13,32,64,.035)", border: "1px solid rgba(13,32,64,.08)",
            fontSize: "0.75rem", color: MUTE, lineHeight: 1.75,
          },
        },
          h("b", { style: { color: "var(--text-2)" } }, "How these were identified. "),
          "By posting structure, not by name. Each is a cash receipt large relative to this company's own ",
          "receipt distribution, from a counterparty that is not a known customer, whose same-day offsetting ",
          "entry landed somewhere other than a liability, cash, receivable or equity account — with at least ",
          "two such counterparties inside a rolling window. ",
          h("b", { style: { color: "var(--text-2)" } }, "The memo token is corroboration applied afterwards"),
          ", never a reason a row is here: matching free text alone flags ordinary trading partners in bulk, ",
          "so it labels rows the structure already identified and admits none of its own."));
    }

    // ── Tab: Funder relationships (outflow-only) ──────────────────────────────
    function relationshipsTab() {
      return h("div", null,
        h(K.Card, {
          title: "Funder relationship evidence — advance not in ledger",
          sub: "Financing counterparties that appear only as money going out. These are not advances and are in no total on this page.",
          padding: 0,
        },
          h("div", { style: { overflowX: "auto" } },
            h("table", { style: { width: "100%", borderCollapse: "collapse", minWidth: 820 } },
              h("thead", null, h("tr", null,
                h("th", { style: th }, "Counterparty"),
                h("th", { style: th }, "First seen"),
                h("th", { style: th }, "Last seen"),
                h("th", { style: thR }, "Entries"),
                h("th", { style: thR }, "Cash paid out"),
                h("th", { style: th }, "Booked to"),
                h("th", { style: thR }, "Amount advanced"))),
              h("tbody", null,
                f.relationships.map((r, i) => h("tr", { key: i },
                  h("td", { style: td },
                    h("div", { style: { fontWeight: 700, color: "var(--text-1)" } }, r.counterparty),
                    h("div", { style: { marginTop: "0.3125rem" } }, tokenChip(r.memo_token))),
                  h("td", { style: Object.assign({}, td, { fontFamily: MONO, fontSize: "0.6875rem" }) }, fmtDay(r.first_date)),
                  h("td", { style: Object.assign({}, td, { fontFamily: MONO, fontSize: "0.6875rem" }) }, fmtDay(r.last_date)),
                  h("td", { style: tdR }, String(r.entry_count)),
                  h("td", { style: Object.assign({}, tdR, { fontWeight: 700 }) }, Mf(r.outflow_total)),
                  h("td", { style: Object.assign({}, td, { fontSize: "0.6875rem" }) },
                    (r.booked_to || []).length ? (r.booked_to || []).map((b, j) => h("div", { key: j }, b)) : "—"),
                  h("td", { style: tdR },
                    h("span", { style: { fontStyle: "italic", color: MUTE, fontSize: "0.6875rem" } }, "not in ledger")))))))),

        h("div", {
          style: {
            marginTop: "1rem", padding: "0.875rem 1rem", borderRadius: 8,
            background: "color-mix(in srgb, var(--warning) 8%, transparent)",
            border: "1px solid color-mix(in srgb, var(--warning) 30%, var(--border))",
            fontSize: "0.75rem", color: "var(--text-2)", lineHeight: 1.75,
          },
        },
          h("b", null, "What this block claims, exactly. "),
          "That a financing relationship with this counterparty exists — the company paid it money, and the ",
          "fee was booked to the account shown. It claims nothing about how much was advanced, when, or ",
          "whether anything was advanced through this ledger at all. The advance may have been routed ",
          "through an account this check cannot see, taken by a related entity, or predate the data. ",
          h("b", null, "Ask the company for the agreement"), " — that is the only thing that resolves it."));
    }

    // ── Tab: limits ───────────────────────────────────────────────────────────
    function limitsTab() {
      const rows = [
        ["Amount advanced, per counterparty, per date", true, "Measured from the cash receipts themselves."],
        ["The account each advance was miscoded to", true, "Read from the offsetting entry."],
        ["Which portion inflated revenue", true, "The revenue-offset rows, kept separate from the rest."],
        ["First and last advance, window length", true, "From the identified transaction dates."],
        ["Repayments present in the ledger", true, "Searched per identified funder: three or more debits to that counterparty on a 1–35 day rhythm. Found streams are shown with their cadence; where none exists the schedule says so and shows the outflow evidence behind that statement."],
        ["Amount repaid to date (in full)", false, "Different from the line above. What was searched is what these books record; repayments netted from receipts before the cash arrives never appear as a debit at all, so even a found stream is a floor, not a total."],
        ["Remaining balance / payoff", false, "Requires the total obligation (from the agreement) and the full amount repaid. Neither is available, and a found repayment stream does not supply either."],
        ["MCA withholding % of revenue", false, "Repayments ÷ revenue. Deliberately not computed: on a ledger where the repayments are absent the numerator is unknown, and computing it from a loosely-matched cadence turns a company's payroll run into a distress signal. Measured on a tenant with clean books, the ungated version of that ratio read 31.8%."],
        ["Effective rate / APR / factor rate", false, "Requires the total repayment amount and the payment schedule. Both live in the funding agreements, not in the ledger."],
        ["Total cost of the financing", false, "Same reason as the rate: it is a property of the agreement, not of the postings."],
        ["Whether more financing exists", false, "Advances routed through receivables, or taken by an entity whose books are not connected here, are outside what any GL-based check can see."],
      ];
      return h("div", null,
        h(K.Card, {
          title: "What this schedule can and cannot tell you",
          sub: "Stated rather than implied. The empty columns above are empty for the reasons below.",
          padding: 0,
        },
          h("div", { style: { overflowX: "auto" } },
            h("table", { style: { width: "100%", borderCollapse: "collapse", minWidth: 700 } },
              h("thead", null, h("tr", null,
                h("th", { style: th }, ""),
                h("th", { style: th }, "Figure"),
                h("th", { style: th }, "Why"))),
              h("tbody", null,
                rows.map(([label, ok, why], i) => h("tr", { key: i },
                  h("td", { style: Object.assign({}, td, { width: 32, textAlign: "center", fontSize: "0.9375rem" }) }, ok ? "✅" : "⊘"),
                  h("td", { style: Object.assign({}, td, { fontWeight: 700, color: ok ? "var(--text-1)" : MUTE, width: "34%" }) }, label),
                  h("td", { style: Object.assign({}, td, { color: "var(--text-2)", lineHeight: 1.65 }) }, why))))))),

        h(K.Card, { title: "What to do about it" },
          h("div", { style: { padding: "1.125rem", fontSize: "0.8125rem", color: "var(--text-2)", lineHeight: 1.8 } },
            h("ol", { style: { margin: 0, paddingLeft: "1.25rem" } },
              h("li", null, h("b", null, "Obtain each funding agreement"), " — principal, total repayment, payment size and frequency. That is the only source for the four figures above that this ledger cannot produce."),
              h("li", null, h("b", null, "Fix it at source"), ": journal the advances to a financing liability so the obligation appears on the balance sheet and the repayments have somewhere to go."),
              h("li", null, h("b", null, "Do not remap the revenue account"), " in reporting. It also carries real sales — remapping would move genuine revenue along with the advances and turn a books defect into a reporting defect."),
              h("li", null, h("b", null, "Reconcile any factoring or clearing account"), " to establish whether repayments are being netted out of receipts before the cash lands.")))));
    }

    return h(K.Shell, { hero },
      tabBar,
      tab === "schedule" ? scheduleTab() : null,
      tab === "advances" ? advancesTab() : null,
      tab === "relationships" ? relationshipsTab() : null,
      tab === "limits" ? limitsTab() : null);
  }

  window.DebtSchedulePage = Page;
})();
