/* =========================================================================
   Veto · Home — a calm overview, a launchpad, and a setup guide.

   Not the tasks page. The home answers, at a glance: where do I stand?
   Big reconciled numbers up top; scroll and the detail reveals itself with
   no clicks (money movement, recent files). New users get a nudge-y, three-
   step setup. The cure-lists and holds live on Inbox; the office god-view
   lives on /overview. Here we keep it minimal.

   Inspirations honored: Mercury (big number → scroll into detail, hover
   breakdowns) and Acctual (guided "Step n of 3" onboarding).
   ========================================================================= */

const HOME_REVIEWER = { name: "Madeline Lane", initials: "ML", first: "Madeline" };
const HOME_ORG = "805 Escrow · Westlake Village";
const HELD_RELEASE = {
  fileId: "SP-0214",
  seller: "James Whitfield",
  payee: "Whitfield Coast Holdings LLC",
  officer: "Madeline Lane",
  amount: 182742,
  action: "Release seller proceeds",
  domain: "Money out",
  freshness: "42m",
  detectedAt: "May 21 · 3:18 PM",
};
const HOME_DAY = 86400000;

function greetingHome(name) {
  const h = new Date().getHours();
  return (h < 12 ? "Good morning" : h < 18 ? "Good afternoon" : "Good evening") + ", " + name;
}

/* ------------------------------------------------------- trust (Plaid) */
const BANKS = ["First Republic", "Wells Fargo", "Chase", "Bank of America", "Comerica", "City National"];
function useTrust() {
  const [bank, setBank] = useState(() => { try { return localStorage.getItem("veto.trust.bank") || ""; } catch { return ""; } });
  const connect = (b) => { try { localStorage.setItem("veto.trust.bank", b); } catch {} setBank(b); };
  return [bank, connect];
}
function PlaidModal({ onClose, onConnect }) {
  return (
    <div className="fixed inset-0 z-[100] flex items-center justify-center bg-ink/25 px-4 backdrop-blur-[1px]" onMouseDown={onClose}>
      <div className="w-full max-w-[400px] overflow-hidden rounded-2xl border border-line bg-background shadow-[0_24px_64px_-24px_rgba(0,0,0,0.4)]" onMouseDown={(e) => e.stopPropagation()}>
        <div className="border-b border-line px-5 py-4">
          <div className="text-[13px] font-semibold text-ink">Select your bank</div>
          <div className="text-[12px] text-ink-soft">Veto uses Plaid. Read-only. We never move money.</div>
        </div>
        <div className="max-h-[320px] overflow-y-auto p-2">
          {BANKS.map((b) => (
            <button key={b} onClick={() => onConnect(b)} className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left transition hover:bg-surface">
              <span className="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-surface-2 text-ink-soft"><Icon name="landmark" size={15} /></span>
              <span className="flex-1 text-[13px] text-ink">{b}</span>
              <Icon name="chevron-right" size={14} className="text-ink-soft" />
            </button>
          ))}
        </div>
        <div className="border-t border-line px-5 py-3 text-center text-[11px] text-ink-soft">Secured by Plaid · bank-grade encryption</div>
      </div>
    </div>
  );
}

/* ----------------------------------------------------------- data layer */
function buildFile(id) {
  const cf = CASE_FILES[id];
  const r = getReadiness(id) || buildReadinessFrom(id, cf.reviewer, cf.reviewerInitials, cf.closing);
  const mv = fileMovements(id);
  return { id, cf, r, mv, state: r.recording.state, closingMs: Date.parse(cf.closing) || Number.MAX_SAFE_INTEGER };
}
function homeMetrics() {
  const files = REVIEWERS.ML.openFiles.map(buildFile);
  const anchor = Math.min(...files.map((f) => f.closingMs));
  const today = files.filter((f) => Math.abs(f.closingMs - anchor) < HOME_DAY / 2);
  // One escrow per property: multiple review lines (SP/BF/RF) hold the SAME money,
  // so dedupe to the richest line-file before summing — no repeated addresses, no
  // double-counted dollars.
  const byProp = {};
  files.forEach((f) => { const s = streetOf(f.cf.property); if (!byProp[s] || f.mv.totalIn > byProp[s].mv.totalIn) byProp[s] = f; });
  const escrows = Object.values(byProp);
  const week = escrows.filter((f) => f.closingMs <= anchor + 7 * HOME_DAY).sort((a, b) => (a.closingMs - b.closingMs) || (b.mv.totalIn - a.mv.totalIn));
  const clear = escrows.filter((f) => f.state === "Clear to record");
  let recorded = 0, expected = 0;
  escrows.forEach((f) => {
    recorded += f.mv.deposits.filter((d) => d.status === "Recorded").reduce((s, d) => s + d.amount, 0);
    expected += f.mv.totalIn;
  });
  const movingThisWeek = week.reduce((n, f) => n + f.mv.totalIn, 0);
  const weekIn = movingThisWeek;
  const weekOut = week.reduce((n, f) => n + f.mv.totalOut, 0);
  return { files, escrows, anchor, today, week, clear, inTrust: recorded, pending: expected - recorded, expected, movingThisWeek, weekIn, weekOut };
}
function countdown(closingMs, anchor) {
  const d = Math.round((closingMs - anchor) / HOME_DAY);
  if (d <= 0) return "Today";
  if (d === 1) return "Tomorrow";
  return shortClosing(new Date(closingMs).toLocaleDateString("en-US", { month: "short", day: "numeric" }));
}

const STATE_DOT = { "Clear to record": "bg-[var(--ok)]", "Holds open": "bg-[var(--warn)]", "Evidence open": "bg-ink-soft/40" };

