/* move.jsx, "Move money": the full money-movement hub and every rail Yoshi
   offers, replacing the old peer-to-peer transfer flow.

   Hub lists the rails; each opens a self-contained sub-flow:
     · between        Yoshi ↔ Yoshi book transfer (instant, free)
     · bank           ACH in/out of a linked external bank (push & pull)
     · wire           same-day domestic wire
     · check          mobile check deposit
     · instant        cash out to a debit card in seconds
     · directdeposit  account + routing numbers, pre-filled form
     · recurring      schedule a repeating move
     · billpay        pay a card, rent, or utility

   Exported as TransferFlow (overrides the stub in transfer.jsx). */

/* ---------- data ----------------------------------------------------------- */
const YOSHI_ACCTS = [
  { id: "cash",   name: "Cash",      sub: "Yoshi ••8841 · 3.30% APY", bal: 82420.55, kind: "cash", glyph: "logo" },
  { id: "broker", name: "Brokerage", sub: "Yoshi ••3392 · Individual", bal: 5120.00, kind: "investment", glyph: "logo", cashBal: 5120.00 },
];
const EXT_ACCTS = [
  { id: "chase", name: "Chase Checking", bank: "Chase", displaySub: "Chase •4471", sub: "Checking ••4471", glyph: "C", bal: 7682.97, synced: "1 hour ago", supportsInstant: true },
  { id: "boa", name: "Bank of America Savings", bank: "Bank of America", displaySub: "Bank of America •8830", sub: "Savings ••8830",  glyph: "B", bal: 14920.40, synced: "Today", supportsInstant: false },
];
const DEBT_ACCOUNTS = [
  { id: "sapphire", name: "Chase Sapphire", sub: "Credit card ••6841", glyph: "C", bal: 3210.44, min: 64.00 },
  { id: "amex",     name: "Amex Gold",      sub: "Credit card ••2008", glyph: "A", bal: 1180.62, min: 40.00 },
  { id: "mortgage", name: "Method Mortgage", sub: "Mortgage ••0192",   glyph: "M", bal: 238400.00, min: 2140.00 },
];
const DEBIT_CARDS = [
  { id: "visa", bank: "Visa debit", sub: "Chase ••2291", glyph: "VISA" },
];
const WIRE_RECIPIENTS = [
  { id: "title", name: "Bay Area Title Co.", sub: "Wells Fargo · ••5567" },
  { id: "landlord", name: "Ridgeline Property Mgmt", sub: "Chase · ••1180" },
];
const MANUAL_RECIPIENTS = [
  { id: "maya", name: "Maya Cohen", sub: "Bank account ••4471", glyph: "M", supportsInstant: true },
  { id: "title", name: "Bay Area Title Co.", sub: "Bank account ••5567", glyph: "B", supportsInstant: false },
];
const PAYEES = [
  { id: "sapphire", name: "Chase Sapphire", sub: "Credit card ••6841", amt: 3210.44, debt: true, min: 64.00 },
  { id: "rent",     name: "Oakwood Property", sub: "Rent · ACH ••2031", amt: 2450.00 },
  { id: "pge",      name: "Pacific Gas & Electric", sub: "Utility ••5520", amt: 142.18 },
];
/* deterministic "last pulled" time per payee — biller balances are fetched
   on different schedules, so the label varies by payee but stays stable. */
const PULL_TIMES = ["6 hours ago", "2 hours ago", "40 minutes ago", "3 hours ago", "yesterday", "1 hour ago", "9 hours ago"];
const pulledAgo = (id) => { let h = 0; for (const c of String(id)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return PULL_TIMES[h % PULL_TIMES.length]; };

const QUICK_ACTIONS = [
  { id: "bank",  icon: "swap",  name: "Transfer cash" },
];
/* The rail list below the primary Transfer tile. Securities transfers live
   inside the main Transfer form when From is an investment account. */
const RAILS_MORE = [
  { id: "directdeposit", icon: "download", name: "Direct deposit",     sub: "Land your paycheck here" },
  { id: "check",         icon: "receipt",  name: "Deposit a check",    sub: "Take a picture of your paper check" },
  { id: "sendcheck",     icon: "doc",      name: "Send a check",       sub: "Mail a paper check" },
  { id: "recurring",     icon: "clock",    name: "Recurring transfer", sub: "On a set schedule" },
];

/* ---------- shared bits ---------------------------------------------------- */

/* masked account number with tap-to-reveal toggle */
const MaskedAccount = ({ full, masked }) => {
  const [revealed, setRevealed] = useState(false);
  return (
    <span onClick={() => setRevealed(!revealed)} style={{ cursor: "pointer", fontFamily: "var(--f-mono)", letterSpacing: "0.02em", userSelect: "none" }}>
      {revealed ? full : masked}
    </span>
  );
};

const fmtAmt = (amt) => amt === "" ? "0" : amt;

/* format a raw amount string ("1234.5") with thousands separators for display,
   preserving a trailing "." and any in-progress decimals the user is typing */
const commaAmt = (raw) => {
  if (raw === "" || raw == null) return "";
  const neg = raw[0] === "-" ? "-" : "";
  const [intPart, ...rest] = raw.replace(/^-/, "").split(".");
  const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  return neg + (rest.length ? grouped + "." + rest.join("") : grouped);
};

const useAmount = (initial = "") => {
  const [amt, setAmt] = useState(initial);
  const onKey = (k) => setAmt(p => {
    if (k === "⌫") return p.slice(0, -1);
    if (k === "." && p.includes(".")) return p;
    if (p.includes(".") && p.split(".")[1].length >= 2) return p;
    if (p === "" && k === ".") return "0.";
    if (p === "0" && k !== ".") return k;
    return p + k;
  });
  return [amt, setAmt, onKey, parseFloat(amt || "0")];
};

const MvKeypad = ({ onKey }) => (
  <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 1, background: "var(--rule)" }}>
    {["1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "⌫"].map(k => (
      <button key={k} className="press" onClick={() => onKey(k)} style={{ padding: "15px 0", background: "var(--bg)", border: "none", fontFamily: "var(--f-display)", fontSize: typeSize(22), fontWeight: 500, color: "var(--ink)", cursor: "pointer" }}>{k}</button>
    ))}
  </div>
);

/* slide-up number-pad sheet · mirrors a native mobile numeric keyboard.
   Opens when an inline amount field is tapped; the field stays read-only so
   the device keyboard never appears. */
const KeypadSheet = ({ open, onClose, amt, amtNum, onKey, label, sub, onMax, error, ctaLabel = "Done" }) => {
  if (!open) return null;
  return (
    <>
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.4)", zIndex: 40, animation: "scrim-in 200ms ease both" }} />
      <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, zIndex: 41, background: "var(--bg)", borderTop: "1px solid var(--rule-2)", borderRadius: "16px 16px 0 0", display: "flex", flexDirection: "column", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both", boxShadow: "0 -16px 40px -20px rgba(0,0,0,0.4)" }}>
        <div style={{ display: "flex", justifyContent: "center", paddingTop: 8, flex: "none" }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>

        <div style={{ flex: "none", padding: "14px 20px 8px", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 12 }}>
          <div style={{ minWidth: 0 }}>
            {label && <Eyebrow style={{ marginBottom: 6 }}>{label}</Eyebrow>}
            <div style={{ display: "flex", alignItems: "baseline" }}>
              <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(28), fontWeight: 500, color: amtNum ? "var(--ink-2)" : "var(--rule-2)", lineHeight: 1 }}>$</span>
              <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(40), fontWeight: 500, letterSpacing: "-0.02em", color: amtNum ? "var(--ink)" : "var(--rule-2)", fontVariantNumeric: "tabular-nums", lineHeight: 1, marginLeft: 2 }}>{amt === "" ? "0" : commaAmt(amt)}</span>
            </div>
            {(error || sub) && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: error ? "var(--signal-neg)" : "var(--ink-3)", marginTop: 7 }}>{error || sub}</div>}
          </div>
          {onMax && <button className="press" onClick={onMax} style={{ flex: "none", padding: "7px 14px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, color: "var(--ink)", cursor: "pointer" }}>Max</button>}
        </div>

        <MvKeypad onKey={onKey} />
        <div style={{ padding: "12px 18px", paddingBottom: "max(18px, env(safe-area-inset-bottom))", flex: "none" }}>
          <Btn full disabled={!!error} onClick={onClose}>{ctaLabel}</Btn>
        </div>
      </div>
    </>
  );
};

const MoveSuccess = ({ title, sub, onDone }) => (
  <div style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "0 28px", textAlign: "center" }}>
    <div style={{ width: 200, margin: "0 0 26px" }}><Seam draw /></div>
    <div style={{ width: 70, height: 70, borderRadius: 999, border: "1.5px solid var(--accent)", display: "grid", placeItems: "center", marginBottom: 20 }}>
      <svg width="36" height="36" viewBox="0 0 24 24" style={{ display: "block" }}>
        <path d="M5 12.5 L10 17.5 L19 6.5" fill="none" stroke="var(--accent)" strokeWidth="2.2" strokeLinecap="square" />
      </svg>
    </div>
    <Eyebrow color="var(--accent)">Done</Eyebrow>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(24), fontWeight: 600, letterSpacing: "-0.025em", marginTop: 7 }}>{title}</div>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), color: "var(--ink-2)", marginTop: 8, lineHeight: 1.5, maxWidth: 290 }}>{sub}</div>
    <div style={{ width: "100%", maxWidth: 320, marginTop: 26 }}><Btn full onClick={onDone}>Back to home</Btn></div>
  </div>
);

const InstantDepositHandoffScreen = ({ source, onConfirmed }) => {
  const bankName = source.bank || source.name;
  const steps = [
    `Check your email for a message from ${bankName}.`,
    `Open ${bankName} and look for the payment request in Pay & Transfer, alerts, or activity.`,
    "Once approved, your deposit should settle in a few minutes."
  ];
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0, padding: "30px 26px 24px", boxSizing: "border-box" }}>
      <div style={{ flex: 1, minHeight: 0 }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 10, marginTop: 18, color: "var(--ink)" }}>
          <div style={{ width: 46, height: 46, display: "grid", placeItems: "center" }}>
            <Logo size={31} />
          </div>
          <div aria-hidden="true" style={{ width: 34, display: "flex", justifyContent: "center", alignItems: "center", gap: 5, color: "var(--accent)" }}>
            <span style={{ width: 4, height: 4, borderRadius: 999, background: "currentColor", opacity: 0.78 }} />
            <span style={{ width: 4, height: 4, borderRadius: 999, background: "currentColor", opacity: 0.78 }} />
            <span style={{ width: 4, height: 4, borderRadius: 999, background: "currentColor", opacity: 0.78 }} />
          </div>
          <div style={{ width: 46, height: 46, display: "grid", placeItems: "center" }}>
            <Icon name="bank" size={34} color="var(--ink)" stroke={1.7} />
          </div>
        </div>

        <h1 style={{ margin: "60px 0 0", fontFamily: "var(--f-display)", fontSize: typeSize(29), fontWeight: 540, letterSpacing: 0, lineHeight: 1.18, color: "var(--ink)", textAlign: "center" }}>
          Go to {bankName} to complete your transfer
        </h1>

        <div style={{ display: "grid", gap: 26, marginTop: 54 }}>
          {steps.map((body, i) => (
            <div key={body} style={{ display: "grid", gridTemplateColumns: "38px 1fr", gap: 16, alignItems: "start" }}>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--accent)", fontWeight: 600, letterSpacing: "0.08em", paddingTop: 3 }}>
                {String(i + 1).padStart(2, "0")}
              </div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), color: "var(--ink)", lineHeight: 1.44, letterSpacing: 0 }}>{body}</div>
            </div>
          ))}
        </div>
      </div>

      <Btn full onClick={onConfirmed}>Got it</Btn>
    </div>
  );
};

// amount-entry screen body: big number + context + keypad + CTA
const AmountBody = ({ context, amt, amtNum, onKey, info, error, ctaLabel, onCta, ctaDisabled, note }) => (
  <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
    <div style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "12px 24px" }}>
      {context}
      <div style={{ display: "flex", alignItems: "baseline", marginTop: 12 }}>
        <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(30), fontWeight: 500, color: amtNum ? "var(--ink-3)" : "var(--rule-2)" }}>$</span>
        <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(52), fontWeight: 500, color: amtNum ? "var(--ink)" : "var(--rule-2)", letterSpacing: "-0.02em", fontVariantNumeric: "tabular-nums" }}>{fmtAmt(amt)}</span>
      </div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: error ? "var(--signal-neg)" : "var(--ink-3)", marginTop: 12, textAlign: "center", lineHeight: 1.45, minHeight: 16 }}>
        {error || info}
      </div>
    </div>
    <div style={{ flex: "none" }}>
      <MvKeypad onKey={onKey} />
      <div style={{ padding: "12px 18px 28px" }}>
        <Btn full disabled={ctaDisabled} onClick={onCta}>{ctaLabel} <Icon name="arrow" size={18} color="var(--accent-ink)" /></Btn>
        {note && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), color: "color-mix(in srgb, var(--ink-3) 62%, transparent)", textAlign: "center", marginTop: 9, lineHeight: 1.4 }}>{note}</div>}
      </div>
    </div>
  </div>
);

const ReviewRow = ({ label, value, sub, accent, last }) => (
  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 12, padding: "12px 14px", borderBottom: last ? "none" : "1px dashed var(--rule)" }}>
    <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", flex: "none" }}>{label}</span>
    <div style={{ textAlign: "right", minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, color: accent || "var(--ink)" }}>{value}</div>
      {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3 }}>{sub}</div>}
    </div>
  </div>
);

