// Bylt Capital CRM — the lender rolodex: who the office is signed up with, the
// ISO rep and where files go at each lender, each lender's requirements (what
// the lender publishes, and the office's own box from its ISO rep), and for
// Admins: importing the lender directory, renaming, merging and removing.

const tel = (p) => "tel:" + String(p || "").replace(/[^\d+]/g, "");
const mailTo = (e) => "mailto:" + encodeURIComponent(String(e || "")).replace(/%40/g, "@");
// Websites are stored as a bare domain ("kapitus.com"); portals as http(s) links.
const siteHref = (w) => (/^[a-z0-9-]+(\.[a-z0-9-]+)+$/i.test(String(w || "")) ? "https://" + w : null);
const portalHref = (u) => (/^https?:\/\/[^\s"<>]+$/i.test(String(u || "")) ? u : null);
const ordinal = (n) => { const v = n % 100; return n + (v >= 11 && v <= 13 ? "th" : ({ 1: "st", 2: "nd", 3: "rd" }[n % 10] || "th")); };
const hasRep = (r) => !!(r && (r.name || r.email || r.phone));

// ── How requirement values read ─────────────────────────────────────────────
const UNIT_TEXT = {
  usd: (n) => fmt.moneyShort(n),
  months: (n) => (n >= 12 && n % 12 === 0 ? `${n / 12} ${n === 12 ? "year" : "years"}` : `${n} ${n === 1 ? "month" : "months"}`),
  score: (n) => String(n),
  position: (n) => ordinal(n),
  count: (n) => String(n),
  factor: (n) => String(n),
  points: (n) => `${n} ${n === 1 ? "point" : "points"}`,
};
const unitText = (def, n) => (UNIT_TEXT[def.unit] || String)(n);
const CHOICE_TEXT = { yes: "Yes", case: "Case by case", no: "No" };
// One requirement value in words. Falls back to the text as published.
function fmtRequirement(def, v) {
  if (!v) return "";
  const has = (x) => x !== undefined && x !== null && x !== "";
  if (def.kind === "min" && has(v.min)) return `At least ${unitText(def, v.min)}`;
  if (def.kind === "max" && has(v.max)) return `At most ${unitText(def, v.max)}`;
  if (def.kind === "range" && (has(v.min) || has(v.max))) {
    if (has(v.min) && has(v.max)) return v.min === v.max ? `${unitText(def, v.min)} only` : `${unitText(def, v.min)} to ${unitText(def, v.max)}`;
    return has(v.min) ? `From ${unitText(def, v.min)}` : `Up to ${unitText(def, v.max)}`;
  }
  if ((def.kind === "states" || def.kind === "grades") && (v.list || []).length) return v.list.join(", ");
  if (def.kind === "choice" && v.choice) return CHOICE_TEXT[v.choice] || v.choice;
  return v.text || "";
}
// The short chips on the list.
function reqChips(summary) {
  const s = summary || {}, out = [];
  const add = (key, text) => { if (text) out.push({ key, text, ours: s[key].from === "box" }); };
  if (s.timeInBusiness && s.timeInBusiness.min != null) add("timeInBusiness", `${s.timeInBusiness.min >= 12 && s.timeInBusiness.min % 12 === 0 ? s.timeInBusiness.min / 12 + " yr" : s.timeInBusiness.min + " mo"}+ in business`);
  if (s.monthlyRevenue && s.monthlyRevenue.min != null) add("monthlyRevenue", `${fmt.moneyShort(s.monthlyRevenue.min)}+/mo`);
  if (s.creditScore && s.creditScore.min != null) add("creditScore", `${s.creditScore.min}+ credit`);
  if (s.amount && (s.amount.min != null || s.amount.max != null)) add("amount", s.amount.min != null && s.amount.max != null ? `${fmt.moneyShort(s.amount.min)}–${fmt.moneyShort(s.amount.max)}` : s.amount.max != null ? `Up to ${fmt.moneyShort(s.amount.max)}` : `${fmt.moneyShort(s.amount.min)}+`);
  if (s.positions && (s.positions.min != null || s.positions.max != null)) add("positions", s.positions.min != null && s.positions.max != null ? (s.positions.min === s.positions.max ? `${ordinal(s.positions.max)} position only` : `${ordinal(s.positions.min)}–${ordinal(s.positions.max)} position`) : `Up to ${ordinal(s.positions.max)} position`);
  if (s.paper && (s.paper.list || []).length) add("paper", `${s.paper.list.length > 1 ? s.paper.list[0] + "–" + s.paper.list[s.paper.list.length - 1] : s.paper.list[0]} paper`);
  return out;
}

// ── The office's box: form state <-> stored shape ───────────────────────────
function boxToForm(defs, box) {
  const out = {};
  for (const d of defs) {
    const v = (box || {})[d.key] || {};
    out[d.key] = {
      min: v.min != null ? String(v.min) : "", max: v.max != null ? String(v.max) : "",
      list: d.kind === "grades" ? (v.list || []) : (v.list || []).join(", "),
      choice: v.choice || "", text: v.text || "",
    };
  }
  return out;
}
// Returns { box } or { error }.
function formToBox(defs, form) {
  const box = {};
  const number = (d, raw, which) => {
    const s = String(raw || "").trim();
    if (!s) return null;
    const n = d.unit === "usd" ? fmt.parseMoney(s) : Number(s.replace(/,/g, ""));
    if (typeof n !== "number" || !isFinite(n) || n < 0) throw new Error(`${d.label}: the ${which} isn't a number.`);
    return n;
  };
  try {
    for (const d of defs) {
      const f = form[d.key] || {}, v = {};
      if (d.kind === "min" || d.kind === "range") { const n = number(d, f.min, "minimum"); if (n != null) v.min = n; }
      if (d.kind === "max" || d.kind === "range") { const n = number(d, f.max, "maximum"); if (n != null) v.max = n; }
      if (v.min != null && v.max != null && v.min > v.max) throw new Error(`${d.label}: the minimum is above the maximum.`);
      if (d.kind === "states" && String(f.list || "").trim()) v.list = String(f.list).split(/[\s,]+/).map(x => x.trim().toUpperCase()).filter(Boolean);
      if (d.kind === "grades" && (f.list || []).length) v.list = f.list;
      if (d.kind === "choice" && f.choice) v.choice = f.choice;
      if (d.kind === "text" && String(f.text || "").trim()) v.text = String(f.text).trim();
      if (Object.keys(v).length) box[d.key] = v;
    }
  } catch (e) { return { error: e.message }; }
  return { box };
}

const GROUP_LABELS = { merchant: "What a merchant must meet", pricing: "Pricing", process: "How they work" };

function RequirementsEditor({ defs, form, setForm, published }) {
  const set = (key, field, value) => setForm(f => ({ ...f, [key]: { ...f[key], [field]: value } }));
  const groups = ["merchant", "pricing", "process"];
  return groups.map(g => (
    <div key={g} className="req-group">
      <div className="req-group-head">{GROUP_LABELS[g]}</div>
      {defs.filter(d => d.group === g).map(d => {
        const f = form[d.key] || {};
        const pub = published && published[d.key];
        const pubText = pub ? fmtRequirement(d, pub) : "";
        const hint = pubText ? `Published: ${pubText}` : "Not published";
        const id = "req-" + d.key;
        let control;
        if (d.kind === "min" || d.kind === "max") {
          const field = d.kind;
          control = <input id={id} className="input" inputMode="decimal" autoComplete="off" value={f[field]} onChange={e => set(d.key, field, e.target.value)} placeholder={d.kind === "min" ? "Minimum" : "Maximum"} />;
        } else if (d.kind === "range") {
          control = (
            <div className="req-range">
              <input id={id} className="input" inputMode="decimal" autoComplete="off" value={f.min} onChange={e => set(d.key, "min", e.target.value)} placeholder="Min" aria-label={`${d.label} minimum`} />
              <span className="muted">to</span>
              <input className="input" inputMode="decimal" autoComplete="off" value={f.max} onChange={e => set(d.key, "max", e.target.value)} placeholder="Max" aria-label={`${d.label} maximum`} />
            </div>
          );
        } else if (d.kind === "states") {
          control = <input id={id} className="input" autoComplete="off" value={f.list} onChange={e => set(d.key, "list", e.target.value)} placeholder="e.g. CA, NY" />;
        } else if (d.kind === "grades") {
          control = (
            <div className="check-row" role="group" aria-label={d.label}>
              {["A", "B", "C", "D"].map(gr => (
                <label key={gr}><input type="checkbox" checked={(f.list || []).includes(gr)}
                  onChange={e => set(d.key, "list", ["A", "B", "C", "D"].filter(x => (x === gr ? e.target.checked : (f.list || []).includes(x))))} />{gr}</label>
              ))}
            </div>
          );
        } else if (d.kind === "choice") {
          control = <Select id={id} value={f.choice} onChange={x => set(d.key, "choice", x)} options={[{ value: "", label: "—" }].concat((d.options || []).map(o => ({ value: o, label: CHOICE_TEXT[o] || o })))} />;
        } else {
          control = <textarea id={id} className="textarea req-text" value={f.text} onChange={e => set(d.key, "text", e.target.value)} maxLength={1000} rows={2} />;
        }
        return (
          <div className="req-row" key={d.key}>
            <label className="label" htmlFor={d.kind === "grades" ? undefined : id}>{d.label}{d.unit === "usd" ? " ($)" : d.unit === "months" ? " (months)" : d.unit === "position" ? " (position #)" : ""}</label>
            {control}
            <div className="hint lh">{hint}</div>
          </div>
        );
      })}
    </div>
  ));
}

// Read-only: what applies to this lender, and where each value came from.
function RequirementsTable({ defs, effective, emptyText }) {
  const shown = defs.filter(d => effective && effective[d.key] && (fmtRequirement(d, effective[d.key]) || effective[d.key].note));
  if (!shown.length) return <div className="muted" style={{ fontSize: 13.5 }}>{emptyText}</div>;
  return (
    <dl className="req-table">
      {shown.map(d => {
        const v = effective[d.key];
        return (
          <div className="kv" key={d.key}>
            <dt>{d.label}</dt>
            <dd>
              {fmtRequirement(d, v) || null}
              {v.note ? <span className="sub">{v.note}</span> : null}
              <span className={`req-src${v.from === "box" ? " ours" : ""}`}>{v.from === "box" ? "Your box" : "Published"}</span>
            </dd>
          </div>
        );
      })}
    </dl>
  );
}

// The directory's profile of the lender (public data, read-only).
function LenderProfile({ lender }) {
  const g = lender.guide;
  if (!g) return null;
  const t = g.tier || {};
  const rows = [
    ["Directory tier", t.label ? `${t.label}${t.confidence ? ` · ${t.confidence} confidence` : ""}` : ""],
    ["How it was rated", [t.method, t.basis].filter(Boolean).join(". ")],
    ["Products", (g.products || []).join(", ")],
    ["Business model", g.businessModel],
    ["ISO program", g.isoProgram],
    ["Underwriting notes", g.underwriting],
    ["Research", [g.research, g.completeness != null ? `${g.completeness}% of fields published` : ""].filter(Boolean).join(" · ")],
    ["Guidelines as of", g.asOf ? fmt.date(g.asOf + "T12:00:00Z") : ""],
  ].filter(r => r[1]);
  return (
    <details className="profile">
      <summary>Published profile{(lender.directoryIds || [])[0] ? ` · ${lender.directoryIds[0]}` : ""}</summary>
      <dl>
        {rows.map(([k, v]) => <div className="kv" key={k}><dt>{k}</dt><dd>{v}</dd></div>)}
        {g.sourceUrl || g.profileUrl ? (
          <div className="kv"><dt>Sources</dt><dd>
            {g.sourceUrl ? <a href={g.sourceUrl} target="_blank" rel="noopener noreferrer">Lender's guidelines</a> : null}
            {g.sourceUrl && g.profileUrl ? " · " : ""}
            {g.profileUrl ? <a href={g.profileUrl} target="_blank" rel="noopener noreferrer">Directory profile</a> : null}
          </dd></div>
        ) : null}
      </dl>
      <p className="hint lh" style={{ margin: "8px 16px 12px" }}>Public information, not the office's agreement with the lender. Confirm the current box with the ISO rep before relying on it.</p>
    </details>
  );
}

function LenderSheet({ lender, lenders, canEdit, onClose, onSaved, toast, confirm }) {
  const editing = !!lender;
  const [full, setFull] = useState(null);
  const [defs, setDefs] = useState(null);
  const [loadError, setLoadError] = useState("");
  const l = full || lender || {};
  const [name, setName] = useState(l.name || "");
  const [website, setWebsite] = useState(l.website || "");
  const [signedUp, setSignedUp] = useState(!!l.signedUp);
  const [signedUpAt, setSignedUpAt] = useState(l.signedUpAt || "");
  // Platform financing: the office's own choice, or (null) the built-in list of names (platformAuto).
  const [platform, setPlatform] = useState(l.platform === true || l.platform === false ? l.platform : null);
  const [rep, setRep] = useState({ name: (l.isoRep || {}).name || "", email: (l.isoRep || {}).email || "", phone: (l.isoRep || {}).phone || "" });
  const [portalUrl, setPortalUrl] = useState(l.portalUrl || "");
  const [submitEmail, setSubmitEmail] = useState(l.submitEmail || "");
  const [notes, setNotes] = useState(l.notes || "");
  const [contacts, setContacts] = useState(() => (l.contacts || []).map((c, i) => ({ key: "c" + i, name: c.name || "", role: c.role || "", email: c.email || "", phone: c.phone || "" })));
  const [box, setBox] = useState(null);
  const [into, setInto] = useState("");
  const seq = useRef((l.contacts || []).length);
  const save = useSave(onSaved);
  const edit = (key, field, value) => setContacts(list => list.map(c => (c.key === key ? { ...c, [field]: value } : c)));

  // The list carries a summary; the box, published profile and requirement list come from the server.
  useEffect(() => {
    let live = true;
    const load = editing ? API.lender(lender.id) : API.lenderRequirements();
    load.then(r => {
      if (!live) return;
      setDefs(r.requirements || []);
      if (r.lender) setFull(r.lender);
      setBox(boxToForm(r.requirements || [], r.lender ? r.lender.box : {}));
    }).catch(e => live && setLoadError(e.message || "Couldn't load this lender."));
    return () => { live = false; };
  }, []);

  const submit = () => {
    if (!name.trim()) return save.setError("Enter the lender's name.");
    if (submitEmail.trim() && !/^\S+@\S+\.\S+$/.test(submitEmail.trim())) return save.setError("The submissions email doesn't look like an email address.");
    if (rep.email.trim() && !/^\S+@\S+\.\S+$/.test(rep.email.trim())) return save.setError("The ISO rep's email doesn't look like an email address.");
    const list = contacts.map(c => ({ name: c.name.trim(), role: c.role.trim(), email: c.email.trim(), phone: c.phone.trim() })).filter(c => c.name || c.email || c.phone);
    const bad = list.find(c => c.email && !/^\S+@\S+\.\S+$/.test(c.email));
    if (bad) return save.setError(`"${bad.email}" doesn't look like an email address.`);
    const body = {
      name: name.trim(), website: website.trim(), signedUp, signedUpAt: signedUp ? signedUpAt : "",
      isoRep: { name: rep.name.trim(), email: rep.email.trim(), phone: rep.phone.trim() },
      portalUrl: portalUrl.trim(), submitEmail: submitEmail.trim(), notes, contacts: list,
      ...(editing && platform !== (l.platform === true || l.platform === false ? l.platform : null) ? { platform } : {}),
      ...(!editing && platform !== null ? { platform } : {}),
    };
    if (defs && box) {
      const b = formToBox(defs, box);
      if (b.error) return save.setError(b.error);
      body.box = b.box;
    }
    save.run(() => (editing ? API.updateLender(l.id, body) : API.createLender(body)));
  };
  const others = (lenders || []).filter(x => x.id !== l.id);
  const merge = () => {
    const target = others.find(x => x.id === into);
    if (!target) return save.setError("Choose the lender to merge into.");
    confirm({
      title: `Merge ${l.name} into ${target.name}?`,
      body: `Every response and funded deal filed under ${l.name} moves to ${target.name}, along with its contacts, ISO rep and requirements where ${target.name} has none. ${l.name} comes off the list. This can't be undone.`,
      confirmLabel: "Merge lenders",
      onConfirm: async () => { const r = await API.mergeLender(l.id, target.id); toast(`Merged into ${target.name}${r && r.moved ? ` · ${r.moved} ${r.moved === 1 ? "response" : "responses"} moved` : ""}`); onSaved(null, true); },
    });
  };
  const remove = () => confirm({
    title: `Remove ${l.name}?`, body: "It comes off your lender list. A lender with responses on file can't be removed; merge it into another instead. Importing the directory again adds a removed directory lender back.", confirmLabel: "Remove lender",
    onConfirm: async () => { await API.deleteLender(l.id); toast("Lender removed"); onSaved(null, true); },
  });
  const site = siteHref(l.website), portal = portalHref(l.portalUrl);
  const sameSite = (full && full.sameWebsite) || [];

  if (!canEdit) {
    const r = l.isoRep || {};
    return (
      <Sheet eyebrow="Lenders" title={l.name} onClose={onClose}>
        <div className="form-section">
          <div className="lender-status">
            {l.signedUp ? <Pill status="active" label="Signed up" /> : <Pill status="none" label="Not signed up" plain />}
            {l.signedUp && l.signedUpAt ? <span className="muted"> since {fmt.date(l.signedUpAt + "T12:00:00Z")}</span> : null}
          </div>
          <dl style={{ marginTop: 10 }}>
            <div className="kv"><dt>Website</dt><dd>{site ? <a href={site} target="_blank" rel="noopener noreferrer">{l.website}</a> : <span className="muted">Not on file</span>}</dd></div>
            <div className="kv"><dt>ISO rep</dt><dd>{hasRep(r) ? <>{r.name || null}{r.phone ? <div><a href={tel(r.phone)}>{r.phone}</a></div> : null}{r.email ? <div><a href={mailTo(r.email)}>{r.email}</a></div> : null}</> : <span className="muted">Not on file</span>}</dd></div>
            {portal ? <div className="kv"><dt>ISO portal</dt><dd><a href={portal} target="_blank" rel="noopener noreferrer">Open portal</a></dd></div> : null}
            <div className="kv"><dt>Send files to</dt><dd>{l.submitEmail ? <a href={mailTo(l.submitEmail)}>{l.submitEmail}</a> : <span className="muted">Not on file. Ask an Admin to add one.</span>}</dd></div>
          </dl>
        </div>
        <div className="form-section">
          <span className="label">Requirements</span>
          {loadError ? <div className="err">{loadError}</div> : !defs ? <div className="muted">Loading…</div>
            : <RequirementsTable defs={defs} effective={l.effective} emptyText="No requirements on file for this lender." />}
        </div>
        <div className="form-section">
          <span className="label">Other contacts</span>
          {(l.contacts || []).length ? (l.contacts || []).map((c, i) => (
            <div className="contact-card" key={i}>
              <b>{c.name || c.email || c.phone}</b>{c.role ? <span className="muted"> · {c.role}</span> : null}
              <div>{c.email ? <a href={mailTo(c.email)}>{c.email}</a> : null}{c.email && c.phone ? " · " : ""}{c.phone ? <a href={tel(c.phone)}>{c.phone}</a> : null}</div>
            </div>
          )) : <div className="muted" style={{ marginTop: 6, fontSize: 13.5 }}>No other contacts on file.</div>}
        </div>
        {l.notes ? <div className="form-section"><span className="label">Notes</span><div className="notes" style={{ marginTop: 6 }}>{l.notes}</div></div> : null}
        {full && full.guide ? <div className="form-section"><LenderProfile lender={full} /></div> : null}
      </Sheet>
    );
  }

  return (
    <Sheet eyebrow="Lenders" title={editing ? l.name || "Edit lender" : "Add lender"} onClose={onClose} onSubmit={submit}
           submitLabel={editing ? "Save lender" : "Add lender"} busy={save.busy} error={save.error}>
      <div className="form-section">
        <div className="form-grid">
          <Field label="Lender name" full hint={editing ? "Renaming carries through to every response and funded deal filed under the old name." : null}>
            <TextInput value={name} onChange={setName} />
          </Field>
          <Field label="Website" full hint={site && website.trim() === l.website ? <a href={site} target="_blank" rel="noopener noreferrer">Open {l.website}</a> : "For example kapitus.com"}>
            <TextInput value={website} onChange={setWebsite} autoComplete="off" inputMode="url" />
          </Field>
        </div>
        {editing && (l.otherNames || []).length ? <div className="hint lh" style={{ marginTop: 10 }}>Also filed under: {(l.otherNames || []).join(", ")}</div> : null}
      </div>

      <div className="form-section">
        <span className="label">On bank statements</span>
        <div className="switch-row boxed">
          <div><div className="t">Platform financing</div><div className="d">{(platform === null ? !!l.platformAuto : platform)
            ? "Like PayPal, Square or DoorDash: listed apart on statements and left out of the position count and the leverage %. A second leverage figure includes it."
            : "Counted as a position, and in the leverage %."}{platform === null ? <span className="muted"> {l.platformAuto ? "Set from the name." : ""}</span> : <> <button type="button" className="link-btn" onClick={() => setPlatform(null)}>Go by the name</button></>}</div></div>
          <Switch checked={platform === null ? !!l.platformAuto : platform} onChange={setPlatform} label="Platform financing" />
        </div>
      </div>

      <div className="form-section">
        <span className="label">ISO agreement</span>
        <div className="switch-row boxed">
          <div><div className="t">Signed up with this lender</div><div className="d">{signedUp ? "The office can submit deals here." : "No ISO agreement yet."}</div></div>
          <Switch checked={signedUp} onChange={setSignedUp} label="Signed up with this lender" />
        </div>
        <div className="form-grid" style={{ marginTop: 14 }}>
          {signedUp ? <Field label="Signed up on" full><TextInput type="date" value={signedUpAt} onChange={setSignedUpAt} /></Field> : null}
          <Field label="ISO rep name" full><TextInput value={rep.name} onChange={x => setRep(r => ({ ...r, name: x }))} autoComplete="off" /></Field>
          <Field label="ISO rep phone"><TextInput value={rep.phone} onChange={x => setRep(r => ({ ...r, phone: x }))} inputMode="tel" autoComplete="off" /></Field>
          <Field label="ISO rep email"><TextInput value={rep.email} onChange={x => setRep(r => ({ ...r, email: x }))} type="email" autoComplete="off" /></Field>
          <Field label="ISO portal link" full><TextInput value={portalUrl} onChange={setPortalUrl} inputMode="url" autoComplete="off" placeholder="https://" /></Field>
          <Field label="Submissions email" full hint="Where deal files go.">
            <TextInput value={submitEmail} onChange={setSubmitEmail} type="email" autoComplete="off" />
          </Field>
        </div>
      </div>

      <div className="form-section">
        <span className="label">Requirements — your box</span>
        <p className="hint lh" style={{ margin: "-4px 0 12px" }}>What the ISO rep told you. Anything you fill in here is used instead of the published value; leave a field empty to keep the published one.</p>
        {loadError ? <div className="err">{loadError}</div> : !defs || !box ? <div className="muted">Loading…</div>
          : <RequirementsEditor defs={defs} form={box} setForm={setBox} published={full && full.guide ? full.guide.requirements : null} />}
        {full && full.boxUpdatedAt ? <div className="hint lh" style={{ marginTop: 10 }}>Box last changed {fmt.when(full.boxUpdatedAt)}{full.boxUpdatedBy ? ` by ${full.boxUpdatedBy}` : ""}.</div> : null}
      </div>

      <div className="form-section">
        <span className="label">Other contacts</span>
        {contacts.map((c, i) => (
          <div className="offer-pkg" key={c.key} style={{ marginTop: 10 }}>
            <div className="offer-pkg-head">
              <b>Contact {i + 1}</b>
              <button type="button" className="icon-btn" onClick={() => setContacts(list => list.filter(x => x.key !== c.key))} aria-label={`Remove contact ${i + 1}`}><Icon name="trash" size={15} /></button>
            </div>
            <div className="form-grid">
              <Field label="Name"><TextInput value={c.name} onChange={x => edit(c.key, "name", x)} /></Field>
              <Field label="Role"><TextInput value={c.role} onChange={x => edit(c.key, "role", x)} placeholder="Underwriter, renewals…" /></Field>
              <Field label="Email"><TextInput value={c.email} onChange={x => edit(c.key, "email", x)} type="email" autoComplete="off" /></Field>
              <Field label="Phone"><TextInput value={c.phone} onChange={x => edit(c.key, "phone", x)} inputMode="tel" autoComplete="off" /></Field>
            </div>
          </div>
        ))}
        {contacts.length < 12 ? <div><button type="button" className="btn btn-sm" style={{ marginTop: 10 }} onClick={() => setContacts(list => list.concat([{ key: "c" + (++seq.current), name: "", role: "", email: "", phone: "" }]))}><Icon name="plus" size={13} />Add a contact</button></div> : null}
      </div>
      <div className="form-section">
        <Field label="Notes" hint="What they like, turnaround, who to call first, anything else.">
          <textarea className="textarea" value={notes} onChange={e => setNotes(e.target.value)} maxLength={2000} />
        </Field>
      </div>
      {full && full.guide ? <div className="form-section"><LenderProfile lender={full} /></div> : null}
      {editing ? (
        <div className="form-section">
          <span className="label">Merge or remove</span>
          {sameSite.length ? <div className="banner info" style={{ marginBottom: 12 }}>Same website as {sameSite.map(x => x.name).join(", ")}. If it's the same company, merge them.</div> : null}
          {others.length ? (
            <div className="line-edit" style={{ gridTemplateColumns: "minmax(0, 1fr) auto" }}>
              <Field label={`Merge ${l.name} into`}>
                <Select value={into} onChange={setInto} options={[{ value: "", label: "Choose a lender…" }].concat(sameSite.map(x => ({ value: x.id, label: `${x.name} (same website)` }))).concat(others.filter(x => !sameSite.some(s => s.id === x.id)).map(x => ({ value: x.id, label: x.name })))} />
              </Field>
              <div className="line-out"><button type="button" className="btn" disabled={!into || save.busy} onClick={merge}>Merge</button></div>
            </div>
          ) : null}
          <p className="hint lh" style={{ margin: "8px 0 12px" }}>For the same lender entered under two names. Its responses, funded deals and contacts move to the one you choose.</p>
          <button type="button" className="btn btn-sm btn-danger" disabled={save.busy} onClick={remove}><Icon name="trash" size={14} />Remove from the list</button>
        </div>
      ) : null}
    </Sheet>
  );
}

// ── Importing the lender directory (Admins) ─────────────────────────────────
function ImportLendersSheet({ onClose, onDone }) {
  const [csv, setCsv] = useState("");
  const [fileName, setFileName] = useState("");
  const [plan, setPlan] = useState(null);
  const [defs, setDefs] = useState([]);
  const [checking, setChecking] = useState(false);
  const save = useSave(onDone);
  useEffect(() => { API.lenderRequirements().then(r => setDefs(r.requirements || [])).catch(() => {}); }, []);

  const pick = async (file) => {
    save.setError(""); setPlan(null); setCsv(""); setFileName(file ? file.name : "");
    if (!file) return;
    if (file.size > 5 * 1024 * 1024) return save.setError("That file is too large (5 MB at most).");
    setChecking(true);
    try {
      const text = await file.text();
      const p = await API.importLenders(text, false);
      setCsv(text); setPlan(p);
    } catch (e) { save.setError(e.message || "Couldn't read that file."); }
    finally { setChecking(false); }
  };
  const writes = plan ? plan.create.length + plan.update.length : 0;
  const submit = () => {
    if (!plan) return save.setError("Choose the directory file first.");
    if (!writes) return save.setError("Nothing to import: every lender in this file is already up to date.");
    save.run(() => API.importLenders(csv, true));
  };
  const few = (names, n = 12) => names.slice(0, n).join(", ") + (names.length > n ? ` and ${names.length - n} more` : "");

  return (
    <Sheet eyebrow="Lenders" title="Import lender directory" onClose={onClose} onSubmit={submit}
           submitLabel={plan ? `Import ${writes} ${writes === 1 ? "lender" : "lenders"}` : "Import"} busyLabel="Importing…" busy={save.busy || checking} error={save.error}>
      <div className="form-section">
        <Field label="Directory file (.csv)" hint="Nothing is saved until you press Import. Lenders already on file keep their name, sign-up status, ISO rep, contacts, notes and your box; only the published information is refreshed.">
          <input type="file" className="input" accept=".csv,text/csv" onChange={e => pick(e.target.files && e.target.files[0])} />
        </Field>
        {checking ? <div className="muted" style={{ marginTop: 10 }}>Checking {fileName}…</div> : null}
      </div>
      {plan ? <>
        <div className="form-section">
          <span className="label">What the import will do</span>
          <dl>
            <div className="kv"><dt>Rows in file</dt><dd>{plan.rows}</dd></div>
            <div className="kv"><dt>New lenders</dt><dd>{plan.create.length}{plan.create.length ? <span className="sub">{few(plan.create)}</span> : null}</dd></div>
            <div className="kv"><dt>Updated</dt><dd>{plan.update.length}{plan.update.length ? <span className="sub">{few(plan.update)}</span> : null}</dd></div>
            <div className="kv"><dt>Already up to date</dt><dd>{plan.unchanged}</dd></div>
            <div className="kv"><dt>Skipped</dt><dd>{plan.skipped.length}{plan.skipped.map(s => <span className="sub" key={s.row}>Row {s.row} · {s.name}: {s.reason}</span>)}</dd></div>
            <div className="kv"><dt>With requirements</dt><dd>{plan.withRequirements} of {plan.rows - plan.skipped.length}<span className="sub">publish at least one requirement a deal can be checked against</span></dd></div>
          </dl>
        </div>
        {plan.sameWebsite.length ? (
          <div className="form-section">
            <span className="label">Same website, probably the same lender ({plan.sameWebsite.length})</span>
            <p className="hint lh" style={{ margin: "-4px 0 8px" }}>They're imported as separate lenders. Once you've confirmed, merge them from either lender's page.</p>
            {plan.sameWebsite.map(g => <div className="sub" key={g.website} style={{ fontSize: 13.5, color: "var(--ink)" }}>{g.names.join(" · ")} <span className="muted">({g.website})</span></div>)}
          </div>
        ) : null}
        {defs.length ? (
          <div className="form-section">
            <span className="label">Published in this file</span>
            <table className="list compact">
              <thead><tr><th>Requirement</th><th className="num">Lenders</th><th className="num">As numbers</th></tr></thead>
              <tbody>{defs.filter(d => plan.coverage[d.key] && plan.coverage[d.key].published).map(d => (
                <tr key={d.key}><td>{d.label}</td><td className="num tnum">{plan.coverage[d.key].published}</td><td className="num tnum">{d.kind === "text" ? <span className="muted">text</span> : plan.coverage[d.key].numbers}</td></tr>
              ))}</tbody>
            </table>
            <p className="hint lh" style={{ marginTop: 8 }}>"As numbers" are the values a deal can be checked against later. The rest is kept as the published wording.</p>
          </div>
        ) : null}
      </> : null}
    </Sheet>
  );
}

// ── Find a Lender: the merchant's figures in, the lenders whose requirements allow them out ──
const US_STATE_CODES = "AL AK AZ AR CA CO CT DE DC FL GA HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND OH OK OR PA PR RI SC SD TN TX UT VT VA WA WV WI WY".split(" ");
const FIND_BLANK = { tib: "", tibUnit: "years", credit: "", revenue: "", amount: "", positions: "", state: "", industry: "", adb: "", nsfs: "", negDays: "", def: "", paper: "", onlySigned: false, showGaps: false };
const MATCH_MARK = { pass: "✓", fail: "✕", unknown: "?", warn: "!" };
const MATCH_STATUS = { fits: { pill: "approved", label: "Fits" }, unverified: { pill: "submitted", label: "Has gaps" }, no: { pill: "declined", label: "Doesn't fit" }, nodata: { pill: "none", label: "Nothing to check" } };

// Turns the form into the query the server reads. Returns { q } or { error }.
function findQuery(f) {
  const q = {};
  const money = (v, label) => { if (!String(v).trim()) return null; const n = fmt.parseMoney(v); if (typeof n !== "number" || !isFinite(n) || n < 0) throw new Error(`${label} isn't an amount.`); return Math.round(n); };
  const whole = (v, label) => { if (!String(v).trim()) return null; const n = Number(String(v).replace(/,/g, "")); if (!isFinite(n) || n < 0) throw new Error(`${label} isn't a number.`); return n; };
  try {
    const t = whole(f.tib, "Time in business"); if (t != null) q.tib = Math.round(f.tibUnit === "years" ? t * 12 : t);
    const c = whole(f.credit, "Credit score"); if (c != null) q.credit = c;
    const r = money(f.revenue, "Monthly revenue"); if (r != null) q.revenue = r;
    const a = money(f.amount, "Amount requested"); if (a != null) q.amount = a;
    const d = money(f.adb, "Average daily balance"); if (d != null) q.adb = d;
    const n = whole(f.nsfs, "NSFs"); if (n != null) q.nsfs = n;
    const g = whole(f.negDays, "Negative days"); if (g != null) q.negDays = g;
  } catch (e) { return { error: e.message }; }
  if (f.positions !== "") q.positions = f.positions;
  if (f.state) q.state = f.state;
  if (f.industry.trim()) q.industry = f.industry.trim();
  if (f.def) q.default = f.def;
  if (f.paper) q.paper = f.paper;
  return { q };
}

function MatchRow({ r, defs, onOpen }) {
  const st = MATCH_STATUS[r.status];
  const def = (k) => (defs || []).find(d => d.key === k) || { key: k, kind: "text" };
  const shown = r.status === "no" ? r.checks.filter(c => c.result === "fail") : r.checks;
  return (
    <div className="match-row">
      <div className="match-head">
        <button type="button" className="link-btn strong" onClick={() => onOpen(r.id)}>{r.name}</button>
        <span className="match-pills">
          {r.signedUp ? <Pill status="active" label="Signed up" /> : null}
          <Pill status={st.pill} label={st.label} plain={r.status === "nodata"} />
        </span>
      </div>
      {r.isoRep && (r.isoRep.name || r.isoRep.phone) ? <div className="sub">ISO rep: {[r.isoRep.name, r.isoRep.phone, r.isoRep.email].filter(Boolean).join(" · ")}</div> : null}
      <div className="match-checks">
        {shown.map(c => {
          const v = c.value ? fmtRequirement(def(c.key), c.value) : "";
          const txt = c.key === "restrictedIndustries" ? (c.result === "warn" ? "Industry may be restricted" : "Industry not on their restricted list")
            : c.key === "restrictedStates" ? (c.result === "fail" ? c.detail : c.result === "pass" ? "Funds your state" : "State: not published")
            : c.result === "unknown" ? `${c.label}: not published` : `${c.label}: ${v || c.detail}`;
          return <span key={c.key} className={`mchip ${c.result}`} title={c.detail || ""}><b aria-hidden="true">{MATCH_MARK[c.result]}</b>{txt}</span>;
        })}
      </div>
    </div>
  );
}

function FindLenderSheet({ form, setForm, onClose, onOpen }) {
  const f = form;
  const set = (k, v) => setForm(x => ({ ...x, [k]: v }));
  const [defs, setDefs] = useState([]);
  const [res, setRes] = useState(null);
  const [error, setError] = useState("");
  const [busy, setBusy] = useState(false);
  const [moreOpen, setMoreOpen] = useState(!!(f.adb || f.nsfs || f.negDays || f.def || f.paper));
  useEffect(() => { API.lenderRequirements().then(r => setDefs(r.requirements || [])).catch(() => {}); }, []);
  const built = findQuery(f);
  const key = built.q ? JSON.stringify(built.q) : "";
  useEffect(() => {
    if (built.error) { setError(built.error); return; }
    setError("");
    if (!Object.keys(built.q).length) { setRes(null); return; }
    let live = true;
    const t = setTimeout(async () => {
      setBusy(true);
      try { const r = await API.findLenders(built.q); if (live) setRes(r); }
      catch (e) { if (live) setError(e.message || "Couldn't check the lenders."); }
      finally { if (live) setBusy(false); }
    }, 300);
    return () => { live = false; clearTimeout(t); };
  }, [key, built.error]);

  const all = res ? res.results.filter(r => !f.onlySigned || r.signedUp) : [];
  const fits = all.filter(r => r.status === "fits");
  const gaps = all.filter(r => r.status === "unverified");
  const nos = all.filter(r => r.status === "no");
  const none = all.filter(r => r.status === "nodata").length;
  const shownList = fits.concat(f.showGaps ? gaps : []);
  const posOptions = [{ value: "", label: "—" }].concat(Array.from({ length: 8 }, (_, i) => ({ value: String(i), label: i === 0 ? "None (this would be 1st)" : `${i} (this would be ${ordinal(i + 1)})` })));

  return (
    <Sheet eyebrow="Lenders" title="Find a lender" onClose={onClose}>
      <div className="form-section">
        <p className="hint lh" style={{ margin: "-4px 0 12px" }}>Fill in what you know about the merchant. Lenders are checked against your box first, then what they publish. Leave a box empty to skip it.</p>
        <div className="form-grid">
          <Field label="Time in business">
            <div className="tib-input">
              <input className="input" inputMode="decimal" autoComplete="off" value={f.tib} onChange={e => set("tib", e.target.value)} aria-label="Time in business" />
              <Segmented label="Unit" value={f.tibUnit} onChange={v => set("tibUnit", v)} options={[{ id: "years", label: "Years" }, { id: "months", label: "Months" }]} />
            </div>
          </Field>
          <Field label="Credit score"><TextInput value={f.credit} onChange={v => set("credit", v)} inputMode="numeric" autoComplete="off" placeholder="e.g. 620" /></Field>
          <Field label="Monthly revenue"><TextInput value={f.revenue} onChange={v => set("revenue", v)} inputMode="decimal" autoComplete="off" placeholder="e.g. 45k" /></Field>
          <Field label="Amount requested"><TextInput value={f.amount} onChange={v => set("amount", v)} inputMode="decimal" autoComplete="off" placeholder="e.g. 100k" /></Field>
          <Field label="Open positions"><Select value={f.positions} onChange={v => set("positions", v)} options={posOptions} /></Field>
          <Field label="State"><Select value={f.state} onChange={v => set("state", v)} options={[{ value: "", label: "—" }].concat(US_STATE_CODES.map(s => ({ value: s, label: s })))} /></Field>
          <Field label="Industry" full hint="Checked against each lender's restricted-industry wording; a match is flagged, not ruled out."><TextInput value={f.industry} onChange={v => set("industry", v)} autoComplete="off" placeholder="e.g. trucking, restaurant" /></Field>
        </div>
        <button type="button" className="link-btn" style={{ marginTop: 12 }} onClick={() => setMoreOpen(o => !o)} aria-expanded={moreOpen}>{moreOpen ? "Fewer details" : "More details: balance, NSFs, negative days, defaults, paper"}</button>
        {moreOpen ? (
          <div className="form-grid" style={{ marginTop: 12 }}>
            <Field label="Average daily balance"><TextInput value={f.adb} onChange={v => set("adb", v)} inputMode="decimal" autoComplete="off" /></Field>
            <Field label="NSFs last month"><TextInput value={f.nsfs} onChange={v => set("nsfs", v)} inputMode="numeric" autoComplete="off" /></Field>
            <Field label="Negative days last month"><TextInput value={f.negDays} onChange={v => set("negDays", v)} inputMode="numeric" autoComplete="off" /></Field>
            <Field label="Prior default"><Select value={f.def} onChange={v => set("def", v)} options={[{ value: "", label: "—" }, { value: "no", label: "No" }, { value: "yes", label: "Yes" }]} /></Field>
            <Field label="Paper"><Select value={f.paper} onChange={v => set("paper", v)} options={[{ value: "", label: "—" }, { value: "A", label: "A" }, { value: "B", label: "B" }, { value: "C", label: "C" }, { value: "D", label: "D" }]} /></Field>
          </div>
        ) : null}
        <div className="find-toggles">
          <label className="toggle-label"><Switch checked={f.onlySigned} onChange={v => set("onlySigned", v)} label="Only lenders we're signed up with" /><span>Only lenders we're signed up with</span></label>
          <label className="toggle-label"><Switch checked={f.showGaps} onChange={v => set("showGaps", v)} label="Include lenders with gaps" /><span>Include lenders with gaps{res ? ` (${gaps.length})` : ""}</span></label>
        </div>
        <button type="button" className="btn btn-sm" style={{ marginTop: 12 }} onClick={() => setForm(FIND_BLANK)}>Clear</button>
      </div>
      <div className="form-section" aria-live="polite">
        {error ? <div className="err">{error}</div>
          : !res ? <div className="muted" style={{ fontSize: 13.5 }}>Results appear as you fill in the boxes.</div>
          : <>
            <div className="match-summary">
              <b>{fits.length} {fits.length === 1 ? "lender fits" : "lenders fit"}</b>
              <span className="muted"> · {gaps.length} with gaps · {nos.length} don't fit · {none} publish nothing to check{busy ? " · updating…" : ""}</span>
            </div>
            <p className="hint lh" style={{ margin: "4px 0 10px" }}>"Fits" means every figure you entered was checked and passed. "Has gaps" means nothing failed but the lender doesn't publish some of them. Confirm with the ISO rep before submitting.</p>
            {shownList.length ? shownList.map(r => <MatchRow key={r.id} r={r} defs={defs} onOpen={onOpen} />)
              : <div className="muted" style={{ fontSize: 13.5, margin: "8px 0" }}>{gaps.length && !f.showGaps ? `No lender passes every check. Turn on "Include lenders with gaps" to see the ${gaps.length} where nothing failed.` : "No lender matches these figures."}</div>}
            {nos.length ? (
              <details className="profile" style={{ marginTop: 14 }}>
                <summary>{nos.length} {nos.length === 1 ? "lender doesn't" : "lenders don't"} fit, and why</summary>
                <div style={{ padding: "4px 14px 10px" }}>{nos.map(r => <MatchRow key={r.id} r={r} defs={defs} onOpen={onOpen} />)}</div>
              </details>
            ) : null}
          </>}
      </div>
    </Sheet>
  );
}

const LENDER_FILTER_KEY = "ef.lenders.withRequirements";
function LendersView({ user, lenders, refreshLenders, toast, confirm }) {
  const [q, setQ] = useState("");
  const [sheet, setSheet] = useState(null);
  const [filter, setFilter] = useState("all");
  const [findForm, setFindForm] = useState(FIND_BLANK);
  const [onlyReq, setOnlyReq] = useState(() => { try { return localStorage.getItem(LENDER_FILTER_KEY) === "1"; } catch { return false; } });
  const isAdmin = !!canOf(user).manage;
  useEffect(() => { refreshLenders(); }, []);
  const setOnly = (v) => { setOnlyReq(v); try { localStorage.setItem(LENDER_FILTER_KEY, v ? "1" : "0"); } catch { /* private window */ } };

  const needle = q.trim().toLowerCase();
  const searched = lenders.filter(l => (!onlyReq || l.hasRequirements) && matches(needle, l.name, l.website, l.submitEmail, l.notes, (l.otherNames || []).join(" "),
    l.isoRep ? `${l.isoRep.name} ${l.isoRep.email} ${l.isoRep.phone}` : "", (l.contacts || []).map(c => `${c.name} ${c.email} ${c.phone}`).join(" ")));
  const filters = [
    { id: "all", label: "All", n: searched.length },
    { id: "signed", label: "Signed up", n: searched.filter(l => l.signedUp).length },
    { id: "not", label: "Not signed up", n: searched.filter(l => !l.signedUp).length },
  ];
  const rows = searched.filter(l => filter === "all" || (filter === "signed" ? l.signedUp : !l.signedUp))
    .sort((a, b) => (b.signedUp ? 1 : 0) - (a.signedUp ? 1 : 0) || (a.name || "").localeCompare(b.name || ""));
  const showStats = lenders.some(l => l.stats && l.stats.submissions);

  return (
    <main className="workspace single">
      <div className="page">
        <div className="page-head">
          <div>
            <h1>Lenders</h1>
            <p>{isAdmin ? "Who you're signed up with, your ISO rep at each lender, and what each one requires." : "Who the office is signed up with, who to call, and what each lender requires."}</p>
          </div>
          <div className="page-tools lender-tools">
            <PageSearch value={q} onChange={setQ} placeholder="Search lenders" />
            <button className="btn btn-primary" onClick={() => setSheet({ kind: "find" })}><Icon name="search" size={14} />Find a lender</button>
            {isAdmin ? <button className="btn" onClick={() => setSheet({ kind: "import" })}><Icon name="upload" size={14} />Import</button> : null}
            {isAdmin ? <button className="btn" onClick={() => setSheet({ kind: "lender" })}><Icon name="plus" size={14} />Add lender</button> : null}
          </div>
        </div>
        <div className="lender-filters">
          <div className="filters" role="group" aria-label="Filter by sign-up">
            {filters.map(f => (
              <button key={f.id} className="filter" aria-pressed={filter === f.id} onClick={() => setFilter(f.id)}>{f.label} <span className="n">{f.n}</span></button>
            ))}
          </div>
          <label className="toggle-label">
            <Switch checked={onlyReq} onChange={setOnly} label="Only lenders with requirements" />
            <span>Only lenders with requirements</span>
          </label>
        </div>
        <div className="card">
          {!rows.length ? <Empty title={lenders.length ? "No lenders match" : "No lenders yet"}>{lenders.length ? "Try a different search or filter." : isAdmin ? "Import the lender directory, add one here, or log a lender's response on a deal." : "Lenders appear here once an Admin adds them."}</Empty> : (
            <div className="table-wrap">
              <table className="list lenders">
                <thead><tr><th>Lender</th><th>Status</th><th>ISO rep</th><th>Requirements</th>{showStats ? <><th className="num">Responses</th><th className="num">Funded</th></> : null}<th /></tr></thead>
                <tbody>
                  {rows.map(l => {
                    const s = l.stats || {};
                    const r = l.isoRep || {};
                    const chips = reqChips(l.summary);
                    const site = siteHref(l.website);
                    return (
                      <tr key={l.id}>
                        <td><div className="who"><Avatar name={l.name} size="sm" square /><div>
                          <button className="link-btn strong" onClick={() => setSheet({ kind: "lender", lender: l })}>{l.name}</button>
                          {site ? <a className="sub" href={site} target="_blank" rel="noopener noreferrer">{l.website}</a> : null}
                        </div></div></td>
                        <td data-label="Status">{l.signedUp ? <Pill status="active" label="Signed up" /> : <Pill status="none" label="Not signed up" plain />}{(l.platform === true || (l.platform !== false && l.platformAuto)) ? <> <span className="pill plain s-neutral" title="Listed apart on statements; not counted as a position">Platform</span></> : null}</td>
                        <td data-label="ISO rep">
                          {hasRep(r) ? <>
                            <span style={{ fontSize: 13 }}>{r.name || r.email || r.phone}</span>
                            {r.phone && r.name ? <span className="sub"><a href={tel(r.phone)}>{r.phone}</a></span> : null}
                            {r.email && (r.name || r.phone) ? <span className="sub"><a href={mailTo(r.email)}>{r.email}</a></span> : null}
                          </> : <span className="muted">None</span>}
                        </td>
                        <td data-label="Requirements">
                          {chips.length ? <div className="chips tight">{chips.map(c => <span key={c.key} className={`chip${c.ours ? " ours" : ""}`} title={c.ours ? "From your ISO rep" : "Published"}>{c.text}</span>)}</div>
                            : <span className="muted">{l.hasRequirements ? "See lender" : "None published"}</span>}
                        </td>
                        {showStats ? <>
                          <td className="num tnum" data-label="Responses">{s.submissions ? <>{s.submissions}<span className="sub">{[s.offers ? `${s.offers} with terms` : "", s.declines ? `${s.declines} declined` : "", s.pending ? `${s.pending} pending` : ""].filter(Boolean).join(" · ")}</span></> : <span className="muted">0</span>}</td>
                          <td className="num tnum" data-label="Funded">{s.funded ? <>{fmt.money(s.fundedAmount)}<span className="sub">{s.funded} {s.funded === 1 ? "deal" : "deals"}</span></> : <span className="muted">—</span>}</td>
                        </> : null}
                        <td className="acts"><button className="btn btn-sm" onClick={() => setSheet({ kind: "lender", lender: l })}>{isAdmin ? "Edit" : "Open"}</button></td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </div>
      {sheet && sheet.kind === "lender" ? (
        <LenderSheet key={sheet.lender ? sheet.lender.id : "new"} lender={sheet.lender} lenders={lenders} canEdit={isAdmin} toast={toast} confirm={confirm}
          onClose={() => setSheet(sheet.fromFind ? { kind: "find" } : null)}
          onSaved={async (_l, quiet) => { const was = sheet.lender, back = sheet.fromFind; setSheet(back ? { kind: "find" } : null); await refreshLenders(); if (!quiet) toast(was ? "Lender saved" : "Lender added"); }} />
      ) : null}
      {sheet && sheet.kind === "find" ? (
        <FindLenderSheet form={findForm} setForm={setFindForm} onClose={() => setSheet(null)}
          onOpen={(id) => { const l = lenders.find(x => x.id === id); if (l) setSheet({ kind: "lender", lender: l, fromFind: true }); }} />
      ) : null}
      {sheet && sheet.kind === "import" ? (
        <ImportLendersSheet onClose={() => setSheet(null)}
          onDone={async (r) => { setSheet(null); await refreshLenders(); toast(`Imported · ${r.created || 0} new · ${r.update.length} updated`); }} />
      ) : null}
    </main>
  );
}

Object.assign(window, { LendersView });