/* hover breakdown popover — Mercury's dark pill */
function StatPop({ trigger, rows }) {
  const [open, setOpen] = useState(false);
  return (
    <span className="relative inline-flex flex-col" onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
      {trigger}
      {open && (
        <span className="absolute left-0 top-full z-40 mt-2 min-w-[210px] rounded-xl bg-ink p-3 shadow-[0_18px_44px_-18px_rgba(0,0,0,0.55)]"
          style={{ animation: "hintIn .13s cubic-bezier(0.16,1,0.3,1)", transformOrigin: "left top" }}>
          {rows.map((r, i) => (
            <span key={i} className={cn("flex items-center justify-between gap-8 py-0.5 text-[12px]", r.strong && "mt-1 border-t border-background/15 pt-1.5")}>
              <span className="text-background/65">{r.k}</span>
              <span className="tabular-nums text-background">{r.v}</span>
            </span>
          ))}
        </span>
      )}
    </span>
  );
}

/* ------------------------------------------------------------- Sparkline
   Tufte-minimal: one thin line, no fill, no gridlines, a single dot marking
   the current value. Maximize data-ink. */
/* ------------------------------------------------------------- Sparkline
   Tufte-minimal: one thin line, no fill, no gridlines, a single dot marking
   the current value. With `interactive`, hovering the line reveals a crosshair
   + the value at that point (direct labeling — what Tufte would actually want). */
function Sparkline({ data, values, reference, refLabel, className, dot = true, interactive = false }) {
  const W = 100, H = 38, pad = 3;
  const ref = useRef(null);
  const [hi, setHi] = useState(null);
  const series = values || data;
  const lo = Math.min(...series, reference != null ? reference : Infinity);
  const hiV = Math.max(...series, reference != null ? reference : -Infinity);
  const norm = (v) => H - pad - ((v - lo) / (hiV - lo || 1)) * (H - pad * 2);
  const pts = series.map((v, i) => [(i / (series.length - 1)) * W, norm(v)]);
  const line = pts.map((p) => p.join(",")).join(" ");
  const endY = pts[pts.length - 1][1];
  const refY = reference != null ? norm(reference) : null;
  const onMove = (e) => {
    const r = ref.current.getBoundingClientRect();
    const t = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
    setHi(Math.round(t * (series.length - 1)));
  };
  const hp = hi != null ? pts[hi] : null;
  return (
    <div ref={ref} className={cn("relative", className)}
      onMouseMove={interactive ? onMove : undefined} onMouseLeave={interactive ? () => setHi(null) : undefined}>
      <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" className="h-full w-full" aria-hidden="true">
        {refY != null && <line x1="0" y1={refY} x2={W} y2={refY} stroke="var(--ink)" strokeWidth="0.5" strokeDasharray="2 2" vectorEffect="non-scaling-stroke" opacity="0.22" />}
        <polyline points={line} fill="none" stroke="var(--ink)" strokeWidth="0.75" strokeLinejoin="round" strokeLinecap="round" vectorEffect="non-scaling-stroke" opacity={hi != null ? 0.6 : 0.4} />
      </svg>
      {refLabel && refY != null && (
        <span className="pointer-events-none absolute left-0 -translate-y-1/2 bg-background pr-1.5 text-[10px] text-muted-foreground" style={{ top: `${(refY / H) * 100}%` }}>{refLabel}</span>
      )}
      {dot && hi == null && <span className="absolute h-[5px] w-[5px] -translate-y-1/2 translate-x-1/2 rounded-full bg-ink" style={{ right: 0, top: `${(endY / H) * 100}%` }} />}
      {interactive && hp && (
        <>
          <span className="pointer-events-none absolute bottom-0 top-0 w-px bg-ink/15" style={{ left: `${hp[0]}%` }} />
          <span className="pointer-events-none absolute h-2 w-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-ink ring-2 ring-background" style={{ left: `${hp[0]}%`, top: `${(hp[1] / H) * 100}%` }} />
          {values && (
            <span className="pointer-events-none absolute z-10 whitespace-nowrap rounded-md bg-ink px-2 py-1 text-[11px] font-medium tabular-nums text-background shadow-sm"
              style={{ left: `${Math.min(86, Math.max(14, hp[0]))}%`, top: `${(hp[1] / H) * 100}%`, transform: "translate(-50%, calc(-100% - 9px))" }}>{fmtUSD(values[hi])}</span>
          )}
        </>
      )}
    </div>
  );
}

/* ============================================================ Setup band
   Four passes live here — flip SETUP_PASS to compare. Each pass folds in the
   feedback: benefit-driven copy (not "finish setting up Veto"), the whole tile
   is the click target, and it already shows progress (the office step lands
   pre-completed, so you open at 1 of 3, not a cold 0). */
const SETUP_PASS = 4;
const SETUP_STEPS = [
  { key: "trust", icon: "landmark", title: "Connect your trust account", blurb: "Bank activity can be matched back to the records that support it.", est: "3 min" },
  { key: "reviewers", icon: "users", title: "Add a second reviewer", blurb: "A file can't record without a second review — invite the people you close with.", est: "2 min" },
];
function useSetup(bankConnected) {
  const [marked, setMarked] = useState(() => { try { return JSON.parse(localStorage.getItem("veto.setup") || "[]"); } catch { return []; } });
  const [dismissed, setDismissed] = useState(() => { try { return localStorage.getItem("veto.setup.dismiss") === "1"; } catch { return false; } });
  const mark = (k) => setMarked((d) => { const n = [...new Set([...d, k])]; try { localStorage.setItem("veto.setup", JSON.stringify(n)); } catch {} return n; });
  const dismiss = () => { try { localStorage.setItem("veto.setup.dismiss", "1"); } catch {} setDismissed(true); };
  let invitesDone = false; try { invitesDone = localStorage.getItem("veto.setup.invitesDone") === "1"; } catch {}
  const done = new Set([...marked, bankConnected ? "trust" : null, invitesDone ? "reviewers" : null].filter(Boolean));
  return { done, mark, dismissed, dismiss };
}
function SetupBand(props) {
  const total = SETUP_STEPS.length;
  const count = props.done.size;
  const activeKey = (SETUP_STEPS.find((s) => !props.done.has(s.key)) || {}).key;
  const ctx = { ...props, total, count, activeKey, pct: (count / total) * 100, act: { trust: props.onTrust, reviewers: props.onReviewers } };
  return [SetupPass1, SetupPass2, SetupPass3, SetupPass4][SETUP_PASS - 1](ctx);
}