const ReviewScreen = ({ eyebrow, amt, rows, cta, onCta, footerNote }) => {
  const [gate, setGate] = useState(false);
  return (
  <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
    <div className="scroll" style={{ padding: "10px 20px" }}>
      <Eyebrow>{eyebrow}</Eyebrow>
      {amt != null && <div style={{ marginTop: 6 }}><Money value={amt} size={40} weight={500} /></div>}
      <div style={{ marginTop: amt != null ? 18 : 12, border: "1px solid var(--rule)" }}>{rows}</div>
      {footerNote && (
        <div style={{ display: "flex", gap: 8, marginTop: 12, padding: "10px 12px", background: "var(--bg-2)", alignItems: "center" }}>
          <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} />
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", lineHeight: 1.45 }}>{footerNote}</span>
        </div>
      )}
    </div>
    <div style={{ flex: "none", padding: "0 20px" }}><Seam style={{ marginBottom: 12 }} /></div>
    <div style={{ flex: "none", padding: "0 20px 30px" }}><Btn full onClick={() => setGate(true)}>{cta}</Btn></div>
    {gate && <PasskeyGate title={eyebrow} amount={amt} detail="Use your passkey to authorize this transfer. Yoshi moves it immediately after." cta="Authorize with passkey" onSuccess={() => { setGate(false); onCta(); }} onCancel={() => setGate(false)} />}
  </div>
  );
};

// a selectable account / option row used across flows
const PickRow = ({ glyph, glyphColor, accent, name, sub, right, selected, onClick, last }) => (
  <button className="press" onClick={onClick} style={{
    width: "100%", display: "grid", gridTemplateColumns: glyph ? "38px 1fr auto" : "1fr auto", gap: 12, alignItems: "center",
    padding: "12px 16px", background: selected ? "var(--bg-2)" : "var(--bg-card)", border: "none",
    borderBottom: last ? "none" : "1px solid var(--rule)", textAlign: "left", cursor: "pointer",
    boxShadow: selected ? "inset 2px 0 0 var(--accent)" : "none",
  }}>
    {glyph && (
      <span style={{ width: 38, height: 38, display: "grid", placeItems: "center", background: accent ? "var(--accent)" : "var(--ink)", color: accent ? "var(--accent-ink)" : "var(--bg-card)", fontFamily: "var(--f-display)", fontSize: typeSize(glyph.length > 2 ? 9 : 15), fontWeight: 700, letterSpacing: glyph.length > 2 ? "0.04em" : 0 }}>{glyph}</span>
    )}
    <div style={{ minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{name}</div>
      {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sub}</div>}
    </div>
    {right !== undefined ? right : <Icon name={selected ? "check" : "back"} size={16} stroke={selected ? 2.2 : 1.5} color={selected ? "var(--accent)" : "var(--ink-3)"} style={selected ? {} : { transform: "scaleX(-1)" }} />}
  </button>
);

const SectionLabel = ({ children, style }) => (
  <div style={{ padding: "16px 18px 7px", ...style }}><Eyebrow>{children}</Eyebrow></div>
);

// shared chrome for a rail screen
const RailScreen = ({ title, sub, onBack, onClose, children, left, padX }) => (
  <div className="push-enter" style={{ position: "absolute", inset: 0, background: "var(--bg)", display: "flex", flexDirection: "column", zIndex: 2 }}>
    {window.StatusBar && <window.StatusBar />}
    {window.__YOSHI_WEB && <div style={{ height: 16, flex: "none" }} />}
    <NavBar padX={padX} title={window.__YOSHI_WEB ? <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(21), fontWeight: 700, letterSpacing: "-0.025em" }}>{title}</span> : title} sub={sub} onBack={window.__YOSHI_WEB ? undefined : onBack} left={left} border={!window.__YOSHI_WEB}
      right={window.__YOSHI_WEB
        ? (onBack ? <button className="press" onClick={onBack} aria-label="Close" style={{ background: "none", border: "none", color: "var(--ink-3)", display: "flex", cursor: "pointer" }}><Icon name="close" size={20} /></button> : undefined)
        : <button className="press" onClick={onClose} style={{ background: "none", border: "none", color: "var(--ink-3)", display: "flex" }}><Icon name="close" size={20} /></button>} />
    {children}
  </div>
);

/* ========================================================================== *
   The hub + router
 * ========================================================================== */
const TransferFlow = ({ onClose, preset, nav, flash, intent, initialRail, mock, onRailChange, rootTitle }) => {
  const [rail, setRail] = useState(initialRail || null);
  React.useEffect(() => { onRailChange && onRailChange(rail); }, [rail]);
  const back = () => setRail(null);
  const f = flash || (() => {});

  const askAuto = () => {
    onClose();
    nav.ask("Help me set up an automation.",
      "Love it. Tell me the rule in plain words, like “sweep anything over $20,000.00 in Checking into Savings” or “buy $500.00 of VTI every payday”, and I’ll set it up as a standing automation, inside the caps you’ve set. You approve it once, then it runs on its own and you can pause it anytime.");
  };
  const askRecurring = () => {
    onClose();
    nav.ask("Set up a recurring transfer.",
      "Happy to. Tell me the move in plain words, like “transfer $500.00 from Chase to Cash every payday” or “send $1,000.00 to Savings on the 1st”, and I’ll set it up as a standing automation you approve once. It runs on its own inside your caps, and you can pause it anytime.");
  };
  const open = (id) => {
    if (id === "automation") return askAuto();
    if (id === "recurring") return askRecurring();
    setRail(id);
  };

  const railProps = { onBack: back, onClose, nav, flash: f, preset, switchRail: open };
  const web = window.__YOSHI_WEB;
  const activeRail = (
    <>
      {rail === "wire" && <WireFlow {...railProps} />}
      {rail === "check" && <CheckFlow {...railProps} />}
      {rail === "sendcheck" && <CheckPayFlow {...railProps} />}
      {rail === "instant" && <InstantFlow {...railProps} />}
      {rail === "directdeposit" && <DirectDepositFlow {...railProps} />}
      {rail === "recurring" && <RecurringFlow {...railProps} />}
      {rail === "billpay" && <BillPayFlow {...railProps} />}
    </>
  );

  return (
    <div style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)" }} data-screen-label="Transfer">
      {/* the cash-transfer screen is the landing page; the other rails sit below Confirm */}
      <BankFlow root onClose={onClose} nav={nav} flash={f} preset={preset} mock={mock} rootTitle={rootTitle}
        initialDir={intent === "withdraw" ? "out" : "in"}
        railsFooter={RAILS_MORE.filter((r) => !(window.__YOSHI_WEB && r.id === "check")).map((r, i, a) => <RailRow key={r.id} r={r} onClick={() => open(r.id)} last={i === a.length - 1} />)} />

      {/* on web a chosen rail opens as a raised card centered on a darker field,
          mirroring the Automations / Briefs reading pane; mobile pushes full-screen */}
      {rail && (web ? (
        <div style={{ position: "absolute", inset: 0, zIndex: 4, display: "flex", justifyContent: "center", background: "var(--bg-2)" }}>
          <div style={{ position: "relative", width: "100%", maxWidth: 720, display: "flex", flexDirection: "column", minHeight: 0, margin: "18px 22px", background: "var(--bg)", border: "1px solid var(--rule-2)", borderRadius: 14, overflow: "hidden", boxShadow: "0 22px 48px -20px rgba(0,0,0,0.4), 0 4px 14px -8px rgba(0,0,0,0.2)" }}>
            {activeRail}
          </div>
        </div>
      ) : activeRail)}
    </div>
  );
};

const RailRow = ({ r, onClick, last }) => (
  <button className="press" onClick={onClick} style={{
    width: "100%", display: "grid", gridTemplateColumns: "26px 1fr auto", gap: 15, alignItems: "center",
    padding: "15px 18px", background: "none", border: "none", borderBottom: last ? "none" : "1px solid var(--rule)", textAlign: "left", cursor: "pointer",
  }}>
    <Icon name={r.icon} size={22} stroke={1.5} color="var(--ink)" />
    <div style={{ minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14.5), fontWeight: 500 }}>{r.name}</div>
      {r.promo && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, color: "var(--accent-pos)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{r.promo}</div>}
      {r.sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{r.sub}</div>}
    </div>
    <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
  </button>
);

/* ---------- an inline account selector (collapsed row → expandable list) --- */
const InlineSelect = ({ label, options, value, onChange, balances }) => {
  const [open, setOpen] = useState(false);
  const sel = options.find(o => o.id === value) || options[0];
  return (
    <div style={{ border: "1px solid var(--rule)" }}>
      <button className="press" onClick={() => setOpen(o => !o)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 14px", background: "var(--bg-card)", border: "none", cursor: "pointer", textAlign: "left" }}>
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", width: 38, flex: "none" }}>{label}</span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>{sel.name}</div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>{sel.sub}</div>
        </div>
        {balances && <Money value={sel.bal} size={11.5} color="var(--ink-3)" />}
        <Icon name={open ? "up" : "down"} size={15} color="var(--ink-3)" />
      </button>
      {open && (
        <div style={{ borderTop: "1px solid var(--rule)" }}>
          {options.map(o => (
            <PickRow key={o.id} name={o.name} sub={o.sub} selected={o.id === value}
              right={balances ? <Money value={o.bal} size={11} color="var(--ink-3)" /> : undefined}
              onClick={() => { onChange(o.id); setOpen(false); }} last />
          ))}
        </div>
      )}
    </div>
  );
};

/* ========================================================================== *
   1 · Between your accounts, instant book transfer
 * ========================================================================== */
const BetweenFlow = ({ onBack, onClose, preset }) => {
  const [step, setStep] = useState("setup");
  const [fromId, setFromId] = useState(YOSHI_ACCTS.find(a => a.id === preset) ? preset : "cash");
  const [toId, setToId] = useState("broker");
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const from = YOSHI_ACCTS.find(a => a.id === fromId);
  const to = YOSHI_ACCTS.find(a => a.id === toId);
  const over = amtNum > from.bal;

  // keep from/to distinct
  const setFrom = (id) => { setFromId(id); if (id === toId) setToId(YOSHI_ACCTS.find(a => a.id !== id).id); };
  const setTo = (id) => { setToId(id); if (id === fromId) setFromId(YOSHI_ACCTS.find(a => a.id !== id).id); };
  const swap = () => { setFromId(toId); setToId(fromId); };

  return (
    <RailScreen title="Between accounts" onBack={step === "setup" ? onBack : () => setStep(step === "review" ? "amount" : "setup")} onClose={onClose}>
      {step === "setup" && (
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
          <div className="scroll" style={{ padding: "14px 18px" }}>
            <InlineSelect label="From" options={YOSHI_ACCTS} value={fromId} onChange={setFrom} balances />
            <div style={{ display: "flex", justifyContent: "center", margin: "-1px 0" }}>
              <button className="press" onClick={swap} style={{ width: 34, height: 34, borderRadius: 999, background: "var(--bg-2)", border: "1px solid var(--rule-2)", display: "grid", placeItems: "center", cursor: "pointer", zIndex: 1 }}>
                <Icon name="swap" size={16} color="var(--ink)" />
              </button>
            </div>
            <InlineSelect label="To" options={YOSHI_ACCTS} value={toId} onChange={setTo} balances />
            <div style={{ display: "flex", alignItems: "center", gap: 7, marginTop: 14, padding: "10px 12px", background: "var(--bg-2)" }}>
              <Icon name="bolt" size={15} color="var(--accent)" />
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-2)" }}>Instant and free between your Yoshi accounts.</span>
            </div>
          </div>
          <div style={{ flex: "none", padding: "0 18px 30px" }}><Btn full onClick={() => setStep("amount")}>Enter amount <Icon name="arrow" size={18} color="var(--accent-ink)" /></Btn></div>
        </div>
      )}
      {step === "amount" && (
        <AmountBody amt={amt} amtNum={amtNum} onKey={onKey}
          context={<div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>{from.name} → <span style={{ color: "var(--ink)", fontWeight: 600 }}>{to.name}</span></div>}
          info={`${usd(from.bal)} available in ${from.name}`} error={over ? "Exceeds available balance" : null}
          ctaLabel="Review" ctaDisabled={!amtNum || over} onCta={() => setStep("review")} />
      )}
      {step === "review" && (
        <ReviewScreen eyebrow="You're moving" amt={amtNum} cta="Confirm · Passkey" onCta={() => setStep("done")}
          footerNote="Authorized by you. Yoshi moves it instantly between your accounts."
          rows={<>
            <ReviewRow label="From" value={from.name} sub={from.sub} />
            <ReviewRow label="To" value={to.name} sub={to.sub} />
            <ReviewRow label="Arrives" value="Instantly" accent="var(--accent-pos)" />
            <ReviewRow label="Fee" value="$0.00" last />
          </>} />
      )}
      {step === "done" && <MoveSuccess title={`${usd(amtNum)} moved`} sub={`From ${from.name} to ${to.name}, instantly. It's in your stream.`} onDone={onClose} />}
    </RailScreen>
  );
};

/* ========================================================================== *
   2 · Bank transfer · single-screen From → To, like a wire form
 * ========================================================================== */
