/* simple/flows.jsx — the layers around the three tabs.
   Move and Trade are the ORIGINAL full flows (TransferFlow / TradeSheet),
   re-wired into this shell so they behave exactly as before. What lives
   here now: HoldingView, ReceiptSheet, ProfileSheet (with the Connections
   portability hero + Automations), and an Automations list sheet.
   Externals render as UI only in the Move picker (original flow) and the
   Connections health list below. */

/* ---------- external accounts · connection health (Profile) ---------------- */
const EXT_ACCOUNTS = [
  { id: "chase",    name: "Chase Checking",   sub: "••3392", value: 8210.32,   kind: "cash",   health: "ok" },
  { id: "schwab",   name: "Schwab Brokerage", sub: "••7719", value: 186400.00, kind: "invest", health: "ok" },
  { id: "coinbase", name: "Coinbase",         sub: "••2044", value: 12840.00,  kind: "crypto", health: "reauth" },
];

const AGENT_CONNECTIONS = [
  { name: "ChatGPT",  sub: "Read + propose · your caps apply", on: true },
  { name: "Claude",   sub: "Read + propose · your caps apply", on: true },
  { name: "Codex",    sub: "Read only", on: true },
];

/* ============================================================
   HoldingView · 4 elements: chart, your position, Buy/Sell,
   Ask Yoshi. Buy/Sell open the original TradeSheet.
   ============================================================ */
const HOLD_RANGES = ["1D", "1W", "1M", "6M", "1Y", "ALL"];
const HoldingView = ({ id, nav, onClose }) => {
  const m = MARKET.find((x) => x.id === id) || HOLDINGS.find((x) => x.id === id);
  const h = securityHolding(id);
  const [range, setRange] = useState("6M");
  const retFor = { "1D": m.dch, "1W": h?.perf?.["1W"] ?? m.dch * 2.1, "1M": h?.perf?.["1M"] ?? 4.2, "6M": (h?.perf?.["1Y"] ?? 18) * 0.6, "1Y": h?.perf?.["1Y"] ?? securityYearReturn(m), "ALL": (h?.perf?.["1Y"] ?? 20) * 2.4 }[range];
  const data = series(id.length * 5 + HOLD_RANGES.indexOf(range) * 17 + 3, 40, m.last / (1 + retFor / 100), m.last, 0.02);
  return (
    <div className="push-enter" data-screen-label="Holding" style={{ position: "absolute", inset: 0, zIndex: 290, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {window.StatusBar && <window.StatusBar />}
      <NavBar title={m.ticker} sub={m.name} onBack={onClose} />
      <div className="scroll" style={{ padding: "14px 20px 24px" }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
          <Money value={m.last} size={30} weight={600} />
          <Delta pct={m.dch} size={13} showAbs={false} />
        </div>
        <div style={{ margin: "12px 0 0" }}>
          <Chart data={data} height={140} range={range} scrub accent={retFor >= 0 ? "var(--accent-pos)" : "var(--signal-neg)"} />
        </div>
        <div style={{ display: "flex", gap: 4, marginTop: 10 }}>
          {HOLD_RANGES.map((r) => (
            <button key={r} className="press" onClick={() => setRange(r)} style={{ flex: 1, padding: "7px 0", background: r === range ? "var(--bg-2)" : "none", border: "none", borderRadius: 8, fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: r === range ? 700 : 500, color: r === range ? "var(--ink)" : "var(--ink-3)", cursor: "pointer" }}>{r}</button>
          ))}
        </div>

        {h && (
          <>
            <Eyebrow style={{ margin: "20px 0 7px" }}>Your position</Eyebrow>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
              <StatCell label="Value" value={usd(h.value)} />
              <StatCell label={h.kind === "crypto" ? "Amount" : "Shares"} value={String(h.shares)} border />
              <StatCell label="Total return" value={signed(h.pl)} color={h.pl >= 0 ? "var(--accent-pos)" : "var(--signal-neg)"} />
              <StatCell label="Avg cost" value={usd(h.avg)} border />
            </div>
          </>
        )}

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 18 }}>
          <Btn onClick={() => { onClose(); nav.sheet({ type: "trade", id: m.id, side: "BUY" }); }}>Buy</Btn>
          <Btn kind="ghost" onClick={() => { onClose(); nav.sheet({ type: "trade", id: m.id, side: "SELL" }); }}>Sell</Btn>
        </div>
        <Btn kind="quiet" full style={{ marginTop: 8 }} onClick={() => { onClose(); nav.ask(`Tell me about ${m.ticker}`, securityTake(m.id).take + " Want me to size a trade or set up a rule?"); }}>
          Ask Yoshi about {m.ticker}
        </Btn>
      </div>
    </div>
  );
};

/* ============================================================
   HoldingsDetailSheet · a scannable table. Sticky Holding column,
   the rest scroll horizontally. Sortable headers. Today = short-
   term, Return = long-term, side by side. Total pinned up top.
   ============================================================ */
const HOLD_W = 132;
const HRANGES = ["Today", "1W", "1M", "6M", "YTD", "1Y", "ALL"];
const HRANGE_WORD = { Today: "today", "1W": "past week", "1M": "past month", "6M": "past 6 months", YTD: "year to date", "1Y": "past year", ALL: "all time" };
const HRANGE_MENU = { Today: "Today", "1W": "Past week", "1M": "Past month", "6M": "Past 6 months", YTD: "Year to date", "1Y": "Past year", ALL: "All time" };
const periodPct = (h, range) => {
  if (range === "Today") return h.dch;
  if (range === "1W") return h.perf && h.perf["1W"] != null ? h.perf["1W"] : h.dch * 2;
  if (range === "1M") return h.perf && h.perf["1M"] != null ? h.perf["1M"] : 4.2;
  if (range === "6M") return (h.perf && h.perf["1Y"] != null ? h.perf["1Y"] : 18) * 0.6;
  if (range === "YTD") return (h.perf && h.perf["1Y"] != null ? h.perf["1Y"] : 18) * 0.72;
  if (range === "1Y") return h.perf && h.perf["1Y"] != null ? h.perf["1Y"] : securityYearReturn(h);
  return h.plPct;
};
const periodAbs = (h, range) => range === "Today" ? h.dayAbs : (h.value - h.value / (1 + periodPct(h, range) / 100));

const GainSubRow = ({ term, amt }) => (
  <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, padding: "4px 0" }}>
    <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>{term}</span>
    <Money value={amt} size={11.5} weight={500} sign color="var(--ink-2)" dim="var(--ink-3)" />
  </div>
);
// collapsed = total; tap to drop down to long-term / short-term detail. kept
// visually quiet — it's supporting detail, not the headline.
const ExpandGain = ({ label, total, lt, st, first }) => {
  const [open, setOpen] = useState(false);
  const c = total >= 0 ? "var(--accent-pos)" : "var(--signal-neg)";
  return (
    <div style={{ borderTop: first ? "none" : "1px solid var(--rule)" }}>
      <button className="press" onClick={() => setOpen((o) => !o)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 600, color: "var(--ink-2)" }}>{label}</span>
        <span style={{ marginLeft: "auto" }}><Money value={total} size={12} weight={500} sign color={c} dim={c} /></span>
        <Icon name="down" size={13} color="var(--ink-3)" style={{ transform: open ? "rotate(180deg)" : "none", transition: "transform .15s", flex: "none" }} />
      </button>
      {open && (
        <div style={{ padding: "0 14px 9px" }}>
          <GainSubRow term="Long-term" amt={lt} />
          <GainSubRow term="Short-term" amt={st} />
        </div>
      )}
    </div>
  );
};

// mock: holdings span multiple trading accounts, filtered by a header dropdown
const TRADING_ACCTS = [{ id: "all", name: "All holdings" }, { id: "brokerage", name: "Brokerage" }, { id: "roth", name: "High Risk" }, { id: "crypto", name: "Crypto" }];
const HOLDING_ACCT = { vti: "brokerage", voo: "brokerage", msft: "brokerage", nvda: "roth", aapl: "roth", btc: "crypto", eth: "crypto", sol: "crypto" };
const BROKERAGE_NUMBERS = { account: "YS-8829-0021-7731", dtc: "0793" };