/* ---- Pass 1 · fix the three complaints: whole tile clickable, real progress, kinder copy */
function SetupPass1({ done, act, onDismiss, first, total, count, pct }) {
  return (
    <section className="mt-8 overflow-hidden rounded-2xl border border-line bg-surface/40">
      <div className="flex items-center justify-between gap-4 px-5 pt-5">
        <div>
          <h2 className="om-display text-[15px] font-semibold tracking-[-0.01em] text-ink">You're almost set, {first}.</h2>
          <p className="mt-0.5 text-[12.5px] text-ink-soft">Two quick steps and Veto ties money movement back to current records.</p>
        </div>
        <div className="flex shrink-0 items-center gap-3">
          <span className="font-mono text-[12px] tabular-nums text-ink-soft">{count} of {total}</span>
          <button onClick={onDismiss} aria-label="Dismiss" className="grid h-7 w-7 place-items-center rounded-md text-muted-foreground transition hover:bg-surface hover:text-ink"><Icon name="x" size={14} /></button>
        </div>
      </div>
      <div className="mt-4 h-1 w-full bg-line"><div className="h-full bg-ink transition-all duration-500" style={{ width: pct + "%" }} /></div>
      <div className="grid grid-cols-1 gap-px bg-line sm:grid-cols-3">
        {SETUP_STEPS.map((s) => {
          const isDone = done.has(s.key);
          return (
            <button key={s.key} type="button" onClick={() => !isDone && act[s.key] && act[s.key]()}
              className={cn("group relative flex flex-col items-start gap-3 bg-background p-5 text-left transition", isDone ? "cursor-default" : "hover:bg-surface/50")}>
              {!isDone && <Icon name="arrow-up-right" size={14} className="absolute right-4 top-4 text-muted-foreground opacity-0 transition group-hover:opacity-100" />}
              <span className={cn("grid h-9 w-9 place-items-center rounded-xl border transition", isDone ? "border-transparent bg-[var(--ok-bg)] text-[var(--ok)]" : "border-line bg-surface text-ink-soft group-hover:border-ink/25 group-hover:text-ink")}>
                <Icon name={isDone ? "check" : s.icon} size={16} strokeWidth={isDone ? 3 : 1.6} />
              </span>
              <div>
                <div className={cn("text-[13.5px] font-medium", isDone ? "text-ink-soft" : "text-ink")}>{s.title}</div>
                <p className="mt-1 text-[12px] leading-relaxed text-ink-soft">{s.blurb}</p>
              </div>
            </button>
          );
        })}
      </div>
    </section>
  );
}

/* ---- Pass 2 · Acctual structure: time estimates, hover lift, arrow slides in */
function SetupPass2({ done, act, onDismiss, first, total, count, pct }) {
  return (
    <section className="mt-8 rounded-2xl border border-line bg-background p-6">
      <div className="flex items-end justify-between gap-4">
        <div>
          <h2 className="om-display text-[19px] font-semibold tracking-[-0.015em] text-ink">Let's protect your trust account, {first}.</h2>
          <p className="mt-1 text-[13px] text-ink-soft">Two steps left — about five minutes, and every closing is covered.</p>
        </div>
        <span className="shrink-0 font-mono text-[12px] tabular-nums text-ink-soft">Step {count} of {total}</span>
      </div>
      <div className="mt-4 h-1.5 w-full overflow-hidden rounded-full bg-line"><div className="h-full rounded-full bg-ink transition-all duration-500" style={{ width: pct + "%" }} /></div>
      <div className="mt-5 grid grid-cols-1 gap-3 sm:grid-cols-3">
        {SETUP_STEPS.map((s) => {
          const isDone = done.has(s.key);
          return (
            <button key={s.key} type="button" onClick={() => !isDone && act[s.key] && act[s.key]()}
              className={cn("group flex flex-col items-start gap-3 rounded-xl border p-4 text-left transition", isDone ? "cursor-default border-line bg-surface/40" : "border-line bg-background hover:-translate-y-0.5 hover:border-ink/20 hover:shadow-[0_10px_28px_-18px_rgba(0,0,0,0.4)]")}>
              <div className="flex w-full items-center justify-between">
                <span className={cn("grid h-9 w-9 place-items-center rounded-xl transition", isDone ? "bg-[var(--ok-bg)] text-[var(--ok)]" : "bg-surface text-ink-soft group-hover:text-ink")}>
                  <Icon name={isDone ? "check" : s.icon} size={16} strokeWidth={isDone ? 3 : 1.6} />
                </span>
                <span className="font-mono text-[10.5px] uppercase tracking-[0.05em] text-muted-foreground">{s.est}</span>
              </div>
              <div>
                <div className={cn("text-[13.5px] font-medium", isDone ? "text-ink-soft" : "text-ink")}>{s.title}</div>
                <p className="mt-1 text-[12px] leading-relaxed text-ink-soft">{s.blurb}</p>
              </div>
              {!isDone && <span className="mt-1 inline-flex items-center gap-1 text-[12px] font-medium text-ink">Start <Icon name="arrow-right" size={13} className="transition group-hover:translate-x-1" /></span>}
            </button>
          );
        })}
      </div>
      <div className="mt-4 flex justify-end"><button onClick={onDismiss} className="text-[12px] text-ink-soft underline decoration-line underline-offset-4 transition hover:text-ink">Skip for now</button></div>
    </section>
  );
}