const BankFlow = ({ onBack, onClose, preset, initialDir, root, railsFooter, nav, mock, rootTitle = "Transfer" }) => {
  // two-column form + "more ways" rail on wide web windows only
  const web = window.__YOSHI_WEB && window.innerWidth >= 1100;
  const [fromKey, setFromKey] = useState(() => initialDir === "out" ? "yoshi:cash" : "external:chase");
  const [toKey, setToKey] = useState(() => initialDir === "out" ? "external:chase" : "yoshi:cash");
  const [gate, setGate] = useState(false);
  const [pickFrom, setPickFrom] = useState(false);
  const [pickTo, setPickTo] = useState(false);
  const [linking, setLinking] = useState(false);
  const [selectedHoldingIds, setSelectedHoldingIds] = useState(() => defaultTransferHoldingIds(BROKERAGES[0].id));
  const [selectingInvestments, setSelectingInvestments] = useState(false);
  const [securitiesTransferRequested, setSecuritiesTransferActive] = useState(false);
  const [speed, setSpeed] = useState(() => initialDir === "out" ? "standard" : EXT_ACCTS.find(account => account.id === "chase")?.supportsInstant ? "instant" : "standard");
  const [pickSpeed, setPickSpeed] = useState(false);
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const [padOpen, setPadOpen] = useState(false);
  const [done, setDone] = useState(false);
  const [instantDepositHandoff, setInstantDepositHandoff] = useState(() => mock === "rtp-handoff");

  const yoshiTransferAccounts = YOSHI_ACCTS.map(account => ({
    key: `yoshi:${account.id}`,
    sourceId: account.id,
    side: "yoshi",
    kind: account.kind || (account.id === "broker" ? "investment" : "cash"),
    name: account.id === "broker" ? "Yoshi Brokerage" : "Yoshi Cash",
    bank: account.id === "broker" ? "Yoshi Brokerage" : "Yoshi Cash",
    sub: account.id === "broker" ? "Individual · ••3392" : "Yoshi · 3.30% APY",
    glyph: "logo",
    bal: account.bal,
    available: account.cashBal ?? account.bal,
    holdingsId: account.id === "broker" ? "yoshi-broker" : null,
    synced: account.id === "broker" ? "Investments" : "Cash",
  }));
  const externalDepositAccounts = EXT_ACCTS.map(account => ({
    key: `external:${account.id}`,
    sourceId: account.id,
    side: "external",
    kind: "depository",
    name: account.name,
    bank: account.bank,
    sub: account.displaySub,
    rawSub: account.sub,
    glyph: account.glyph,
    bal: account.bal,
    available: account.bal,
    synced: account.synced,
    supportsInstant: account.supportsInstant === true,
  }));
  const externalDebtAccounts = DEBT_ACCOUNTS.map(account => ({
    key: `debt:${account.id}`,
    sourceId: account.id,
    side: "external",
    kind: "debt",
    name: account.name,
    bank: account.name,
    sub: account.sub,
    glyph: account.glyph,
    bal: account.bal,
    available: 0,
    synced: "Debt",
    min: account.min,
  }));
  const externalInvestmentAccounts = BROKERAGES.map(account => ({
    key: `investment:${account.id}`,
    sourceId: account.id,
    side: "external",
    kind: "investment",
    name: account.name,
    bank: account.bank,
    sub: account.displaySub,
    rawSub: account.sub,
    glyph: account.glyph,
    bal: account.bal,
    available: account.cashBal ?? account.bal,
    holdingsId: account.id,
    synced: account.retirement ? "Retirement" : "Investments",
    retirement: account.retirement === true,
  }));
  const manualRecipientAccounts = MANUAL_RECIPIENTS.map(account => ({
    key: `manual:${account.id}`,
    sourceId: account.id,
    side: "manual",
    kind: "manual",
    name: account.name,
    bank: account.name,
    sub: account.sub,
    glyph: account.glyph,
    bal: null,
    available: 0,
    supportsInstant: account.supportsInstant,
  }));
  const externalTransferAccounts = [...externalDepositAccounts, ...externalInvestmentAccounts, ...externalDebtAccounts];
  const allTransferAccounts = [...externalTransferAccounts, ...yoshiTransferAccounts];
  const fromAccount = allTransferAccounts.find(account => account.key === fromKey) || externalTransferAccounts[0];
  const fromOptions = fromAccount.side === "external" ? externalTransferAccounts : yoshiTransferAccounts;
  const fromIsInvestment = fromAccount.kind === "investment";
  const holdings = fromIsInvestment ? transferHoldingsFor(fromAccount.holdingsId) : [];
  const selectedTransferHoldings = holdings.filter(h => h.transferable && selectedHoldingIds.includes(h.id));
  const selectedValue = selectedHoldingValue(selectedTransferHoldings);
  const selectedCount = selectedTransferHoldings.length;
  const selectedAssetSummary = selectedTransferSummary(selectedTransferHoldings);
  const securitiesTransferActive = fromIsInvestment && securitiesTransferRequested && selectedCount > 0;
  const investmentSelectionRequired = fromAccount.side === "yoshi" && fromAccount.kind === "investment";
  const cashTransferControlsVisible = !securitiesTransferActive && !investmentSelectionRequired;

  const accountDisabledReason = (account, role, source = fromAccount, securities = securitiesTransferActive) => {
    if (role === "from" && account.kind === "debt") return "To only";
    if (role === "from" && account.retirement) return "Non transferable";
    if (role === "to" && fromAccount.side === "yoshi" && account.retirement && source.side === "yoshi") return "Non transferable";
    if (role === "to" && source.side === "yoshi" && account.retirement) return "Non transferable";
    if (role === "to" && securities && account.kind === "cash") return "Cash only";
    if (role === "to" && securities && account.kind !== "investment") return "Cash only";
    if (role === "to" && !securities && source.side === "external" && account.side === "yoshi" && account.kind === "investment") return "Investments only";
    return null;
  };
  const toIncludesManualRecipients = fromAccount.key === "yoshi:cash" && !securitiesTransferActive;
  const baseToOptions = fromAccount.side === "external" ? yoshiTransferAccounts : investmentSelectionRequired ? externalInvestmentAccounts : toIncludesManualRecipients ? [...externalTransferAccounts, ...manualRecipientAccounts] : externalTransferAccounts;
  const toOptions = baseToOptions.filter(account => account.side !== "yoshi" || !accountDisabledReason(account, "to"));
  const toAccount = toOptions.find(account => account.key === toKey) || toOptions.find(account => !accountDisabledReason(account, "to")) || toOptions[0] || yoshiTransferAccounts[0];
  const externalAccountGroups = [
    { label: null, accounts: externalDepositAccounts },
    { label: "Investment accounts", accounts: externalInvestmentAccounts },
    { label: "Debt accounts", accounts: externalDebtAccounts },
    { label: "Manual recipients", accounts: manualRecipientAccounts },
  ];
  const yoshiAccountGroups = [{ label: null, accounts: yoshiTransferAccounts }];
  const groupedAccountOptions = (options) => {
    const optionKeys = new Set(options.map(account => account.key));
    const groups = options.some(account => account.side === "external") ? externalAccountGroups : yoshiAccountGroups;
    return groups
      .map(group => ({ ...group, accounts: group.accounts.filter(account => optionKeys.has(account.key)) }))
      .filter(group => group.accounts.length > 0);
  };
  const into = fromAccount.side === "external";
  const fromBal = securitiesTransferActive ? selectedValue : fromAccount.available;
  const isDebtPayment = !securitiesTransferActive && toAccount.kind === "debt";
  const isManualRecipient = !securitiesTransferActive && toAccount.kind === "manual";
  const inboundToYoshiCash = fromAccount.side === "external" && toAccount.side === "yoshi" && toAccount.kind === "cash";
  const manualDeliveryOptions = [
    { value: "standard", title: "ACH", sub: "1–3 business days", fee: "Free" },
    { value: "wire", title: "Wire", sub: "Same-day domestic", fee: "$15.00" },
    ...(toAccount.supportsInstant ? [{ value: "instant", title: "Instant", sub: "Arrives in seconds", fee: "1.50% fee" }] : []),
  ];
  const inboundDeliveryOptions = [
    { value: "standard", title: "Standard", sub: "1–3 business days", fee: "Free" },
    ...(fromAccount.supportsInstant ? [{ value: "instant", title: "Instant", sub: "Arrives in seconds", fee: "Free" }] : []),
  ];
  const cashDeliveryOptions = [
    { value: "standard", title: "Standard", sub: "1–3 business days", fee: "Free" },
    { value: "instant", title: "Instant", sub: "Arrives in seconds", fee: into ? "Free" : "1.50% fee" },
    { value: "wire", title: "Wire", sub: "Same-day domestic", fee: "$15.00" },
  ];
  const activeDeliveryOptions = isManualRecipient ? manualDeliveryOptions : inboundToYoshiCash ? inboundDeliveryOptions : cashDeliveryOptions;
  const effectiveSpeed = activeDeliveryOptions.some(option => option.value === speed) ? speed : activeDeliveryOptions[0]?.value || "standard";
  const selectedDeliveryOption = activeDeliveryOptions.find(option => option.value === effectiveSpeed) || activeDeliveryOptions[0] || cashDeliveryOptions[0];
  const selectedManualDelivery = manualDeliveryOptions.find(option => option.value === speed) || manualDeliveryOptions[0];
  const manualRecipientDisclosure = MANUAL_RECIPIENT_DISCLOSURES[speed] || MANUAL_RECIPIENT_DISCLOSURES.standard;
  const debtMinPayment = toAccount.min ?? Math.max(35, Math.round(toAccount.bal * 0.02));
  const debtMaxPayment = Math.min(toAccount.bal, fromBal);
  const transferLimit = isDebtPayment ? debtMaxPayment : fromBal;
  const debtAmountDisplay = amt === "" ? "" : isDebtPayment ? usd(amtNum).replace(/^\$/, "") : commaAmt(amt);
  const over = cashTransferControlsVisible && amtNum > transferLimit;
  const amountShortcutButtonStyle = { flex: "none", padding: "6px 13px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, color: "var(--ink)", cursor: "pointer" };
  const amountClearButtonStyle = { flex: "none", width: 30, height: 30, padding: 0, background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, display: "grid", placeItems: "center", cursor: "pointer" };
  const fee = effectiveSpeed === "wire" ? 15 : effectiveSpeed === "instant" && fromAccount.side === "yoshi" ? Math.max(0.25, amtNum * 0.015) : 0;
  const arrival = effectiveSpeed === "wire" ? "Same-day · by 2pm PT" : effectiveSpeed === "instant" ? "In seconds" : "1–3 business days";
  const speedLabel = selectedDeliveryOption.title;
  const deliveryFeeLabel = isManualRecipient ? selectedManualDelivery.fee : fee ? usd(fee) : selectedDeliveryOption.fee;
  const investmentTransferDetailsVisible = securitiesTransferActive || investmentSelectionRequired;
  const acatDisclosureRows = fromAccount.side === "yoshi" ? [...ACAT_DISCLOSURES, OUTBOUND_ACAT_FEE_DISCLOSURE] : ACAT_DISCLOSURES;
  const showTransferConfirm = securitiesTransferActive || cashTransferControlsVisible || investmentSelectionRequired;
  const needsOriginatingBankApproval = cashTransferControlsVisible && inboundToYoshiCash && effectiveSpeed === "instant";
  const cashTransferConfirmNote = needsOriginatingBankApproval
    ? `${usd(amtNum)} request will go to ${fromAccount.bank || fromAccount.name}. Approve it in that bank app to complete the instant deposit.`
    : `${usd(amtNum)} will move from ${fromAccount.name} to ${toAccount.name}. ${arrival}.`;

  const defaultDeliverySpeedFor = (source, destination) => (
    source.side === "external" && destination.side === "yoshi" && destination.kind === "cash" && source.supportsInstant ? "instant" : "standard"
  );

  const defaultToAccountFor = (source, securities = false) => {
    const sourceToOptions = source.side === "external" ? yoshiTransferAccounts : source.side === "yoshi" && source.kind === "investment" ? externalInvestmentAccounts : externalTransferAccounts;
    const sourceRequiresInvestments = source.side === "yoshi" && source.kind === "investment";
    const eligibleOptions = sourceToOptions.filter(account => !securities && !sourceRequiresInvestments || account.kind === "investment");
    return eligibleOptions.find(account => !accountDisabledReason(account, "to", source, securities)) || eligibleOptions[0] || yoshiTransferAccounts[0];
  };
  const chooseFromAccount = (account) => {
    if (accountDisabledReason(account, "from", account, false)) return;
    setFromKey(account.key);
    setPickFrom(false);
    setPickTo(false);
    setSecuritiesTransferActive(false);
    if (account.kind === "investment") {
      setSelectedHoldingIds(defaultTransferHoldingIds(account.holdingsId));
    }
    const nextTo = defaultToAccountFor(account, false);
    setToKey(nextTo.key);
    setSpeed(defaultDeliverySpeedFor(account, nextTo));
  };
  const chooseToAccount = (account) => {
    if (accountDisabledReason(account, "to")) return;
    setToKey(account.key);
    setSpeed(defaultDeliverySpeedFor(fromAccount, account));
    setPickTo(false);
    setPickFrom(false);
  };
  const swapTransferDirection = () => {
    const fromCandidate = toAccount;
    const nextFrom = accountDisabledReason(fromCandidate, "from", fromCandidate, false)
      ? (fromAccount.side === "external" ? yoshiTransferAccounts[0] : externalDepositAccounts[0])
      : fromCandidate;
    const nextTo = fromAccount.side !== nextFrom.side && !accountDisabledReason(fromAccount, "to", nextFrom, false)
      ? fromAccount
      : defaultToAccountFor(nextFrom, false);
    setFromKey(nextFrom.key);
    setToKey(nextTo.key);
    setSpeed(defaultDeliverySpeedFor(nextFrom, nextTo));
    setPickFrom(false);
    setPickTo(false);
    setSecuritiesTransferActive(false);
    if (nextFrom.kind === "investment") setSelectedHoldingIds(defaultTransferHoldingIds(nextFrom.holdingsId));
  };
  const toggleHolding = (id) => {
    setSelectedHoldingIds(ids => ids.includes(id) ? ids.filter(x => x !== id) : [...ids, id]);
  };
  const finishSelectingInvestments = () => {
    const hasSelection = selectedTransferHoldings.length > 0;
    setSecuritiesTransferActive(hasSelection);
    setSelectingInvestments(false);
    if (hasSelection && (toAccount.side === fromAccount.side || toAccount.kind !== "investment" || accountDisabledReason(toAccount, "to", fromAccount, true))) {
      setToKey(defaultToAccountFor(fromAccount, true).key);
    }
  };
  const confirmCashTransfer = () => {
    setGate(false);
    if (needsOriginatingBankApproval) {
      setInstantDepositHandoff(true);
      return;
    }
    setDone(true);
  };

  if (selectingInvestments && fromIsInvestment) return (
    <InvestmentSelectionScreen firm={fromAccount} holdings={holdings} selectedIds={selectedHoldingIds}
      onToggle={toggleHolding} onBulk={setSelectedHoldingIds} onBack={finishSelectingInvestments} onClose={onClose} />
  );
  if (linking) return <AddExternal onBack={() => setLinking(false)} onClose={onClose} onSaved={() => setLinking(false)} />;
  if (instantDepositHandoff) return (
    <RailScreen padX={web ? 64 : 16} title="" onClose={onClose}>
      <InstantDepositHandoffScreen source={fromAccount} onConfirmed={() => { setInstantDepositHandoff(false); setDone(true); }} />
    </RailScreen>
  );
  if (done) return (
    <RailScreen padX={web ? 64 : 16} title={root ? rootTitle : into ? "Deposit" : "Withdraw"} onBack={root ? undefined : onBack} onClose={onClose}>
      {securitiesTransferActive
        ? <MoveSuccess title="Transfer started" sub={`We've kicked off the ACATS transfer of ${selectedAssetSummary} from ${fromAccount.name} into ${toAccount.name}. It settles in about a week and you'll see the selected assets land in your stream.`} onDone={onClose} />
        : <MoveSuccess title={`${usd(amtNum)} ${into ? "on the way in" : "sent"}`} sub={`${fromAccount.name} → ${toAccount.name}. ${arrival}. We'll post it to your stream when it settles.`} onDone={onClose} />}
    </RailScreen>
  );

  // one account slot · From can be cash or investment; To switches with it
  const AccountCard = ({ role }) => {
    const account = role === "from" ? fromAccount : toAccount;
    const open = role === "from" ? pickFrom : pickTo;
    const options = role === "from" ? fromOptions : toOptions;
    const accountGroups = groupedAccountOptions(options);
    const setOpen = role === "from" ? setPickFrom : setPickTo;
    const setOtherOpen = role === "from" ? setPickTo : setPickFrom;
    const showAddAccountRow = options.some(account => account.side === "external");
    const canChangeAccount = options.length > 1;
    let optionRowIndex = 0;
    return (
    <div style={{ position: "relative", zIndex: open && canChangeAccount ? 6 : 1 }}>
      <button className="press" onClick={canChangeAccount ? () => { setOpen(o => !o); setOtherOpen(false); } : undefined} style={{
        width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 14px",
        background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, cursor: canChangeAccount ? "pointer" : "default", textAlign: "left"
      }}>
        <span style={{ width: 36, height: 36, flex: "none", display: "grid", placeItems: "center",
          background: "var(--bg-2)", color: "var(--ink)", fontFamily: "var(--f-display)", fontWeight: 700, fontSize: typeSize(15) }}>
          {account.glyph === "logo" ? <Logo size={20} /> : account.glyph}
        </span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{account.name}</div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>{account.sub}</div>
        </div>
        <div style={{ textAlign: "right", flex: "none" }}>
          {account.kind !== "manual" && (
            <>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13.5), fontVariantNumeric: "tabular-nums" }}>{usd(account.bal)}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 3 }}>{account.synced}</div>
            </>
          )}
        </div>
        {canChangeAccount && <Icon name="down" size={16} color="var(--ink-3)" style={{ flex: "none" }} />}
      </button>
      {open && canChangeAccount && (
        <div style={{ position: "absolute", left: 0, right: 0, top: "calc(100% + 4px)", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, boxShadow: "0 14px 30px -14px rgba(0,0,0,0.35)", overflow: "hidden" }}>
          {accountGroups.map(group => (
            <React.Fragment key={group.label || "accounts"}>
              {group.label && (
                <div style={{ padding: "11px 14px 6px", borderTop: optionRowIndex ? "1px solid var(--rule)" : "none", background: "color-mix(in srgb, var(--ink) 3%, var(--bg-card))" }}>
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 650, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--ink-3)" }}>{group.label}</div>
                </div>
              )}
              {group.accounts.map((a) => {
                const disabledReason = accountDisabledReason(a, role);
                const selected = a.key === account.key;
                const rowIsFirst = optionRowIndex === 0;
                optionRowIndex += 1;
                return (
                <button key={a.key} className="press" disabled={!!disabledReason} onClick={() => role === "from" ? chooseFromAccount(a) : chooseToAccount(a)} style={{
                  width: "100%", display: "flex", alignItems: "center", gap: 11, padding: "11px 14px",
                  background: selected ? "var(--bg-2)" : disabledReason ? "color-mix(in srgb, var(--ink) 4%, var(--bg-card))" : "none",
                  border: "none", borderTop: rowIsFirst || group.label ? "none" : "1px solid var(--rule)", cursor: disabledReason ? "not-allowed" : "pointer", textAlign: "left", opacity: disabledReason ? 0.48 : 1,
                }}>
                  <span style={{ width: 30, height: 30, flex: "none", border: "1px solid var(--rule-2)", color: "var(--ink)", display: "grid", placeItems: "center", fontFamily: "var(--f-display)", fontWeight: 700, fontSize: typeSize(a.glyph && a.glyph.length > 2 ? 9 : 13) }}>{a.glyph === "logo" ? <Logo size={17} /> : a.glyph}</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{a.name}</div>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{a.sub}</div>
                  </div>
                  {disabledReason
                    ? <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", textTransform: "uppercase", letterSpacing: "0.06em" }}>{disabledReason}</span>
                    : a.kind === "manual"
                      ? null
                    : <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--ink-3)" }}>{usd(a.bal)}</span>}
                </button>
              );})}
            </React.Fragment>
          ))}
          {showAddAccountRow && <button className="press" onClick={() => { setOpen(false); setOtherOpen(false); setLinking(true); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 11, padding: "11px 14px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
            <span style={{ width: 30, height: 30, flex: "none", border: "1px dashed var(--rule-2)", display: "grid", placeItems: "center" }}><Icon name="plus" size={15} color="var(--ink-2)" /></span>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 500 }}>Add account</span>
          </button>}
        </div>
      )}
    </div>
  );};
  const investmentEditButtonStyle = { flex: "none", display: "inline-flex", alignItems: "center", justifyContent: "center", minWidth: 66, height: web ? 38 : 28, boxSizing: "border-box", padding: "0 12px", background: "color-mix(in srgb, var(--ink) 4%, var(--bg-card))", border: "1px solid var(--rule-2)", borderRadius: 9, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(web ? 13 : 11), fontWeight: 600, color: "var(--ink)" };

  return (
    <RailScreen padX={web ? 64 : 16} title={root ? rootTitle : into ? "Deposit" : "Withdraw"} onBack={root ? undefined : onBack} onClose={onClose}>
      <div className="scroll" style={{ padding: web ? "20px 64px 28px" : "16px 18px 28px" }}>
        <div style={web ? { display: "grid", gridTemplateColumns: "repeat(2, minmax(280px, 1fr))", gap: 40, alignItems: "start" } : undefined}>
        <div>
        <Eyebrow style={{ marginBottom: 7 }}>From</Eyebrow>
        <AccountCard role="from" />

        <div style={{ position: "relative", height: 42, margin: "0" }}>
          <Eyebrow style={{ position: "absolute", left: 0, top: "50%", transform: "translateY(-50%)" }}>To</Eyebrow>
          {!securitiesTransferActive && <button className="press" onClick={swapTransferDirection} aria-label="Switch direction" style={{ position: "absolute", left: "50%", top: "50%", transform: "translate(-50%, -50%)", background: "none", border: "none", padding: 6, display: "grid", placeItems: "center", cursor: "pointer" }}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--ink)" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
              <path d="M8.5 4 V19 M8.5 19 L5.5 16 M8.5 19 L11.5 16" />
              <path d="M15.5 20 V5 M15.5 5 L12.5 8 M15.5 5 L18.5 8" />
            </svg>
          </button>}
        </div>
        <AccountCard role="to" />

        {!securitiesTransferActive && fromIsInvestment && (
          <>
            <Eyebrow style={{ margin: "18px 0 7px" }}>Investments</Eyebrow>
            <button className="press" onClick={() => setSelectingInvestments(true)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, cursor: "pointer", textAlign: "left" }}>
              <span style={{ width: 36, height: 36, flex: "none", display: "grid", placeItems: "center", background: "var(--bg-2)" }}><Icon name="trade" size={18} color="var(--ink)" /></span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>Add investments</div>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>{investmentSelectionRequired ? "Select securities to transfer" : "Optional · transfer cash and securities"}</div>
              </div>
              <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
            </button>
          </>
        )}

        {securitiesTransferActive && (
          <>
            <Eyebrow style={{ margin: "18px 0 7px" }}>Amount</Eyebrow>
            <button className="press" onClick={() => setSelectingInvestments(true)} aria-label="Edit selected investments" style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, minHeight: 50, padding: "11px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, cursor: "pointer", color: "var(--ink)", textAlign: "left" }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                {selectedCount ? <Money value={selectedValue} size={20} weight={500} /> : <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, color: "var(--ink-3)" }}>Select investments</span>}
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>{selectedCount ? `${selectedAssetSummary} selected` : "Choose at least one transferable holding"}</div>
              </div>
              <span style={investmentEditButtonStyle}>Edit</span>
            </button>
            {selectedCount > 0 && (
              <div style={{ marginTop: 8, border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
                {selectedTransferHoldings.slice(0, 3).map((h, i) => (
                  <div key={h.id} style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 10, padding: "10px 12px", borderBottom: i === Math.min(3, selectedTransferHoldings.length) - 1 && selectedTransferHoldings.length <= 3 ? "none" : "1px dashed var(--rule)" }}>
                    <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600 }}>{h.symbol}</span>
                    <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{holdingShareText(h)}</span>
                    <Money value={h.value} size={11.5} color="var(--ink-2)" />
                  </div>
                ))}
                {selectedTransferHoldings.length > 3 && (
                  <div style={{ padding: "10px 12px", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", textAlign: "center" }}>+{selectedTransferHoldings.length - 3} more holdings included</div>
                )}
              </div>
            )}
          </>
        )}

        {investmentTransferDetailsVisible && (
          <>
            <Eyebrow style={{ margin: "20px 0 8px" }}>Additional details</Eyebrow>
            <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
              {acatDisclosureRows.map(([ic, t, d], i) => (
                <div key={t} style={{ display: "flex", gap: 11, alignItems: "center", padding: "12px 14px", borderBottom: i === acatDisclosureRows.length - 1 ? "none" : "1px solid var(--rule)" }}>
                  <Icon name={ic} size={16} color="var(--ink-3)" stroke={1.6} style={{ flex: "none" }} />
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600 }}>{t}</div>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3, lineHeight: 1.45 }}>{d}</div>
                  </div>
                </div>
              ))}
            </div>
          </>
        )}

        {cashTransferControlsVisible && (
          <>
            <Eyebrow style={{ margin: "18px 0 7px" }}>Amount <span style={{ textTransform: "none", letterSpacing: 0, color: "var(--ink-3)", fontWeight: 500 }}>({usd(fromBal)} available)</span></Eyebrow>
            <div onClick={() => setPadOpen(true)} style={{ display: "flex", alignItems: "center", gap: 9, minHeight: 50, padding: "0 14px", background: "var(--bg-card)", border: `1px solid ${over ? "var(--signal-neg)" : "var(--rule-2)"}`, borderRadius: 12, cursor: "pointer" }}>
              <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(18), color: amtNum ? "var(--ink-3)" : "var(--rule-2)" }}>$</span>
              <input readOnly inputMode="none" value={debtAmountDisplay} onFocus={() => setPadOpen(true)} placeholder="Enter amount"
                style={{ flex: 1, minWidth: 0, border: "none", background: "transparent", outline: "none", color: "var(--ink)", fontFamily: "var(--f-mono)", fontSize: typeSize(18), fontVariantNumeric: "tabular-nums", cursor: "pointer" }} />
              <div style={{ display: "flex", gap: 6, flex: "none" }}>
                {amt === "" ? (
                  <>
                    {isDebtPayment && <button className="press" onClick={(e) => { e.stopPropagation(); setAmt(String(debtMinPayment)); }} style={amountShortcutButtonStyle}>Min</button>}
                    <button className="press" onClick={(e) => { e.stopPropagation(); setAmt(String(isDebtPayment ? debtMaxPayment : fromBal)); }} style={amountShortcutButtonStyle}>Max</button>
                  </>
                ) : (
                  <button className="press" aria-label="Clear amount" onClick={(e) => { e.stopPropagation(); setAmt(""); }} style={amountClearButtonStyle}>
                    <Icon name="close" size={14} color="var(--ink-2)" />
                  </button>
                )}
              </div>
            </div>
            {over && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--signal-neg)", marginTop: 6 }}>Exceeds maximum payment</div>}

            {isDebtPayment && (
              <>
                <Eyebrow style={{ margin: "20px 0 8px" }}>Additional details</Eyebrow>
                <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
                  {DEBT_DISCLOSURES.map(([ic, t], i) => (
                    <div key={t} style={{ display: "flex", gap: 11, alignItems: "center", padding: "12px 14px", borderBottom: i === DEBT_DISCLOSURES.length - 1 ? "none" : "1px solid var(--rule)" }}>
                      <Icon name={ic} size={16} color="var(--ink-3)" stroke={1.6} style={{ flex: "none" }} />
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600 }}>{t}</div>
                      </div>
                    </div>
                  ))}
                </div>
              </>
            )}

            {!isDebtPayment && (
              <>
                <Eyebrow style={{ margin: "18px 0 7px" }}>Delivery</Eyebrow>
                <div style={{ border: "1px solid var(--rule-2)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)" }}>
                  <button className="press" onClick={() => setPickSpeed(o => !o)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, minHeight: 50, padding: "0 14px", background: "var(--bg-card)", border: "none", cursor: "pointer", textAlign: "left" }}>
                    <div style={{ flex: 1 }}>
                      <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>{speedLabel}</span>
                      <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), color: "var(--ink-3)", marginLeft: 7 }}>{selectedDeliveryOption.sub}</span>
                    </div>
                    <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12.5), color: deliveryFeeLabel === "Free" ? "var(--accent-pos)" : "var(--ink-2)" }}>{deliveryFeeLabel}</span>
                    <Icon name={pickSpeed ? "up" : "down"} size={15} color="var(--ink-3)" style={{ flex: "none" }} />
                  </button>
                  {pickSpeed && (
                    <div style={{ borderTop: "1px solid var(--rule)" }}>
                      {activeDeliveryOptions.map((option, i) => (
                        <SpeedCard key={option.value} on={effectiveSpeed === option.value} onClick={() => { setSpeed(option.value); setPickSpeed(false); }} title={option.title} sub={option.sub} fee={option.fee} last={i === activeDeliveryOptions.length - 1} />
                      ))}
                    </div>
                  )}
                </div>

                {isManualRecipient && (
                  <>
                    <Eyebrow style={{ margin: "20px 0 8px" }}>Additional details</Eyebrow>
                    <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
                      <div style={{ display: "flex", gap: 11, alignItems: "center", padding: "12px 14px" }}>
                        <Icon name={manualRecipientDisclosure[0]} size={16} color="var(--ink-3)" stroke={1.6} style={{ flex: "none" }} />
                        <div style={{ minWidth: 0 }}>
                          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600 }}>{manualRecipientDisclosure[1]}</div>
                          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3, lineHeight: 1.45 }}>{manualRecipientDisclosure[2]}</div>
                        </div>
                      </div>
                    </div>
                  </>
                )}
              </>
            )}
          </>
        )}

        {showTransferConfirm && (
          <div style={{ marginTop: 22 }}>
            <Seam style={{ marginBottom: 16 }} />
            {cashTransferControlsVisible && !isDebtPayment && amtNum > 0 && !over && (
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginBottom: 12, lineHeight: 1.5, textAlign: "center" }}>
                {cashTransferConfirmNote}
              </div>
            )}
            {investmentTransferDetailsVisible
              ? <Btn full disabled={!securitiesTransferActive || !selectedCount} onClick={() => setGate(true)}>Confirm with passkey</Btn>
              : <Btn full disabled={!amtNum || over} onClick={() => setGate(true)}>Confirm with passkey</Btn>}
          </div>
        )}
        {gate && securitiesTransferActive && <PasskeyGate title="Transfer investments" amount={selectedValue} detail={`Yoshi transfers ${selectedAssetSummary} from ${fromAccount.name} once you authorize.`} cta="Authorize with passkey" onSuccess={() => { setGate(false); setDone(true); }} onCancel={() => setGate(false)} />}
        {gate && cashTransferControlsVisible && !securitiesTransferActive && <PasskeyGate title={into ? "Deposit" : isDebtPayment ? `Pay ${toAccount.name}` : "Withdraw"} amount={amtNum}
          detail={needsOriginatingBankApproval
            ? `Yoshi will send an instant transfer request to ${fromAccount.bank || fromAccount.name}. Approve it in your bank app to complete the deposit.`
            : `${fromAccount.name} to ${toAccount.name}. ${arrival}.`}
          cta="Authorize with passkey" onSuccess={confirmCashTransfer} onCancel={() => setGate(false)} />}

        </div>

        {/* other ways to move money — stacked on phone, a right rail on web */}
        {railsFooter && (web ? (
          <div style={{ borderLeft: "1px solid var(--rule)", paddingLeft: 28, alignSelf: "stretch", display: "flex", flexDirection: "column" }}>
            <div style={{ padding: "2px 0 10px" }}><Eyebrow>More ways to move money</Eyebrow></div>
            <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>{railsFooter}</div>
          </div>
        ) : (
          <div style={{ margin: "26px -18px 0", borderTop: "1px solid var(--rule)" }}>
            <div style={{ padding: "16px 18px 4px" }}><Eyebrow>More ways to move money</Eyebrow></div>
            {railsFooter}
          </div>
        ))}
        </div>
        {cashTransferControlsVisible && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: web ? 28 : 18, lineHeight: 1.5, textAlign: "center" }}>
          <a href="https://yoshi.ai/cash-account-disclosures/" target="_blank" rel="noreferrer" style={{ color: "inherit", textDecoration: "underline", textUnderlineOffset: 2 }}>
            Authorization terms and conditions
          </a>
        </div>}
      </div>
      <KeypadSheet open={padOpen && cashTransferControlsVisible} onClose={() => setPadOpen(false)} amt={amt} amtNum={amtNum} onKey={onKey}
        label="Amount" sub={over ? null : isDebtPayment ? `${usd(debtMaxPayment)} max` : `${usd(fromBal)} available`} error={over ? "Exceeds maximum payment" : null}
        onMax={() => setAmt(String(isDebtPayment ? debtMaxPayment : fromBal))} ctaLabel="Done" />
    </RailScreen>
  );
};