const HoldingsDetailSheet = ({ nav, onClose, initialAcct = "all", z = 288, embedded, onSelect }) => {
  const [tab, setTab] = useState("holdings");
  const [acctSel, setAcctSel] = useState(initialAcct);
  const [acctOpen, setAcctOpen] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  const [revealOpen, setRevealOpen] = useState(false);
  const [editing, setEditing] = useState(false);
  const [nick, setNick] = useState("Brokerage");
  const [draft, setDraft] = useState("Brokerage");
  const [q, setQ] = useState("");
  const [sort, setSort] = useState({ key: "value", dir: "desc" });
  const [valRange, setValRange] = useState("Today");
  const [rangeOpen, setRangeOpen] = useState(false);
  const [hf, setHf] = useState("all");
  const [tf, setTf] = useState("all");
  const allBase = [...GROUPS.investments.items, ...GROUPS.crypto.items];
  const base = acctSel === "all" ? allBase : allBase.filter((h) => (HOLDING_ACCT[h.id] || "brokerage") === acctSel);
  const acctName = TRADING_ACCTS.find((a) => a.id === acctSel).name;
  const total = base.reduce((s, h) => s + h.value, 0);
  const dayAbs = base.reduce((s, h) => s + h.dayAbs, 0);
  const dayPct = (dayAbs / (total - dayAbs)) * 100;
  const totalPL = base.reduce((s, h) => s + h.pl, 0);
  // unrealized split long/short-term; realized is a YTD figure
  const unrealLong = Math.round(totalPL * 0.8);
  const unrealShort = totalPL - unrealLong;
  const realizedLong = 10120, realizedShort = 4700;
  const kinds = [...new Set(base.map((h) => h.kind))];
  const hFilters = [["all", "All"], ["gainers", "Gainers"], ["losers", "Losers"], ...(kinds.includes("stock") ? [["stock", "Stocks"]] : []), ...(kinds.includes("etf") ? [["etf", "ETFs"]] : []), ...(kinds.includes("crypto") ? [["crypto", "Crypto"]] : [])];
  const tFilters = [["all", "All"], ["buys", "Buys"], ["sells", "Sells"], ["div", "Dividends"]];

  // account-level change for the selected period (Today = live day move)
  const valChange = valRange === "Today"
    ? { abs: dayAbs, pct: dayPct }
    : (() => { let abs = 0; base.forEach((h) => { abs += periodAbs(h, valRange); }); const b = total - abs; return { abs, pct: b ? (abs / b) * 100 : 0 }; })();

  // the change column follows the time dropdown
  const changeCol = { key: "chg", label: valRange === "Today" ? "Today" : valRange, w: 110, sort: (h) => periodPct(h, valRange), render: (h) => <Delta abs={periodAbs(h, valRange)} pct={periodPct(h, valRange)} size={11.5} /> };
  const COLS = [
    { key: "value", label: "Value", w: 104, sort: (h) => h.value, render: (h) => <Money value={h.value} size={12.5} /> },
    changeCol,
    { key: "port", label: "%Port", w: 76, sort: (h) => h.value, render: (h, t) => <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--ink-2)", fontVariantNumeric: "tabular-nums" }}>{((h.value / t) * 100).toFixed(1)}%</span> },
    { key: "cost", label: "Cost basis", w: 104, sort: (h) => h.cost, render: (h) => <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--ink-2)", fontVariantNumeric: "tabular-nums" }}>{usd(h.cost)}</span> },
    { key: "return", label: "Return", w: 112, sort: (h) => h.plPct, render: (h) => <Delta abs={h.pl} pct={h.plPct} size={11.5} /> },
  ];

  const col = COLS.find((c) => c.key === sort.key);
  const sortFn = sort.key === "holding" ? (h) => h.ticker : (col ? col.sort : (h) => h.value);
  const rows = base
    .filter((h) => {
      if (hf === "gainers" && h.dch < 0) return false;
      if (hf === "losers" && h.dch >= 0) return false;
      if (hf === "stock" && h.kind !== "stock") return false;
      if (hf === "etf" && h.kind !== "etf") return false;
      if (hf === "crypto" && h.kind !== "crypto") return false;
      const s = q.trim().toLowerCase();
      return !s || (h.ticker + " " + h.name).toLowerCase().includes(s);
    })
    .sort((a, b) => { const av = sortFn(a), bv = sortFn(b); const cmp = typeof av === "string" ? av.localeCompare(bv) : av - bv; return sort.dir === "asc" ? cmp : -cmp; });

  const txns = acctTxns(base);
  const shownTxns = txns.filter((t) => {
    if (tf === "buys" && t.side !== "BUY") return false;
    if (tf === "sells" && t.side !== "SELL") return false;
    if (tf === "div" && t.side !== "DIV") return false;
    const s = q.trim().toLowerCase();
    return !s || t.ticker.toLowerCase().includes(s);
  });

  const toggleSort = (key) => setSort((s) => s.key === key ? { key, dir: s.dir === "desc" ? "asc" : "desc" } : { key, dir: key === "holding" ? "asc" : "desc" });
  const caret = (key) => sort.key === key ? (sort.dir === "desc" ? "▼" : "▲") : "▾";
  const thStyle = { fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--ink-3)", background: "none", border: "none", cursor: "pointer", padding: 0, whiteSpace: "nowrap" };
  const totalW = HOLD_W + COLS.reduce((s, c) => s + c.w, 0);

  return (
    <div className="push-enter" data-screen-label="Holdings" style={{ position: "absolute", inset: 0, zIndex: z, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {window.StatusBar && <window.StatusBar />}
      <NavBar onBack={embedded ? undefined : onClose}
        title={
          <button className="press" onClick={() => setAcctOpen((o) => !o)} style={{ display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.02em", color: "var(--ink)" }}>
            {acctSel === "all" ? "All holdings" : acctName} <Icon name="down" size={14} color="var(--ink-3)" style={{ transform: acctOpen ? "rotate(180deg)" : "none", transition: "transform .15s" }} />
          </button>
        }
        right={<button className="press" aria-label="Account options" onClick={() => setMenuOpen(true)} style={{ flex: "none", display: "grid", placeItems: "center", width: 30, height: 30, background: "none", border: "1px solid transparent", borderRadius: 8, cursor: "pointer", color: "var(--ink-2)" }}><Icon name="more" size={18} /></button>} />

      {acctOpen && (
        <>
          <div onClick={() => setAcctOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 30 }} />
          <div style={{ position: "absolute", top: 44, left: 44, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 190 }}>
            {TRADING_ACCTS.map((a, i) => (
              <button key={a.id} className="press" onClick={() => { setAcctSel(a.id); setAcctOpen(false); }} style={{ width: "100%", textAlign: "left", padding: "11px 13px", background: a.id === acctSel ? "var(--bg-2)" : "none", border: "none", borderTop: i === 0 ? "none" : "1px solid var(--rule)", cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, color: a.id === acctSel ? "var(--ink)" : "var(--ink-2)" }}>{a.name}</button>
            ))}
          </div>
        </>
      )}

      {menuOpen && (
        <OptionsSheet title={acctSel === "all" ? "Brokerage" : acctName} letter={(acctSel === "all" ? "Brokerage" : acctName)[0]} onClose={() => setMenuOpen(false)} actions={[
          { icon: "eye", label: "Reveal account number", onClick: () => setRevealOpen(true) },
          { icon: "doc", label: "Edit nickname", onClick: () => { setDraft(nick); setEditing(true); } },
          { icon: "receipt", label: "Documents", onClick: () => nav.sheet({ type: "documents" }) },
          { icon: "trade", label: "Trade", onClick: () => nav.sheet({ type: "trade" }) },
        ]} />
      )}
      {revealOpen && (
        <div style={{ position: "fixed", inset: 0, zIndex: 60, display: "flex", alignItems: "flex-end" }} data-screen-label="Account numbers">
          <div onClick={() => setRevealOpen(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
          <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 20px max(24px,env(safe-area-inset-bottom))", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
            <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 14px" }} />
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
              <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600 }}>Account number</span>
              <button className="press" onClick={() => setRevealOpen(false)} aria-label="Close" style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", cursor: "pointer" }}><Icon name="close" size={19} /></button>
            </div>
            <div style={{ border: "1px solid var(--rule-2)", background: "var(--bg-card)", borderRadius: 12, overflow: "hidden" }}>
              <CopyRow label="Brokerage account" value={BROKERAGE_NUMBERS.account} first />
              <CopyRow label="DTC number" value={BROKERAGE_NUMBERS.dtc} />
            </div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", padding: "10px 2px 0", lineHeight: 1.4 }}>Use these for an ACATS transfer into this account.</div>
          </div>
        </div>
      )}
      {editing && (
        <div style={{ position: "fixed", inset: 0, zIndex: 60, display: "flex", alignItems: "flex-end" }} data-screen-label="Edit nickname">
          <div onClick={() => setEditing(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.4)", animation: "scrim-in 200ms ease both" }} />
          <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", borderTop: "1px solid var(--rule-2)", borderRadius: "16px 16px 0 0", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
            <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
            <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>Edit nickname</div>
              <input autoFocus value={draft} onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { const v = draft.trim(); if (v) setNick(v); setEditing(false); } }}
                style={{ width: "100%", boxSizing: "border-box", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, outline: "none", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, color: "var(--ink)" }} />
              <button className="press" onClick={() => { const v = draft.trim(); if (v) setNick(v); setEditing(false); }} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
            </div>
          </div>
        </div>
      )}

      {/* total + a time dropdown for the change (like the account view) */}
      <div style={{ flex: "none", padding: "14px 20px 12px" }}>
        <Eyebrow>Total holdings</Eyebrow>
        <div style={{ marginTop: 6 }}><Money value={total} size={28} weight={600} /></div>
        <div style={{ marginTop: 5, position: "relative" }}>
          <button className="press" onClick={() => setRangeOpen((o) => !o)} style={{ display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "none", padding: 0, cursor: "pointer" }}>
            <Delta abs={valChange.abs} pct={valChange.pct} size={12.5} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>{HRANGE_WORD[valRange]}</span>
            <Icon name="down" size={14} color="var(--ink-3)" style={{ transform: rangeOpen ? "rotate(180deg)" : "none", transition: "transform .15s" }} />
          </button>
          {rangeOpen && (
            <>
              <div onClick={() => setRangeOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 30 }} />
              <div style={{ position: "absolute", top: 26, left: 0, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 168 }}>
                {HRANGES.map((r, i) => (
                  <button key={r} className="press" onClick={() => { setValRange(r); setRangeOpen(false); }} style={{ width: "100%", textAlign: "left", padding: "10px 13px", background: r === valRange ? "var(--bg-2)" : "none", border: "none", borderTop: i === 0 ? "none" : "1px solid var(--rule)", cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, color: r === valRange ? "var(--ink)" : "var(--ink-2)" }}>{HRANGE_MENU[r]}</button>
                ))}
              </div>
            </>
          )}
        </div>
      </div>

      {/* gains — total up front, tap to expand long-term vs short-term */}
      <div style={{ flex: "none", padding: "0 20px 12px" }}>
        <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)" }}>
          <ExpandGain first label="Unrealized gain" total={totalPL} lt={unrealLong} st={unrealShort} />
          <ExpandGain label="Realized gain · YTD" total={realizedLong + realizedShort} lt={realizedLong} st={realizedShort} />
        </div>
      </div>

      {/* Holdings / Transactions toggle */}
      <div style={{ flex: "none", padding: "0 20px" }}>
        <Segmented options={[{ value: "holdings", label: "Holdings" }, { value: "transactions", label: "Transactions" }]} value={tab} onChange={(v) => { setTab(v); setQ(""); }} />
      </div>

      {/* search + filters — the original toolbar (inline filter dropdown).
          paddingBottom balances the toolbar's own top padding above it. */}
      <div style={{ flex: "none", paddingBottom: 6 }}>
        {tab === "holdings"
          ? <AcctToolbar q={q} setQ={setQ} placeholder="Search holdings" filter={hf} setFilter={setHf} filters={hFilters} />
          : <AcctToolbar q={q} setQ={setQ} placeholder="Search transactions" filter={tf} setFilter={setTf} filters={tFilters} />}
      </div>

      <style>{`
        .yo-htable { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: var(--rule-2) transparent; }
        .yo-htable::-webkit-scrollbar { height: 7px; }
        .yo-htable::-webkit-scrollbar-thumb { background: var(--rule-2); border-radius: 999px; }
        .yo-htable::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }
        .yo-htable::-webkit-scrollbar-track { background: transparent; }
      `}</style>

      {tab === "holdings" ? (
        <div className="scroll" style={{ borderTop: "1px solid var(--rule)" }}>
          <div className="yo-htable">
            <div style={{ minWidth: totalW }}>
              {/* header row */}
              <div style={{ display: "flex", alignItems: "center", height: 38, borderBottom: "1px solid var(--rule)", background: "var(--bg-2)" }}>
                <button className="press" onClick={() => toggleSort("holding")} style={{ ...thStyle, position: "sticky", left: 0, zIndex: 2, flex: "1 0 " + HOLD_W + "px", minWidth: HOLD_W, height: "100%", background: "var(--bg-2)", display: "flex", alignItems: "center", gap: 3, padding: "0 12px", borderRight: "1px solid var(--rule)" }}>
                  Holding <span style={{ color: sort.key === "holding" ? "var(--accent)" : "var(--ink-3)", fontSize: typeSize(8) }}>{caret("holding")}</span>
                </button>
                {COLS.map((c) => (
                  <button key={c.key} className="press" onClick={() => toggleSort(c.key)} style={{ ...thStyle, width: c.w, flex: "none", height: "100%", display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 3, padding: "0 12px" }}>
                    {c.label} <span style={{ color: sort.key === c.key ? "var(--accent)" : "var(--ink-3)", fontSize: typeSize(8) }}>{caret(c.key)}</span>
                  </button>
                ))}
              </div>
              {rows.map((h) => (
                <button key={h.id} className="press" onClick={() => onSelect ? onSelect(h.id) : nav.security(h.id)} style={{ display: "flex", alignItems: "center", width: "100%", minHeight: 56, background: "none", border: "none", borderBottom: "1px solid var(--rule)", cursor: "pointer", padding: 0, textAlign: "left" }}>
                  <div style={{ position: "sticky", left: 0, zIndex: 1, flex: "1 0 " + HOLD_W + "px", minWidth: HOLD_W, background: "var(--bg)", borderRight: "1px solid var(--rule)", padding: "9px 12px", height: "100%", display: "flex", flexDirection: "column", justifyContent: "center" }}>
                    <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12.5), fontWeight: 700, color: h.kind === "crypto" ? "var(--alloc-crypto)" : "var(--accent)" }}>{h.ticker}</div>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{h.name}</div>
                  </div>
                  {COLS.map((c) => (
                    <div key={c.key} style={{ width: c.w, flex: "none", padding: "0 12px", display: "flex", flexDirection: "column", alignItems: "flex-end", textAlign: "right" }}>
                      {c.render(h, total)}
                    </div>
                  ))}
                </button>
              ))}
              {rows.length === 0 && (
                <div style={{ padding: "30px 20px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink-3)" }}>No holdings match "{q}".</div>
              )}
            </div>
          </div>
          <div style={{ height: 20 }} />
        </div>
      ) : (
        <div className="scroll" style={{ borderTop: "1px solid var(--rule)" }}>
          {shownTxns.length ? shownTxns.map((t, i) => <AcctTxnRow key={t.id} t={t} last={i === shownTxns.length - 1} nav={nav} external={false} />) : (
            <div style={{ padding: "30px 20px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink-3)" }}>No transactions match.</div>
          )}
          <div style={{ height: 20 }} />
        </div>
      )}
    </div>
  );
};

