/* 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. External
   funding accounts remain available only inside the Move flow. */

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={500} />
          <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 ? 500 : 500, color: r === range ? "var(--ink)" : "var(--ink-3)", cursor: "pointer" }}>{r === "Today" ? "today" : 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: performanceMovementLabel, "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: 500, 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" ? performanceMovementLabel : 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: "proportional-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: "proportional-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: 500, letterSpacing: "normal", 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: 500, letterSpacing: "normal", 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: 500, 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={[
          // Reveal account number + Edit nickname target one specific account, so they are
          // hidden on the "All holdings" aggregate (there is no single account to reveal/rename).
          ...(acctSel === "all" ? [] : [
            { 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: 500 }}>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: 500, letterSpacing: "normal", 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: 500, 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: 500, 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={500} /></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" }}>
            <SummaryPerformance abs={valChange.abs} pct={valChange.pct} size={12.5} label={HRANGE_WORD[valRange]} split={valRange === "Today"} afterAbs={base.reduce((sum, h) => sum + afterHoursHoldingChange(h), 0)} firstRowAccessory={<Icon name="down" size={14} color="var(--ink-3)" style={{ alignSelf: "center", 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: "100%", marginTop: 6, 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: 500, 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 style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderTop: "1px solid var(--rule)" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 500, color: "var(--ink-2)" }}>Investment income · YTD</span>
            <span style={{ marginLeft: "auto" }}><Money value={2340.50} size={12} weight={500} sign color="var(--accent-pos)" dim="var(--accent-pos)" /></span>
          </div>
        </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: 500, 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={500} sign={tx.net !== 0} color={tx.net > 0 ? "var(--accent-pos)" : "var(--ink)"} />
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 500, 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: 500, letterSpacing: "normal", 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>
    </>
  );
};

/* ============================================================
   InflightSheet · the detail for a money instruction still in
   flight. A vertical stepper says where it is; the actions are
   honest about the point of no return:
   · once + queued   → Cancel (free, guaranteed)
   · once + working  → Attempt cancel (a race — say so)
   · once + pending  → no button, but say WHY there isn't one
   · recurring       → Skip this run / Manage rule
   ============================================================ */
const InflightStepper = ({ steps, at }) => (
  <div style={{ marginTop: 4 }}>
    {steps.map(([label, sub], i) => {
      const done = i < at, here = i === at;
      return (
        <div key={i} style={{ display: "grid", gridTemplateColumns: "18px 1fr", gap: 12 }}>
          <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
            <span style={{ width: 10, height: 10, borderRadius: 999, flex: "none", marginTop: 5,
              background: done ? "var(--accent)" : here ? "var(--bg)" : "var(--bg-2)",
              border: here ? "2px solid var(--accent)" : done ? "none" : "1.5px solid var(--rule-2)" }} />
            {i < steps.length - 1 && <span style={{ width: 1.5, flex: 1, minHeight: 18, background: done ? "var(--accent)" : "var(--rule-2)", margin: "3px 0" }} />}
          </div>
          <div style={{ paddingBottom: i < steps.length - 1 ? 14 : 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: here ? 500 : 500, color: done || here ? "var(--ink)" : "var(--ink-3)" }}>{label}</div>
            {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: here ? "var(--accent)" : "var(--ink-3)", marginTop: 1 }}>{sub}</div>}
          </div>
        </div>
      );
    })}
  </div>
);

const InflightSheet = ({ item, nav, onClose, onCancel, onSkip }) => {
  const [requesting, setRequesting] = useState(false); // working-state cancel race
  if (!item) return null;
  const recurring = item.kind === "recurring";
  // queued/working are always cancelable; pending only when the item says so
  // (an outgoing transfer still holdable — incoming money can't be recalled)
  const cancelable = item.kind === "once" && (item.state === "queued" || item.state === "working" || (item.state === "pending" && item.cancelable));
  const stateLabel = item.state === "working" ? "Working" : item.state === "pending" ? "Pending" : item.state === "queued" ? "Queued" : "Scheduled";
  const attemptCancel = () => {
    if (item.state === "working") {
      // honest about the race: request it, then resolve
      setRequesting(true);
      setTimeout(() => onCancel(item), 1400);
    } else onCancel(item);
  };
  const note = requesting ? "Cancel requested — if the order fills first, the fill stands."
    : recurring ? item.skipNote
    : item.state === "working" ? "The order is at the exchange. Cancel is an attempt — it may fill first."
    : item.cancelNote || item.noCancelNote;
  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="In flight" 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))" }}>

          {/* header — who/what + the state chip up top */}
          <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
            <span style={{ width: 6, height: 6, borderRadius: 999, flex: "none", background: item.state === "pending" ? "var(--ink-3)" : "var(--accent)" }} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 500, letterSpacing: "normal", textTransform: "uppercase", color: item.state === "pending" ? "var(--ink-2)" : "var(--accent)" }}>
              {recurring ? "↻ Recurring · " + stateLabel : "One-time · " + stateLabel}
            </span>
            <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{item.chip}</span>
          </div>
          <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginTop: 8, paddingBottom: 13, borderBottom: "1px solid var(--rule)" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 500, letterSpacing: "normal", minWidth: 0 }}>{item.title}</span>
            <span style={{ marginLeft: "auto" }}><Money value={item.net} size={16} sign color={item.net > 0 ? "var(--accent-pos)" : "var(--ink)"} /></span>
          </div>

          {/* where it is in the lifecycle */}
          <div style={{ padding: "14px 0 2px" }}>
            <InflightStepper steps={item.steps || []} at={item.stepAt || 0} />
          </div>

          {/* the point-of-no-return line — absence of a button is never silent */}
          {note && (
            <div style={{ display: "flex", gap: 8, alignItems: "flex-start", marginTop: 12, padding: "10px 12px", background: "var(--bg-2)" }}>
              <Icon name={requesting ? "clock" : "info"} size={14} color="var(--ink-3)" style={{ marginTop: 1, flex: "none" }} />
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", lineHeight: 1.5 }}>{note}</span>
            </div>
          )}

          {/* actions — kind-appropriate, never a bare "Cancel" on a rule */}
          <div style={{ display: "grid", gridTemplateColumns: recurring || cancelable ? "1fr 1fr" : "1fr", gap: 8, marginTop: 14 }}>
            {recurring ? (
              <>
                <Btn kind="ghost" onClick={() => onSkip(item)}>Skip this run</Btn>
                <Btn kind="quiet" onClick={() => { onClose(); nav.automation(item.autoId); }}>Manage rule</Btn>
              </>
            ) : cancelable ? (
              <>
                <Btn kind="danger" onClick={attemptCancel} disabled={requesting}>{requesting ? "Cancel requested…" : item.state === "working" ? "Attempt cancel" : item.category === "Transfer" ? "Cancel transfer" : "Cancel order"}</Btn>
                <Btn kind="ghost" onClick={onClose}>Close</Btn>
              </>
            ) : (
              <Btn kind="ghost" onClick={onClose}>Close</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, automations }) => (
  <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} automations={automations} /> : 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: "var(--ld-white)", 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: "var(--ld-type-body)", fontWeight: 500 }}>{title}</div>
        {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: "var(--ld-type-small)", color: "var(--ink-3)", marginTop: 2 }}>{sub}</div>}
      </div>
      {right}
    </Tag>
  );
};