/* ---- Pass 3 · numbered steps + explicit active/done/upcoming states */
function SetupPass3({ done, act, onDismiss, first, total, count, activeKey, pct }) {
  return (
    <section className="mt-8 rounded-2xl border border-line bg-background p-6">
      <div className="flex items-center justify-between gap-4">
        <h2 className="om-display text-[19px] font-semibold tracking-[-0.015em] text-ink">You're {count} of {total} of the way there, {first}.</h2>
        <div className="flex items-center gap-3">
          <span className="font-mono text-[12px] tabular-nums text-ink-soft">~5 min left</span>
          <button onClick={onDismiss} className="text-[12px] text-ink-soft underline decoration-line underline-offset-4 transition hover:text-ink">Skip</button>
        </div>
      </div>
      <div className="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3">
        {SETUP_STEPS.map((s, i) => {
          const isDone = done.has(s.key);
          const isActive = s.key === activeKey;
          return (
            <button key={s.key} type="button" onClick={() => !isDone && act[s.key] && act[s.key]()}
              className={cn("group flex flex-col items-start gap-3 rounded-xl border p-4 text-left transition",
                isDone ? "cursor-default border-line bg-background" : isActive ? "border-ink/30 bg-surface/40 shadow-[0_0_0_1px_var(--ink)]" : "border-line bg-background hover:border-ink/20")}>
              <div className="flex w-full items-center justify-between">
                <span className={cn("grid h-8 w-8 place-items-center rounded-full text-[12px] font-semibold transition",
                  isDone ? "bg-[var(--ok-bg)] text-[var(--ok)]" : isActive ? "bg-ink text-background" : "bg-surface text-ink-soft")}>
                  {isDone ? <Icon name="check" size={14} strokeWidth={3} /> : i + 1}
                </span>
                <Icon name={s.icon} size={16} className={cn(isActive ? "text-ink" : "text-muted-foreground")} />
              </div>
              <div>
                <div className={cn("text-[13.5px] font-medium", isDone ? "text-ink-soft" : "text-ink")}>{s.title}</div>
                <p className="mt-1 text-[12px] leading-relaxed text-ink-soft">{s.blurb}</p>
              </div>
              <span className={cn("mt-1 text-[11.5px]", isDone ? "text-[var(--ok)]" : isActive ? "font-medium text-ink" : "text-muted-foreground")}>{isDone ? "Done" : isActive ? "Continue →" : s.est}</span>
            </button>
          );
        })}
      </div>
    </section>
  );
}

/* ---- Pass 4 · final: inline progress meter, refined tiles, active step led */
function SetupPass4({ done, act, onDismiss, first, total, count, activeKey, pct }) {
  const remaining = total - count;
  return (
    <section className="mt-8 rounded-2xl border border-line bg-gradient-to-b from-surface/50 to-background p-6">
      <div className="flex flex-wrap items-center justify-between gap-x-6 gap-y-3">
        <div>
          <h2 className="om-display text-[20px] font-semibold tracking-[-0.015em] text-ink">{remaining === 1 ? "One step" : "Two steps"} from trust-account controls, {first}.</h2>
          <p className="mt-1 text-[13px] text-ink-soft">Connect your bank and open a file — then Veto records each money action against the support behind it.</p>
        </div>
        <div className="flex items-center gap-3">
          <div className="flex items-center gap-2">
            <div className="h-1.5 w-28 overflow-hidden rounded-full bg-line"><div className="h-full rounded-full bg-ink transition-all duration-500" style={{ width: pct + "%" }} /></div>
            <span className="font-mono text-[11.5px] tabular-nums text-ink-soft">{count}/{total}</span>
          </div>
          <button onClick={onDismiss} className="text-[12px] text-ink-soft transition hover:text-ink">Skip</button>
        </div>
      </div>
      <div className="mt-5 grid grid-cols-1 gap-3 sm:grid-cols-2">
        {SETUP_STEPS.map((s) => {
          const isDone = done.has(s.key);
          const isActive = s.key === activeKey;
          return (
            <button key={s.key} type="button" onClick={() => !isDone && act[s.key] && act[s.key]()}
              className={cn("group relative flex flex-col gap-4 overflow-hidden rounded-xl border p-5 text-left transition",
                isDone ? "cursor-default border-line bg-background/60" : "border-line bg-background hover:-translate-y-0.5 hover:border-ink/25 hover:shadow-[0_12px_30px_-18px_rgba(0,0,0,0.45)]",
                isActive && "ring-1 ring-ink/15")}>
              {isActive && <span aria-hidden className="absolute inset-x-0 top-0 h-[3px] bg-ink" />}
              <div className="flex items-center justify-between">
                <span className={cn("grid h-11 w-11 place-items-center rounded-2xl transition",
                  isDone ? "bg-[var(--ok-bg)] text-[var(--ok)]" : isActive ? "bg-ink text-background" : "bg-surface text-ink-soft group-hover:text-ink")}>
                  <Icon name={isDone ? "check" : s.icon} size={19} strokeWidth={isDone ? 3 : 1.6} />
                </span>
                {isDone
                  ? <span className="inline-flex items-center gap-1 text-[11.5px] font-medium text-[var(--ok)]"><Icon name="check" size={12} strokeWidth={3} /> Done</span>
                  : <span className="font-mono text-[10.5px] uppercase tracking-[0.06em] text-muted-foreground">{s.est}</span>}
              </div>
              <div>
                <div className={cn("text-[14px] font-semibold tracking-[-0.005em]", isDone ? "text-ink-soft" : "text-ink")}>{s.title}</div>
                <p className="mt-1 text-[12px] leading-relaxed text-ink-soft">{s.blurb}</p>
              </div>
              {!isDone && (
                <span className={cn("mt-auto inline-flex items-center gap-1.5 text-[12.5px] font-medium", isActive ? "text-ink" : "text-ink-soft group-hover:text-ink")}>
                  {isActive ? "Continue" : "Start"} <Icon name="arrow-right" size={13} className="transition group-hover:translate-x-1" />
                </span>
              )}
            </button>
          );
        })}
      </div>
    </section>
  );
}