/* ============================================================
   ReceiptSheet · the audit-trail detail. Who acted, when, under
   what authority — the trust contract, one tap deep.
   ============================================================ */
const ReceiptSheet = ({ tx, nav, onClose }) => {
  const val = tx.net !== 0 ? tx.net : (tx.amount || 0);
  const rows = [
    ["When", tx.when === "Just now" ? "Just now" : tx.when],
    ["Category", tx.category],
    ["Detail", tx.detail],
    ["Executed by", tx.by || "You"],
    tx.by && tx.by !== "You"
      ? ["Authority", "Standing rule · within your approved limits"]
      : ["Authority", "You approved · passkey"],
  ];
  return (
    <>
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.4)", zIndex: 340, animation: "scrim-in 200ms ease both" }} />
      <div data-screen-label="Receipt" style={{ position: "absolute", left: 0, right: 0, bottom: 0, zIndex: 341, background: "var(--bg)", borderTop: "1px solid var(--rule-2)", borderRadius: "16px 16px 0 0", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
        <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
        <div style={{ padding: "12px 20px max(24px,env(safe-area-inset-bottom))" }}>
          <div style={{ textAlign: "center", padding: "6px 0 14px", borderBottom: "1px solid var(--rule)" }}>
            <Money value={val} size={28} weight={600} sign={tx.net !== 0} color={tx.net > 0 ? "var(--accent-pos)" : "var(--ink)"} />
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, marginTop: 6 }}>{tx.title}</div>
          </div>
          {rows.map(([l, v]) => (
            <div key={l} style={{ display: "flex", alignItems: "baseline", gap: 12, padding: "10px 0", borderBottom: "1px solid var(--rule)" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", flex: "none", width: 96 }}>{l}</span>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink)", textAlign: "right", flex: 1, minWidth: 0 }}>{v}</span>
            </div>
          ))}
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 14 }}>
            <Btn kind="ghost" onClick={onClose}>Close</Btn>
            <Btn kind="quiet" onClick={() => { onClose(); nav.ask(`Tell me about "${tx.title}"`, `That was ${tx.detail} on ${tx.when}. ${tx.by && tx.by !== "You" ? `It ran under your "${tx.by}" rule, within the limits you approved.` : "You placed and approved it yourself."} Anything you'd like changed?`); }}>Ask Yoshi</Btn>
          </div>
        </div>
      </div>
    </>
  );
};

/* ============================================================
   AutomationsListSheet · how you see the rules you've created.
   Reuses the original AutomationsTab; each row opens the original
   AutomationSheet (detail + in-sheet chat to modify/pause).
   ============================================================ */
const AutomationsListSheet = ({ nav, onClose }) => (
  <div className="push-enter" data-screen-label="Automations" style={{ position: "absolute", inset: 0, zIndex: 285, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
    {window.StatusBar && <window.StatusBar />}
    <NavBar title="Automations" sub="Rules Yoshi runs for you" onBack={onClose} />
    <div className="scroll">
      {window.AutomationsTab ? <window.AutomationsTab nav={nav} /> : null}
      <div style={{ height: 16 }} />
    </div>
  </div>
);

/* ============================================================
   ProfileSheet · the hamburger menu. Settings, card, Automations,
   Connections (the portability hero), docs & help.
   ============================================================ */
const MiniSwitch = ({ on, onChange }) => (
  <button className="press" role="switch" aria-checked={on} onClick={() => onChange(!on)} style={{ width: 40, height: 23, padding: 0, position: "relative", background: on ? "var(--accent)" : "var(--rule-2)", border: "none", borderRadius: 999, cursor: "pointer", flex: "none", transition: "background 180ms ease" }}>
    <span style={{ position: "absolute", top: 2.5, left: on ? 20 : 2.5, width: 18, height: 18, borderRadius: "50%", background: "#fff", transition: "left 180ms var(--ease)", boxShadow: "0 1px 3px rgba(0,0,0,0.3)" }} />
  </button>
);

const PRow = ({ title, sub, right, onClick, first }) => {
  const Tag = onClick ? "button" : "div";
  return (
    <Tag className={onClick ? "press" : undefined} onClick={onClick} style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", background: "none", border: "none", borderTop: first ? "none" : "1px solid var(--rule)", cursor: onClick ? "pointer" : "default" }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{title}</div>
        {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2 }}>{sub}</div>}
      </div>
      {right}
    </Tag>
  );
};

const PGroup = ({ label, children, clip = true }) => (
  <>
    <div style={{ padding: "20px 20px 8px" }}><Eyebrow>{label}</Eyebrow></div>
    <div style={{ margin: "0 20px", border: "1px solid var(--rule-2)", background: "var(--bg-card)", borderRadius: 12, overflow: clip ? "hidden" : "visible" }}>{children}</div>
  </>
);

const HealthDot = ({ ok }) => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: "var(--f-display)", fontSize: typeSize(10.5), fontWeight: 600, color: ok ? "var(--accent-pos)" : "var(--accent)" }}>
    <span style={{ width: 6, height: 6, borderRadius: 999, background: ok ? "var(--accent-pos)" : "var(--accent)" }} />
    {ok ? "Connected" : "Reconnect"}
  </span>
);

/* card details — no card art; reveal, then copy each number separately */
const CARD_INFO = [
  ["Card number", "4417 8829 0021 4417"],
  ["Expiry", "08 / 28"],
  ["CVV", "492"],
];
const CopyRow = ({ label, value, first }) => {
  const [copied, setCopied] = useState(false);
  const copy = () => { try { navigator.clipboard.writeText(value.replace(/[^0-9]/g, "")); } catch (e) {} setCopied(true); setTimeout(() => setCopied(false), 1400); };
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 14px", borderTop: first ? "none" : "1px solid var(--rule)" }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)" }}>{label}</div>
        <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(14.5), color: "var(--ink)", marginTop: 3, letterSpacing: "0.06em" }}>{value}</div>
      </div>
      <button className="press" onClick={copy} aria-label={"Copy " + label} style={{ background: "none", border: "none", cursor: "pointer", padding: "4px 2px" }}>
        <CopyActionLabel copied={copied} size={13} />
      </button>
    </div>
  );
};
/* a linked external account row — rename inline, hide from totals (dim +
   "Hidden" chip + Unhide), or reconnect, via a small per-row menu. */
const HiddenPill = () => (
  <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(8.5), fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, padding: "2px 7px", whiteSpace: "nowrap" }}>Hidden</span>
);