const PGroup = ({ label, children, clip = true, collapsed = false, onToggle }) => (
  <>
    <div style={{ padding: "20px 20px 8px" }}>
      {onToggle ? (
        <button className="press" type="button" onClick={onToggle} aria-expanded={!collapsed} aria-label={(collapsed ? "Show " : "Hide ") + label}
          style={{ display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "none", padding: "2px 0", cursor: "pointer", textAlign: "left" }}>
          <span style={{ fontFamily: "var(--f-display)", fontSize: window.__YOSHI_WEB ? 11.5 : IOS_TYPE.caption2, fontWeight: 500, letterSpacing: "normal", textTransform: "uppercase", color: "var(--accent)" }}>{label}</span>
          <Icon name="back" size={14} color="var(--accent)" style={{ transform: collapsed ? "scaleX(-1)" : "rotate(-90deg)", transition: "transform 160ms ease" }} />
        </button>
      ) : <Eyebrow>{label}</Eyebrow>}
    </div>
    {!collapsed && <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: 500, 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: 500, letterSpacing: "normal", textTransform: "uppercase", color: "var(--ink-3)" }}>{label}</div>
        <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(14.5), color: "var(--ink)", marginTop: 3, letterSpacing: "normal" }}>{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 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: 500 }}>{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: 500, color: "var(--ink-2)", cursor: "pointer" }}>Cancel</button>
      </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: 500, color: "var(--ld-black)" }}>{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: 500 }}>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 · 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",
};
const WEB_AGENT_OPTIONS = [
  { id: "chatgpt", name: "ChatGPT", sub: "Authorize Yoshi in ChatGPT", cta: "Authorize", action: "authorize", img: AGENT_ICON.chatgpt },
  { id: "claude", name: "Claude", sub: "Authorize when Claude is ready", cta: "Authorize", action: "authorize", img: AGENT_ICON.claude },
  { id: "mcp", name: "Yoshi MCP", sub: "agents.yoshi.ai/mcp", cta: "Copy URL", action: "copy", icon: "connect" },
];
const AGENT_SEED = [];
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: 500 }}>
    {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 through OptionsSheet
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: 500, 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: 500, letterSpacing: "normal", 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: 500, 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: 500, 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: 500, color: tone === "accent" ? "var(--accent)" : "var(--ink)" }}>{children}</button>
);