/* ============================================================ Big numbers */
const SPARKS = {
  "7D": [0.5, 0.55, 0.52, 0.6, 0.58, 0.7, 0.74],
  "30D": [0.30, 0.42, 0.36, 0.55, 0.5, 0.64, 0.6, 0.74, 0.68, 0.86, 0.92],
  "90D": [0.2, 0.28, 0.24, 0.35, 0.3, 0.42, 0.5, 0.46, 0.6, 0.66, 0.7, 0.78, 0.74, 0.88, 0.92],
};
const AXIS = { "7D": ["May 27", "Jun 2"], "30D": ["May 13", "Jun 2"], "90D": ["Mar 5", "Jun 2"] };
const RANGE_WORDS = { "7D": "7 days", "30D": "30 days", "90D": "90 days" };
function BalanceCard({ m }) {
  const [view, setView] = useState("chart");
  const [range, setRange] = useState("30D");
  const dataN = SPARKS[range];
  const lastN = dataN[dataN.length - 1];
  const chartValues = dataN.map((v) => Math.round(m.inTrust * (0.62 + 0.38 * (v / lastN))));
  const change = chartValues[chartValues.length - 1] - chartValues[0];
  const breakdown = [
    { k: "Recorded", v: fmtUSD(m.inTrust) },
    { k: "Pending review", v: fmtUSD(m.pending) },
    { k: "Expected at close", v: fmtUSD(m.expected), strong: true },
  ];
  return (
    <div className="rounded-2xl border border-line bg-background p-6">
      <div className="flex items-center justify-between gap-3">
        <div className="text-[13px] text-ink-soft">In trust · {m.escrows.length} open escrows</div>
        <div className="flex items-center gap-2">
          {view === "chart" && (
            <div className="inline-flex items-center rounded-lg border border-line bg-surface/50 p-0.5">
              {["7D", "30D", "90D"].map((r) => (
                <button key={r} type="button" onClick={() => setRange(r)}
                  className={cn("rounded-md px-2 py-1 text-[11.5px] font-medium tabular-nums transition", range === r ? "bg-background text-ink shadow-sm ring-1 ring-line" : "text-ink-soft hover:text-ink")}>{r}</button>
              ))}
            </div>
          )}
          <div className="inline-flex items-center rounded-lg border border-line bg-surface/50 p-0.5">
            {[["chart", "trending-up", "Chart"], ["table", "table", "Table"]].map(([key, icon, label]) => (
              <Hint key={key} label={label} side="top">
                <button type="button" onClick={() => setView(key)} aria-label={label}
                  className={cn("grid h-7 w-7 place-items-center rounded-md transition", view === key ? "bg-background text-ink shadow-sm ring-1 ring-line" : "text-ink-soft hover:text-ink")}>
                  <Icon name={icon} size={14} />
                </button>
              </Hint>
            ))}
          </div>
        </div>
      </div>
      <StatPop
        trigger={<div className="mt-3 cursor-default om-display tabular-nums text-[clamp(34px,4.2vw,46px)] font-semibold leading-none tracking-[-0.02em] text-ink">{fmtUSD(m.inTrust)}</div>}
        rows={breakdown}
      />
      <div className="mt-2 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[12.5px]">
        <span className={cn("font-medium tabular-nums", change >= 0 ? "text-[var(--ok)]" : "text-[var(--destructive)]")}>{change >= 0 ? "+" : "−"}{fmtUSD(Math.abs(change))}</span>
        <span className="text-ink-soft">recorded in the last {RANGE_WORDS[range]}</span>
      </div>
      <div className="mt-2 flex items-center gap-1.5 text-[12.5px] text-[var(--ok)]"><Icon name="check" size={13} strokeWidth={3} /> Reconciles across {m.escrows.length} escrows · no anomalies</div>
      <div className="mt-6 flex min-h-[140px] flex-col">
        {view === "chart" ? (
          <>
            <Sparkline data={dataN} values={chartValues} interactive reference={m.expected} refLabel={`Expected $${(m.expected / 1e6).toFixed(1)}M`} className="h-24 w-full" />
            <div className="mt-2 flex justify-between text-[11px] tabular-nums text-muted-foreground"><span>{AXIS[range][0]}</span><span>{AXIS[range][1]}</span></div>
          </>
        ) : (
          <div className="flex flex-col">
            {breakdown.map((r, i) => (
              <div key={r.k} className={cn("flex items-center justify-between py-3 text-[13px]", i > 0 && "border-t border-line")}>
                <span className="text-ink-soft">{r.k}</span>
                <span className={cn("tabular-nums", r.strong ? "font-semibold text-ink" : "text-ink")}>{r.v}</span>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function ClosingCard({ m }) {
  return (
    <div className="flex h-full flex-col rounded-2xl border border-line bg-background p-6">
      <div className="flex items-center justify-between">
        <div className="om-display whitespace-nowrap text-[14px] font-semibold text-ink">Closing this week</div>
        <Link to="/files" className="text-[12px] text-ink-soft underline decoration-line underline-offset-4 transition hover:text-ink">View all</Link>
      </div>
      <ul className="mt-3 flex flex-col">
        {m.week.map((f) => (
          <li key={f.id}>
            <Link to="/file/$id" params={{ id: f.id }} className="group flex items-center gap-3 border-b border-line py-2.5 transition last:border-0 hover:opacity-80">
              <span className={cn("h-1.5 w-1.5 shrink-0 rounded-full", STATE_DOT[f.state])} />
              <span className="min-w-0 flex-1 truncate text-[13px] text-ink">{streetOf(f.cf.property)}</span>
              <span className="shrink-0 text-[12px] tabular-nums text-ink-soft">{fmtUSD(f.mv.totalIn)}</span>
              <span className="w-14 shrink-0 text-right text-[11px] tabular-nums text-muted-foreground">{countdown(f.closingMs, m.anchor)}</span>
            </Link>
          </li>
        ))}
      </ul>
      <div className="mt-auto flex items-center justify-between border-t border-line pt-4">
        <span className="text-[12px] text-ink-soft">Moving this week</span>
        <span className="text-[14px] font-medium tabular-nums text-ink">{fmtUSD(m.movingThisWeek)}</span>
      </div>
    </div>
  );
}

/* ---------------------------------------------------- this week movement */
function MovementCard({ label, info, value, tone, avg, avgN, up }) {
  const [open, setOpen] = useState(false);
  const valCls = tone === "in" ? "text-[var(--ok)]" : "text-ink";
  const sd = up ? [0.3, 0.5, 0.45, 0.7, 0.62, 0.85] : [0.8, 0.55, 0.62, 0.4, 0.48, 0.3];
  const sv = avgN ? sd.map((v) => Math.round(avgN * (0.7 + 0.55 * (v - 0.3)))) : null;
  return (
    <div className="rounded-2xl border border-line bg-background p-6">
      <div className="flex items-center gap-1.5">
        <span className="text-[13.5px] text-ink-soft">{label}</span>
        {info && (
          <span className="relative inline-flex" onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
            <Icon name="alert-circle" size={13} className="text-muted-foreground" />
            {open && <span className="absolute left-1/2 top-full z-40 mt-2 w-[220px] -translate-x-1/2 rounded-xl bg-ink p-3 text-[11.5px] leading-relaxed text-background shadow-[0_18px_44px_-18px_rgba(0,0,0,0.55)]">{info}</span>}
          </span>
        )}
      </div>
      <div className={cn("mt-1.5 om-display text-[26px] font-semibold tabular-nums tracking-[-0.02em]", valCls)}>{value}</div>
      <div className="mt-5 flex items-end justify-between gap-4 border-t border-line pt-4">
        <div className="min-w-0">
          <div className="whitespace-nowrap text-[11px] uppercase tracking-[0.05em] text-muted-foreground">3-mo average</div>
          <div className="mt-0.5 text-[14px] tabular-nums text-ink">{avg}</div>
        </div>
        <Sparkline data={sd} values={sv} interactive className="h-9 w-28 shrink-0" />
      </div>
    </div>
  );
}

/* ------------------------------------------------ money movement week */
function ThisWeek({ m }) {
  const [off, setOff] = useState(0);
  const fmtD = (ms) => new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric" });
  const start = m.anchor + off * 7 * HOME_DAY;
  const factor = off === 0 ? 1 : off < 0 ? Math.max(0.5, 1 + 0.12 * off) : Math.max(0, 1 - 0.42 * off);
  const wIn = Math.round(m.weekIn * factor);
  const wOut = Math.round(m.weekOut * factor);
  return (
    <div className="mt-10">
      <div className="flex items-center gap-3">
        <h2 className="om-display text-[17px] font-semibold tracking-[-0.01em] text-ink">Money movement</h2>
        <div className="flex items-center gap-1">
          <button type="button" onClick={() => setOff(off - 1)} aria-label="Previous week" className="grid h-6 w-6 place-items-center rounded-md text-ink-soft transition hover:bg-surface hover:text-ink"><Icon name="chevron-left" size={15} /></button>
          <span className="min-w-[124px] text-center text-[12px] tabular-nums text-ink-soft">{fmtD(start)} – {fmtD(start + 7 * HOME_DAY)}</span>
          <button type="button" onClick={() => setOff(off + 1)} aria-label="Next week" className="grid h-6 w-6 place-items-center rounded-md text-ink-soft transition hover:bg-surface hover:text-ink"><Icon name="chevron-right" size={15} /></button>
          {off !== 0 && <button type="button" onClick={() => setOff(0)} className="ml-1 text-[11.5px] text-ink-soft underline decoration-line underline-offset-2 transition hover:text-ink">This week</button>}
        </div>
      </div>
      <div className="mt-4 grid gap-4 sm:grid-cols-2">
        <MovementCard label="Deposits in" value={fmtUSD(wIn)} tone="in" avg={fmtUSD(Math.round(m.weekIn * 0.92))} avgN={Math.round(m.weekIn * 0.92)} up
          info="Funds expected into trust this week: buyer wires, earnest money, and lender funding across open files." />
        <MovementCard label="Disbursements out" value={fmtUSD(wOut)} tone="out" avg={fmtUSD(Math.round(m.weekOut * 0.97))} avgN={Math.round(m.weekOut * 0.97)}
          info="Money scheduled to leave trust: payoffs, seller proceeds, commissions, and fees, once each file is clear to record." />
      </div>
    </div>
  );
}

/* ------------------------------------------------------- recent files */
function StatusText({ status }) {
  const cls = status === "Recorded" ? "text-[var(--ok)]" : status === "In review" ? "text-[var(--warn)]" : "text-ink-soft";
  const dot = status === "Recorded" ? "bg-[var(--ok)]" : status === "In review" ? "bg-[var(--warn)]" : "bg-ink-soft/40";
  return <span className={cn("inline-flex items-center gap-1.5 text-[12px]", cls)}><span className={cn("h-1.5 w-1.5 rounded-full", dot)} />{status}</span>;
}
function RecentFiles({ m }) {
  const rows = useMemo(() => allFilesRows()
    .filter((r) => r.status === "In review")
    .sort((a, b) => (Date.parse(a.closing) || 0) - (Date.parse(b.closing) || 0))
    .slice(0, 7), []);
  return (
    <section className="mt-12">
      <div className="flex items-center justify-between gap-3">
        <h2 className="om-display text-[17px] font-semibold tracking-[-0.01em] text-ink">Files in review</h2>
        <Link to="/files" className="inline-flex items-center gap-1 text-[12.5px] font-medium text-ink-soft transition hover:text-ink">View all <Icon name="chevron-right" size={13} /></Link>
      </div>
      <div className="mt-4">
        <div className="grid grid-cols-[88px_minmax(0,1fr)_170px_120px] items-center gap-4 border-b border-line px-3 pb-2 text-[11px] uppercase tracking-[0.05em] text-muted-foreground">
          <span>Closing</span><span>Property</span><span>Line</span><span className="text-right">Amount</span>
        </div>
        <ul className="flex flex-col">
          {rows.map((r) => {
            const rd = getReadiness(r.id) || buildReadinessFrom(r.id, "Madeline Lane", "ML", r.closing);
            const st = rd.recording.state;
            const mv = fileMovements(r.id);
            const lineAmt = [...mv.deposits, ...mv.disbursements].filter((x) => x.line === r.line).reduce((s, x) => s + x.amount, 0) || mv.totalIn;
            return (
              <li key={r.id}>
                <Link to="/file/$id" params={{ id: r.id }} className="group grid grid-cols-[88px_minmax(0,1fr)_170px_120px] items-center gap-4 rounded-lg px-3 py-3 transition hover:bg-surface/60">
                  <span className="text-[12.5px] tabular-nums text-ink-soft">{shortClosing(r.closing)}</span>
                  <span className="flex min-w-0 items-center gap-2.5">
                    <Hint label={st} side="top"><span className={cn("h-1.5 w-1.5 shrink-0 rounded-full", STATE_DOT[st])} /></Hint>
                    <span className="truncate text-[13.5px] text-ink">{r.street}</span>
                  </span>
                  <span className="truncate text-[12.5px] text-ink-soft">{r.lineLabel}</span>
                  <span className="text-right text-[12.5px] tabular-nums text-ink">{fmtUSD(lineAmt)}</span>
                </Link>
              </li>
            );
          })}
        </ul>
      </div>
    </section>
  );
}

/* ------------------------------------------------------------- actions */
function ActionPill({ to, onClick, icon, label, primary, desc, shortcut }) {
  const cls = cn("inline-flex items-center gap-2 rounded-lg px-3.5 py-2 text-[13px] font-medium transition",
    primary ? "bg-ink text-background hover:bg-ink/90" : "border border-line bg-background text-ink hover:border-ink/25 hover:bg-surface/60");
  const inner = <><Icon name={icon} size={14} strokeWidth={primary ? 2 : 1.7} /> {label}</>;
  const el = onClick ? <button type="button" onClick={onClick} className={cls}>{inner}</button> : <Link to={to} className={cls}>{inner}</Link>;
  return <Hint label={desc || label} shortcut={shortcut} side="top">{el}</Hint>;
}

/* ----------------------------------------------------- control health
   The owner's control posture as a STATUS gauge — not a worklist. It answers
   "is the office enforcing its own policy right now?" and hands the actual
   clearing off to Tasks. Awareness here; urgency there. */
function Gauge({ label, n, fg }) {
  return (
    <span className="inline-flex items-center gap-2 text-[12.5px]">
      <span className="h-2 w-2 rounded-full" style={{ backgroundColor: fg }} />
      <span className="font-mono font-semibold tabular-nums" style={{ color: fg }}>{n}</span>
      <span className="text-ink-soft">{label}</span>
    </span>
  );
}
function ControlHealth() {
  const actions = typeof controlledActions === "function" ? controlledActions() : [];
  const heldAction = actions.find((a) => a.fileId === "SP-0214") || actions.find((a) => a.state === "Blocked" || a.state === "Held");
  const held = heldAction ? {
    fileId: heldAction.fileId,
    seller: HELD_RELEASE.seller,
    payee: HELD_RELEASE.payee,
    officer: HELD_RELEASE.officer,
    amount: heldAction.amount,
    action: heldAction.action,
    domain: heldAction.domain,
    freshness: heldAction.freshness,
    detectedAt: HELD_RELEASE.detectedAt,
  } : HELD_RELEASE;
  const needs = held ? 1 : 0;
  if (!needs) {
    return (
      <section className="mt-6 flex items-center gap-3 rounded-2xl border border-line bg-background px-5 py-4">
        <span className="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-[var(--ok-bg)] text-[var(--ok)]"><Icon name="check" size={15} strokeWidth={3} /></span>
        <div className="min-w-0">
          <div className="om-display text-[11.5px] font-semibold uppercase tracking-[0.06em] text-ink-soft">Office control · today</div>
          <div className="text-[13.5px] font-medium text-ink">All actions covered — nothing needs a record.</div>
        </div>
      </section>
    );
  }
  return (
    <section className="mt-6 rounded-2xl border border-line bg-background p-5 lg:p-6">
      <div className="flex items-start justify-between gap-4">
        <div className="min-w-0">
          <span className="om-display text-[11.5px] font-semibold uppercase tracking-[0.06em] text-ink-soft">{HOME_ORG} · office control</span>
          <p className="mt-2 max-w-[540px] text-[clamp(16px,1.9vw,19px)] font-medium leading-snug tracking-[-0.01em] text-ink text-pretty">
            <span className="font-semibold">1 release held</span> for owner review.
          </p>
        </div>
        <Link to="/overview" className="group mt-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-lg bg-ink px-3.5 py-2 text-[12.5px] font-medium text-background transition hover:bg-ink/90">
          Open Review Register <Icon name="arrow-right" size={13} className="transition group-hover:translate-x-0.5" />
        </Link>
      </div>
      <div className="mt-5 overflow-hidden rounded-xl border border-line">
        <div className="flex flex-col gap-4 bg-surface/35 px-4 py-4 sm:flex-row sm:items-start sm:justify-between">
          <div className="min-w-0">
            <div className="flex flex-wrap items-center gap-2">
              <span className="inline-flex items-center gap-1.5 rounded-full bg-[var(--warn-bg)] px-2 py-0.5 text-[11px] font-medium text-[var(--warn)]">
                <span className="h-1.5 w-1.5 rounded-full bg-[var(--warn)]" />Held
              </span>
              <span className="font-mono text-[11px] text-muted-foreground">{held.fileId}</span>
            </div>
            <div className="mt-2 text-[14px] font-medium text-ink">Payout destination linked to the acting officer</div>
            <p className="mt-1 max-w-[560px] text-[12.5px] leading-relaxed text-ink-soft">The payout destination ({held.payee}) names an entity whose managing member of record matches the acting officer on this file. Held pending an owner exception record. Veto records the association. It does not allege intent.</p>
          </div>
          <div className="shrink-0 text-left sm:text-right">
            <div className="font-mono text-[18px] font-semibold tabular-nums text-ink">{fmtUSD(held.amount)}</div>
            <div className="mt-1 text-[11.5px] text-ink-soft">{held.domain} · {held.freshness}</div>
          </div>
        </div>
        <div className="grid grid-cols-1 divide-y divide-line lg:grid-cols-3 lg:divide-x lg:divide-y-0">
          <div className="px-4 py-3.5">
            <div className="text-[10.5px] uppercase tracking-[0.06em] text-muted-foreground">Seller</div>
            <div className="mt-1.5 text-[12.5px] text-ink">{held.seller}</div>
          </div>
          <div className="px-4 py-3.5">
            <div className="text-[10.5px] uppercase tracking-[0.06em] text-muted-foreground">Payout destination</div>
            <div className="mt-1.5 text-[12.5px] text-ink">{held.payee}</div>
            <div className="mt-1 font-mono text-[11px] text-muted-foreground">{held.detectedAt}</div>
          </div>
          <div className="px-4 py-3.5">
            <div className="text-[10.5px] uppercase tracking-[0.06em] text-muted-foreground">Acting officer</div>
            <div className="mt-1.5 text-[12.5px] text-ink">{held.officer}</div>
            <div className="mt-1 text-[11.5px] text-ink-soft">Routed to Review Register</div>
          </div>
        </div>
        <div className="flex flex-wrap items-center gap-2 border-t border-line px-4 py-3">
          <Link to="/overview" className="inline-flex items-center gap-1.5 rounded-md bg-ink px-3 py-1.5 text-[12px] font-medium text-background transition hover:bg-ink/90">
            Open Review Register <Icon name="arrow-right" size={12} />
          </Link>
          <Link to="/file/$id" params={{ id: held.fileId }} className="inline-flex items-center gap-1.5 rounded-md border border-line px-3 py-1.5 text-[12px] font-medium text-ink transition hover:bg-surface">
            Open file
          </Link>
          <Link to="/tasks" className="inline-flex items-center gap-1.5 rounded-md border border-line px-3 py-1.5 text-[12px] font-medium text-ink transition hover:bg-surface">
            View task
          </Link>
          <span className="ml-auto text-[11.5px] text-ink-soft">Veto records the association. The office decides.</span>
        </div>
      </div>
    </section>
  );
}

/* ================================================================= Home */
function Home() {
  const m = useMemo(() => homeMetrics(), []);
  const [bank, connectBank] = useTrust();
  const { done, dismissed, dismiss, mark } = useSetup(!!bank);
  const [plaid, setPlaid] = useState(false);
  const demoRole = useDemoRoleRead();
  const activeReviewer = reviewerForRole(demoRole);
  const firstName = demoRole === "owner" ? "Principal" : ((activeReviewer.name || HOME_REVIEWER.name).split(" ")[0] || HOME_REVIEWER.first);
  const showSetup = !dismissed && done.size < SETUP_STEPS.length;
  const subhead = (m.today.length
    ? `${m.today.length} ${m.today.length === 1 ? "file closes" : "files close"} today`
    : `${m.week.length} ${m.week.length === 1 ? "closing" : "closings"} this week`);

  return (
    <AppShell activeLine="home">
      <div className="mx-auto w-full max-w-[1080px] px-6 py-10 lg:px-12 lg:py-12">
        {/* Greeting + one smart line */}
        <h1 className="om-display text-[clamp(28px,3.4vw,38px)] font-semibold tracking-[-0.025em] text-ink">{greetingHome(firstName)}.</h1>
        <p className="mt-2 text-[15px] text-ink-soft"><span className="text-ink">{HOME_ORG}</span> · Everything reconciles. <span className="text-ink">{subhead}</span>{m.clear.length > 0 ? <>, <span className="text-ink">{m.clear.length}</span> clear to record.</> : "."}</p>

        {/* Action row — things you can initiate */}
        <div className="mt-6 flex flex-wrap items-center gap-2.5">
          <ActionPill to="/files/new" icon="plus" label="New file" primary desc="Open a new escrow file" shortcut="⌘N" />
          <ActionPill to="/files/new" icon="file-plus" label="Create record" desc="Start a release, change-impact, or readiness record" />
          <ActionPill to="/payoff" icon="landmark" label="Request demand" desc="Request a payoff demand from a servicer" />
          <ActionPill to="/tasks" icon="check-circle" label="Tasks" desc="Open the action queue" shortcut="G T" />
        </div>

        {/* First-run setup — two real trust-account control tasks */}
        {showSetup && (
          <SetupBand done={done} first={firstName}
            onTrust={() => setPlaid(true)}
            onReviewers={() => { mark("reviewers"); navigate("/invite"); }}
            onDismiss={dismiss} />
        )}

        {/* Control posture — status, routes to Tasks */}
        <ControlHealth />

        {/* Big numbers — scroll reveals the rest */}
        <div className="mt-8 grid gap-4 lg:grid-cols-[minmax(0,1fr)_360px]">
          <BalanceCard m={m} />
          <ClosingCard m={m} />
        </div>

        {/* Money movement — step through weeks */}
        <ThisWeek m={m} />

        {/* Recent files */}
        <RecentFiles m={m} />

        <footer className="mt-14 border-t border-line pt-5"><p className="text-[12px] text-ink-soft">Veto records the review. The office decides.</p></footer>
      </div>
      {plaid && <PlaidModal onClose={() => setPlaid(false)} onConnect={(b) => { connectBank(b); setPlaid(false); }} />}
    </AppShell>
  );
}

Object.assign(window, { Home });