const TapCard = ({ icon, title, sub, onClick }) => (
  <button className="press" onClick={onClick} style={{ display: "flex", alignItems: "center", gap: 13, padding: "15px 15px", background: "var(--bg-card)", border: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
    <span style={{ width: 36, height: 36, flex: "none", borderRadius: 999, background: "var(--bg-2)", display: "grid", placeItems: "center" }}><Icon name={icon} size={18} color="var(--ink)" /></span>
    <div style={{ flex: 1 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600 }}>{title}</div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>{sub}</div>
    </div>
    <Icon name="back" size={16} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
  </button>
);

const SpeedCard = ({ on, onClick, title, sub, fee, last }) => (
  <button className="press" onClick={onClick} style={{ width: "100%", display: "flex", alignItems: "center", gap: 13, padding: "12px 15px", background: on ? "color-mix(in srgb, var(--accent) 10%, var(--bg-card))" : "var(--bg-card)", border: "none", borderBottom: last ? "none" : "1px solid var(--rule)", boxShadow: on ? "inset 2px 0 0 var(--accent)" : "none", cursor: "pointer", textAlign: "left" }}>
    <div style={{ flex: 1 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14.5), fontWeight: 600 }}>{title}</div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>{sub}</div>
    </div>
    <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12.5), fontWeight: 500, color: fee === "Free" ? "var(--accent-pos)" : "var(--ink-2)" }}>{fee}</span>
  </button>
);