/* one menu row, matching the original account-options dropdown */
const AcctMenuItem = ({ icon, title, sub, borderTop, onClick }) => (
  <button className="press" onClick={onClick} style={{ width: "100%", display: "flex", alignItems: sub ? "flex-start" : "center", gap: 10, padding: "12px 13px", background: "none", border: "none", borderTop: borderTop ? "1px solid var(--rule)" : "none", cursor: "pointer", textAlign: "left" }}>
    <Icon name={icon} size={17} color="var(--ink-2)" stroke={1.6} style={sub ? { marginTop: 1 } : undefined} />
    <span style={{ minWidth: 0 }}>
      <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{title}</span>
      {sub && <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, lineHeight: 1.4 }}>{sub}</span>}
    </span>
  </button>
);

/* a bottom action sheet — the mobile idiom for row options, matching main's
   edit-actions sheets (scrim + rounded sheet + action rows + Cancel) */
const SheetActionRow = ({ icon, label, sub, tone, first, onClick }) => (
  <button className="press" onClick={onClick} style={{ width: "100%", display: "flex", alignItems: sub ? "flex-start" : "center", gap: 11, padding: "14px", background: "none", border: "none", borderTop: first ? "none" : "1px solid var(--rule)", cursor: "pointer", textAlign: "left", color: tone === "danger" ? "var(--signal-neg)" : "var(--ink)" }}>
    <Icon name={icon} size={18} color={tone === "danger" ? "var(--signal-neg)" : "var(--ink-2)"} stroke={1.6} style={sub ? { marginTop: 1 } : undefined} />
    <span style={{ minWidth: 0 }}>
      <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>{label}</span>
      {sub && <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, lineHeight: 1.4 }}>{sub}</span>}
    </span>
  </button>
);

const OptionsSheet = ({ title, letter, iconName, imgSrc, actions, onClose }) => {
  const rows = actions.map((a, i) => <SheetActionRow key={a.label} {...a} first={i === 0} onClick={() => { onClose(); a.onClick(); }} />);
  // Web: a dropdown anchored just under the kebab (top-right of the sheet),
  // like the main web app — not a bottom sheet. Click outside to dismiss.
  if (window.__YOSHI_WEB) {
    return (
      <>
        <div onClick={onClose} style={{ position: "absolute", inset: 0, zIndex: 50 }} />
        <div style={{ position: "absolute", top: 46, right: 14, zIndex: 51, minWidth: 280, maxWidth: 340, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, overflow: "hidden", boxShadow: "0 18px 44px -18px rgba(0,0,0,0.6)", animation: "fade-swap 140ms ease both" }} data-screen-label="Options">
          {rows}
        </div>
      </>
    );
  }
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "flex-end" }} data-screen-label="Options">
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
      <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 20px max(24px,env(safe-area-inset-bottom))", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
        <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 14px" }} />
        <div style={{ border: "1px solid var(--rule-2)", borderRadius: 12, background: "var(--bg-card)", overflow: "hidden" }}>
          {rows}
        </div>
        <button className="press" onClick={onClose} style={{ width: "100%", marginTop: 10, padding: "12px", background: "none", border: "1px solid var(--rule-2)", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, color: "var(--ink-2)", cursor: "pointer" }}>Cancel</button>
      </div>
    </div>
  );
};

const ExtAccountRow = ({ acct, name, hidden, first, onRename, onToggleHide, onReconnect }) => {
  const [menu, setMenu] = useState(false);
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(name);
  const border = first ? "none" : "1px solid var(--rule)";
  const stale = acct.health !== "ok";
  const save = () => { const v = draft.trim(); if (v) onRename(v); setEditing(false); };

  return (
    <div style={{ position: "relative" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", borderTop: border, opacity: hidden ? 0.55 : 1 }}>
        {/* logo avatar — same as main (initial in a bordered circle) */}
        <span style={{ width: 30, height: 30, flex: "none", borderRadius: 999, display: "grid", placeItems: "center", background: "var(--bg-2)", border: "1px solid var(--rule-2)", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 700, letterSpacing: "-0.01em" }}>{name[0]}</span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{name}</span>
            {hidden && <HiddenPill />}
          </div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2 }}>{(!hidden && stale) ? acct.sub : acct.sub + " · " + usd(acct.value)}</div>
        </div>
        {/* no 'Connected' label; only a Re-auth pill when the connection is stale (matches main) */}
        {!hidden && stale && (
          <span role="button" tabIndex={0} className="press" onClick={onReconnect} style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--signal-warn)", background: "color-mix(in srgb, var(--signal-warn) 14%, transparent)", border: "1px solid color-mix(in srgb, var(--signal-warn) 38%, transparent)", borderRadius: 999, padding: "5px 11px", cursor: "pointer", whiteSpace: "nowrap" }}>
            <Icon name="connect" size={12.5} color="var(--signal-warn)" /> Re-auth
          </span>
        )}
        {/* kebab — same 30x30 boxed button as the main app's account options */}
        <button className="press" aria-label="Account options" onClick={() => setMenu((o) => !o)} style={{ flex: "none", display: "grid", placeItems: "center", width: 30, height: 30, background: menu ? "var(--bg-2)" : "none", border: "1px solid " + (menu ? "var(--rule-2)" : "transparent"), borderRadius: 8, cursor: "pointer", color: "var(--ink-2)" }}>
          <Icon name="more" size={18} />
        </button>
      </div>

      {/* options — a bottom action sheet, the mobile idiom (matches main) */}
      {menu && (
        <OptionsSheet title={name} letter={name[0]} onClose={() => setMenu(false)} actions={[
          { icon: "doc", label: "Rename", onClick: () => { setDraft(name); setEditing(true); } },
          { icon: "eye", label: hidden ? "Show account" : "Hide account", sub: hidden ? "Count this account again everywhere." : "Remains connected", onClick: onToggleHide },
        ]} />
      )}

      {/* nickname editor — same bottom sheet as the main app */}
      {editing && (
        <>
          <div onClick={() => setEditing(false)} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", zIndex: 40, animation: "scrim-in 200ms ease both" }} />
          <div style={{ position: "fixed", left: 0, right: 0, bottom: 0, zIndex: 41, background: "var(--bg)", borderTop: "1px solid var(--rule-2)", borderRadius: "16px 16px 0 0", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
            <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
            <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>Rename</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, color: "var(--ink-2)", marginBottom: 6 }}>Account nickname</div>
              <input autoFocus value={draft} onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") save(); }}
                style={{ width: "100%", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, outline: "none", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, color: "var(--ink)" }} />
              <button className="press" onClick={save} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
            </div>
          </div>
        </>
      )}
    </div>
  );
};

const Avatar = ({ name, size = 30 }) => {
  const initials = name.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
  return (
    <span style={{ width: size, height: size, flex: "none", borderRadius: 999, background: "linear-gradient(135deg, color-mix(in srgb, var(--accent) 60%, var(--bg-2)), color-mix(in srgb, var(--alloc-crypto) 45%, var(--bg-2)))", display: "grid", placeItems: "center", fontFamily: "var(--f-display)", fontSize: typeSize(size * 0.4), fontWeight: 700, color: "#14130f" }}>{initials}</span>
  );
};

// card number / expiry / CVV, revealed in a bottom sheet with per-field copy
const CardInfoSheet = ({ onClose }) => (
  <div style={{ position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "flex-end" }} data-screen-label="Card info">
    <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
    <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 20px max(24px,env(safe-area-inset-bottom))", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
      <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 16px" }} />
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 14 }}>
        <span style={{ width: 40, height: 40, flex: "none", borderRadius: 12, display: "grid", placeItems: "center", background: "var(--bg-2)", border: "1px solid var(--rule-2)", color: "var(--ink)" }}><Icon name="eye" size={20} stroke={1.5} /></span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600 }}>Card details</div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2 }}>Yoshi debit · ··4417</div>
        </div>
        <button className="press" onClick={onClose} aria-label="Close" style={{ flex: "none", background: "none", border: "none", color: "var(--ink-3)", padding: 4, display: "flex", cursor: "pointer" }}><Icon name="close" size={19} /></button>
      </div>
      <div style={{ border: "1px solid var(--rule-2)", borderRadius: 12, background: "var(--bg-card)", overflow: "hidden" }}>
        {CARD_INFO.map(([l, v], i) => <CopyRow key={l} label={l} value={v} first={i === 0} />)}
      </div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", padding: "10px 2px 0", lineHeight: 1.4 }}>Only visible to you. Keep these numbers private.</div>
    </div>
  </div>
);

/* ---- agents & API keys · list rows managed by a kebab, like accounts ------ */
// brand icons live in main (assets/); reuse them so ChatGPT/Claude/etc. keep
// their logos instead of a letter. The filter recolors the mark to the ink.
const AGENT_ICON = {
  chatgpt: "assets/agent-chatgpt.svg",
  claude: "assets/agent-claude.svg",
  perplexity: "assets/agent-perplexity.svg",
  openclaw: "assets/agent-openclaw.svg",
};
const AGENT_SEED = [
  { id: "chatgpt", name: "ChatGPT", img: AGENT_ICON.chatgpt },
  { id: "claude", name: "Claude", img: AGENT_ICON.claude },
  { id: "portfolio", name: "portfolio-copilot", icon: "connect" },
];
const AGENT_LIBRARY = [
  { id: "perplexity", name: "Perplexity", img: AGENT_ICON.perplexity },
  { id: "cursor", name: "Cursor", icon: "terminal" },
  { id: "gemini", name: "Gemini" },
  { id: "openclaw", name: "OpenClaw", img: AGENT_ICON.openclaw },
];
const YOSHI_ENDPOINTS = [
  { id: "mcp", name: "Yoshi MCP", sub: "agents.yoshi.ai/mcp" },
  { id: "api", name: "Yoshi API brief", sub: "Give an agent the API context" },
  { id: "sdk", name: "Yoshi SDKs", sub: "pip · npm · Homebrew · Go" },
];
const API_KEY_SEED = [
  { id: "k1", name: "Local scripts", sub: "ys_demo_••••ce12 · Created May 12" },
  { id: "k_ray", name: "Raycast agent", sub: "ys_demo_••••e8f0 · Used 2h ago" },
];
const agentAvatar = (letter, iconName, imgSrc) => (
  <span style={{ width: 30, height: 30, flex: "none", borderRadius: 999, display: "grid", placeItems: "center", background: "var(--bg-2)", border: "1px solid var(--rule-2)", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 700 }}>
    {imgSrc ? <img src={imgSrc} alt="" style={{ width: 17, height: 17, objectFit: "contain", filter: "brightness(0) invert(1)" }} />
      : iconName ? <Icon name={iconName} size={16} color="var(--ink)" stroke={1.5} />
      : letter}
  </span>
);

