// Bylt Capital CRM — clients: one file per business, with every advance it is
// paying back (ours and other brokers'), the running balance, deposits by
// month, its deals, and renewal follow-ups.

const BROKERED_BY = [{ value: "Bylt Capital", label: "Bylt Capital" }, { value: "Other broker", label: "Other broker" }, { value: "Unknown", label: "Unknown" }];
const FREQ_OPTS = [{ value: "Daily", label: "Daily" }, { value: "Weekly", label: "Weekly" }];
const brokerPill = (by, a) => a && a.source === "merchant" ? <span className="pill plain s-neutral" title="The client added this in the portal">Added by client</span> : <span className={`pill plain ${by === "Bylt Capital" ? "s-new" : "s-neutral"}`}>{by || "Unknown"}</span>;
const payLabel = (a) => `${fmt.money(a.payment)} / ${a.freq === "Weekly" ? "wk" : "day"}`;
// "2026-09-25" → "Sep 25 ’26" (a plain calendar day; no time-zone shifting)
const dayYear = (d) => (d ? `${fmt.day(d)} ’${String(d).slice(2, 4)}` : "—");
const pctColor = (a) => (a.renewalDue ? "var(--st-submitted)" : "var(--st-funded)");
// The first payment after a funding day: the next business day (daily) or 7 days later (weekly). Same rule as src/advances.js.
function firstPaymentFrom(fundedOn, freq) {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(fundedOn || ""));
  if (!m) return "";
  const d = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3]));
  if (freq === "Weekly") d.setUTCDate(d.getUTCDate() + 7);
  else do { d.setUTCDate(d.getUTCDate() + 1); } while (d.getUTCDay() === 0 || d.getUTCDay() === 6);
  return d.toISOString().slice(0, 10);
}

// ── Running balance (donut + figures) ──────────────────────────────────────
function BalanceCard({ totals, big }) {
  const t = totals || { open: 0, remaining: 0, payback: 0, pct: 0, monthly: 0, payoff: null };
  const size = big ? 84 : 64;
  return (
    <div className="card balance-card">
      <div className="donut" style={{ width: size, height: size, "--pct": t.pct + "%" }} role="img" aria-label={`${t.pct}% repaid`}><span style={{ width: size - 20, height: size - 20 }}>{t.pct}%</span></div>
      <div style={{ minWidth: 0 }}>
        <div className="label">Running balance</div>
        <div className="serif tnum amt">{fmt.money(t.remaining)}</div>
        <div className="dim" style={{ fontSize: 12.5 }}>
          {t.open ? <>{t.open} open · <b className="tnum" style={{ color: "var(--st-declined)" }}>{fmt.money(Math.round(t.monthly))} / mo</b>{t.payoff ? ` · payoff ${fmt.day(t.payoff)}` : ""}</> : "No open advances"}
        </div>
        {t.payback ? <div className="dim tnum" style={{ fontSize: 12 }}>of {fmt.money(t.payback)} payback</div> : null}
      </div>
    </div>
  );
}

// ── The timeline under the chart ───────────────────────────────────────────
// Each event is a dot on the line and a label under it. Labels are measured (the chip font, on a canvas) and
// laid out row by row: fundings first, then payments starting, then stopping, each on the first row where it
// overlaps nothing, kept inside the chart at the edges; a label below the first row hangs from its dot on a thin
// stem. The same thing twice within ten days (two payments to Melio starting) is one label, "Melio ×2".
let tlCanvas = null;
const tlTextWidth = (text) => {
  try {
    tlCanvas = tlCanvas || document.createElement("canvas").getContext("2d");
    tlCanvas.font = "600 10.5px Inter, system-ui, sans-serif";
    return Math.ceil(tlCanvas.measureText(text).width);
  } catch (e) { return text.length * 6; }
};
const TL_ROW = 24, TL_GAP = 8, TL_MAX_ROWS = 8;
const TL_ORDER = { funded: 0, start: 1, end: 2 };
function layoutTimeline(evs, width) {
  const rows = [];   // the [left, right] spans (px) taken on each row
  const order = [...evs].sort((a, b) => (TL_ORDER[a.kind] ?? 1) - (TL_ORDER[b.kind] ?? 1) || a.x - b.x);
  for (const e of order) {
    const w = tlTextWidth(e.label) + 16, px = (e.x / 100) * width;
    const left = Math.max(0, Math.min(width - w, px - w / 2)), right = left + w;
    const fits = (spans) => spans.every(([l, r]) => right + TL_GAP <= l || left >= r + TL_GAP);
    let r = rows.findIndex(fits);
    if (r < 0) { r = rows.length < TL_MAX_ROWS ? rows.push([]) - 1 : rows.length - 1; }
    rows[r].push([left, right]);
    Object.assign(e, { px, left, w, row: r });
  }
  return Math.max(1, rows.length);
}
function ChartTimeline({ events, fromStatements }) {
  const ref = useRef(null);
  const [width, setWidth] = useState(0);
  React.useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    setWidth(el.clientWidth);
    if (typeof ResizeObserver === "undefined") return;
    const ro = new ResizeObserver(() => setWidth(el.clientWidth));
    ro.observe(el);
    return () => ro.disconnect();
  }, []);
  const evs = [];
  for (const e of events) {
    const near = evs.find(x => x.kind === e.kind && x.funder === e.funder && Math.abs(Date.parse(x.date) - Date.parse(e.date)) <= 10 * 864e5);
    if (near) { near.n++; continue; }
    evs.push({ ...e, n: 1 });
  }
  for (const e of evs) e.label = `${e.funder}${e.n > 1 ? ` ×${e.n}` : ""}${e.kind === "end" ? " ends" : ""}`;
  const rows = width ? layoutTimeline(evs, width) : 1;
  const what = (e) => e.kind === "funded" ? `${e.funder} on ${fmt.day(e.date)} (deposit on the statement)` : `${e.funder} · ${fromStatements ? (e.kind === "end" ? "last payment seen" : "first payment seen") : (e.kind === "end" ? "paid off" : "funded")} ${fmt.day(e.date)}`;
  const cls = (e) => (e.kind === "end" ? " end" : e.kind === "funded" ? " funded" : "") + (e.platform ? " platform" : "");
  return (
    <div className="chart-timeline" ref={ref} style={{ height: 16 + rows * TL_ROW }} aria-label="Fundings and payments on this timeline">
      {width ? evs.map((e, i) => (e.row ? <span key={"s" + i} className="tl-stem" style={{ left: e.px, height: 12 + e.row * TL_ROW }} /> : null)) : null}
      {width ? evs.map((e, i) => <i key={"d" + i} className={`tl-dot${cls(e)}`} style={{ left: e.px }} title={what(e)} />) : null}
      {width ? evs.map((e, i) => <span key={"c" + i} className={`chip tl-chip${cls(e)}`} style={{ left: e.left, top: 12 + e.row * TL_ROW }} title={what(e)}>{e.label}</span>) : null}
    </div>
  );
}