/* ---- add an external account manually (account # + routing) --------------- */
const AddExternal = ({ onBack, onClose, onSaved }) => {
  const [name, setName] = useState("");
  const [acct, setAcct] = useState("");
  const [routing, setRouting] = useState("");
  const [addressLine1, setAddressLine1] = useState("");
  const [addressLine2, setAddressLine2] = useState("");
  const [city, setCity] = useState("");
  const [state, setState] = useState("");
  const [zip, setZip] = useState("");
  const valid = name.trim() && acct.trim().length >= 4 && routing.length === 9 && addressLine1.trim() && city.trim() && state.trim().length === 2 && zip.trim().length >= 5;
  const addExternalContentStyle = { width: "100%", maxWidth: 430, margin: "0 auto" };
  const addExternalShellStyle = { position: "absolute", inset: 0, zIndex: 2, display: "flex", justifyContent: "center", background: "var(--bg-2)" };
  const addExternalPanelStyle = { position: "relative", width: "100%", maxWidth: 720, display: "flex", flexDirection: "column", minHeight: 0, margin: "18px 22px", background: "var(--bg)", border: "1px solid var(--rule-2)", borderRadius: 14, overflow: "hidden", boxShadow: "0 22px 48px -20px rgba(0,0,0,0.4), 0 4px 14px -8px rgba(0,0,0,0.2)" };
  const addExternalScreen = (
    <RailScreen title="Add account" onBack={onBack} onClose={onClose}>
      <div className="scroll" style={{ padding: "14px 18px" }}>
        <div style={addExternalContentStyle}>
          <button className="press" onClick={onSaved} style={{ width: "100%", display: "flex", alignItems: "center", justifyContent: "center", gap: 8, padding: "13px", background: "var(--accent)", border: "none", borderRadius: 10, cursor: "pointer" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, color: "var(--accent-ink)" }}>Connect instantly with Plaid</span>
          </button>
          <div style={{ display: "flex", alignItems: "center", gap: 10, margin: "16px 0" }}>
            <div style={{ flex: 1, height: 1, background: "var(--rule)" }} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", letterSpacing: "0.06em", textTransform: "uppercase" }}>or enter details</span>
            <div style={{ flex: 1, height: 1, background: "var(--rule)" }} />
          </div>
          <Field label="Account nickname" value={name} onChange={setName} placeholder="e.g. Chase Checking" />
          <Field label="Account number" value={acct} onChange={v => setAcct(v.replace(/\D/g, ""))} placeholder="Account number" mono />
          <Field label="Routing number" value={routing} onChange={v => setRouting(v.replace(/\D/g, "").slice(0, 9))} placeholder="9-digit routing number" mono />
          <Field label="Street address" value={addressLine1} onChange={setAddressLine1} placeholder="Street address" />
          <Field label="Apt or suite" value={addressLine2} onChange={setAddressLine2} placeholder="Optional" />
          <Field label="City" value={city} onChange={setCity} placeholder="City" />
          <Field label="State" value={state} onChange={v => setState(v.replace(/[^a-zA-Z]/g, "").toUpperCase().slice(0, 2))} placeholder="CA" />
          <Field label="ZIP code" value={zip} onChange={v => setZip(v.replace(/[^\d-]/g, "").slice(0, 10))} placeholder="ZIP code" mono />
        </div>
      </div>
      <div style={{ flex: "none", padding: "0 18px 30px" }}>
        <div style={addExternalContentStyle}>
          <Btn full disabled={!valid} onClick={onSaved}>Continue</Btn>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 10, lineHeight: 1.5, textAlign: "center" }}>
            By tapping continue, you agree to{" "}
            <a href="https://yoshi.ai/cash-account-disclosures/" target="_blank" rel="noreferrer" style={{ color: "inherit", textDecoration: "underline", textUnderlineOffset: 2 }}>
              terms and authorizations
            </a>.
          </div>
        </div>
      </div>
    </RailScreen>
  );
  if (window.__YOSHI_WEB) return (
    <div className="push-enter" style={addExternalShellStyle}>
      <div style={addExternalPanelStyle}>{addExternalScreen}</div>
    </div>
  );
  return addExternalScreen;
};

const Field = ({ label, value, onChange, placeholder, mono }) => (
  <div style={{ marginBottom: 13 }}>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, color: "var(--ink-2)", marginBottom: 6 }}>{label}</div>
    <input value={value} onChange={e => onChange(e.target.value)} placeholder={placeholder}
      style={{ width: "100%", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", outline: "none", color: "var(--ink)", fontFamily: mono ? "var(--f-mono)" : "var(--f-display)", fontSize: typeSize(14) }} />
  </div>
);

/* ========================================================================== *
   3 · Wire transfer
 * ========================================================================== */
const WireFlow = ({ onBack, onClose, preset }) => {
  const [step, setStep] = useState("recipient");
  const [recId, setRecId] = useState(WIRE_RECIPIENTS[0].id);
  const [fromId, setFromId] = useState(preset && YOSHI_ACCTS.find(a => a.id === preset) ? preset : "cash");
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const rec = WIRE_RECIPIENTS.find(r => r.id === recId);
  const from = YOSHI_ACCTS.find(a => a.id === fromId);
  const fee = 15;
  const over = amtNum + fee > from.bal;

  return (
    <RailScreen title="Wire transfer" onBack={step === "recipient" ? onBack : () => setStep(step === "review" ? "amount" : "recipient")} onClose={onClose}>
      {step === "recipient" && (
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
          <div className="scroll" style={{ padding: "8px 0" }}>
            <SectionLabel style={{ paddingTop: 10 }}>Wire to</SectionLabel>
            <div style={{ borderTop: "1px solid var(--rule)" }}>
              {WIRE_RECIPIENTS.map((r, i) => (
                <PickRow key={r.id} glyph="DOM" name={r.name} sub={r.sub} selected={r.id === recId} onClick={() => setRecId(r.id)} last={i === WIRE_RECIPIENTS.length - 1} />
              ))}
            </div>
            <button className="press" onClick={() => {}} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 18px", background: "none", border: "none", borderBottom: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
              <span style={{ width: 38, height: 38, border: "1px dashed var(--rule-2)", display: "grid", placeItems: "center" }}><Icon name="plus" size={18} color="var(--ink-2)" /></span>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 500 }}>Add a wire recipient</span>
            </button>
            <SectionLabel>Pay from</SectionLabel>
            <div style={{ borderTop: "1px solid var(--rule)" }}>
              {YOSHI_ACCTS.map((a, i) => <PickRow key={a.id} name={a.name} sub={a.sub} right={<Money value={a.bal} size={11} color="var(--ink-3)" />} selected={a.id === fromId} onClick={() => setFromId(a.id)} last={i === YOSHI_ACCTS.length - 1} />)}
            </div>
          </div>
          <div style={{ flex: "none", padding: "12px 18px 30px" }}><Btn full onClick={() => setStep("amount")}>Enter amount <Icon name="arrow" size={18} color="var(--accent-ink)" /></Btn></div>
        </div>
      )}
      {step === "amount" && (
        <AmountBody amt={amt} amtNum={amtNum} onKey={onKey}
          context={<div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>Wire to <span style={{ color: "var(--ink)", fontWeight: 600 }}>{rec.name}</span></div>}
          info={`Domestic wire · ${usd(fee)} fee · cutoff 2:00pm PT`}
          error={over ? "Amount plus fee exceeds balance" : null}
          ctaLabel="Review" ctaDisabled={!amtNum || over} onCta={() => setStep("review")} />
      )}
      {step === "review" && (
        <ReviewScreen eyebrow="You're wiring" amt={amtNum} cta="Confirm · Passkey" onCta={() => setStep("done")}
          footerNote="Wires are final and can't be reversed once sent. Double-check the recipient details."
          rows={<>
            <ReviewRow label="To" value={rec.name} sub={rec.sub} />
            <ReviewRow label="Type" value="Domestic wire" />
            <ReviewRow label="From" value={from.name} sub={from.sub} />
            <ReviewRow label="Arrives" value="Today, if sent by 2pm PT" accent="var(--accent-pos)" />
            <ReviewRow label="Wire fee" value={usd(fee)} last />
          </>} />
      )}
      {step === "done" && <MoveSuccess title={`${usd(amtNum)} wired`} sub={`To ${rec.name}. Sent same-day on the wire network. The confirmation is in your stream.`} onDone={onClose} />}
    </RailScreen>
  );
};