// a list row with a kebab that opens Rename / Revoke — same idiom as ExtAccountRow
const ManagedRow = ({ name, sub, badge, first, avatar = true, iconName, imgSrc, extraActions = [], onRename, onRevoke, renameLabel = "Rename", revokeLabel = "Revoke access" }) => {
  const [menu, setMenu] = useState(false);
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(name);
  const save = () => { const v = draft.trim(); if (v && onRename) onRename(v); setEditing(false); };
  return (
    <div style={{ position: "relative" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", borderTop: first ? "none" : "1px solid var(--rule)" }}>
        {avatar && agentAvatar(name[0], iconName, imgSrc)}
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{name}</span>
            {badge}
          </div>
          {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sub}</div>}
        </div>
        <button className="press" aria-label="Options" onClick={() => setMenu((o) => !o)} style={{ flex: "none", display: "grid", placeItems: "center", width: 30, height: 30, background: menu ? "var(--bg-2)" : "none", border: "1px solid " + (menu ? "var(--rule-2)" : "transparent"), borderRadius: 8, cursor: "pointer", color: "var(--ink-2)" }}>
          <Icon name="more" size={18} />
        </button>
      </div>
      {menu && (
        <OptionsSheet title={name} letter={avatar ? name[0] : null} iconName={iconName} imgSrc={imgSrc} onClose={() => setMenu(false)} actions={[
          ...extraActions,
          ...(onRename ? [{ icon: "doc", label: renameLabel, onClick: () => { setDraft(name); setEditing(true); } }] : []),
          { icon: "close", label: revokeLabel, tone: "danger", onClick: () => onRevoke && onRevoke() },
        ]} />
      )}
      {editing && (
        <>
          <div onClick={() => setEditing(false)} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", zIndex: 40, animation: "scrim-in 200ms ease both" }} />
          <div style={{ position: "fixed", left: 0, right: 0, bottom: 0, zIndex: 41, background: "var(--bg)", borderTop: "1px solid var(--rule-2)", borderRadius: "16px 16px 0 0", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
            <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
            <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>{renameLabel}</div>
              <input autoFocus value={draft} onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") save(); }}
                style={{ width: "100%", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, outline: "none", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, color: "var(--ink)" }} />
              <button className="press" onClick={save} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
            </div>
          </div>
        </>
      )}
    </div>
  );
};

const SmallBtn = ({ children, onClick, tone }) => (
  <button className="press" onClick={onClick} style={{ flex: "none", display: "inline-flex", alignItems: "center", gap: 5, padding: "6px 11px", background: "none", border: "1px solid var(--rule-2)", borderRadius: 8, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, color: tone === "accent" ? "var(--accent)" : "var(--ink)" }}>{children}</button>
);

// the "Add an agent" bottom sheet — authorize a pre-built agent or connect
// your own (MCP / API / SDKs). Opened from the Connections · agents section.
const AddAgentSheet = ({ onAuthorize, onCopy, onClose }) => {
  const [sdkOpen, setSdkOpen] = useState(false);
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "flex-end" }} data-screen-label="Add an agent">
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
      <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 0 0", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", maxHeight: "82%", display: "flex", flexDirection: "column", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
        <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 14px", flex: "none" }} />
        <div style={{ flex: "none", padding: "0 20px 10px", display: "flex", alignItems: "center", gap: 12 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.02em" }}>Add an agent</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), color: "var(--ink-3)", marginTop: 3 }}>Authorize a pre-built one, or connect your own</div>
          </div>
          <button className="press" onClick={onClose} aria-label="Close" style={{ flex: "none", background: "none", border: "none", color: "var(--ink-3)", padding: 4, display: "flex", cursor: "pointer" }}><Icon name="close" size={19} /></button>
        </div>
        <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
          <div style={{ padding: "4px 20px 4px" }}><Eyebrow>Authorize an external agent</Eyebrow></div>
          {AGENT_LIBRARY.map((ag) => (
            <div key={ag.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 20px", borderTop: "1px solid var(--rule)" }}>
              {agentAvatar(ag.name[0], ag.icon, ag.img)}
              <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{ag.name}</span>
              <SmallBtn onClick={() => onAuthorize(ag)}>Authorize</SmallBtn>
            </div>
          ))}
          <div style={{ padding: "14px 20px 4px", borderTop: "1px solid var(--rule)" }}><Eyebrow>Or connect your own</Eyebrow></div>
          {YOSHI_ENDPOINTS.map((e) => (
            <div key={e.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 20px", borderTop: "1px solid var(--rule)" }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{e.name}</div>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{e.sub}</div>
              </div>
              {e.id === "sdk"
                ? <SmallBtn onClick={() => setSdkOpen(true)}>View SDKs</SmallBtn>
                : <SmallBtn tone="accent" onClick={() => onCopy(e.name)}><Icon name="copy" size={13} color="var(--accent)" /> Copy</SmallBtn>}
            </div>
          ))}
        </div>
      </div>
      {sdkOpen && <SdkSheet onClose={() => setSdkOpen(false)} />}
    </div>
  );
};

// Connections · agents — connected list + an "Add an agent" bottom sheet to
// authorize a pre-built agent or connect your own
const AgentsSection = ({ flash }) => {
  const [agents, setAgents] = useState(AGENT_SEED);
  const [addOpen, setAddOpen] = useState(false);
  const rename = (id, v) => { setAgents((a) => a.map((x) => x.id === id ? { ...x, name: v } : x)); flash("Renamed to " + v); };
  const revoke = (id, nm) => { setAgents((a) => a.filter((x) => x.id !== id)); flash("Revoked access · " + nm); };
  const authorize = (ag) => { setAgents((a) => a.find((x) => x.id === ag.id) ? a : [...a, { id: ag.id, name: ag.name, img: ag.img, icon: ag.icon }]); setAddOpen(false); flash("Authorized · " + ag.name); };
  return (
    <>
      <PGroup label="Connections · agents" clip={false}>
        {agents.map((ag, i) => (
          <ManagedRow key={ag.id} first={i === 0} name={ag.name} iconName={ag.icon} imgSrc={ag.img}
            onRename={(v) => rename(ag.id, v)} onRevoke={() => revoke(ag.id, ag.name)} revokeLabel="Revoke access" />
        ))}
      </PGroup>
      <button className="press" onClick={() => setAddOpen(true)} style={{ width: "calc(100% - 40px)", margin: "10px 20px 0", display: "flex", alignItems: "center", justifyContent: "center", gap: 7, padding: "12px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, color: "var(--accent)" }}>
        <Icon name="plus" size={16} color="var(--accent)" /> Add an agent
      </button>
      {addOpen && <AddAgentSheet onAuthorize={authorize} onCopy={(name) => flash("Copied · " + name)} onClose={() => setAddOpen(false)} />}
    </>
  );
};

// full key-generation flow in a bottom sheet: name it, generate, copy once
const genApiToken = () => { const hex = "0123456789abcdef"; let s = "ys_demo_"; for (let i = 0; i < 28; i++) s += hex[Math.floor(Math.random() * 16)]; return s; };
const maskApiToken = (t) => "ys_demo_••••" + String(t).slice(-4);

const GenerateKeySheet = ({ onClose, onCreated }) => {
  const [name, setName] = useState("");
  const [made, setMade] = useState(null); // { name, token } once generated
  const [copied, setCopied] = useState(false);
  const generate = () => { const nm = name.trim(); if (!nm) return; setMade({ name: nm, token: genApiToken() }); };
  const copy = () => { try { navigator.clipboard && navigator.clipboard.writeText(made.token); } catch (_) {} setCopied(true); setTimeout(() => setCopied(false), 1600); };
  const done = () => { onCreated(made); onClose(); };
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "flex-end" }} data-screen-label="Generate key">
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
      <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 20px max(24px,env(safe-area-inset-bottom))", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
        <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 16px" }} />
        {!made ? (
          <>
            <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 16 }}>
              <span style={{ width: 40, height: 40, flex: "none", borderRadius: 12, display: "grid", placeItems: "center", background: "var(--bg-2)", border: "1px solid var(--rule-2)", color: "var(--ink)" }}><Icon name="key" size={20} stroke={1.5} /></span>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600 }}>Generate an API key</div>
            </div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, color: "var(--ink-2)", marginBottom: 6 }}>Name this key</div>
            <input autoFocus value={name} onChange={(e) => setName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") generate(); }} placeholder="e.g. Trading bot"
              style={{ width: "100%", boxSizing: "border-box", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 10, outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600 }} />
            <button className="press" onClick={generate} disabled={!name.trim()} style={{ width: "100%", marginTop: 14, padding: "12px", background: name.trim() ? "var(--accent)" : "var(--rule-2)", color: name.trim() ? "var(--accent-ink)" : "var(--ink-3)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer" }}>Generate key</button>
            <button className="press" onClick={onClose} style={{ width: "100%", marginTop: 8, padding: "12px", background: "none", border: "1px solid var(--rule-2)", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, color: "var(--ink-2)", cursor: "pointer" }}>Cancel</button>
          </>
        ) : (
          <>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, marginBottom: 12 }}>{made.name}</div>
            <div style={{ padding: "11px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                <code style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--ink)", wordBreak: "break-all" }}>{made.token}</code>
                <button className="press" onClick={copy} style={{ flex: "none", display: "inline-flex", alignItems: "center", gap: 5, background: "var(--bg-2)", color: "var(--accent)", border: "1px solid var(--rule-2)", borderRadius: 8, padding: "6px 10px", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, cursor: "pointer" }}><Icon name="copy" size={13} color="var(--accent)" /> {copied ? "Copied" : "Copy"}</button>
              </div>
            </div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", margin: "9px 0 0", lineHeight: 1.4 }}>Copy it now. For your security, you won't see the full key again.</div>
            <button className="press" onClick={done} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", color: "var(--accent-ink)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer" }}>Done</button>
          </>
        )}
      </div>
    </div>
  );
};