const AgentOptionRow = ({ option, first, onAuthorize, onCopy }) => {
  const act = () => {
    if (option.action === "copy") { onCopy(option.name); return; }
    onAuthorize(option);
  };
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", borderTop: first ? "none" : "1px solid var(--rule)" }}>
      {agentAvatar(option.name[0], option.icon, option.img)}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{option.name}</div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{option.sub}</div>
      </div>
      <SmallBtn tone={option.action === "copy" ? "accent" : undefined} onClick={act}>
        {option.action === "copy" && <Icon name="copy" size={13} color="var(--accent)" />}
        {option.cta}
      </SmallBtn>
    </div>
  );
};

// Connections · agents — matches the web-onboarding choices, then manages authorized rows in place.
const AgentsSection = ({ flash }) => {
  const [agents, setAgents] = useState(AGENT_SEED);
  const visibleOptions = WEB_AGENT_OPTIONS.filter((option) => !agents.some((agent) => agent.id === option.id));
  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 }]); flash("Authorized · " + ag.name); };
  return (
    <>
      <PGroup label="Connections · agents" clip={false}>
        {visibleOptions.map((option, i) => (
          <AgentOptionRow key={option.id} option={option} first={i === 0} onAuthorize={authorize} onCopy={(name) => flash("Copied · " + name)} />
        ))}
        {agents.map((ag, i) => (
          <ManagedRow key={ag.id} first={visibleOptions.length === 0 && 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>
    </>
  );
};

const CASH_ACCT_DETAIL = {
  chk: { account: "8829 0021 8841", routing: "021 000 021", interestMonth: "$48.00" },
  sav: { account: "8829 0021 2207", routing: "021 000 021", interestMonth: "$168.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", time: "8:02 AM", occurredAt: "2026-06-01T08:02:00-07:00", net: 6240.00 },
    { icon: "receipt", title: "Rent", sub: "Bill pay · Greystar", when: "Jun 1", time: "7:00 AM", occurredAt: "2026-06-01T07:00:00-07:00", net: -2800.00 },
    { icon: "down", title: "Interest earned", sub: "3.30% APY", when: "May 31", time: "12:01 AM", occurredAt: "2026-05-31T00:01:00-07:00", net: 48.00 },
    { icon: "swap", title: "Moved to Reserve", sub: "Instant · buffer rule", when: "May 28", time: "2:18 PM", occurredAt: "2026-05-28T14:18:00-07:00", net: -6000.00, cashFlowScope: "internal" },
    { icon: "swap", title: "Zelle from Jordan", sub: "Received", when: "May 24", time: "3:22 PM", occurredAt: "2026-05-24T15:22:00-07:00", net: 320.00 },
  ],
  sav: [
    { icon: "down", title: "Interest earned", sub: "3.30% APY", when: "May 31", time: "12:01 AM", occurredAt: "2026-05-31T00:01:00-07:00", net: 168.00 },
    { icon: "swap", title: "From Cash", sub: "Instant · buffer rule", when: "May 28", time: "2:18 PM", occurredAt: "2026-05-28T14:18:00-07:00", net: 6000.00, cashFlowScope: "internal" },
    { icon: "swap", title: "From Cash", sub: "Instant · buffer rule", when: "May 14", time: "1:12 PM", occurredAt: "2026-05-14T13:12:00-07:00", net: 8000.00, cashFlowScope: "internal" },
    { icon: "down", title: "Interest earned", sub: "3.30% APY", when: "Apr 30", time: "12:01 AM", occurredAt: "2026-04-30T00:01:00-07:00", 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: 500 }}>{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>
);

const ACCOUNT_INSTANT_FUNDING_TITLE = "Instant funding lets you invest up to $10,000.00 while your deposit is pending";
const ACCOUNT_INSTANT_FUNDING_BODY = "Any amount above your instant funding limit remains pending and becomes available when your deposit settles. If the deposit doesn’t settle, the provisionally available balance will be removed, which could cause a negative balance. Availability isn’t guaranteed.";

const InstantFundingPill = ({ amount, nav }) => {
  const [open, setOpen] = useState(false);
  if (!amount) return null;
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 2 }}>
      <div role="status" style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "7px 11px 7px 9px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, color: "var(--ink-3)" }}>
        <Icon name="bolt" size={12.5} color="var(--accent)" stroke={1.6} />
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), lineHeight: 1.35 }}>
          Incl.{" "}<span style={{ fontFamily: "var(--f-mono)", fontWeight: 500, color: "var(--accent-pos)", fontVariantNumeric: "proportional-nums" }}>{usd(amount)}</span>{" "}instant funds
        </span>
      </div>
      <span style={{ position: "relative", display: "inline-flex" }}>
        <button className="press" type="button" aria-label="About instant funding" aria-expanded={open} onClick={() => setOpen((value) => !value)} style={{ width: 24, height: 28, display: "grid", placeItems: "center", padding: 0, background: "transparent", border: "none", borderRadius: 999, color: "var(--ink-3)", cursor: "pointer" }}>
          <Icon name="info" size={15} stroke={1.5} />
        </button>
        <InfoPop open={open} onClose={() => setOpen(false)} title={ACCOUNT_INSTANT_FUNDING_TITLE}>{ACCOUNT_INSTANT_FUNDING_BODY}</InfoPop>
      </span>
    </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, instantFundingAmount = 0 }) => {
  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 = [["in", "Money in"], ["out", "Money out"]];
  const accountTxns = CASH_ACCT_TXNS[acct.id] || [];
  const settledExternalTxns = accountTxns.filter((t) => t.status !== "pending" && t.cashFlowScope !== "internal" && !/interest/i.test(t.title));
  const moneyIn = settledExternalTxns.reduce((sum, t) => sum + (t.net > 0 ? t.net : 0), 0);
  const moneyOut = settledExternalTxns.reduce((sum, t) => sum + (t.net < 0 ? Math.abs(t.net) : 0), 0);
  const cashMetrics = [
    { value: (acct.apr || "").replace(" APY", ""), label: "Current APY", info: true },
    { value: `+${d.interestMonth || "$0.00"}`, label: "Interest earned MTD", positive: true },
    { value: `+${usd(moneyIn)}`, label: "Money In 30D", positive: true },
    { value: `−${usd(moneyOut)}`, label: "Money Out 30D" },
  ];
  const txns = sortTransactionsNewestFirst(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", containerType: "inline-size" }}>
      {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: 500, letterSpacing: "normal", 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>
          {instantFundingAmount > 0 && <div style={{ marginTop: 8 }}><InstantFundingPill amount={instantFundingAmount} nav={nav} /></div>}
          <div className="cash-metrics-grid" aria-label="Cash account metrics" style={{ marginTop: 18 }}>
            {cashMetrics.map((metric) => (
              <div className="cash-metric-card" key={metric.label} style={{ background: "var(--bg-card)", border: "1px solid var(--rule)", borderRadius: 11, boxShadow: "0 8px 22px -8px rgba(0,0,0,0.32), 0 2px 6px -2px rgba(0,0,0,0.18)" }}>
                <div className="cash-metric-value" style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(17), fontWeight: 500, fontVariantNumeric: "proportional-nums", color: metric.positive ? "var(--accent-pos)" : "var(--ink)", whiteSpace: "nowrap" }}>{metric.value}</div>
                <div className="cash-metric-label" style={{ display: "flex", alignItems: "center", gap: 4 }}>
                  <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), lineHeight: 1.25, color: "var(--ink-3)" }}>{metric.label}</span>
                  {metric.info && iBtn("apy", "About your APY", APY_EXPLAINER)}
                </div>
              </div>
            ))}
          </div>
        </section>
        <div style={{ flex: "none", padding: "20px 20px 8px" }}>
          <Eyebrow>Transactions</Eyebrow>
        </div>
        <div style={{ flex: "none", padding: "0 20px 16px", 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 === tf ? "all" : 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: 500, 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 }}>{transactionStamp(tx)}</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: "Transfer", 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: 500 }}>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: 500, letterSpacing: "normal", 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: 500, 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: 500, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