/* ========================================================================== *
   4 · Deposit a check, mobile capture
 * ========================================================================== */
const CheckFlow = ({ onBack, onClose, nav, switchRail }) => {
  const [step, setStep] = useState("capture");
  const [front, setFront] = useState(false);
  const [bk, setBk] = useState(false);
  const [toId, setToId] = useState("cash");
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const to = YOSHI_ACCTS.find(a => a.id === toId);
  const instant = Math.min(amtNum, 200);

  return (
    <RailScreen title="Deposit a check" onBack={step === "capture" ? onBack : () => setStep(step === "review" ? "amount" : "capture")} onClose={onClose}>
      {step === "capture" && (
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
          <div className="scroll" style={{ padding: "14px 18px" }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-2)", lineHeight: 1.5, marginBottom: 14 }}>Endorse the back of the check, then capture both sides on a flat, dark surface.</div>
            <CheckCapture label="Front of check" captured={front} onToggle={() => setFront(f => !f)} />
            <div style={{ height: 12 }} />
            <CheckCapture label="Back of check" hint="Sign + “for Yoshi mobile deposit only”" captured={bk} onToggle={() => setBk(b => !b)} />
          </div>
          <div style={{ flex: "none", padding: "0 18px 28px" }}>
            <Btn full disabled={!front || !bk} onClick={() => setStep("amount")}>Enter amount <Icon name="arrow" size={18} color="var(--accent-ink)" /></Btn>
            <button className="press" onClick={() => switchRail && switchRail("sendcheck")} style={{ width: "100%", marginTop: 10, padding: "10px 0", background: "none", border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", gap: 5 }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>Want to pay someone with a check?</span>
              <Icon name="back" size={13} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
            </button>
          </div>
        </div>
      )}
      {step === "amount" && (
        <AmountBody amt={amt} amtNum={amtNum} onKey={onKey}
          context={<div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>Deposit into <span style={{ color: "var(--ink)", fontWeight: 600 }}>{to.name}</span></div>}
          info="Enter the exact amount written on the check"
          ctaLabel="Review" ctaDisabled={!amtNum} onCta={() => setStep("review")} />
      )}
      {step === "review" && (
        <ReviewScreen eyebrow="You're depositing" amt={amtNum} cta="Submit deposit · Passkey" onCta={() => setStep("done")}
          footerNote="Hold a deposited check until it clears. Funds reverse if the check bounces."
          rows={<>
            <ReviewRow label="Into" value={to.name} sub={to.sub} />
            <ReviewRow label="Front" value="Captured" accent="var(--accent-pos)" />
            <ReviewRow label="Back" value="Captured" accent="var(--accent-pos)" />
            <ReviewRow label="Available now" value={usd(instant)} sub={amtNum > 200 ? `Rest by Jun 2` : "Full amount today"} last />
          </>} />
      )}
      {step === "done" && <MoveSuccess title={`${usd(amtNum)} deposited`} sub={`Into ${to.name}. ${usd(instant)} is available now; the rest clears by Jun 2.`} onDone={onClose} />}
    </RailScreen>
  );
};

const CheckCapture = ({ label, hint, captured, onToggle }) => (
  <button className="press" onClick={onToggle} style={{ width: "100%", border: `1px solid ${captured ? "var(--accent)" : "var(--rule-2)"}`, background: "var(--bg-card)", padding: 0, cursor: "pointer", display: "block", textAlign: "left" }}>
    <div style={{
      height: 120, display: "grid", placeItems: "center",
      background: captured ? "color-mix(in srgb, var(--accent) 9%, var(--bg-2))" : "repeating-linear-gradient(135deg, var(--bg-2) 0 9px, var(--bg-card) 9px 18px)",
    }}>
      {captured
        ? <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }}><span style={{ width: 38, height: 38, borderRadius: 999, border: "1.5px solid var(--accent)", display: "grid", placeItems: "center" }}><Icon name="check" size={20} stroke={2.2} color="var(--accent)" /></span><span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10.5), color: "var(--accent)" }}>captured</span></div>
        : <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 5 }}><Icon name="receipt" size={26} color="var(--ink-3)" /><span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10.5), color: "var(--ink-3)", letterSpacing: "0.04em" }}>tap to capture</span></div>}
    </div>
    <div style={{ padding: "9px 12px", borderTop: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 8 }}>
      <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600 }}>{label}</span>
      {hint && <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", textAlign: "right" }}>{hint}</span>}
    </div>
  </button>
);

/* ========================================================================== *
   5 · Instant transfer, cash out to a debit card
 * ========================================================================== */
const InstantFlow = ({ onBack, onClose, preset }) => {
  const [step, setStep] = useState("card");
  const [cardId, setCardId] = useState(DEBIT_CARDS[0].id);
  const [fromId, setFromId] = useState(preset && YOSHI_ACCTS.find(a => a.id === preset) ? preset : "cash");
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const card = DEBIT_CARDS.find(c => c.id === cardId);
  const from = YOSHI_ACCTS.find(a => a.id === fromId);
  const fee = Math.max(0.25, amtNum * 0.015);
  const over = amtNum + fee > from.bal;

  return (
    <RailScreen title="Instant transfer" onBack={step === "card" ? onBack : () => setStep(step === "review" ? "amount" : "card")} onClose={onClose}>
      {step === "card" && (
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
          <div className="scroll" style={{ padding: "8px 0" }}>
            <SectionLabel style={{ paddingTop: 10 }}>To debit card</SectionLabel>
            <div style={{ borderTop: "1px solid var(--rule)" }}>
              {DEBIT_CARDS.map((c, i) => <PickRow key={c.id} glyph={c.glyph} name={c.bank} sub={c.sub} selected={c.id === cardId} onClick={() => setCardId(c.id)} last={i === DEBIT_CARDS.length - 1} />)}
            </div>
            <button className="press" onClick={() => {}} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 18px", background: "none", border: "none", borderBottom: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
              <span style={{ width: 38, height: 38, border: "1px dashed var(--rule-2)", display: "grid", placeItems: "center" }}><Icon name="plus" size={18} color="var(--ink-2)" /></span>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 500 }}>Add a debit card</span>
            </button>
            <SectionLabel>From</SectionLabel>
            <div style={{ borderTop: "1px solid var(--rule)" }}>
              {YOSHI_ACCTS.map((a, i) => <PickRow key={a.id} name={a.name} sub={a.sub} right={<Money value={a.bal} size={11} color="var(--ink-3)" />} selected={a.id === fromId} onClick={() => setFromId(a.id)} last={i === YOSHI_ACCTS.length - 1} />)}
            </div>
          </div>
          <div style={{ flex: "none", padding: "12px 18px 30px" }}><Btn full onClick={() => setStep("amount")}>Enter amount <Icon name="arrow" size={18} color="var(--accent-ink)" /></Btn></div>
        </div>
      )}
      {step === "amount" && (
        <AmountBody amt={amt} amtNum={amtNum} onKey={onKey}
          context={<div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>Instant to <span style={{ color: "var(--ink)", fontWeight: 600 }}>{card.bank} {card.sub}</span></div>}
          info={amtNum ? `1.50% fee · ${usd(fee)} · arrives in seconds` : "1.50% fee, $0.25 minimum · arrives in seconds"}
          error={over ? "Amount plus fee exceeds balance" : null}
          ctaLabel="Review" ctaDisabled={!amtNum || over} onCta={() => setStep("review")} />
      )}
      {step === "review" && (
        <ReviewScreen eyebrow="You're sending" amt={amtNum} cta="Confirm · Passkey" onCta={() => setStep("done")}
          footerNote="Instant transfers settle on the card networks and can't be canceled once sent."
          rows={<>
            <ReviewRow label="To" value={`${card.bank} ${card.sub}`} />
            <ReviewRow label="From" value={from.name} sub={from.sub} />
            <ReviewRow label="Arrives" value="In seconds" accent="var(--accent-pos)" />
            <ReviewRow label="Fee" value={`${usd(fee)} · 1.50%`} last />
          </>} />
      )}
      {step === "done" && <MoveSuccess title={`${usd(amtNum)} sent`} sub={`To ${card.bank} ${card.sub}, in seconds. It's in your stream.`} onDone={onClose} />}
    </RailScreen>
  );
};

/* ========================================================================== *
   6 · Direct deposit, account & routing, pre-filled form
 * ========================================================================== */
const DirectDepositFlow = ({ onBack, onClose, flash }) => {
  const ROUTING = "121000358";
  const ACCT = "8841 0023 7790";
  const copy = (label, text) => { try { navigator.clipboard && navigator.clipboard.writeText(text.replace(/\s/g, "")); } catch (e) {} flash(`${label} copied`); };
  return (
    <RailScreen title="Direct deposit" onBack={onBack} onClose={onClose}>
      <div className="scroll" style={{ padding: "14px 18px 24px" }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink-2)", lineHeight: 1.5 }}>Send your paycheck, or any direct deposit, straight to your Yoshi Checking. Deposits land up to two days early.</div>

        <Eyebrow style={{ margin: "20px 0 8px" }}>Your account details</Eyebrow>
        <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
          <CopyRow label="Account number" value={ACCT} showFull onCopy={() => copy("Account number", ACCT)} />
          <CopyRow label="Routing number" value={ROUTING} showFull onCopy={() => copy("Routing number", ROUTING)} />
          <CopyRow label="Account type" value="Checking" plain last />
        </div>

        <Eyebrow style={{ margin: "20px 0 8px" }}>The easy way</Eyebrow>
        <button className="press" onClick={() => flash("Pre-filled form sent to your email")} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 15px", background: "var(--bg-card)", border: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
          <span style={{ width: 34, height: 34, flex: "none", border: "1px solid var(--rule-2)", display: "grid", placeItems: "center" }}><Icon name="doc" size={17} color="var(--ink)" /></span>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 500 }}>Email a pre-filled form</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3 }}>Hand it to payroll, everything's filled in</div>
          </div>
          <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
        </button>

        <Eyebrow style={{ margin: "20px 0 8px" }}>Set up with your employer</Eyebrow>
        <div style={{ border: "1px solid var(--rule)" }}>
          {["Gusto", "ADP", "Workday", "Rippling"].map((p, i, arr) => (
            <button key={p} className="press" onClick={() => flash(`Opening ${p} connection`)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "12px 15px", background: "none", border: "none", borderBottom: i === arr.length - 1 ? "none" : "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
              <span style={{ width: 30, height: 30, flex: "none", background: "var(--ink)", color: "var(--bg-card)", display: "grid", placeItems: "center", fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 700 }}>{p[0]}</span>
              <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 500 }}>{p}</span>
              <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
            </button>
          ))}
        </div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 36, lineHeight: 1.6 }}>Yoshi is a fintech company, not an FDIC-insured depository institution. Deposits in Yoshi Cash accounts are FDIC-insured through Column N.A., Member FDIC. FDIC deposit insurance covers the failure of an insured bank. Early access to direct deposit depends on when your payer submits it.</div>
      </div>
    </RailScreen>
  );
};

const CopyRow = ({ label, value, onCopy, plain, showFull, last }) => {
  const masked = "••" + value.slice(-4);
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", borderBottom: last ? "none" : "1px dashed var(--rule)" }}>
      <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--ink-3)", flex: 1 }}>{label}</span>
      {plain || showFull ?
        <span style={{ fontFamily: showFull ? "var(--f-mono)" : "var(--f-display)", fontSize: typeSize(14), fontWeight: 500, fontVariantNumeric: "tabular-nums" }}>{value}</span> :
        <MaskedAccount full={value} masked={masked} />
      }
      {!plain && <button className="press" onClick={onCopy} aria-label={`Copy ${label}`} style={{ background: "none", border: "none", display: "inline-flex", alignItems: "center", padding: 2, cursor: "pointer" }}><CopyActionLabel copied={false} size={12} /></button>}
    </div>
  );
};

/* ========================================================================== *
   7 · Recurring transfer, schedule a repeating move
 * ========================================================================== */