// API keys — separate area, each key managed by a kebab
const ApiKeysMenuSection = ({ flash }) => {
  const [keys, setKeys] = useState(API_KEY_SEED);
  const [genOpen, setGenOpen] = useState(false);
  const rename = (id, v) => { setKeys((k) => k.map((x) => x.id === id ? { ...x, name: v } : x)); flash("Renamed to " + v); };
  const revoke = (id, nm) => { setKeys((k) => k.filter((x) => x.id !== id)); flash("Revoked · " + nm); };
  const onCreated = ({ name, token }) => { setKeys((k) => [...k, { id: "k" + token.slice(-6), name, sub: maskApiToken(token) + " · Just now" }]); flash("Generated · " + name); };
  return (
    <>
      <div style={{ padding: "20px 20px 8px" }}><Eyebrow>API keys</Eyebrow></div>
      <div style={{ margin: "0 20px", border: "1px solid var(--rule-2)", background: "var(--bg-card)", borderRadius: 12 }}>
        {keys.map((k, i) => (
          <ManagedRow key={k.id} first={i === 0} avatar={false} name={k.name} sub={k.sub}
            onRename={(v) => rename(k.id, v)} onRevoke={() => revoke(k.id, k.name)} revokeLabel="Revoke key" />
        ))}
      </div>
      <button className="press" onClick={() => setGenOpen(true)} style={{ width: "calc(100% - 40px)", margin: "10px 20px 0", display: "flex", alignItems: "center", justifyContent: "center", gap: 7, padding: "12px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, color: "var(--accent)" }}>
        <Icon name="plus" size={16} color="var(--accent)" /> Generate key
      </button>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", padding: "10px 20px 0", lineHeight: 1.45 }}>Keys are scoped and propose-only. An agent calling with your key can draft moves; only you can approve them.</div>
      {genOpen && <GenerateKeySheet onClose={() => setGenOpen(false)} onCreated={onCreated} />}
    </>
  );
};

/* ---- Accounts breakdown · the drill-in from the Home balance -------------- */
// prototype account/routing numbers + interest, per cash account
const CASH_ACCT_DETAIL = {
  chk: { account: "8829 0021 8841", routing: "021 000 021", interestMonth: "$48.00", interestYtd: "$296.00" },
  sav: { account: "8829 0021 2207", routing: "021 000 021", interestMonth: "$168.00", interestYtd: "$1,240.00" },
};
const APY_EXPLAINER = "APY is an annual rate. Our current base rate is 3.30% and is subject to change. Interest accrues daily and is paid out monthly. Payouts vary month to month with your balance and the number of days in the month.";
// per-account transactions, shown in the cash detail (like main)
const CASH_ACCT_TXNS = {
  chk: [
    { icon: "down", title: "Pay day", sub: "Acme Corp · direct deposit", when: "Jun 1", net: 6240.00 },
    { icon: "receipt", title: "Rent", sub: "Bill pay · Greystar", when: "Jun 1", net: -2800.00 },
    { icon: "down", title: "Interest earned", sub: "3.30% APY", when: "May 31", net: 48.00 },
    { icon: "swap", title: "Card autopay", sub: "Yoshi Card ··2291", when: "May 28", net: -1240.40 },
    { icon: "swap", title: "Moved to Reserve", sub: "Instant · buffer rule", when: "May 28", net: -6000.00 },
    { icon: "swap", title: "Zelle from Jordan", sub: "Received", when: "May 24", net: 320.00 },
  ],
  sav: [
    { icon: "down", title: "Interest earned", sub: "3.30% APY", when: "May 31", net: 168.00 },
    { icon: "swap", title: "From Cash", sub: "Instant · buffer rule", when: "May 28", net: 6000.00 },
    { icon: "swap", title: "From Cash", sub: "Instant · buffer rule", when: "May 14", net: 8000.00 },
    { icon: "down", title: "Interest earned", sub: "3.30% APY", when: "Apr 30", net: 155.00 },
  ],
};

// a tap-to-open detail sheet for the ⓘ icons (APY / FDIC)
const InfoSheet = ({ title, body, onClose }) => (
  <div style={{ position: "fixed", inset: 0, zIndex: 60, display: "flex", alignItems: "flex-end" }} data-screen-label="Info">
    <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
    <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 20px max(24px,env(safe-area-inset-bottom))", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
      <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 14px" }} />
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
        <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600 }}>{title}</span>
        <button className="press" onClick={onClose} aria-label="Close" style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", cursor: "pointer" }}><Icon name="close" size={19} /></button>
      </div>
      <p style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink-2)", lineHeight: 1.55, margin: 0 }}>{body}</p>
    </div>
  </div>
);