// ── Deposits and positions by month ────────────────────────────────────────
// `fromStatements`: drawn for a lead or deal from its own statements (statements.jsx StatementChart), where the
// timeline marks payments first and last seen on the statements rather than fundings on the ledger.
function DepositsChart({ months, events, totals, range, setRange, onEditMonths, canWrite, fromStatements }) {
  const all = months || [];
  const shown = range === "all" ? all : all.slice(-range);
  const vals = shown.map(m => m.deposits);
  const max = Math.max(1, ...vals);
  const avg = vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0;
  const maxPos = Math.max(1, ...shown.map(m => m.positions));
  const first = shown[0] ? shown[0].month : null, last = shown[shown.length - 1] ? shown[shown.length - 1].month : null;
  // Where an event sits along the axis: its month's slot, nudged by its day.
  const slots = shown.map(m => m.month);
  const place = (date) => {
    const k = String(date).slice(0, 7), i = slots.indexOf(k);
    if (i < 0) return null;
    const day = Number(String(date).slice(8, 10)) || 15;
    return ((i + (day - 1) / 31) / slots.length) * 100;
  };
  const evs = (events || []).map(e => ({ ...e, x: place(e.date) })).filter(e => e.x !== null).sort((a, b) => a.x - b.x || (a.kind === "funded" ? -1 : 1));
  const stat = (label, value, tone) => <div className="stat"><div className="label">{label}</div><div className="v tnum" style={tone ? { color: tone } : null}>{value}</div></div>;
  return (
    <div className="card chart-card">
      <div className="card-head">
        <span className="label">Deposits and positions</span>
        <span style={{ display: "flex", gap: 6, alignItems: "center" }}>
          {canWrite && onEditMonths ? <button className="link-btn" onClick={onEditMonths}>Edit months</button> : null}
          <Segmented label="Months shown" value={range} onChange={setRange} options={[{ id: 6, label: "6 mo" }, { id: 12, label: "12 mo" }, { id: "all", label: "All" }]} />
        </span>
      </div>
      <div className="card-body">
        <div className="stats three">
          {stat("Avg monthly deposits", totals.avgDeposits ? fmt.money(totals.avgDeposits) : "—", "var(--st-funded)")}
          {stat("Total monthly payments", <>{fmt.money(Math.round(totals.monthly))}{totals.paymentShare !== null && totals.paymentShare !== undefined ? <span className="dim" style={{ fontSize: 12, fontWeight: 400, marginLeft: 6 }}>{totals.paymentShare}%</span> : null}</>, "var(--st-declined)")}
          {stat("No. of positions", String(totals.positions || 0), "var(--st-submitted)")}
        </div>
        {!shown.length ? (
          <div className="dim" style={{ padding: "18px 0 6px", fontSize: 13 }}>{fromStatements ? "No months yet. Deposits come from the bank statements once they are read." : `No months yet. Deposits come from bank statements read on this client's deals${canWrite ? ", or type a month in with Edit months" : ""}.`}</div>
        ) : (
          <>
            <div className="chart" style={{ gridTemplateColumns: `repeat(${shown.length}, minmax(0, 1fr))` }}>
              {shown.map(m => (
                <div className="m" key={m.month} title={`${fmt.month(m.month, true)}: ${fmt.money(m.deposits)} deposits${m.source === "manual" ? " (typed in)" : ""} · ${m.positions} ${m.positions === 1 ? "position" : "positions"}`}>
                  <div className="lbl tnum">{fmt.moneyShort(m.deposits)}</div>
                  <div className="bars" style={{ height: Math.max(3, Math.round((m.deposits / max) * 100)) + "%" }}>
                    <div className={`dep${m.deposits < avg ? " low" : ""}${m.source === "manual" ? " manual" : ""}`} />
                    <div className="pos" style={{ height: Math.max(16, Math.round((m.positions / maxPos) * 100)) + "%" }}>{m.positions}</div>
                  </div>
                </div>
              ))}
            </div>
            <div className="chart-months" style={{ gridTemplateColumns: `repeat(${shown.length}, minmax(0, 1fr))` }}>
              {shown.map(m => <div key={m.month}>{fmt.month(m.month)}{m.month.slice(5) === "01" || m === shown[0] ? <span className="dim"> ’{m.month.slice(2, 4)}</span> : null}</div>)}
            </div>
            {evs.length ? <ChartTimeline events={evs} fromStatements={fromStatements} /> : <div className="chart-timeline empty" />}
            <div className="chart-legend">
              <span><i style={{ background: "#00D800" }} />Deposits</span><span><i style={{ background: "#F2B544" }} />Below average</span><span><i style={{ background: "var(--blue)" }} />Positions</span>
              {evs.some(e => e.platform) ? <span><i className="dot" style={{ background: "#6B4FBB" }} />Platform financing</span> : null}
              {evs.some(e => e.kind === "funded") ? <span><i className="dot" style={{ background: "#0E7A3A", outline: "2px solid #BFE6CC" }} />Funding received</span> : null}
              <span><i className="dot" style={{ background: "var(--st-funded)" }} />{fromStatements ? "Payments start" : "New funding"}</span><span><i className="dot" style={{ background: "var(--ink-mute)" }} />{fromStatements ? "Payments stop" : "Funding end"}</span>
              {shown.some(m => m.source === "manual") ? <span><i style={{ background: "#00D800", outline: "2px dashed var(--ink-mute)", outlineOffset: -2 }} />Typed in</span> : null}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ── Current positions ──────────────────────────────────────────────────────
function PositionsCard({ advances, suggestions, canWrite, onAddFrom, go }) {
  const open = advances.filter(a => a.status !== "paid").sort((a, b) => b.schedule.monthly - a.schedule.monthly);
  return (
    <div className="card card-flush">
      <div className="card-head"><span className="label">Current positions ({open.length})</span><span className="dim" style={{ fontSize: 12 }}>by payment</span></div>
      {open.length ? open.map(a => (
        <div className={`row${a.renewalDue ? " renew" : ""}`} key={a.id}>
          <div className="grow">
            <div className="title">{a.funder}{a.renewalDue ? <Pill status="submitted" label="Renewal due" /> : null}</div>
            <div className="line tnum">{fmt.money(Math.round(a.schedule.monthly))} monthly · {a.brokeredBy === "Bylt Capital" ? (a.dealId ? <a href={`#/deals/${encodeURIComponent(a.dealId)}`}>Bylt Capital</a> : "Bylt Capital") : "Pre-existing"} → Active</div>
          </div>
          <div className="right"><div className="tnum" style={{ fontWeight: 600, color: pctColor(a) }}>{a.schedule.pct}%</div><div className="when tnum">{fmt.moneyShort(a.schedule.remaining)} left</div></div>
        </div>
      )) : <div className="row dim" style={{ fontSize: 13 }}>No open advances on file.</div>}
      {suggestions && suggestions.length ? (
        <div className="row" style={{ fontSize: 12.5, display: "block" }}>
          <span className="dim">Statement reader found {suggestions.length === 1 ? "one more" : suggestions.length + " more"}: </span>
          {suggestions.slice(0, 3).map((p, i) => (
            <span key={i}>{i ? " · " : ""}<b>{p.lender || p.payee}</b> {fmt.money(p.amount)}/{p.cadence === "weekly" ? "wk" : "day"}{canWrite ? <> — <button className="link-btn" onClick={() => onAddFrom(p)}>add to ledger?</button></> : null}</span>
          ))}
        </div>
      ) : null}
    </div>
  );
}

// ── Advance ledger: a table on wide screens, cards on phones ───────────────
function AdvanceLedger({ advances, totals, showPaid, canWrite, isAdmin, onAdd, onEdit, onLog, onDelete, onSnooze }) {
  const list = (advances || []).filter(a => showPaid || a.status !== "paid");
  const [open, setOpen] = useState({});
  const paidCount = (advances || []).filter(a => a.status === "paid").length;
  const kv = (pairs) => <div className="kv-grid">{pairs.map(([k, v]) => <div key={k}><div className="k">{k}</div><div className="tnum">{v}</div></div>)}</div>;
  const row = (a) => {
    const s = a.schedule, paid = a.status === "paid";
    return (
      <tr key={a.id} className={`${a.renewalDue ? "renew" : ""}${paid ? " paid" : ""}`}>
        <td><b>{a.funder}</b>{a.renewalDue ? <> <Pill status="submitted" label="Renewal due" /></> : null}{a.snoozed ? <> <span className="pill plain s-neutral">Snoozed to {fmt.day(a.snoozedUntil)}</span></> : null}</td>
        <td>{brokerPill(a.brokeredBy, a)}</td>
        <td className="tnum">{dayYear(a.fundedOn)}</td>
        <td className="tnum">{dayYear(a.fundedAt)}</td>
        <td className="num tnum">{fmt.money(a.amount)}</td>
        <td className="num tnum">{fmt.num(a.factor) ? fmt.num(a.factor).toFixed(2) : "—"}</td>
        <td className="tnum">{fmt.term(a.term, a.termUnit)}</td>
        <td className="num tnum">{paid ? "—" : payLabel(a)}</td>
        <td className="num tnum">{fmt.money(s.remaining)}</td>
        <td>
          {paid ? <span style={{ fontWeight: 600 }}>Paid off {fmt.day(a.paidOffAt)}</span> : (
            <span className="prog"><span className="progress"><i style={{ width: s.pct + "%", background: pctColor(a) }} /></span><b className="tnum" style={{ color: a.renewalDue ? "var(--st-submitted)" : "inherit" }}>{s.pct}%</b></span>
          )}
        </td>
        <td className="acts">
          {canWrite ? <>
            {!paid ? <button className="btn btn-sm" onClick={() => onLog(a)}>Log</button> : null}
            <button className="btn btn-sm" onClick={() => onEdit(a)}>Edit</button>
            {a.renewalDue && !paid ? <button className="btn btn-sm" onClick={() => onSnooze(a)}>Snooze</button> : null}
          </> : null}
        </td>
      </tr>
    );
  };
  const card = (a) => {
    const s = a.schedule, paid = a.status === "paid", more = !!open[a.id];
    return (
      <div className={`lc${a.renewalDue ? " renew" : ""}${paid ? " paid" : ""}`} key={a.id}>
        <div className="lc-top">
          <div className="donut sm" style={{ "--pct": s.pct + "%", "--fill": pctColor(a) }} role="img" aria-label={`${s.pct}% repaid`}><span>{s.pct}%</span></div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="lc-head"><b>{a.funder}</b><span>{brokerPill(a.brokeredBy, a)}{a.renewalDue ? <Pill status="submitted" label="Renew" /> : null}</span></div>
            <div className="dim tnum" style={{ fontSize: 12.5 }}>{paid ? `Paid off ${fmt.day(a.paidOffAt)}` : `${fmt.money(s.remaining)} remaining of ${fmt.money(s.payback)}`}</div>
          </div>
        </div>
        {kv([["Payment", paid ? "—" : payLabel(a)], ["Monthly", paid ? "—" : fmt.money(Math.round(s.monthly))], ["Funded", dayYear(a.fundedOn)], ["First payment", dayYear(a.fundedAt)]])}
        {more ? kv([["Amount", fmt.money(a.amount)], ["Factor", fmt.num(a.factor) ? fmt.num(a.factor).toFixed(2) : "—"], ["Payback", fmt.money(s.payback)], ["Term", fmt.term(a.term, a.termUnit)], ["Paid to date", fmt.money(s.paid)], ["Est. payoff", dayYear(s.payoff)], ["Payments", `${s.made} of ${s.total}`]]) : null}
        <div className="lc-foot">
          <button className="link-btn" onClick={() => setOpen(o => ({ ...o, [a.id]: !more }))}>{more ? "Less" : "More details"}</button>
          {canWrite ? <span style={{ display: "flex", gap: 10 }}>
            {!paid ? <button className="link-btn" onClick={() => onLog(a)}>Log payment</button> : null}
            <button className="link-btn" onClick={() => onEdit(a)}>Edit</button>
            {a.renewalDue && !paid ? <button className="link-btn" onClick={() => onSnooze(a)}>Snooze</button> : null}
          </span> : null}
        </div>
      </div>
    );
  };
  return (
    <div className="card ledger">
      <div className="card-head" style={{ flexWrap: "wrap", gap: 8 }}>
        <span className="label">Advance ledger</span>
        <span style={{ display: "flex", gap: 6, alignItems: "center", flexWrap: "wrap" }}>
          <span className="pill plain s-new">Open {totals.open}</span>
          <span className="pill plain s-neutral">Paid off {paidCount}</span>
          {canWrite ? <button className="btn btn-sm" onClick={onAdd}><Icon name="plus" size={14} />Add advance</button> : null}
        </span>
      </div>
      {!list.length ? <div className="card-body dim" style={{ fontSize: 13 }}>{advances && advances.length ? "No open advances. Paid-off ones are on the Advances tab." : "No advances yet. Add a past advance (ours or another broker's) to start the running balance; deals marked funded land here on their own."}</div> : (
        <>
          <div className="table-wrap ledger-table">
            <table className="list">
              <thead><tr><th>Funder</th><th>Brokered by</th><th>Funded</th><th>First payment</th><th className="num">Amount</th><th className="num">Factor</th><th>Term</th><th className="num">Payment</th><th className="num">Remaining</th><th>Progress</th><th /></tr></thead>
              <tbody>{list.map(row)}</tbody>
            </table>
          </div>
          <div className="ledger-cards">{list.map(card)}</div>
          <div className="ledger-foot tnum"><span>Open advances</span><span>{fmt.money(totals.remaining)} remaining of {fmt.money(totals.payback)}</span></div>
        </>
      )}
    </div>
  );
}

function RenewalBanner({ renewals, advances, canWrite, onStart, onSnooze }) {
  if (!renewals || !renewals.length) return null;
  return renewals.map(r => {
    const a = advances.find(x => x.id === r.advanceId);
    return (
      <div className="banner renew-banner" key={r.advanceId}>
        <div><b>Renewal window open.</b> {r.funder} is {r.pct}% paid{r.brokeredBy !== "Bylt Capital" ? ` (${r.brokeredBy.toLowerCase()})` : ""}. The rep, their team leader and Admins were told.</div>
        {canWrite ? <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}><button className="btn btn-sm btn-primary" onClick={() => onStart(a)}>Start renewal deal</button><button className="btn btn-sm" onClick={() => onSnooze(a)}>Snooze 2 weeks</button></div> : null}
      </div>
    );
  });
}

// ── Sheets ─────────────────────────────────────────────────────────────────
function AdvanceSheet({ clientId, advance, seed, lenders, onClose, onSaved, onDelete }) {
  const editing = !!advance;
  const a = advance || seed || {};
  const show = (v) => (v === undefined || v === null || v === 0 ? "" : String(v));
  const [v, set] = useForm({
    funder: a.funder || "", brokeredBy: a.brokeredBy || "Other broker", fundedOn: a.fundedOn || "", fundedAt: a.fundedAt || "", amount: show(a.amount), factor: show(a.factor), payback: show(a.payback),
    term: show(a.term), termUnit: a.termUnit || "days", payment: show(a.payment), freq: a.freq || "Daily", paidToDate: "", notes: a.notes || "",
  });
  const save = useSave(onSaved);
  // The first payment follows the funded date by the rule until someone types a different one.
  const [firstTyped, setFirstTyped] = useState(() => !!a.fundedAt && (!a.fundedOn || firstPaymentFrom(a.fundedOn, a.freq || "Daily") !== a.fundedAt));
  const setFunded = (x) => { set("fundedOn", x); if (!firstTyped) set("fundedAt", firstPaymentFrom(x, v.freq)); };
  const setFreq = (x) => { set("freq", x); if (!firstTyped && v.fundedOn) set("fundedAt", firstPaymentFrom(v.fundedOn, x)); };
  const setFirst = (x) => { set("fundedAt", x); setFirstTyped(!!x && x !== firstPaymentFrom(v.fundedOn, v.freq)); };
  const amount = fmt.parseMoney(v.amount), factor = Number(v.factor) || 0, payback = fmt.parseMoney(v.payback);
  const guessPayback = amount && factor && !payback ? Math.round(amount * factor * 100) / 100 : null;
  const submit = () => {
    if (!v.funder.trim()) return save.setError("Enter who funded it.");
    if (!v.fundedOn && !v.fundedAt) return save.setError("Enter the funded date.");
    if (v.fundedOn && v.fundedAt && v.fundedAt < v.fundedOn) return save.setError("The first payment can't be before the funded date.");
    if (!(amount > 0)) return save.setError("Enter the funded amount.");
    if (!(Number(v.term) > 0)) return save.setError("Enter the term.");
    if (!(fmt.parseMoney(v.payment) > 0)) return save.setError("Enter the payment.");
    const body = { funder: v.funder.trim(), brokeredBy: v.brokeredBy, fundedAt: v.fundedAt || firstPaymentFrom(v.fundedOn, v.freq), amount, factor: v.factor === "" ? "" : factor, payback: v.payback === "" ? "" : payback,
      term: Number(v.term), termUnit: v.termUnit, payment: fmt.parseMoney(v.payment), freq: v.freq, notes: v.notes.trim() };
    if (v.fundedOn) body.fundedOn = v.fundedOn; else if (editing && advance.fundedOn) body.fundedOn = null;
    if (!editing && v.paidToDate !== "") body.paidToDate = fmt.parseMoney(v.paidToDate);
    if (!editing && seed && seed.source) body.source = seed.source;
    save.run(() => (editing ? API.updateAdvance(advance.id, body) : API.createAdvance(clientId, body)));
  };
  return (
    <Sheet eyebrow="Advances" title={editing ? "Edit advance" : "Add advance"} onClose={onClose} onSubmit={submit} submitLabel={editing ? "Save advance" : "Add advance"} busy={save.busy} error={save.error}
           danger={editing && onDelete ? <button type="button" className="btn btn-danger" style={{ marginRight: "auto" }} onClick={onDelete}>Remove</button> : null}>
      {editing && advance.dealId ? <div className="banner info" style={{ margin: "16px 18px 0" }}>This advance comes from deal {advance.dealId}. Its terms were taken from that deal; anything you change here stays.</div> : null}
      <div className="form-section">
        <span className="label">Who</span>
        <div className="form-grid">
          <Field label="Funder" full>
            <TextInput value={v.funder} onChange={x => set("funder", x)} list="adv-lenders" placeholder="Lender name" />
          </Field>
          <datalist id="adv-lenders">{(lenders || []).map(l => <option key={l.id} value={l.name} />)}</datalist>
          <Field label="Brokered by"><Select value={v.brokeredBy} onChange={x => set("brokeredBy", x)} options={BROKERED_BY} /></Field>
        </div>
      </div>
      <div className="form-section">
        <span className="label">Dates</span>
        <div className="form-grid">
          <Field label="Funded date" hint={editing && advance.dealId && !advance.fundedOn ? "Not on the deal." : ""}><TextInput type="date" value={v.fundedOn} onChange={setFunded} /></Field>
          <Field label="First payment date" hint={firstTyped ? "Typed by hand." : v.fundedOn ? (v.freq === "Weekly" ? "7 days after funding (weekly)." : "Next business day after funding (daily).") : "Filled from the funded date."}><TextInput type="date" value={v.fundedAt} onChange={setFirst} /></Field>
        </div>
      </div>
      <div className="form-section">
        <span className="label">Terms</span>
        <div className="form-grid">
          <Field label="Funded amount"><TextInput value={v.amount} onChange={x => set("amount", x)} inputMode="decimal" placeholder="50,000" /></Field>
          <Field label="Factor"><TextInput value={v.factor} onChange={x => set("factor", x)} inputMode="decimal" placeholder="1.35" /></Field>
          <Field label="Payback" hint={guessPayback ? `Left blank, it's ${fmt.money(guessPayback)} (amount × factor).` : ""}><TextInput value={v.payback} onChange={x => set("payback", x)} inputMode="decimal" /></Field>
          <Field label="Term"><TermInput value={v.term} unit={v.termUnit} onChange={x => set("term", x)} onUnit={x => set("termUnit", x)} /></Field>
          <Field label="Payment"><TextInput value={v.payment} onChange={x => set("payment", x)} inputMode="decimal" placeholder="640" /></Field>
          <Field label="Frequency"><Select value={v.freq} onChange={setFreq} options={FREQ_OPTS} /></Field>
          {!editing ? <Field label="Paid to date (optional)" hint="Left blank, progress is worked out from the first payment date." full><TextInput value={v.paidToDate} onChange={x => set("paidToDate", x)} inputMode="decimal" placeholder="e.g. 42,000" /></Field> : null}
          <Field label="Notes" full><textarea className="textarea" rows={2} value={v.notes} onChange={e => set("notes", e.target.value)} /></Field>
        </div>
      </div>
    </Sheet>
  );
}

function LogPaymentSheet({ advance, onClose, onSaved }) {
  const [kind, setKind] = useState("1");
  const [paidToDate, setPaid] = useState("");
  const save = useSave(onSaved);
  const s = advance.schedule;
  const submit = () => {
    if (kind === "paidToDate") {
      const n = fmt.parseMoney(paidToDate);
      if (!(n >= 0) || Number.isNaN(n)) return save.setError("Enter the amount paid so far.");
      return save.run(() => API.updateAdvance(advance.id, { paidToDate: n }));
    }
    if (kind === "paid") return save.run(() => API.updateAdvance(advance.id, { paidInFull: true }));
    save.run(() => API.updateAdvance(advance.id, { logPayments: Number(kind) }));
  };
  return (
    <Sheet eyebrow={advance.funder} title="Log payment" onClose={onClose} onSubmit={submit} submitLabel="Save" busy={save.busy} error={save.error}>
      <div className="form-section">
        <div className="dim" style={{ fontSize: 13, marginBottom: 12 }}>On schedule: {s.made} of {s.total} payments, {fmt.money(s.paid)} paid, {s.pct}% done. Log what happened and the progress moves with it.</div>
        <div className="form-grid">
          <Field label="What happened" full>
            <Select value={kind} onChange={setKind} options={[
              { value: "1", label: "One extra payment posted" }, { value: "-1", label: "One payment missed or bounced" }, { value: "5", label: "Five payments posted (catch-up)" }, { value: "-5", label: "Five payments missed" },
              { value: "paidToDate", label: "Set paid to date by hand" }, { value: "paid", label: "Paid in full" }]} />
          </Field>
          {kind === "paidToDate" ? <Field label="Paid to date" full><TextInput value={paidToDate} onChange={setPaid} inputMode="decimal" placeholder={fmt.money(s.paid)} /></Field> : null}
        </div>
      </div>
    </Sheet>
  );
}

function MonthsSheet({ months, onClose, onSaved, clientId }) {
  // The last 12 calendar months, with what the statements say beside each box.
  const now = new Date();
  const keys = [];
  for (let i = 11; i >= 0; i--) { const d = new Date(Date.UTC(now.getFullYear(), now.getMonth() - i, 1)); keys.push(d.toISOString().slice(0, 7)); }
  const byKey = Object.fromEntries((months || []).map(m => [m.month, m]));
  const [v, setV] = useState(Object.fromEntries(keys.map(k => [k, byKey[k] && byKey[k].source === "manual" ? String(byKey[k].deposits) : ""])));
  const save = useSave(onSaved);
  const submit = () => {
    const body = {};
    for (const k of keys) {
      const was = byKey[k] && byKey[k].source === "manual" ? String(byKey[k].deposits) : "";
      if (v[k] === was) continue;
      if (v[k] === "") { body[k] = null; continue; }
      const n = fmt.parseMoney(v[k]); if (Number.isNaN(n) || n === "") return save.setError(`${fmt.month(k, true)}: enter an amount like 108,000.`);
      body[k] = n;
    }
    save.run(() => API.putClientMonths(clientId, body));
  };
  return (
    <Sheet eyebrow="Deposits by month" title="Edit months" onClose={onClose} onSubmit={submit} submitLabel="Save months" busy={save.busy} error={save.error}>
      <div className="form-section">
        <div className="dim" style={{ fontSize: 13, marginBottom: 12 }}>A month typed here shows on the chart in place of what the statements say. Leave a box blank to use the statement figure.</div>
        <div className="form-grid">
          {keys.map(k => (
            <Field key={k} label={fmt.month(k, true)} hint={byKey[k] && byKey[k].statementDeposits !== null && byKey[k].statementDeposits !== undefined ? `Statements: ${fmt.money(byKey[k].statementDeposits)}` : "No statement for this month"}>
              <TextInput value={v[k]} onChange={x => setV(p => ({ ...p, [k]: x }))} inputMode="decimal" placeholder={byKey[k] && byKey[k].statementDeposits != null ? String(Math.round(byKey[k].statementDeposits)) : "—"} />
            </Field>
          ))}
        </div>
      </div>
    </Sheet>
  );
}

function MergeSheet({ client, clients, onClose, onSaved }) {
  const others = clients.filter(c => c.id !== client.id);
  const [from, setFrom] = useState(others[0] ? others[0].id : "");
  const [force, setForce] = useState(false);
  const save = useSave(onSaved);
  const pick = others.find(c => c.id === from);
  const einOf = (c) => String(c.ein || "").replace(/\D/g, "");
  const mismatch = pick && einOf(pick) && einOf(client) && einOf(pick) !== einOf(client);
  const submit = () => {
    if (!from) return save.setError("Pick the client to fold in.");
    if (mismatch && !force) return save.setError("The EINs differ. Tick the box if you are sure they are the same business.");
    save.run(() => API.mergeClients(client.id, from, force));
  };
  return (
    <Sheet eyebrow="Admin" title={`Merge into ${client.biz}`} onClose={onClose} onSubmit={submit} submitLabel="Merge" busy={save.busy} error={save.error}>
      <div className="form-section">
        <div className="dim" style={{ fontSize: 13, marginBottom: 12 }}>The chosen record's deals, advances and typed months move to {client.biz}; its details fill any blanks here; its name is kept as an alias; then it is removed. This can't be undone.</div>
        <div className="form-grid">
          <Field label="Client to fold in" full>
            <Select value={from} onChange={setFrom} options={others.map(c => ({ value: c.id, label: `${c.biz}${c.ein ? " · EIN " + c.ein : ""} (${c.id})` }))} />
          </Field>
          {mismatch ? <div className="banner" style={{ gridColumn: "1 / -1" }}>EINs differ: {client.ein} here, {pick.ein} on {pick.biz}. <label style={{ display: "flex", gap: 8, alignItems: "center", marginTop: 8 }}><input type="checkbox" checked={force} onChange={e => setForce(e.target.checked)} /> I'm sure they are the same business</label></div> : null}
        </div>
      </div>
    </Sheet>
  );
}

function DuplicatesSheet({ onClose, onMerged, toast }) {
  const [data, setData] = useState(null);
  const [error, setError] = useState("");
  const [busy, setBusy] = useState("");
  const load = async () => { try { setData(await API.clientDuplicates()); } catch (e) { setError(e.message); } };
  useEffect(() => { load(); }, []);
  const merge = async (g) => {
    const [into, ...rest] = g.clients;
    setBusy(g.key);
    try {
      for (const c of rest) await API.mergeClients(into.id, c.id, g.kind !== "ein");
      toast(`Merged ${rest.length} into ${into.biz}`);
      await load(); onMerged();
    } catch (e) { toast(e.message, true); }
    setBusy("");
  };
  const why = { ein: "Same EIN", phone: "Same phone", email: "Same email", name: "Same name" };
  return (
    <Sheet eyebrow="Admin" title="Find duplicates" onClose={onClose}>
      <div className="form-section">
        {error ? <div className="banner">{error}</div> : !data ? <div className="dim">Checking…</div> : !data.groups.length ? <div className="dim">Checked {data.checked} clients by EIN, phone, email and name: no duplicates found.</div> : (
          <>
            <div className="dim" style={{ fontSize: 13, marginBottom: 12 }}>Checked {data.checked} clients. Same-EIN groups can be merged straight away; the others are suggestions, so look before merging. The oldest record is kept.</div>
            {data.groups.map(g => (
              <div className="card" key={g.kind + g.key} style={{ marginBottom: 10 }}>
                <div className="card-head"><span className="label">{why[g.kind]}{g.kind === "ein" ? " " + g.key : ""}</span>
                  <button className="btn btn-sm btn-primary" disabled={busy === g.key} onClick={() => merge(g)}>{busy === g.key ? "Merging…" : `Merge ${g.clients.length} → ${g.clients[0].biz}`}</button></div>
                {g.clients.map(c => <div className="row" key={c.id}><div className="grow"><div className="title">{c.biz} <span className="dim" style={{ fontWeight: 400 }}>{c.id}</span></div><div className="line">{[c.owner, c.ein ? "EIN " + c.ein : "", c.phone, c.email].filter(Boolean).join(" · ") || "No details"}</div></div></div>)}
              </div>
            ))}
          </>
        )}
      </div>
    </Sheet>
  );
}

// ── Client rail ────────────────────────────────────────────────────────────
function ClientRail({ clients, balances, selectedId, onSelect, onNew, canWrite, onExport }) {
  const [q, setQ] = useState("");
  const [filter, setFilter] = useState("all");
  const needle = q.trim().toLowerCase();
  const bal = (c) => balances[c.id];
  const visible = clients.filter(c => (filter === "all" || (filter === "open" && bal(c) && bal(c).open) || (filter === "renewal" && bal(c) && bal(c).renewal))
    && matches(needle, c.biz, c.owner, c.phone, c.email, c.id, ...(c.aliases || [])));
  const n = (id) => clients.filter(c => id === "all" || (id === "open" && bal(c) && bal(c).open) || (id === "renewal" && bal(c) && bal(c).renewal)).length;
  return (
    <aside className="rail">
      <div className="rail-head">
        <div className="rail-title"><div><div className="label">Businesses</div><h2>Clients</h2></div><span className="rail-acts">{onExport ? <button className="icon-btn" onClick={onExport} aria-label="Export clients" title="Export"><Icon name="download" size={16} /></button> : null}{canWrite ? <button className="btn btn-primary btn-sm" onClick={onNew}><Icon name="plus" size={14} />New</button> : null}</span></div>
        <div className="search"><Icon name="search" size={15} /><input type="search" value={q} onChange={e => setQ(e.target.value)} placeholder="Search business, owner, EIN…" aria-label="Search clients" /></div>
        <div className="filters" role="group" aria-label="Filter clients">
          {[["all", "All"], ["open", "Open balance"], ["renewal", "Renewal"]].map(([id, label]) => <button key={id} className="filter" aria-pressed={filter === id} onClick={() => setFilter(id)}>{label}<span className="n tnum">{n(id)}</span></button>)}
        </div>
      </div>
      <div className="rail-list scroll">
        {!clients.length ? <Empty title="No clients yet">Create a client from a deal's Client tab, or add one here.</Empty>
          : !visible.length ? <div className="empty"><p>No clients match.</p></div>
          : visible.map(c => { const b = bal(c); return (
            <button key={c.id} className="deal-row" aria-current={c.id === selectedId} onClick={() => onSelect(c.id)}>
              <Avatar name={c.biz} />
              <span className="main">
                <span className="name" style={{ display: "block" }}>{c.biz}</span>
                <span className="sub tnum" style={{ display: "block" }}>{b && b.open ? `${b.open} open · ${fmt.moneyShort(b.remaining)} · ${b.pct}% paid` : c.owner || "No open advances"}</span>
              </span>
              {b && b.renewal ? <span className="renew-dot" title="Renewal due" /> : null}
            </button>
          ); })}
      </div>
    </aside>
  );
}

// ── The client file ────────────────────────────────────────────────────────
function ClientFile({ client: listed, clients, team, lenders, user, office, canWrite, onBack, onChanged, onNewDeal, refreshClients, refreshDeals, refreshBalances, toast, confirm, go }) {
  const [tab, setTab] = useState("overview");
  const [data, setData] = useState(null);
  const [error, setError] = useState("");
  const [sheet, setSheet] = useState(null);
  const [range, setRange] = useState(6);
  const [side, setSide] = useState({ activity: null, docs: null });
  const clientId = listed.id;
  const isAdmin = !!canOf(user).manage;
  const showing = useRef(clientId); showing.current = clientId;
  const load = useCallback(async () => {
    try { const d = await API.clientOverview(clientId); if (showing.current === clientId) { setData(d); setError(""); } }
    catch (err) { if (showing.current === clientId && err.status !== 401) setError(err.message); }
  }, [clientId]);
  useEffect(() => { setTab("overview"); setData(null); setSheet(null); setSide({ activity: null, docs: null }); load(); }, [clientId]);
  useEffect(() => {
    const onShow = () => { if (document.visibilityState === "visible") load(); };
    document.addEventListener("visibilitychange", onShow);
    return () => document.removeEventListener("visibilitychange", onShow);
  }, [clientId]);
  // The deals' logs and files, read when those tabs open.
  useEffect(() => {
    if (!data || (tab !== "activity" && tab !== "documents")) return;
    const ids = data.deals.slice(0, 6).map(d => d.id);
    if (tab === "activity" && side.activity === null) {
      Promise.all(ids.map(id => API.activity(id).then(a => a.map(x => ({ ...x, dealId: id }))).catch(() => []))).then(lists => {
        if (showing.current === clientId) setSide(s => ({ ...s, activity: lists.flat().sort((a, b) => String(b.ts).localeCompare(String(a.ts))) }));
      });
    }
    if (tab === "documents" && side.docs === null) {
      Promise.all(ids.map(id => API.deal(id).then(d => (d.docs || []).map(x => ({ ...x, dealId: id }))).catch(() => []))).then(lists => {
        if (showing.current === clientId) setSide(s => ({ ...s, docs: lists.flat().sort((a, b) => String(b.uploaded || "").localeCompare(String(a.uploaded || ""))) }));
      });
    }
  }, [tab, data]);

  const client = (data && data.client) || listed;
  const advances = data ? data.advances : [];
  const totals = data ? data.totals : { open: 0, remaining: 0, payback: 0, pct: 0, monthly: 0, positions: 0, avgDeposits: 0, paymentShare: null };
  const close = () => setSheet(null);
  const changed = async (msg) => { close(); await load(); refreshBalances(); if (msg) toast(msg); };
  const snooze = (a) => confirm({ plain: true, title: "Snooze this renewal?", body: `${a.funder} comes back on Home and here in two weeks, and the alert goes out again then.`, confirmLabel: "Snooze 2 weeks",
    onConfirm: async () => { await API.updateAdvance(a.id, { snoozeDays: 14 }); await load(); refreshBalances(); toast("Snoozed for two weeks"); } });
  const removeAdvance = (a) => confirm({ title: "Remove this advance?", body: `${a.funder} will come off the ledger and the running balance.`, confirmLabel: "Remove",
    onConfirm: async () => { await API.deleteAdvance(a.id); await changed("Advance removed"); } });
  const startRenewal = (a) => onNewDeal({ renewalOf: a });
  const askDelete = () => confirm({ title: "Delete this client?", body: `${client.biz}'s client record and its advances will be removed. Deals are not affected.`, confirmLabel: "Delete client",
    onConfirm: async () => { await API.deleteClient(client.id); await refreshClients(); refreshBalances(); toast("Client deleted"); onBack(); } });

  const tabs = [
    { id: "overview", label: "Overview", short: "Overview" }, { id: "advances", label: "Advances", short: "Ledger", n: advances.length },
    { id: "deals", label: "Deals", short: "Deals", n: data ? data.deals.length : 0 }, { id: "activity", label: "Activity", short: "Log" }, { id: "map", label: "Map", short: "Map" },
    { id: "documents", label: "Documents", short: "Docs" }, { id: "profile", label: "Profile", short: "Profile" },
  ];
  const actions = canWrite ? [
    { label: "New deal", short: "New deal", icon: "offer", run: () => onNewDeal({}) },
    { label: "Add advance", short: "Advance", icon: "plus", run: () => setSheet({ kind: "advance" }) },
    { label: "Log payment", short: "Payment", icon: "note", run: () => { const open = advances.filter(a => a.status !== "paid"); if (!open.length) return toast("No open advance to log a payment on", true); setSheet({ kind: "log", advance: open[0], pick: open.length > 1 }); } },
    { label: "Edit client", short: "Edit", icon: "edit", run: () => setSheet({ kind: "client" }) },
  ] : [];
  const rep = (id) => (team.find(t => t.id === id) || {}).name || "";
  const repLine = data && data.deals.length ? rep(data.deals[0].repId) : "";
  const ledgerProps = { advances, totals, canWrite, isAdmin, onAdd: () => setSheet({ kind: "advance" }), onEdit: (a) => setSheet({ kind: "advance", advance: a }), onLog: (a) => setSheet({ kind: "log", advance: a }), onDelete: removeAdvance, onSnooze: snooze };

  return (
    <>
      <section className="file cfile">
        <div className="file-head cfile-head">
          <button className="back" onClick={onBack}><Icon name="back" size={15} />All clients</button>
          <div className="file-top">
            <div style={{ minWidth: 0, display: "flex", gap: 14, alignItems: "center" }}>
              <Avatar name={client.biz} size="lg" />
              <div style={{ minWidth: 0 }}>
                <div className="file-id"><span className="label">Client · {client.id}</span>{client.aliases && client.aliases.length ? <span className="pill plain s-neutral">also {client.aliases.join(", ")}</span> : null}</div>
                <div className="name-row"><h1 className="file-name">{client.biz}</h1><RemitBadge share={data ? totals.paymentShare : null} basis="average monthly deposits" /></div>
                <div className="file-contact">
                  {client.ein ? <span>EIN {client.ein}</span> : <span className="muted">No EIN on file</span>}
                  {client.owner ? <span>{client.owner}</span> : null}
                  {repLine ? <span>Rep {repLine}</span> : null}
                  {client.phone ? <a href={`tel:${client.phone}`}>{client.phone}</a> : null}
                  {client.geo && client.geo.lat != null ? <ShowOnMap kind="client" id={client.id} /> : null}
                </div>
              </div>
            </div>
            <div className="file-money deskonly">
              <div className="label">Running balance</div>
              <div className="amount tnum">{totals.remaining ? <><span className="cur">$</span>{fmt.num(totals.remaining).toLocaleString("en-US")}</> : <span className="muted">—</span>}</div>
              <div className="facts tnum">{totals.open ? <><span>Open <b>{totals.open}</b></span><span>Paid <b>{totals.pct}%</b></span><span style={{ color: "var(--st-declined)" }}>Monthly <b style={{ color: "inherit" }}>{fmt.moneyShort(Math.round(totals.monthly))}</b></span></> : <span>No open advances</span>}</div>
            </div>
          </div>
          {actions.length ? <div className="file-actions">{actions.map((a, i) => <button key={a.label} className={`btn btn-sm${i === 0 ? " btn-primary" : ""}`} onClick={a.run} aria-label={a.label}><Icon name={a.icon} size={15} /><span className="lg">{a.label}</span><span className="sm" aria-hidden="true">{a.short}</span></button>)}</div> : null}
        </div>
        <div className="cfile-mobile">
          <BalanceCard totals={totals} />
          <RenewalBanner renewals={data ? data.renewals : []} advances={advances} canWrite={canWrite} onStart={startRenewal} onSnooze={snooze} />
        </div>
        <div className="tabs" role="tablist">
          {tabs.map(t => <button key={t.id} role="tab" className="tab" aria-label={t.n ? `${t.label} ${t.n}` : t.label} aria-selected={tab === t.id} onClick={() => setTab(t.id)}><span className="lg">{t.label}</span><span className="sm" aria-hidden="true">{t.short}</span>{t.n ? <span className="n tnum">{t.n}</span> : null}</button>)}
        </div>
        <div className="tab-body scroll" role="tabpanel">
          {error ? <div className="banner" role="alert">Couldn't load this client: {error} <button className="link-btn" onClick={load}>Try again</button></div> : null}
          {!data && !error ? <div className="empty"><p>Loading…</p></div> : null}
          {data && tab === "overview" ? (
            <>
              <DepositsChart months={data.months} events={data.events} totals={totals} range={range} setRange={setRange} canWrite={canWrite} onEditMonths={() => setSheet({ kind: "months" })} />
              <PositionsCard advances={advances} suggestions={data.suggestions} canWrite={canWrite} go={go}
                onAddFrom={(p) => setSheet({ kind: "advance", seed: { funder: p.lender || p.payee, brokeredBy: "Other broker", payment: p.amount, freq: p.cadence === "weekly" ? "Weekly" : "Daily", fundedAt: p.firstDate || "", source: "statement" } })} />
              <AdvanceLedger {...ledgerProps} showPaid={false} />
            </>
          ) : null}
          {data && tab === "advances" ? <AdvanceLedger {...ledgerProps} showPaid={true} /> : null}
          {data && tab === "deals" ? (
            <div className="card card-flush">
              <div className="card-head"><span className="label">Deals with this client</span>{canWrite ? <button className="btn btn-sm" onClick={() => onNewDeal({})}><Icon name="plus" size={14} />New deal</button> : null}</div>
              {data.deals.length ? data.deals.map(d => (
                <div className="row" key={d.id}>
                  <div className="grow">
                    <div className="title"><a href={`#/deals/${encodeURIComponent(d.id)}`}>{d.id}</a><Pill status={d.status} label={statusLabel(DEAL_STATUSES, d.status)} />{d.status === "funded" && d.funder ? <span className="dim" style={{ fontWeight: 400 }}>{d.funder}</span> : null}</div>
                    <div className="when">{d.status === "funded" && d.fundedAt ? `Funded ${fmt.date(d.fundedAt)}` : `Opened ${fmt.date(d.createdAt)}`}{rep(d.repId) ? ` · ${rep(d.repId)}` : ""}{d.status === "funded" ? ` · ${d.paidPct}% repaid` : ""}</div>
                  </div>
                  <div className="right"><div className="big tnum">{d.amount ? fmt.money(d.amount) : "—"}</div>{d.factor ? <div className="when tnum">{d.factor.toFixed(2)} · {fmt.term(d.term, d.termUnit)}</div> : null}</div>
                </div>
              )) : <div className="row dim">No deals yet.</div>}
            </div>
          ) : null}
          {tab === "map" ? <FileMapTab kind="client" id={client.id} geo={client.geo} gplace={client.gplace} biz={client.biz} /> : null}
          {data && tab === "activity" ? (
            <div className="card card-flush">
              <div className="card-head"><span className="label">Activity across {data.deals.length === 1 ? "this client's deal" : `${Math.min(6, data.deals.length)} deals`}</span></div>
              {side.activity === null ? <div className="row dim">Loading…</div> : !side.activity.length ? <div className="row dim">Nothing logged yet.</div>
                : side.activity.slice(0, 200).map((a, i) => <div className="row" key={i}><div className="grow"><div className="line" style={{ color: "var(--ink)" }}>{a.text}</div><div className="when">{fmt.when(a.ts)} · <a href={`#/deals/${encodeURIComponent(a.dealId)}`}>{a.dealId}</a></div></div></div>)}
            </div>
          ) : null}
          {data && tab === "documents" ? (
            <div className="card card-flush">
              <div className="card-head"><span className="label">Files on this client's deals</span></div>
              {side.docs === null ? <div className="row dim">Loading…</div> : !side.docs.length ? <div className="row dim">No files yet. Upload them on a deal.</div>
                : side.docs.map((f, i) => <div className="row" key={i}><div className="grow"><div className="title"><a href={API.documentUrl(f.objectKey)} target="_blank" rel="noopener">{f.name || f.objectKey}</a></div><div className="when">{f.role || "file"} · {fmt.date(f.uploaded)} · <a href={`#/deals/${encodeURIComponent(f.dealId)}`}>{f.dealId}</a></div></div></div>)}
            </div>
          ) : null}
          {data && tab === "profile" ? (
            <>
              <PrivateDetailsCard kind="client" id={clientId} summary={piiSummary.client(data.pii)} canReveal={!!canOf(user).pii} onEnter={() => setSheet({ kind: "pii" })} />
              <div className="grid-2">
                <div className="card"><div className="card-head"><span className="label">Business</span></div><dl>
                  <div className="kv"><dt>Name</dt><dd>{client.biz}</dd></div><div className="kv"><dt>Address</dt><dd>{client.address || "—"}</dd></div>
                  {client.aliases && client.aliases.length ? <div className="kv"><dt>Also known as</dt><dd>{client.aliases.join(", ")}</dd></div> : null}
                  <div className="kv"><dt>Client since</dt><dd>{fmt.date(client.createdAt) || "—"}</dd></div></dl></div>
                <div className="card"><div className="card-head"><span className="label">Owner</span></div><dl>
                  <div className="kv"><dt>Name</dt><dd>{client.owner || "—"}</dd></div><div className="kv"><dt>Phone</dt><dd>{client.phone ? <a href={`tel:${client.phone}`}>{client.phone}</a> : "—"}</dd></div>
                  <div className="kv"><dt>Email</dt><dd>{client.email ? <a href={`mailto:${client.email}`}>{client.email}</a> : "—"}</dd></div></dl></div>
              </div>
              {isAdmin ? (
                <div className="card"><div className="card-head"><span className="label">Admin tools</span></div>
                  <div className="card-body" style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                    <button className="btn btn-sm" onClick={() => setSheet({ kind: "dups" })}>Find duplicates by EIN</button>
                    <button className="btn btn-sm" onClick={() => setSheet({ kind: "merge" })}>Merge another client into this one</button>
                    <button className="btn btn-sm btn-quiet btn-danger" style={{ marginLeft: "auto" }} onClick={askDelete}><Icon name="trash" size={14} />Delete client</button>
                  </div></div>
              ) : null}
            </>
          ) : null}
        </div>
      </section>

      <aside className="context scroll">
        {actions.length ? <div><span className="label">Actions</span><div className="action-grid">{actions.map(a => <button key={a.label} className="action" onClick={a.run}><Icon name={a.icon} />{a.label}</button>)}</div></div> : null}
        <div><BalanceCard totals={totals} /></div>
        <RenewalBanner renewals={data ? data.renewals : []} advances={advances} canWrite={canWrite} onStart={startRenewal} onSnooze={snooze} />
        <div>
          <span className="label">Recent activity</span>
          <div className="mini">
            {side.activity && side.activity.length ? side.activity.slice(0, 5).map((a, i) => <div className="mini-row" key={i}>{a.text}<div className="when">{fmt.when(a.ts)}</div></div>)
              : <div className="mini-row muted">{side.activity ? "Nothing logged yet." : <button className="link-btn" onClick={() => setTab("activity")}>Open the Activity tab</button>}</div>}
          </div>
        </div>
      </aside>

      {sheet && sheet.kind === "advance" ? <AdvanceSheet clientId={clientId} advance={sheet.advance} seed={sheet.seed} lenders={lenders} onClose={close} onSaved={() => changed(sheet.advance ? "Advance saved" : "Advance added")} onDelete={sheet.advance && (!sheet.advance.dealId || isAdmin) ? () => removeAdvance(sheet.advance) : null} /> : null}
      {sheet && sheet.kind === "log" ? <LogPaymentSheet advance={sheet.advance} onClose={close} onSaved={() => changed("Progress updated")} /> : null}
      {sheet && sheet.kind === "months" ? <MonthsSheet clientId={clientId} months={data ? data.months : []} onClose={close} onSaved={() => changed("Months saved")} /> : null}
      {sheet && sheet.kind === "merge" ? <MergeSheet client={client} clients={clients} onClose={close} onSaved={async (r) => { close(); await refreshClients(); await load(); refreshBalances(); toast(`Merged: ${r.moved.deals} deals, ${r.moved.advances} advances moved`); }} /> : null}
      {sheet && sheet.kind === "dups" ? <DuplicatesSheet toast={toast} onClose={close} onMerged={async () => { await refreshClients(); await load(); refreshBalances(); }} /> : null}
      {sheet && sheet.kind === "pii" ? <EnterPiiSheet kind="client" id={clientId} biz={client.biz} has={{ ssn: !!(data && data.pii && data.pii.ssnLast4), ein: !!(data && data.pii && data.pii.einLast4), dob: !!(data && data.pii && data.pii.dob) }} onClose={close} onSaved={async () => { close(); await refreshClients(); await load(); toast("Saved encrypted"); }} /> : null}
      {sheet && sheet.kind === "client" ? <ClientSheet client={client} onClose={close} onDelete={isAdmin ? askDelete : null} onSaved={async (c) => { close(); await refreshClients(); await load(); onChanged(c); toast("Client saved"); }} /> : null}
    </>
  );
}

// ── Clients view ───────────────────────────────────────────────────────────
function ClientsView({ user, clients, deals, team, lenders, office, refreshClients, refreshDeals, selectedId, select, toast, confirm, go }) {
  const [creating, setCreating] = useState(false);
  const [exporting, setExporting] = useState(false);
  const [newDeal, setNewDeal] = useState(null);     // { client, renewalOf }
  const [balances, setBalances] = useState({});
  const canWrite = !!canOf(user).write;
  const refreshBalances = useCallback(async () => { try { setBalances(await API.clientBalances()); } catch (e) { /* the rail shows names without balances */ } }, []);
  // Every funded deal is a client: an Admin opening Clients links any funded deal that still has no record (idempotent, cheap when there is nothing to do).
  useEffect(() => {
    (async () => { if (canOf(user).manage) { try { const r = await API.backfillClients(); if (r.linked && refreshDeals) refreshDeals(); } catch (e) { /* the list still loads */ } } await refreshClients(); refreshBalances(); })();
  }, []);
  const client = clients.find(c => c.id === selectedId) || null;
  // On wide screens, open the first client instead of an empty file.
  useEffect(() => {
    if (!selectedId && clients.length && window.matchMedia("(min-width: 901px)").matches) select(clients[0].id, true);
  }, [selectedId, clients.length]);

  return (
    <main className="workspace clients-ws" data-view={client ? "file" : "list"}>
      <ClientRail clients={clients} balances={balances} selectedId={selectedId} onSelect={select} onNew={() => setCreating(true)} canWrite={canWrite} onExport={canOf(user).manage ? () => setExporting(true) : null} />
      {client ? (
        <ClientFile client={client} clients={clients} team={team} lenders={lenders} user={user} office={office} canWrite={canWrite} go={go}
          onBack={() => select(null)} onChanged={() => {}} onNewDeal={(o) => setNewDeal({ client, ...o })}
          refreshClients={refreshClients} refreshDeals={refreshDeals} refreshBalances={refreshBalances} toast={toast} confirm={confirm} />
      ) : (
        <>
          <section className="file"><div className="tab-body" style={{ display: "grid", placeItems: "center", height: "100%" }}>
            {selectedId ? <Empty title="That client isn't here">It may have been merged or deleted. Pick another from the list.</Empty>
              : <Empty title={clients.length ? "Pick a client" : "No clients yet"}>{clients.length ? "Choose a business on the left to open its file." : "Create a client from a deal's Client tab, or add one here."}</Empty>}
          </div></section>
          <aside className="context" />
        </>
      )}
      {exporting ? <ExportSheet what="clients" onClose={() => setExporting(false)} toast={toast} /> : null}
      {creating ? <ClientSheet onClose={() => setCreating(false)} onSaved={async (c) => { setCreating(false); await refreshClients(); refreshBalances(); toast("Client created"); if (c && c.id) select(c.id); }} /> : null}
      {newDeal ? (
        <DealSheet team={team} lenders={lenders} user={user} onClose={() => setNewDeal(null)}
          seed={{ biz: newDeal.client.biz, contact: newDeal.client.owner, phone: newDeal.client.phone, email: newDeal.client.email, source: newDeal.renewalOf ? `Renewal · ${newDeal.renewalOf.funder}` : "Repeat client",
            ...(newDeal.renewalOf ? { amount: newDeal.renewalOf.amount } : {}) }}
          extra={{ clientId: newDeal.client.id, allowDuplicate: true }}
          onSaved={async (d) => { setNewDeal(null); toast(`Deal ${d.id} created for ${newDeal.client.biz}`); if (refreshDeals) refreshDeals(); go("deals", d.id); }} />
      ) : null}
    </main>
  );
}

Object.assign(window, { ClientsView, AdvanceLedger, DepositsChart, BalanceCard });