const RecurringFlow = ({ onBack, onClose, preset, flash }) => {
  const [step, setStep] = useState("setup");
  const [fromId, setFromId] = useState(preset && YOSHI_ACCTS.find(a => a.id === preset) ? preset : "cash");
  const [toId, setToId] = useState("broker");
  const [freq, setFreq] = useState("monthly");
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const from = YOSHI_ACCTS.find(a => a.id === fromId);
  const to = YOSHI_ACCTS.find(a => a.id === toId);
  const FREQ = { weekly: "Every Friday", biweekly: "Every other Friday", monthly: "On the 1st" };
  const setFrom = (id) => { setFromId(id); if (id === toId) setToId(YOSHI_ACCTS.find(a => a.id !== id).id); };
  const setTo = (id) => { setToId(id); if (id === fromId) setFromId(YOSHI_ACCTS.find(a => a.id !== id).id); };

  return (
    <RailScreen title="Recurring transfer" onBack={step === "setup" ? onBack : () => setStep(step === "review" ? "amount" : "setup")} onClose={onClose}>
      {step === "setup" && (
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
          <div className="scroll" style={{ padding: "14px 18px" }}>
            <div style={{ display: "flex", gap: 9, padding: "11px 13px", marginBottom: 14, background: "color-mix(in srgb, var(--accent) 10%, var(--bg-card))", border: "1px solid var(--accent)", alignItems: "flex-start" }}>
              <Icon name="bolt" size={16} color="var(--accent)" style={{ marginTop: 1 }} />
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-2)", lineHeight: 1.5 }}>This creates a <span style={{ fontWeight: 600, color: "var(--ink)" }}>Yoshi automation</span>, a standing rule that runs on its own. You can pause or change it anytime from your automations.</span>
            </div>
            <InlineSelect label="From" options={YOSHI_ACCTS} value={fromId} onChange={setFrom} balances />
            <div style={{ height: 10 }} />
            <InlineSelect label="To" options={YOSHI_ACCTS} value={toId} onChange={setTo} balances />
            <Eyebrow style={{ margin: "18px 0 9px" }}>How often</Eyebrow>
            <Segmented options={[{ value: "weekly", label: "Weekly" }, { value: "biweekly", label: "2 weeks" }, { value: "monthly", label: "Monthly" }]} value={freq} onChange={setFreq} />
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 30 }}>Runs {FREQ[freq].toLowerCase()}, starting next cycle.</div>
          </div>
          <div style={{ flex: "none", padding: "0 18px 30px" }}><Btn full onClick={() => setStep("amount")}>Enter amount <Icon name="arrow" size={18} color="var(--accent-ink)" /></Btn></div>
        </div>
      )}
      {step === "amount" && (
        <AmountBody amt={amt} amtNum={amtNum} onKey={onKey}
          context={<div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>{from.name} → <span style={{ color: "var(--ink)", fontWeight: 600 }}>{to.name}</span>, {FREQ[freq].toLowerCase()}</div>}
          info="The amount to move each time"
          ctaLabel="Review" ctaDisabled={!amtNum} onCta={() => setStep("review")} />
      )}
      {step === "review" && (
        <ReviewScreen eyebrow="Each transfer" amt={amtNum} cta="Schedule it · Passkey" onCta={() => setStep("done")}
          footerNote="A standing schedule. Yoshi shows each run in your stream and skips it if funds are short."
          rows={<>
            <ReviewRow label="From" value={from.name} sub={from.sub} />
            <ReviewRow label="To" value={to.name} sub={to.sub} />
            <ReviewRow label="Frequency" value={{ weekly: "Weekly", biweekly: "Every 2 weeks", monthly: "Monthly" }[freq]} />
            <ReviewRow label="Schedule" value={FREQ[freq]} />
            <ReviewRow label="First run" value="Next cycle" last />
          </>} />
      )}
      {step === "done" && <MoveSuccess title="Transfer scheduled" sub={`${usd(amtNum)} from ${from.name} to ${to.name}, ${FREQ[freq].toLowerCase()}. Manage it in your automations.`} onDone={onClose} />}
    </RailScreen>
  );
};

/* ========================================================================== *
   7b · Send a check (outbound payment)
 * ========================================================================== */
const CheckPayFlow = ({ onBack, onClose, nav }) => {
  const [step, setStep] = useState("setup");
  const [addr, setAddr] = useState({ name: "", line1: "", line2: "", city: "", state: "", zip: "" });
  const [amt, setAmt, onKey, amtNum] = useAmount("");
  const [padOpen, setPadOpen] = useState(false);
  const yo = YOSHI_ACCTS.find(a => a.id === "cash");
  const over = amtNum > yo.bal;
  const addrValid = addr.name.trim() && addr.line1.trim() && addr.city.trim() && addr.state.length === 2 && addr.zip.length === 5;

  if (step === "done") return (
    <RailScreen title="Send a check" onBack={onBack} onClose={onClose}>
      <MoveSuccess title={`${usd(amtNum)} check sent`} sub={`Check mailed to ${addr.name} at ${addr.line1}, ${addr.city} ${addr.state}. Allow 7–10 business days for delivery.`} onDone={onClose} />
    </RailScreen>
  );

  return (
    <RailScreen title="Send a check" onBack={step === "setup" ? onBack : () => setStep("setup")} onClose={onClose}>
      {step === "review" && (
        <ReviewScreen eyebrow="You're sending" amt={amtNum} cta="Confirm with passkey" onCta={() => setStep("done")}
          footerNote="Yoshi prints and mails the check from your Cash account. Allow 7–10 business days for delivery."
          rows={<>
            <ReviewRow label="From" value="Cash" sub="Yoshi" />
            <ReviewRow label="To" value={addr.name} sub={`${addr.line1}${addr.line2 ? ", " + addr.line2 : ""}, ${addr.city} ${addr.state} ${addr.zip}`} />
            <ReviewRow label="Arrives" value="7–10 business days" />
            <ReviewRow label="Fee" value="$0.00" last />
          </>} />
      )}
      {step === "setup" && (
      <div className="scroll" style={{ padding: "16px 18px 28px" }}>
        <Eyebrow style={{ marginBottom: 7 }}>From</Eyebrow>
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12 }}>
          <span style={{ width: 36, height: 36, flex: "none", display: "grid", placeItems: "center", border: "1px solid var(--rule-2)", color: "var(--ink)" }}><Logo size={20} /></span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>Cash</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>Yoshi · 3.30% APY</div>
          </div>
          <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13.5), fontVariantNumeric: "tabular-nums", flex: "none" }}>{usd(yo.bal)}</div>
        </div>

        <Eyebrow style={{ margin: "18px 0 10px" }}>To</Eyebrow>
        <Field label="Name" value={addr.name} onChange={v => setAddr(a => ({...a, name: v}))} placeholder="Full name or company" />
        <Field label="Address" value={addr.line1} onChange={v => setAddr(a => ({...a, line1: v}))} placeholder="123 Main St" />
        <Field label="Apt / suite (optional)" value={addr.line2} onChange={v => setAddr(a => ({...a, line2: v}))} placeholder="Suite, unit, etc." />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 56px 88px", gap: 8 }}>
          <Field label="City" value={addr.city} onChange={v => setAddr(a => ({...a, city: v}))} placeholder="City" />
          <Field label="State" value={addr.state} onChange={v => setAddr(a => ({...a, state: v.toUpperCase().slice(0,2)}))} placeholder="CA" />
          <Field label="ZIP" value={addr.zip} onChange={v => setAddr(a => ({...a, zip: v.replace(/\D/g,"").slice(0,5)}))} placeholder="94103" mono />
        </div>

        <Eyebrow style={{ margin: "18px 0 7px" }}>Amount <span style={{ textTransform: "none", letterSpacing: 0, color: "var(--ink-3)", fontWeight: 500 }}>({usd(yo.bal)} available)</span></Eyebrow>
        <div onClick={() => setPadOpen(true)} style={{ display: "flex", alignItems: "center", gap: 9, minHeight: 50, padding: "0 14px", background: "var(--bg-card)", border: `1px solid ${over ? "var(--signal-neg)" : "var(--rule-2)"}`, borderRadius: 12, cursor: "pointer" }}>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(18), color: amtNum ? "var(--ink-3)" : "var(--rule-2)" }}>$</span>
          <input readOnly inputMode="none" value={amt === "" ? "" : commaAmt(amt)} onFocus={() => setPadOpen(true)} placeholder="Enter amount"
            style={{ flex: 1, minWidth: 0, border: "none", background: "transparent", outline: "none", color: "var(--ink)", fontFamily: "var(--f-mono)", fontSize: typeSize(18), fontVariantNumeric: "tabular-nums", cursor: "pointer" }} />
        </div>
        {over && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--signal-neg)", marginTop: 6 }}>Exceeds available balance</div>}

        <div style={{ marginTop: 22 }}>
          <Seam style={{ marginBottom: 16 }} />
          <Btn full disabled={!amtNum || over || !addrValid} onClick={() => setStep("review")}>Review</Btn>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", textAlign: "center", marginTop: 9, lineHeight: 1.4 }}>Mailed from Yoshi · 7–10 business days</div>
        </div>
      </div>
      )}
      <KeypadSheet open={padOpen} onClose={() => setPadOpen(false)} amt={amt} amtNum={amtNum} onKey={onKey}
        label="Amount" sub={over ? null : `${usd(yo.bal)} available`} error={over ? "Exceeds available balance" : null} ctaLabel="Done" />
    </RailScreen>
  );
};

/* ========================================================================== *
   8 · Pay a bill
 * ========================================================================== */