const AccountsSheet = ({ nav, onClose, extra = [], focus, inflight = [], instantFundingAvailable = 0 }) => {
  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)" }; };
  // accounts the user just opened from the menu ("Open another …") — start empty at $0,
  // grouped by family, with the just-opened one flagged `fresh` so it highlights.
  const extraFor = (fam) => (extra || []).filter((a) => a.family === fam);
  const mkCashExtra = (a) => ({ id: a.id, name: a.name, sub: "Yoshi", value: 0, kind: "cash", color: "var(--alloc-cash)", fresh: a.id === focus });
  const mkHoldExtra = (a, color) => ({ id: a.id, name: a.name, sub: "0 holdings", value: 0, dayAbs: 0, dayPct: 0, kind: "holdings", holdAcct: a.id, color, fresh: a.id === focus });
  const pendingTransfers = inflight.filter((item) => item.kind === "once" && item.category === "Transfer" && !item.fundedBy);
  const pendingTotal = pendingTransfers.reduce((sum, item) => sum + (item.net || 0), 0);
  const pendingByCashAccount = pendingTransfers.reduce((amounts, item) => {
    const accountText = `${item.acct || ""} ${item.title || ""} ${item.settle?.detail || ""}`.toLowerCase();
    const explicitAccount = CASH.find((account) => {
      const mask = account.sub.match(/\d{4}$/)?.[0];
      return accountText.includes(account.name.toLowerCase()) || (mask && accountText.includes(mask));
    });
    const accountId = explicitAccount?.id || CASH[0]?.id;
    if (accountId) amounts[accountId] = (amounts[accountId] || 0) + (item.net || 0);
    return amounts;
  }, {});
  const groups = [
    { label: "Cash", total: CASH_TOTAL + pendingTotal, color: "var(--alloc-cash)", accounts: [...CASH.map((c) => ({ ...c, value: c.value + (pendingByCashAccount[c.id] || 0), instantFundingAmount: c.id === CASH[0]?.id ? instantFundingAvailable : 0, kind: "cash" })), ...extraFor("cash").map(mkCashExtra)] },
    { 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"),
      ...extraFor("brokerage").map((a) => mkHoldExtra(a, "var(--alloc-invest)")),
    ] },
    { label: "Crypto", total: acctSum("crypto"), color: "var(--alloc-crypto)", accounts: [{ ...trading("crypto", "Crypto", acctHoldings("crypto").length + " holdings"), color: "var(--alloc-crypto)" }, ...extraFor("crypto").map((a) => mkHoldExtra(a, "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 + pendingTotal} 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)" }}>{performanceMovementLabel}</span>
        </div>
        {instantFundingAvailable > 0 && <div style={{ marginTop: 8 }}><InstantFundingPill amount={instantFundingAvailable} nav={nav} /></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: 500, letterSpacing: "normal" }}>{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)" }}>{performanceMovementLabel}</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", animation: a.fresh ? "count-up 380ms ease both" : "none" }}>
                  <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: 500 }}>{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={{ display: "flex", alignItems: "baseline", justifyContent: "flex-end", gap: 5, marginTop: 2 }}><Delta abs={a.dayAbs} pct={a.dayPct} size={10.5} /><span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), color: "var(--ink-3)" }}>{performanceMovementLabel}</span></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: 500, letterSpacing: "normal" }}>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 instantFundingAmount={selected.instantFundingAmount || 0} />
    : <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, initialSection, automations = AUTOMATIONS }) => {
  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(initialSection || null);
  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} automations={automations} /> : null },
    personal: { label: "Personal information", body: <PersonalInfoForm /> },
    investor: { label: "Investor profile", body: <InvestorProfileForm nav={nav} /> },
    security: { label: "Security", body: <SecuritySettings /> },
    delete: { label: "Delete your Yoshi account", body: <DeleteYoshiAccount /> },
    appearance: { label: "Appearance", body: (
      <div style={{ padding: "16px 20px" }}>
        <div style={{ fontSize: 16 }}>Light</div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 8, lineHeight: 1.4 }}>Lucid Dreaming</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: 500, letterSpacing: "normal", 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)" />} />
        </PGroup>

        <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: 500, color: "var(--accent)" }}>
          <Icon name="eye" size={16} color="var(--accent)" /> Reveal card info
        </button>
        {cardInfoOpen && <CardInfoSheet onClose={() => setCardInfoOpen(false)} />}

        <PGroup label="Notifications">
          <PRow first title="Push notifications" sub="On this device" right={<MiniSwitch on={push} onChange={setPush} />} />
          <PRow title="Text" sub="+1 ••• ••• 4471" right={<MiniSwitch on={sms} onChange={setSms} />} />
          <PRow title="Email" sub={PROFILE.email} 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="Light" onClick={() => setSection("appearance")} right={chevron} />
          <PRow title="Delete your Yoshi account" sub="Permanently close Yoshi" onClick={() => setSection("delete")} 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, { AGENT_CONNECTIONS, HoldingView, HoldingsDetailSheet, ReceiptSheet, InflightSheet, AutomationsListSheet, ProfileSheet, AccountsSheet, MiniSwitch, PRow, PGroup, HealthDot });