// type-aware detail — matches main's cash account view: interest/APY stats with
// ⓘ details, searchable transactions, and a kebab → options (incl. Move)
const CashAccountSheet = ({ acct, nav, onBack, embedded }) => {
  const d = CASH_ACCT_DETAIL[acct.id] || {};
  const [menuOpen, setMenuOpen] = useState(false);
  const [info, setInfo] = useState(null);
  const [revealOpen, setRevealOpen] = useState(false);
  const [editing, setEditing] = useState(false);
  const [nick, setNick] = useState(acct.name);
  const [draft, setDraft] = useState(acct.name);
  const [q, setQ] = useState("");
  const [tf, setTf] = useState("all");
  const [tfOpen, setTfOpen] = useState(false);
  const TF = [["all", "All"], ["in", "Money in"], ["out", "Money out"]];
  const txns = (CASH_ACCT_TXNS[acct.id] || []).filter((t) => {
    if (tf === "in" && t.net <= 0) return false;
    if (tf === "out" && t.net >= 0) return false;
    const s = q.trim().toLowerCase();
    return !s || (t.title + " " + t.sub).toLowerCase().includes(s);
  });
  // the ⓘ icon: on web the explainer opens as a small anchored popover next to
  // the icon (InfoPop); on mobile it falls through to the bottom sheet (below)
  const iBtn = (key, title, body, align = "left") => (
    <span style={{ position: "relative", display: "inline-flex", lineHeight: 0 }}>
      <button className="press" aria-label="Details" onClick={() => setInfo((v) => v === key ? null : key)} style={{ background: "none", border: "none", padding: 0, display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", lineHeight: 0 }}><Icon name="info" size={12} stroke={1.5} /></button>
      {window.__YOSHI_WEB && <InfoPop open={info === key} onClose={() => setInfo(null)} title={title} align={align}>{body}</InfoPop>}
    </span>
  );
  return (
    <div className="push-enter" data-screen-label="Cash account" style={{ position: "absolute", inset: 0, zIndex: 330, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {window.StatusBar && <window.StatusBar />}
      <div style={{ flex: "none", display: "flex", alignItems: "center", gap: 10, padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)" }}>
        {!embedded && <button className="press" onClick={onBack} aria-label="Back" style={{ background: "none", border: "none", padding: 6, marginLeft: -6, color: "var(--ink)", display: "flex", cursor: "pointer" }}><Icon name="back" size={20} /></button>}
        <div style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
          {nick} <span style={{ color: "var(--ink-3)", fontWeight: 500 }}>· {acct.sub.replace(/^Yoshi /, "")}</span>
        </div>
        <button className="press" aria-label="Account options" onClick={() => setMenuOpen(true)} style={{ flex: "none", display: "grid", placeItems: "center", width: 30, height: 30, background: "none", border: "1px solid transparent", borderRadius: 8, cursor: "pointer", color: "var(--ink-2)" }}><Icon name="more" size={18} /></button>
      </div>
      <div className="scroll" style={{ paddingBottom: 24 }}>
        <section style={{ flex: "none", padding: "14px 20px 12px" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
            <Eyebrow style={{ margin: 0 }}>Balance</Eyebrow>
            {iBtn("fdic", "FDIC insurance", FDIC_DISCLOSURE)}
          </div>
          <div style={{ marginTop: 5 }}><Money value={acct.value} size={30} weight={500} /></div>
          <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 8 }}>
            <div>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: "var(--accent-pos)" }}>+{d.interestYtd || "$0.00"}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", marginTop: 2 }}>Interest Jan–May</div>
            </div>
            <span style={{ width: 1, alignSelf: "stretch", background: "var(--rule)", flex: "none" }} />
            <div>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: "var(--ink)" }}>{(acct.apr || "").replace(" APY", "")}</div>
              <div style={{ display: "flex", alignItems: "center", gap: 4, marginTop: 2 }}>
                <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)" }}>Current APY</span>
                {iBtn("apy", "About your APY", APY_EXPLAINER)}
              </div>
            </div>
          </div>
        </section>
        <div style={{ flex: "none", padding: "0 20px 10px", position: "relative" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "9px 12px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 10 }}>
            <Icon name="search" size={15} color="var(--ink-3)" />
            <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search transactions" style={{ flex: 1, minWidth: 0, background: "transparent", border: "none", outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(13.5) }} />
            <span style={{ width: 1, height: 16, background: "var(--rule-2)", flex: "none" }} />
            <button className="press" aria-label="Filter transactions" onClick={() => setTfOpen((o) => !o)} style={{ background: "none", border: "none", padding: 0, display: "grid", placeItems: "center", cursor: "pointer", color: tf === "all" ? "var(--ink-3)" : "var(--accent)", flex: "none" }}><Icon name="filter" size={15} /></button>
          </div>
          {tfOpen && (
            <>
              <div onClick={() => setTfOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 30 }} />
              <div style={{ position: "absolute", top: 44, right: 20, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 150 }}>
                {TF.map(([id, label], i) => (
                  <button key={id} className="press" onClick={() => { setTf(id); setTfOpen(false); }} style={{ width: "100%", textAlign: "left", padding: "10px 13px", background: id === tf ? "var(--bg-2)" : "none", border: "none", borderTop: i === 0 ? "none" : "1px solid var(--rule)", cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, color: id === tf ? "var(--ink)" : "var(--ink-2)" }}>{label}</button>
                ))}
              </div>
            </>
          )}
        </div>
        <div style={{ borderTop: "1px solid var(--rule)" }}>
          {txns.map((tx, i) => (
            <button key={i} className="press" onClick={() => nav.sheet({ type: "txn", tx: { ...tx, detail: tx.sub } })} style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 12, padding: "11px 20px", background: "none", border: "none", borderBottom: "1px solid var(--rule)", cursor: "pointer" }}>
              <span style={{ width: 30, height: 30, flex: "none", borderRadius: 9, background: "var(--bg-2)", display: "grid", placeItems: "center", color: "var(--ink-2)" }}><Icon name={tx.icon} size={15} stroke={1.5} /></span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{tx.title}</div>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2 }}>{tx.sub}</div>
              </div>
              <span style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                <Money value={tx.net} size={13} sign color={tx.net > 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2 }}>{tx.when}</div>
              </span>
            </button>
          ))}
        </div>
      </div>

      {menuOpen && (
        <OptionsSheet title={nick} letter={nick[0]} onClose={() => setMenuOpen(false)} actions={[
          { icon: "eye", label: "Reveal account & routing number", onClick: () => setRevealOpen(true) },
          { icon: "doc", label: "Edit nickname", onClick: () => { setDraft(nick); setEditing(true); } },
          { icon: "receipt", label: "Documents", onClick: () => nav.sheet({ type: "documents" }) },
          { icon: "swap", label: "Move", onClick: () => nav.sheet({ type: "transfer", from: "cash" }) },
        ]} />
      )}
      {info && !window.__YOSHI_WEB && <InfoSheet title={info === "apy" ? "About your APY" : "FDIC insurance"} body={info === "apy" ? APY_EXPLAINER : FDIC_DISCLOSURE} onClose={() => setInfo(null)} />}
      {revealOpen && (
        <div style={{ position: "fixed", inset: 0, zIndex: 60, display: "flex", alignItems: "flex-end" }} data-screen-label="Account numbers">
          <div onClick={() => setRevealOpen(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)", animation: "scrim-in 200ms ease both" }} />
          <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", border: "1px solid var(--rule-2)", borderBottom: "none", borderRadius: "18px 18px 0 0", padding: "10px 20px max(24px,env(safe-area-inset-bottom))", boxShadow: "0 -20px 60px -28px rgba(0,0,0,0.75)", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
            <div style={{ width: 36, height: 4, borderRadius: 999, background: "var(--rule-2)", margin: "0 auto 14px" }} />
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
              <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600 }}>Account & routing</span>
              <button className="press" onClick={() => setRevealOpen(false)} aria-label="Close" style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", cursor: "pointer" }}><Icon name="close" size={19} /></button>
            </div>
            <div style={{ border: "1px solid var(--rule-2)", background: "var(--bg-card)", borderRadius: 12, overflow: "hidden" }}>
              <CopyRow label="Account number" value={d.account} first />
              <CopyRow label="Routing number" value={d.routing} />
            </div>
          </div>
        </div>
      )}
      {editing && (
        <div style={{ position: "fixed", inset: 0, zIndex: 60, display: "flex", alignItems: "flex-end" }} data-screen-label="Edit nickname">
          <div onClick={() => setEditing(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.4)", animation: "scrim-in 200ms ease both" }} />
          <div className="push-enter" style={{ position: "relative", width: "100%", boxSizing: "border-box", background: "var(--bg)", borderTop: "1px solid var(--rule-2)", borderRadius: "16px 16px 0 0", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
            <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
            <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>Edit nickname</div>
              <input autoFocus value={draft} onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { const v = draft.trim(); if (v) setNick(v); setEditing(false); } }}
                style={{ width: "100%", boxSizing: "border-box", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, outline: "none", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, color: "var(--ink)" }} />
              <button className="press" onClick={() => { const v = draft.trim(); if (v) setNick(v); setEditing(false); }} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

const AccountsSheet = ({ nav, onClose }) => {
  const [detail, setDetail] = useState(null);
  const [holdingsAcct, setHoldingsAcct] = useState(null);
  const allH = [...GROUPS.investments.items, ...GROUPS.crypto.items];
  const acctHoldings = (id) => allH.filter((h) => HOLDING_ACCT[h.id] === id);
  const acctSum = (id) => acctHoldings(id).reduce((s, h) => s + h.value, 0);
  const acctDay = (id) => acctHoldings(id).reduce((s, h) => s + h.dayAbs, 0);
  const trading = (id, name, sub) => { const v = acctSum(id), da = acctDay(id); return { id, name, sub, value: v, dayAbs: da, dayPct: (da / (v - da)) * 100, kind: "holdings", holdAcct: id, color: "var(--alloc-invest)" }; };
  const groups = [
    { label: "Cash", total: CASH_TOTAL, color: "var(--alloc-cash)", accounts: CASH.map((c) => ({ ...c, kind: "cash" })) },
    { label: "Investments", total: acctSum("brokerage") + acctSum("roth"), color: "var(--alloc-invest)", accounts: [
      trading("brokerage", "Brokerage", acctHoldings("brokerage").length + " holdings"),
      trading("roth", "High Risk", acctHoldings("roth").length + " holdings"),
    ] },
    { label: "Crypto", total: acctSum("crypto"), color: "var(--alloc-crypto)", accounts: [{ ...trading("crypto", "Crypto", acctHoldings("crypto").length + " holdings"), color: "var(--alloc-crypto)" }] },
  ];
  // on web, the accounts view is master-detail: the list stays on the left and
  // selecting an account shows its detail on the right (no drill-in overlay)
  const web = !!window.__YOSHI_WEB;
  const flatAccounts = groups.flatMap((g) => g.accounts);
  const [selId, setSelId] = useState(flatAccounts[0] ? flatAccounts[0].id : null);
  const [txn, setTxn] = useState(null); // web: a tapped txn replaces the detail pane (no modal), like main
  const selected = flatAccounts.find((a) => a.id === selId) || flatAccounts[0];
  // on web, route any txn tap from an embedded pane into this local state so the
  // transaction detail REPLACES the account detail column (rather than opening
  // the shell-level Stream detail or a modal)
  const paneNav = web ? { ...nav, sheet: (o) => (o && o.type === "txn") ? setTxn(o.tx) : nav.sheet(o) } : nav;
  const open = (a) => { if (web) { setSelId(a.id); setTxn(null); } else if (a.kind === "cash") { setDetail(a); } else { setHoldingsAcct(a.holdAcct); } };
  const chevron = <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)", flex: "none" }} />;

  const listBody = (
    <>
      <div style={{ padding: "16px 20px 4px" }}>
        <Eyebrow>Total at Yoshi</Eyebrow>
        <div style={{ marginTop: 5 }}><Money value={NET_WORTH} size={30} weight={500} /></div>
        <div style={{ marginTop: 6, display: "flex", alignItems: "baseline", gap: 6 }}>
          <Delta abs={DAY_ABS} pct={DAY_PCT} size={13} />
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>today</span>
        </div>
      </div>
      {groups.map((g) => {
        const gDay = g.accounts.reduce((s, a) => s + (a.dayAbs || 0), 0);
        return (
        <div key={g.label}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "18px 20px 8px" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, letterSpacing: "-0.015em" }}>{g.label}</span>
            <div style={{ marginLeft: "auto", display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 2 }}>
              <Money value={g.total} size={16} weight={500} />
              {gDay ? <div style={{ display: "flex", alignItems: "baseline", justifyContent: "flex-end", gap: 5 }}><Delta abs={gDay} pct={(gDay / (g.total - gDay)) * 100} size={11} /><span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)" }}>today</span></div> : null}
            </div>
          </div>
          <div style={{ margin: "0 20px", border: "1px solid var(--rule-2)", background: "var(--bg-card)", borderRadius: 12, overflow: "hidden" }}>
            {g.accounts.map((a, i) => {
              const on = web && selected && a.id === selected.id;
              return (
                <button key={a.id} className="press" onClick={() => open(a)} style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", background: on ? "var(--bg-2)" : "none", border: "none", borderLeft: on ? "2px solid var(--accent)" : "2px solid transparent", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer" }}>
                  <span style={{ width: 8, height: 8, borderRadius: 999, flex: "none", background: a.color || g.color }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{a.name}</div>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{a.sub.replace(/^Yoshi /, "")}</div>
                  </div>
                  <div style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                    <Money value={a.value} size={13} />
                    {a.apr ? <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10), color: "var(--accent-pos)", marginTop: 2 }}>{a.apr}</div>
                      : a.dayAbs != null ? <div style={{ marginTop: 2 }}><Delta abs={a.dayAbs} pct={a.dayPct} size={10.5} /></div> : null}
                  </div>
                  {!web && chevron}
                </button>
              );
            })}
          </div>
        </div>
        );
      })}
    </>
  );

  const header = (
    <div style={{ flex: "none", display: "flex", alignItems: "center", gap: 8, padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)" }}>
      <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.02em" }}>Accounts</span>
      <button className="press" onClick={onClose} aria-label="Close" style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", cursor: "pointer" }}><Icon name="close" size={20} /></button>
    </div>
  );

  // the selected account's detail, hosted inline in the right pane (keyed so
  // switching accounts remounts with the right data)
  const detailPane = selected && (selected.kind === "cash"
    ? <CashAccountSheet key={selected.id} acct={selected} nav={paneNav} onBack={() => {}} embedded />
    : <HoldingsDetailSheet key={selected.id} nav={paneNav} initialAcct={selected.holdAcct} z={340} onClose={() => {}} embedded />);

  if (web) {
    return (
      <div className="push-enter" data-screen-label="Accounts" style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
        {header}
        <div style={{ flex: 1, minHeight: 0, display: "flex" }}>
          <aside style={{ width: 384, flex: "none", borderRight: "1px solid var(--rule)", display: "flex", flexDirection: "column", minHeight: 0 }}>
            <div className="scroll" style={{ paddingBottom: 24 }}>{listBody}</div>
          </aside>
          <main style={{ flex: 1, minWidth: 0, position: "relative", minHeight: 0, display: "flex", flexDirection: "column" }}>
            {detailPane}
            {/* a tapped txn replaces the account detail — stack above the embedded
                account sheet (z 330/340) so it fully covers it, like main */}
            {txn && <div style={{ position: "absolute", inset: 0, zIndex: 400 }}><TxnDetailSheet tx={txn} onClose={() => setTxn(null)} nav={nav} /></div>}
          </main>
        </div>
      </div>
    );
  }

  return (
    <div className="push-enter" data-screen-label="Accounts" style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {window.StatusBar && <window.StatusBar />}
      {header}
      <div className="scroll" style={{ paddingBottom: 24 }}>{listBody}</div>
      {detail && <CashAccountSheet acct={detail} nav={nav} onBack={() => setDetail(null)} />}
      {holdingsAcct && <HoldingsDetailSheet nav={nav} initialAcct={holdingsAcct} z={340} onClose={() => setHoldingsAcct(null)} />}
    </div>
  );
};