const BillPayFlow = ({ onBack, onClose, nav, switchRail }) => {
  const [payeeId, setPayeeId] = useState(PAYEES[0].id);
  const [gate, setGate] = useState(false);
  const [pickPayee, setPickPayee] = useState(false);
  const [amt, setAmt, onKey, amtNum] = useAmount(String(PAYEES[0].amt));
  const [padOpen, setPadOpen] = useState(false);
  const [done, setDone] = useState(false);
  const payee = PAYEES.find(p => p.id === payeeId);
  const yo = YOSHI_ACCTS.find(a => a.id === "cash");
  const over = amtNum > yo.bal;
  const choosePayee = (id) => { setPayeeId(id); setAmt(String(PAYEES.find(p => p.id === id).amt)); setPickPayee(false); };
  const askAutopay = () => { onClose(); nav && nav.ask("Set up autopay for this bill.", `I can set up a recurring payment to ${payee.name}. Tell me the amount and cadence — monthly, on a specific date, or tied to a statement — and I’ll build the automation. You approve it once and it runs inside your set limits.`); };

  if (done) return (
    <RailScreen title="Pay a bill" onBack={onBack} onClose={onClose}>
      <MoveSuccess title={`${usd(amtNum)} paid`} sub={`ACH initiated to ${payee.name}. The confirmation is in your stream.`} onDone={onClose} />
    </RailScreen>
  );

  return (
    <RailScreen title="Pay a bill" onBack={onBack} onClose={onClose}>
      <div className="scroll" style={{ padding: "16px 18px 28px" }}>
        {/* FROM */}
        <Eyebrow style={{ marginBottom: 7 }}>From</Eyebrow>
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12 }}>
          <span style={{ width: 36, height: 36, flex: "none", display: "grid", placeItems: "center", border: "1px solid var(--rule-2)", color: "var(--ink)" }}><Logo size={20} /></span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>Cash</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>Yoshi · 3.30% APY</div>
          </div>
          <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13.5), fontVariantNumeric: "tabular-nums", flex: "none" }}>{usd(yo.bal)}</div>
        </div>

        {/* TO */}
        <Eyebrow style={{ margin: "18px 0 7px" }}>To</Eyebrow>
        <div style={{ position: "relative", zIndex: pickPayee ? 6 : 1 }}>
          <button className="press" onClick={() => setPickPayee(o => !o)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, cursor: "pointer", textAlign: "left" }}>
            <span style={{ width: 36, height: 36, flex: "none", display: "grid", placeItems: "center", border: "1px solid var(--rule-2)", color: "var(--ink)", fontFamily: "var(--f-display)", fontWeight: 700, fontSize: typeSize(15) }}>{payee.name[0]}</span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{payee.name}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", marginTop: 3 }}>{payee.sub}</div>
            </div>
            <div style={{ textAlign: "right", flex: "none" }}>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontVariantNumeric: "tabular-nums", color: "var(--ink-2)" }}>{usd(payee.amt)}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", marginTop: 2 }}>{pulledAgo(payee.id)}</div>
            </div>
            <Icon name="down" size={16} color="var(--ink-3)" style={{ flex: "none" }} />
          </button>
          {pickPayee && (
            <div style={{ position: "absolute", left: 0, right: 0, top: "calc(100% + 4px)", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, boxShadow: "0 14px 30px -14px rgba(0,0,0,0.35)", overflow: "hidden" }}>
              {PAYEES.map((p, i) => (
                <button key={p.id} className="press" onClick={() => choosePayee(p.id)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 11, padding: "11px 14px", background: p.id === payeeId ? "var(--bg-2)" : "none", border: "none", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer", textAlign: "left" }}>
                  <span style={{ width: 30, height: 30, flex: "none", border: "1px solid var(--rule-2)", color: "var(--ink)", display: "grid", placeItems: "center", fontFamily: "var(--f-display)", fontWeight: 700, fontSize: typeSize(13) }}>{p.name[0]}</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600 }}>{p.name}</div>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>{p.sub}</div>
                  </div>
                  <div style={{ textAlign: "right", flex: "none" }}>
                    <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--ink-2)" }}>{usd(p.amt)}</div>
                    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", marginTop: 2 }}>{pulledAgo(p.id)}</div>
                  </div>
                </button>
              ))}
              <button className="press" onClick={() => setPickPayee(false)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 11, padding: "11px 14px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
                <span style={{ width: 30, height: 30, flex: "none", border: "1px dashed var(--rule-2)", display: "grid", placeItems: "center" }}><Icon name="plus" size={15} color="var(--ink-2)" /></span>
                <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 500 }}>Add a payee</span>
              </button>
            </div>
          )}
        </div>

        {/* AMOUNT */}
        <Eyebrow style={{ margin: "18px 0 7px" }}>Amount <span style={{ textTransform: "none", letterSpacing: 0, color: "var(--ink-3)", fontWeight: 500 }}>({usd(yo.bal)} available)</span></Eyebrow>
        <div onClick={() => setPadOpen(true)} style={{ display: "flex", alignItems: "center", gap: 9, minHeight: 50, padding: "0 14px", background: "var(--bg-card)", border: `1px solid ${over ? "var(--signal-neg)" : "var(--rule-2)"}`, borderRadius: 12, cursor: "pointer" }}>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(18), color: amtNum ? "var(--ink-3)" : "var(--rule-2)" }}>$</span>
          <input readOnly inputMode="none" value={amt === "" ? "" : commaAmt(amt)} onFocus={() => setPadOpen(true)} placeholder="Enter amount"
            style={{ flex: 1, minWidth: 0, border: "none", background: "transparent", outline: "none", color: "var(--ink)", fontFamily: "var(--f-mono)", fontSize: typeSize(18), fontVariantNumeric: "tabular-nums", cursor: "pointer" }} />
          <div style={{ display: "flex", gap: 6, flex: "none" }}>
            {payee.debt && <button className="press" onClick={(e) => { e.stopPropagation(); setAmt(String(payee.min ?? Math.max(35, Math.round(payee.amt * 0.02)))); }} style={{ padding: "6px 13px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, color: "var(--ink)", cursor: "pointer" }}>Min</button>}
            <button className="press" onClick={(e) => { e.stopPropagation(); setAmt(String(payee.amt)); }} style={{ padding: "6px 13px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, color: "var(--ink)", cursor: "pointer" }}>Max</button>
          </div>
        </div>
        {over && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--signal-neg)", marginTop: 6 }}>Exceeds available balance</div>}

        <div style={{ marginTop: 22 }}>
          <Seam style={{ marginBottom: 16 }} />
          <Btn full disabled={!amtNum || over} onClick={() => setGate(true)}>Confirm</Btn>
          {gate && <PasskeyGate title={`Pay ${payee.name}`} amount={amtNum} detail={`From ${yo.name} · ACH to ${payee.name}.`} cta="Authorize with passkey" onSuccess={() => { setGate(false); setDone(true); }} onCancel={() => setGate(false)} />}
          <button className="press" onClick={askAutopay} style={{ width: "100%", marginTop: 11, padding: "11px 0", background: "none", border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", gap: 5 }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>Set up autopay for this bill</span>
            <Icon name="back" size={13} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
          </button>
          <button className="press" onClick={() => switchRail && switchRail("sendcheck")} style={{ width: "100%", marginTop: 4, padding: "10px 0", background: "none", border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", gap: 5 }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>Want to pay with a check?</span>
            <Icon name="back" size={13} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
          </button>
        </div>
      </div>
      <KeypadSheet open={padOpen} onClose={() => setPadOpen(false)} amt={amt} amtNum={amtNum} onKey={onKey}
        label="Amount" sub={over ? null : `${usd(yo.bal)} available`} error={over ? "Exceeds available balance" : null}
        onMax={() => setAmt(String(payee.amt))} ctaLabel="Done" />
    </RailScreen>
  );
};

/* ========================================================================== *
   9 · Account transfer, ACATS in / out (brokerage account portability)
 * ========================================================================== */
const BROKERAGES = [
  { id: "schwab", name: "Schwab Brokerage", bank: "Charles Schwab", displaySub: "Charles Schwab •4021", sub: "Brokerage · ••4021",  glyph: "S", bal: 148302.41, cashBal: 6240.55 },
  { id: "fidelity", name: "Fidelity Brokerage", bank: "Fidelity", displaySub: "Fidelity •8830", sub: "Brokerage · ••8830",  glyph: "F", bal: 96540.18,  cashBal: 3540.18 },
  { id: "robinhood", name: "Robinhood Individual", bank: "Robinhood", displaySub: "Robinhood •2207", sub: "Individual · ••2207", glyph: "R", bal: 23410.55,  cashBal: 1240.55 },
  { id: "vanguard", name: "Vanguard Brokerage", bank: "Vanguard", displaySub: "Vanguard •5567", sub: "Brokerage · ••5567",  glyph: "V", bal: 312900.00, cashBal: 12900.00 },
  { id: "fidelity-ira", name: "Fidelity Traditional IRA", bank: "Fidelity", displaySub: "Fidelity •1188", sub: "Traditional IRA · ••1188", glyph: "I", bal: 84520.32, cashBal: 2200.00, retirement: true },
];
const YOSHI_BROKERAGES = [
  { id: "existing", name: "Yoshi Brokerage", sub: "Individual · ••3392", glyph: "logo" },
  { id: "new",      name: "New Yoshi brokerage account", sub: "Opened as part of this transfer", glyph: "plus" },
];
const BROKERAGE_HOLDINGS = {
  "yoshi-broker": [
    { id: "yoshi-broker-cash", type: "cash", symbol: "Cash", name: "Uninvested cash", value: 5120.00, transferable: true },
    { id: "yoshi-broker-vti", symbol: "VTI", name: "Vanguard Total Stock Market ETF", shares: 42.18, value: 12004.52, transferable: true },
    { id: "yoshi-broker-sgov", symbol: "SGOV", name: "iShares 0-3 Month Treasury Bond ETF", shares: 80, value: 8072.80, transferable: true },
    { id: "yoshi-broker-aapl", symbol: "AAPL", name: "Apple Inc.", shares: 18, value: 3644.82, transferable: true },
    { id: "yoshi-broker-fractional", symbol: "MSFT", name: "Microsoft fractional lot", shares: 0.44, value: 210.54, transferable: false, reason: "Fractional share" },
  ],
  schwab: [
    { id: "schwab-cash", type: "cash", symbol: "Cash", name: "Uninvested cash", value: 6240.55, transferable: true },
    { id: "schwab-vti", symbol: "VTI", name: "Vanguard Total Stock Market ETF", shares: 182.42, value: 51912.32, transferable: true },
    { id: "schwab-nvda", symbol: "NVDA", name: "NVIDIA Corp.", shares: 96, value: 31042.56, transferable: true },
    { id: "schwab-aapl", symbol: "AAPL", name: "Apple Inc.", shares: 74, value: 14984.26, transferable: true },
    { id: "schwab-sgov", symbol: "SGOV", name: "iShares 0-3 Month Treasury Bond ETF", shares: 220, value: 22198.00, transferable: true },
    { id: "schwab-msft", symbol: "MSFT", name: "Microsoft Corp.", shares: 34, value: 16211.82, transferable: true },
    { id: "schwab-amzn", symbol: "AMZN", name: "Amazon.com Inc.", shares: 42, value: 7831.62, transferable: true },
    { id: "schwab-fractional", symbol: "BRK.B", name: "Berkshire Hathaway Class B", shares: 0.48, value: 203.41, transferable: false, reason: "Fractional share" },
    { id: "schwab-option", symbol: "SPY 0717C", name: "SPY call option", shares: 1, value: 918.42, transferable: false, reason: "Option contract" },
    { id: "schwab-crypto", symbol: "BTC", name: "Bitcoin Trust position", shares: 0.18, value: 13200.00, transferable: false, reason: "Crypto exposure" },
  ],
  fidelity: [
    { id: "fidelity-cash", type: "cash", symbol: "Cash", name: "Core cash position", value: 3540.18, transferable: true },
    { id: "fidelity-fxaix", symbol: "FXAIX", name: "Fidelity 500 Index Fund", shares: 288.13, value: 57500.18, transferable: true },
    { id: "fidelity-vxus", symbol: "VXUS", name: "Vanguard Total International Stock ETF", shares: 212, value: 13436.56, transferable: true },
    { id: "fidelity-goog", symbol: "GOOGL", name: "Alphabet Inc.", shares: 68, value: 11934.68, transferable: true },
    { id: "fidelity-voo", symbol: "VOO", name: "Vanguard S&P 500 ETF", shares: 27, value: 14128.76, transferable: true },
    { id: "fidelity-restricted", symbol: "PLTR-RS", name: "Restricted stock lot", shares: 50, value: 2330.00, transferable: false, reason: "Restricted lot" },
  ],
  robinhood: [
    { id: "robinhood-cash", type: "cash", symbol: "Cash", name: "Brokerage cash", value: 1240.55, transferable: true },
    { id: "robinhood-tsla", symbol: "TSLA", name: "Tesla Inc.", shares: 31, value: 10133.28, transferable: true },
    { id: "robinhood-qqq", symbol: "QQQ", name: "Invesco QQQ Trust", shares: 18, value: 9484.74, transferable: true },
    { id: "robinhood-hood", symbol: "HOOD", name: "Robinhood Markets Inc.", shares: 120, value: 3792.00, transferable: true },
    { id: "robinhood-doge", symbol: "DOGE", name: "Dogecoin", shares: 2400, value: 528.00, transferable: false, reason: "Crypto" },
    { id: "robinhood-fractional", symbol: "AMZN", name: "Amazon.com fractional lot", shares: 0.34, value: 63.29, transferable: false, reason: "Fractional share" },
  ],
  vanguard: [
    { id: "vanguard-cash", type: "cash", symbol: "Cash", name: "Settlement fund cash", value: 12900.00, transferable: true },
    { id: "vanguard-vtsax", symbol: "VTSAX", name: "Vanguard Total Stock Market Index", shares: 1260.77, value: 171820.44, transferable: true },
    { id: "vanguard-vtiax", symbol: "VTIAX", name: "Vanguard Total International Stock Index", shares: 1740.08, value: 59612.74, transferable: true },
    { id: "vanguard-vbtlx", symbol: "VBTLX", name: "Vanguard Total Bond Market Index", shares: 3820.45, value: 37198.58, transferable: true },
  ],
};
const ACAT_DISCLOSURES = [
  ["swap",    "Positions move in-kind", "Holdings transfer over the ACATS network. You stay invested the whole time, nothing is sold."],
  ["clock",   "Takes 5–7 business days", "Once started, the receiving and delivering firms reconcile every position before it lands."],
];
const OUTBOUND_ACAT_FEE_DISCLOSURE = ["receipt", "$75.00 outgoing transfer fee", "Yoshi charges this when securities leave Yoshi Brokerage for another firm."];
const DEBT_DISCLOSURES = [
  ["clock", "Takes 1–5 business days"],
];
const MANUAL_RECIPIENT_DISCLOSURES = {
  standard: ["clock", "ACH takes 1–3 business days", "Funds move over ACH from Yoshi Cash to the recipient's bank account."],
  wire: ["clock", "Wire arrives same day", "Domestic wires are sent from Yoshi Cash and can't be reversed once submitted."],
  instant: ["clock", "Instant arrives in seconds", "Instant transfers use eligible recipient rails and may carry a small fee."],
};
const transferHoldingsFor = (firmId) => BROKERAGE_HOLDINGS[firmId] || [];
const defaultTransferHoldingIds = (firmId) => transferHoldingsFor(firmId).filter(h => h.transferable).map(h => h.id);
const holdingShareText = (h) => h.type === "cash" ? "available cash" : `${Number(h.shares).toLocaleString(undefined, { maximumFractionDigits: h.shares % 1 ? 3 : 0 })} ${h.shares === 1 ? "share" : "shares"}`;
const selectedHoldingValue = (holdings) => holdings.reduce((sum, h) => sum + h.value, 0);
const selectedTransferSummary = (holdings) => {
  const cashValue = selectedHoldingValue(holdings.filter(h => h.type === "cash"));
  const securityCount = holdings.filter(h => h.type !== "cash").length;
  const securityLabel = `${securityCount} ${securityCount === 1 ? "security" : "securities"}`;
  if (cashValue && securityCount) return `${securityLabel} + ${usd(cashValue)} cash`;
  if (cashValue) return `${usd(cashValue)} cash`;
  return securityLabel;
};

const InvestmentSelectionScreen = ({ firm, holdings, selectedIds, onToggle, onBulk, onBack, onClose }) => {
  const transferable = holdings.filter(h => h.transferable);
  const selectedTransferHoldings = holdings.filter(h => h.transferable && selectedIds.includes(h.id));
  const allSelected = selectedTransferHoldings.length === transferable.length;
  const selectedValue = selectedHoldingValue(selectedTransferHoldings);

  return (
    <RailScreen title="Add investments" sub={`${firm.bank} ${firm.rawSub || firm.sub}`} onBack={onBack} onClose={onClose}>
      <div className="scroll" style={{ padding: "14px 18px 98px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 14 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <Eyebrow style={{ marginBottom: 6 }}>Holdings and cash</Eyebrow>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink-2)", lineHeight: 1.45 }}>{selectedTransferHoldings.length} selected · <Money value={selectedValue} size={13} color="var(--ink-2)" /></div>
          </div>
          <button className="press" onClick={() => onBulk(allSelected ? [] : transferable.map(h => h.id))} style={{ flex: "none", padding: "8px 12px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 999, fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 600, color: "var(--ink)", cursor: "pointer" }}>{allSelected ? "Deselect all" : "Select all"}</button>
        </div>

        <div style={{ border: "1px solid var(--rule-2)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)" }}>
          {holdings.map((h, i) => {
            const selected = h.transferable && selectedIds.includes(h.id);
            return (
              <button key={h.id} className="press" disabled={!h.transferable} onClick={() => h.transferable && onToggle(h.id)} style={{
                width: "100%", display: "grid", gridTemplateColumns: "26px 1fr auto", gap: 11, alignItems: "center", padding: "12px 13px",
                background: h.transferable ? "var(--bg-card)" : "color-mix(in srgb, var(--ink) 4%, var(--bg-card))",
                border: "none", borderBottom: i === holdings.length - 1 ? "none" : "1px solid var(--rule)", textAlign: "left",
                cursor: h.transferable ? "pointer" : "not-allowed", opacity: h.transferable ? 1 : 0.48,
              }}>
                <input type="checkbox" checked={selected} disabled={!h.transferable} readOnly aria-label={`${h.symbol} ${h.transferable ? "transfer selected" : "untransferable"}`} style={{ width: 18, height: 18, accentColor: "var(--accent)", pointerEvents: "none" }} />
                <div style={{ minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "baseline", gap: 7, minWidth: 0 }}>
                    <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 650, whiteSpace: "nowrap" }}>{h.symbol}</span>
                    <span style={{ minWidth: 0, fontFamily: "var(--f-display)", fontSize: typeSize(12), color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{h.name}</span>
                  </div>
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 4 }}>{holdingShareText(h)}{!h.transferable ? ` · ${h.reason}` : ""}</div>
                </div>
                <div style={{ textAlign: "right", minWidth: 72 }}>
                  <Money value={h.value} size={12} color={h.transferable ? "var(--ink-2)" : "var(--ink-3)"} />
                  {!h.transferable && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), color: "var(--ink-3)", marginTop: 4, textTransform: "uppercase", letterSpacing: "0.06em" }}>untransferable</div>}
                </div>
              </button>
            );
          })}
        </div>
      </div>
      <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, padding: "12px 18px 30px", background: "linear-gradient(180deg, color-mix(in srgb, var(--bg) 0%, transparent), var(--bg) 24%)" }}>
        <Btn full onClick={onBack}>Done</Btn>
      </div>
    </RailScreen>
  );
};

window.TransferFlow = TransferFlow;