const ProfileSheet = ({ palette, setPalette, nav, onClose, flash }) => {
  const [frozen, setFrozen] = useState(false);
  const [cardInfoOpen, setCardInfoOpen] = useState(false);
  const [push, setPush] = useState(true);
  const [sms, setSms] = useState(true);
  const [email, setEmail] = useState(true);
  const [wa, setWa] = useState(false);
  const [section, setSection] = useState(null);
  const [acctNames, setAcctNames] = useState({});
  const [hiddenAccts, setHiddenAccts] = useState([]);
  const activeAutos = AUTOMATIONS.filter((a) => a.status === "active").length;
  // reuse the original account/settings sections (loaded from profile.jsx)
  const SECTIONS = {
    automations: { label: "Automations", body: window.AutomationsTab ? <window.AutomationsTab nav={nav} /> : null },
    personal: { label: "Personal information", body: <PersonalInfoForm /> },
    investor: { label: "Investor profile", body: <InvestorProfileForm nav={nav} /> },
    security: { label: "Security", body: <SecuritySettings /> },
    close: { label: "Close accounts", body: <CloseAccounts /> },
    appearance: { label: "Appearance", body: (
      <div style={{ padding: "16px 20px" }}>
        <Segmented options={[{ value: "graphite", label: "Graphite" }, { value: "bone", label: "Bone" }]} value={palette} onChange={setPalette} />
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 8, lineHeight: 1.4 }}>Choose the color palette Yoshi uses across the app.</div>
      </div>
    ) },
  };
  const chevron = <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />;
  return (
    <div className="push-enter" data-screen-label="Menu" style={{ position: "absolute", inset: 0, zIndex: 280, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {window.StatusBar && <window.StatusBar />}
      {section ? (
        <>
          <NavBar title={SECTIONS[section].label} onBack={() => setSection(null)} />
          <div className="scroll" style={{ paddingBottom: 24 }}>
            <div style={{ borderTop: "1px solid var(--rule)" }}>{SECTIONS[section].body}</div>
          </div>
        </>
      ) : (
      <>
      {/* the hamburger becomes an X; profile picture + full name sit beside it */}
      <div style={{ flex: "none", display: "flex", alignItems: "center", gap: 11, padding: "6px 16px 10px", minHeight: 40 }}>
        <button className="press" onClick={onClose} aria-label="Close menu" style={{ background: "none", border: "none", padding: 6, marginLeft: -6, color: "var(--ink)", display: "flex", cursor: "pointer", flex: "none" }}>
          <Icon name="close" size={22} />
        </button>
        <Avatar name={PROFILE.name} size={30} />
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{PROFILE.name}</span>
      </div>
      <div className="scroll" style={{ paddingBottom: 30 }}>

        {/* Automations — how you see the rules you've created */}
        <PGroup label="Automations">
          <PRow first title="Manage automations" sub={`${activeAutos} active · ${AUTOMATIONS.length} total`} onClick={() => { if (window.__YOSHI_WEB) { onClose(); nav.sheet({ type: "automations" }); } else { setSection("automations"); } }}
            right={<Icon name="route" size={18} color="var(--ink-3)" />} />
          <PRow title="Build a new rule" sub="Tell Yoshi in plain words" onClick={() => { onClose(); nav.ask("Build a new automation", "Sure. Tell me the rule in plain words — like \"invest $200 every Friday\" or \"sweep idle cash over $15k into Savings.\" I'll set it up inside your limits and show you the first run before it executes."); }}
            right={<Icon name="plus" size={16} color="var(--ink-3)" />} />
        </PGroup>

        {/* open a new Yoshi account. Paper trading isn't a consumer feature —
            it's an API/MCP capability an agent uses on its own. */}
        <PGroup label="Open more accounts">
          <PRow first title="Open another Brokerage account" sub="Taxable investing across stocks and ETFs" onClick={() => { onClose(); nav.ask("Open another Brokerage account", "Happy to. A Yoshi Brokerage account lets you invest in stocks and ETFs in a regular taxable account — I'll walk you through the paperwork and fund it from any of your accounts. Want to start?"); }}
            right={<Icon name="plus" size={16} color="var(--ink-3)" />} />
          <PRow title="Open another Cash account" sub="Yield-bearing cash, 3.30% APY" onClick={() => { onClose(); nav.ask("Open another Cash account", "Done in a tap. A new Yoshi Cash account earns 3.30% APY like the rest of your cash — handy for keeping a bucket separate. Want me to open one?"); }}
            right={<Icon name="plus" size={16} color="var(--ink-3)" />} />
          <PRow title="Open another Crypto account" sub="Buy and hold BTC, ETH, and more" onClick={() => { onClose(); nav.ask("Open another Crypto account", "Sure. A Yoshi Crypto account lets you buy and hold crypto alongside everything else. Want me to set one up?"); }}
            right={<Icon name="plus" size={16} color="var(--ink-3)" />} />
        </PGroup>

        <PGroup label="Connections · accounts" clip={false}>
          {EXT_ACCOUNTS.map((x, i) => {
            const nm = acctNames[x.id] || x.name;
            return (
              <ExtAccountRow key={x.id} acct={x} first={i === 0} name={nm}
                hidden={hiddenAccts.includes(x.id)}
                onRename={(v) => { setAcctNames((s) => ({ ...s, [x.id]: v })); flash("Renamed to " + v); }}
                onToggleHide={() => setHiddenAccts((s) => s.includes(x.id) ? (flash("Unhidden · " + nm), s.filter((y) => y !== x.id)) : (flash("Hidden from totals · " + nm), [...s, x.id]))}
                onReconnect={() => flash("Reconnected · " + nm)} />
            );
          })}
        </PGroup>
        <button className="press" onClick={() => { onClose(); nav.sheet({ type: "link" }); }} style={{ width: "calc(100% - 40px)", margin: "10px 20px 0", display: "flex", alignItems: "center", justifyContent: "center", gap: 7, padding: "12px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, color: "var(--accent)" }}>
          <Icon name="plus" size={16} color="var(--accent)" /> Link another account
        </button>

        <AgentsSection flash={flash} />
        <ApiKeysMenuSection flash={flash} />

        <PGroup label="Card">
          <PRow first title="Freeze card" sub="Blocks new purchases instantly" right={<MiniSwitch on={frozen} onChange={setFrozen} />} />
          <PRow title="Add to Apple Wallet" onClick={() => flash("Added to Wallet")} right={<Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />} />
        </PGroup>
        <button className="press" onClick={() => setCardInfoOpen(true)} style={{ width: "calc(100% - 40px)", margin: "10px 20px 0", display: "flex", alignItems: "center", justifyContent: "center", gap: 7, padding: "12px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, color: "var(--accent)" }}>
          <Icon name="eye" size={16} color="var(--accent)" /> Reveal card info
        </button>
        {cardInfoOpen && <CardInfoSheet onClose={() => setCardInfoOpen(false)} />}

        <PGroup label="Connections · channels">
          <PRow first title="Push notifications" sub="On this device" right={<MiniSwitch on={push} onChange={setPush} />} />
          <PRow title="Text message" sub="+1 ••• ••• 4471" right={<MiniSwitch on={sms} onChange={setSms} />} />
          <PRow title="Email" sub="ryan@… · reports and briefs" right={<MiniSwitch on={email} onChange={setEmail} />} />
        </PGroup>

        <PGroup label="Account">
          <PRow first title="Personal information" sub="Name, contact, trusted contact" onClick={() => setSection("personal")} right={chevron} />
          <PRow title="Investor profile" sub="Suitability and advisory state" onClick={() => setSection("investor")} right={chevron} />
          <PRow title="Security" sub="Passkeys and sign-in" onClick={() => setSection("security")} right={chevron} />
          <PRow title="Appearance" sub={palette === "bone" ? "Bone palette" : "Graphite palette"} onClick={() => setSection("appearance")} right={chevron} />
          <PRow title="Close accounts" sub="Permanently close an account" onClick={() => setSection("close")} right={chevron} />
        </PGroup>

        <PGroup label="Documents & help">
          <PRow first title="Statements & tax docs" sub="Monthly statements, 1099s" onClick={() => { onClose(); nav.sheet({ type: "documents" }); }} right={<Icon name="doc" size={16} color="var(--ink-3)" />} />
          <PRow title="Get help" sub="Just ask — a human joins when needed" onClick={() => { onClose(); nav.sheet({ type: "support" }); }} right={<Icon name="lifebuoy" size={16} color="var(--ink-3)" />} />
        </PGroup>

        <div style={{ padding: "20px 20px 0" }}>
          <Btn kind="ghost" full onClick={() => { nav.signOut(); }}>Sign out</Btn>
        </div>
      </div>
      </>
      )}
    </div>
  );
};

Object.assign(window, { EXT_ACCOUNTS, AGENT_CONNECTIONS, HoldingView, HoldingsDetailSheet, ReceiptSheet, AutomationsListSheet, ProfileSheet, AccountsSheet, MiniSwitch, PRow, PGroup, HealthDot });
