/* accounts.jsx, the unified balance, grouped cash / investments / crypto.
   Tapping an investment or crypto row pushes HoldingDetail (chart + range
   toggle, position, fundamentals, news, transaction history). */

/* ---- hidden-accounts store · lets a user drop a linked account from totals
   & summaries. Persisted to localStorage; broadcasts so the Accounts list and
   the detail screen stay in sync. Keyed by account name. -------------------- */
const HIDDEN_KEY = "yoshi_hidden_accts";
const readHidden = () => {try {return JSON.parse(localStorage.getItem(HIDDEN_KEY) || "[]");} catch (e) {return [];}};
const writeHidden = (arr) => {try {localStorage.setItem(HIDDEN_KEY, JSON.stringify(arr));} catch (e) {}window.dispatchEvent(new CustomEvent("yoshi-hidden"));};
const useHiddenAccts = () => {
  const [hidden, setHidden] = useState(readHidden);
  useEffect(() => {
    const f = () => setHidden(readHidden());
    window.addEventListener("yoshi-hidden", f);
    return () => window.removeEventListener("yoshi-hidden", f);
  }, []);
  const toggle = (name) => {const cur = readHidden();writeHidden(cur.includes(name) ? cur.filter((n) => n !== name) : [...cur, name]);};
  return [hidden, toggle];
};

/* net-worth change · past 30 days · shows both % and $ inline */
const NW_PCT = 3.64;
const NetWorthChange = () => {
  const dollars = Math.round(NET_WORTH_TRUE * NW_PCT / 100);
  const c = NW_PCT >= 0 ? "var(--accent-pos)" : "var(--signal-neg)";
  return (
    <span style={{ display: "inline-flex", alignItems: "baseline", gap: 6, whiteSpace: "nowrap" }}>
      <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12.5), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: c, letterSpacing: "-0.005em" }}>{perf(dollars, NW_PCT)}</span>
      <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)" }}>vs 30d ago</span>
    </span>);

};

/* web-only: info "i" disclosures render as a small popover anchored to the
   icon (a tooltip), instead of the phone's slide-up bottom sheet. */
const InfoPop = ({ open, onClose, title, align = "left", width = 266, children }) => {
  if (!open) return null;
  return (
    <>
      <div onClick={(e) => { e.stopPropagation(); onClose(); }} style={{ position: "fixed", inset: 0, zIndex: 48 }} />
      <div onClick={(e) => e.stopPropagation()} style={{ position: "absolute", top: "calc(100% + 9px)", [align]: 0, zIndex: 49, width, background: "var(--ink)", color: "var(--bg)", borderRadius: 12, padding: "12px 14px", boxShadow: "0 16px 40px -14px rgba(0,0,0,0.55)" }}>
        <span style={{ position: "absolute", top: -5, [align]: 13, width: 10, height: 10, background: "var(--ink)", transform: "rotate(45deg)" }} />
        {title && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", opacity: 0.6, marginBottom: 6 }}>{title}</div>}
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), lineHeight: 1.55, position: "relative" }}>{children}</div>
      </div>
    </>
  );
};

const FDIC_DISCLOSURE = "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. Certain conditions must be satisfied for pass-through FDIC insurance to apply. Funds in Yoshi Investments are not insured by the FDIC; are not deposits; and may lose value.";

/* a small "Hidden" pill for linked accounts dropped from totals */
const HiddenChip = () => <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(8.5), fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, padding: "2px 7px", whiteSpace: "nowrap" }}>Hidden</span>;

/* web tier lead-in — big title + muted caps label, with the tier total set
   large so the number hierarchy reads clearly: tier → group → row. */
const TierHead = ({ title, sub, total, totalColor, neg, action }) =>
<div style={{ display: "flex", alignItems: "baseline", gap: 12, padding: "4px 0 14px", borderBottom: "1px solid var(--rule-2)" }}>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(21), fontWeight: 700, letterSpacing: "-0.025em" }}>{title}</div>
      {sub && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", marginTop: 4, letterSpacing: "0.08em", textTransform: "uppercase" }}>{sub}</div>}
    </div>
    <Money value={total} size={21} weight={600} prefix={neg ? "−$" : "$"} color={totalColor || "var(--ink)"} />
    {action && <span style={{ alignSelf: "center", display: "flex", flex: "none" }}>{action}</span>}
  </div>;

const openedAccountValue = (account) => account.valueOverride != null ? account.valueOverride : acctValue(account.ids || []);

/* Yoshi-held account groups: cash (incl. the card), investments, paper trading */
const YoshiGroups = ({ nav, hasCard, openedAccounts = [] }) => {
  const openedBrokerageAccounts = openedAccounts.filter((account) => account.kind === "brokerage");
  const openedPaperAccounts = openedAccounts.filter((account) => account.kind === "paper");
  const openedBrokerageTotal = openedBrokerageAccounts.reduce((sum, account) => sum + openedAccountValue(account), 0);
  const openedPaperTotal = openedPaperAccounts.reduce((sum, account) => sum + openedAccountValue(account), 0);
  return (
  <>
    <Group title="Cash" total={CASH_TOTAL}>
      <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.cash })}
      bar="var(--accent-2)" name="Cash" sub="Yoshi •8841" value={CASH_TOTAL} push />
      <button className="press" onClick={() => nav.push({ type: "card" })} style={{
        width: "100%", textAlign: "left", background: "none", border: "none",
        display: "grid", gridTemplateColumns: window.__YOSHI_WEB ? "1fr auto 16px" : "3px 1fr auto 16px", gap: 12, padding: "15px 18px",
        borderBottom: "1px solid var(--rule)", alignItems: "center"
      }}>
        {!window.__YOSHI_WEB && <div style={{ background: hasCard ? "var(--ink-3)" : "var(--accent)", width: 3, height: 30 }} />}
        <div style={{ minWidth: 0 }}>
          {hasCard ?
          <>
              <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
                <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, letterSpacing: "-0.005em" }}>Yoshi Card</span>
              </div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3 }}>Debit •2291</div>
            </> :

          <>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, letterSpacing: "-0.005em" }}>Yoshi Card</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3 }}>Virtual debit · set a PIN to use it</div>
            </>
          }
        </div>
        {hasCard ? <span /> :
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, color: "var(--accent)" }}>Activate</span>}
        <Icon name="back" size={16} color="var(--ink-3)" style={{ transform: "scaleX(-1)", justifySelf: "end" }} />
      </button>
    </Group>

    <Group title="Investments" total={INVEST_TOTAL + CRYPTO_TOTAL + openedBrokerageTotal} delta={<RowDelta pct={DAY_PCT} value={INVEST_TOTAL + CRYPTO_TOTAL} />}>
      <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.brokerage })}
      bar="var(--accent-4)" name="Brokerage" sub="Yoshi · Individual Taxable •2208" value={acctValue(ACCOUNTS.brokerage.ids)}
      chip={<RowDelta pct={DAY_PCT} value={acctValue(ACCOUNTS.brokerage.ids)} />} push />
      {openedBrokerageAccounts.map((account) =>
        <Row key={account.id} onClick={() => nav.push({ type: "account", acct: account })}
        bar="var(--accent-4)" name={account.name} sub={account.sub} value={openedAccountValue(account)} valueColor="var(--ink-2)" push />
      )}
      <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.roth })}
      bar="var(--accent-4)" name="Roth IRA" sub="Yoshi · Retirement •7731" value={acctValue(ACCOUNTS.roth.ids)}
      chip={<RowDelta pct={DAY_PCT} value={acctValue(ACCOUNTS.roth.ids)} />} push />
      <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.crypto })}
      bar="var(--accent)" name="Crypto" sub="Yoshi · Spot •6614" value={CRYPTO_TOTAL}
      chip={<RowDelta pct={DAY_PCT} value={CRYPTO_TOTAL} />} push />
    </Group>

    <Group title="Paper trading" total={acctValue(ACCOUNTS.paper.ids) + openedPaperTotal} delta={<RowDelta pct={4.25} value={acctValue(ACCOUNTS.paper.ids)} />}>
      <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.paper })}
      bar="var(--rule-2)" name="Test drive 1" sub="Yoshi · Simulated" value={acctValue(ACCOUNTS.paper.ids)} valueColor="var(--ink-2)"
      chip={<RowDelta pct={4.25} value={acctValue(ACCOUNTS.paper.ids)} />} push />
      {openedPaperAccounts.map((account) =>
        <Row key={account.id} onClick={() => nav.push({ type: "account", acct: account })}
        bar="var(--rule-2)" name={account.name} sub={account.sub} value={openedAccountValue(account)} valueColor="var(--ink-2)" push />
      )}
    </Group>
  </>);
};

/* externally-linked, read-only accounts: cash, investments, debt */
const ExternalGroups = ({ nav }) => {
  const [hidden] = useHiddenAccts();
  const chaseHidden = hidden.includes("Chase");
  const schwabHidden = hidden.includes("Schwab");
  const coinbaseHidden = hidden.includes("Coinbase");
  const externalAccountRows = {
    chase: { name: "Chase Checking", sub: "Chase •4417" },
    schwab: { name: "Schwab Brokerage", sub: "Schwab •5520" },
    coinbase: { name: "Coinbase Crypto", sub: "Coinbase •3097" }
  };
  return (
    <>
      <Group title="Cash" total={chaseHidden ? 0 : 8210.32}>
        <div style={{ opacity: chaseHidden ? 0.5 : 1 }}>
          <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.chase })}
          bar="var(--rule-2)" logo="C" name={externalAccountRows.chase.name} sub={externalAccountRows.chase.sub} value={8210.32} valueColor="var(--ink-2)"
          chip={chaseHidden ? <HiddenChip /> : undefined} push />
        </div>
      </Group>

      <Group title="Investments" total={(schwabHidden ? 0 : acctValue(ACCOUNTS.schwab.ids)) + (coinbaseHidden ? 0 : acctValue(ACCOUNTS.coinbase.ids))} delta={<RowDelta pct={0.9} value={(schwabHidden ? 0 : acctValue(ACCOUNTS.schwab.ids)) + (coinbaseHidden ? 0 : acctValue(ACCOUNTS.coinbase.ids))} />}>
        <div style={{ opacity: schwabHidden ? 0.5 : 1 }}>
          <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.schwab })}
          bar="var(--rule-2)" logo="S" name={externalAccountRows.schwab.name} sub={externalAccountRows.schwab.sub} value={acctValue(ACCOUNTS.schwab.ids)} valueColor="var(--ink-2)"
          chip={schwabHidden ? <HiddenChip /> : <RowDelta pct={0.92} value={acctValue(ACCOUNTS.schwab.ids)} />} push />
        </div>
        <div style={{ opacity: coinbaseHidden ? 0.5 : 1 }}>
          <Row onClick={() => nav.push({ type: "account", acct: ACCOUNTS.coinbase })}
          bar="var(--signal-warn)" logo="C" name={externalAccountRows.coinbase.name} sub={externalAccountRows.coinbase.sub} value={acctValue(ACCOUNTS.coinbase.ids)} valueColor="var(--ink-2)"
          chip={coinbaseHidden ? <HiddenChip /> : undefined}
          reauth={coinbaseHidden ? undefined : () => nav.sheet({ type: "link" })} push />
        </div>
      </Group>

      <Group title="Debt" total={LIAB_TOTAL}>
        {LIABILITIES.map((d) =>
        <Row key={d.id} bar="var(--signal-neg)" logo={d.name[0]} name={d.name} sub={d.sub} value={d.value} valueColor="var(--ink-2)"
        chip={<span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10), color: "var(--ink-3)" }}>{d.apr}</span>}
        onClick={() => nav.push({ type: "account", acct: { name: d.name, sub: d.sub, cash: true, totalLabel: "Balance", note: d.apr, debt: d.debt, accounts: [{ id: d.id, name: d.name, sub: d.sub, apr: "", value: d.value, bar: "var(--signal-neg)" }], txns: DEBT_TXNS } })} />
        )}
      </Group>
    </>
  );
};

/* web right pillar — the externally-linked accounts, with Link account on top */
const AccountsExternalPanel = ({ nav, inline, onRepin }) => {
  const repinBtn = onRepin ? (
    <button className="press" onClick={onRepin} aria-label="Pin linked accounts to the side" title="Pin to the side"
      style={{ width: 28, height: 28, borderRadius: 999, background: "none", border: "none", display: "grid", placeItems: "center", color: "var(--ink-2)", cursor: "pointer", padding: 0 }}>
      <Icon name="external" size={14} />
    </button>
  ) : null;
  const body = inline ? (
      /* docked: align to the center column (14px / max 660), drop the Link button
         (it moves under the total balance), re-pin sits in line with the title. */
      <section style={{ padding: "12px 14px 0", maxWidth: 660, marginLeft: "auto", marginRight: "auto", boxSizing: "border-box" }}>
        <TierHead title="External" sub="Linked · read-only" total={LIAB_TOTAL} totalColor="var(--ink-2)" neg action={repinBtn} />
        <ExternalGroups nav={nav} />
      </section>
  ) : (
      <section style={{ padding: "16px 18px 0" }}>
        <div style={{ height: 31 }} />
        <Btn kind="primary" full onClick={() => nav.sheet({ type: "link" })} style={{ padding: "11px 0", fontSize: typeSize(13) }}>+ Link account</Btn>
        {/* drop "External" down so it lines up with "Yoshi Balance" in the main column */}
        <div style={{ height: 57 }} />
        <TierHead title="External" sub="Linked · read-only" total={LIAB_TOTAL} totalColor="var(--ink-2)" neg />
        <ExternalGroups nav={nav} />
      </section>
  );
  if (inline) return body;
  return (
    <aside className="accounts-ext-col">
      <div className="scroll">{body}</div>
    </aside>
  );
};

const Accounts = ({ nav, hasCard, linkUnderTotal, openedAccounts = [] }) => {
  const [fdicOpen, setFdicOpen] = useState(false);
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      <NavBar title="Accounts" border left={<YouButton nav={nav} />}
      right={<window.BellButton nav={nav} />} />

      <div className="scroll">
        {/* total */}
        <section style={window.__YOSHI_WEB ? { padding: "14px 14px 12px", maxWidth: 660, marginLeft: "auto", marginRight: "auto", boxSizing: "border-box" } : { padding: "14px 18px 12px" }}>
          <Eyebrow>Total value</Eyebrow>
          <div style={{ marginTop: 5, display: "flex", alignItems: "center", gap: 7 }}>
            <Money value={NET_WORTH_TRUE} size={34} weight={500} />
            <span style={{ position: "relative", display: "inline-grid", alignSelf: "flex-start", marginTop: 2 }}>
              <button className="press" aria-label="About FDIC insurance" onClick={() => setFdicOpen((o) => !o)} style={{ background: "none", border: "none", padding: 4, display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer" }}>
                <Icon name="info" size={16} stroke={1.5} />
              </button>
              {window.__YOSHI_WEB &&
                <InfoPop open={fdicOpen} onClose={() => setFdicOpen(false)} title="FDIC insurance">
                  {FDIC_DISCLOSURE}
                </InfoPop>}
            </span>
          </div>
          <div style={{ marginTop: 6 }}><NetWorthChange /></div>

          {/* what net worth is made of — a flat ledger summary (mobile only; on web the Yoshi/External columns carry this) */}
          {!window.__YOSHI_WEB &&
          <div style={{ marginTop: 13 }}>
            <div style={{ display: "flex", alignItems: "center", padding: "12px 0", borderBottom: "1px dashed var(--rule)" }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600 }}>Yoshi balance</div>
              </div>
              <span style={{ marginLeft: "auto", display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 2 }}>
                <Money value={ASSET_TOTAL} size={18} weight={600} />
              </span>
            </div>
            <div style={{ display: "flex", alignItems: "center", padding: "12px 0" }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontFamily: "var(--f-display)", fontWeight: 600, fontSize: typeSize(14) }}>External accounts</div>
              </div>
              <span style={{ marginLeft: "auto", display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 2 }}>
                <Money value={-LIAB_TOTAL} size={18} weight={600} />
              </span>
            </div>
          </div>}
        </section>

        {/* docked Accounts: the Link button moves up here, under the total balance */}
        {window.__YOSHI_WEB && linkUnderTotal &&
        <section style={{ padding: "0 14px 10px", maxWidth: 660, marginLeft: "auto", marginRight: "auto", boxSizing: "border-box" }}>
          <Btn kind="primary" full onClick={() => nav.sheet({ type: "link" })} style={{ padding: "11px 0", fontSize: typeSize(13) }}>+ Link account</Btn>
        </section>}

        {/* actions — mobile only (web puts Transfer in the top nav, Link in the right column) */}
        {!window.__YOSHI_WEB &&
        <section style={{ padding: "0 18px 14px", display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 7 }}>
          <Btn kind="primary" onClick={() => nav.sheet({ type: "transfer" })} style={{ padding: "11px 6px", fontSize: typeSize(13) }}>Transfer</Btn>
          <Btn kind="ghost" onClick={() => nav.sheet({ type: "link" })} style={{ padding: "11px 6px", fontSize: typeSize(13) }}>+ Link account</Btn>
        </section>}

        {window.__YOSHI_WEB ?
        <section style={{ padding: "12px 14px 0", maxWidth: 660, marginLeft: "auto", marginRight: "auto", boxSizing: "border-box" }}>
          <TierHead title="Yoshi Balance" total={ASSET_TOTAL} />
          <YoshiGroups nav={nav} hasCard={hasCard} openedAccounts={openedAccounts} />
        </section> :
        <>
          <Tier title="Yoshi" />
          <YoshiGroups nav={nav} hasCard={hasCard} openedAccounts={openedAccounts} />
          <Tier title="External" />
          <ExternalGroups nav={nav} />
        </>}

        <div style={{ height: 20 }} />
      </div>

      {/* FDIC disclosure · slide-up bottom sheet */}
      {fdicOpen && !window.__YOSHI_WEB &&
      <>
        <div onClick={() => setFdicOpen(false)} 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", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
          <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))", display: "flex", gap: 12, alignItems: "flex-start" }}>
            <p style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(13.5), lineHeight: 1.6, color: "var(--ink-2)", margin: 0, display: "flex", gap: 9, alignItems: "flex-start" }}>
              <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} style={{ flex: "none", marginTop: 2 }} />
              <span>{FDIC_DISCLOSURE}</span>
            </p>
            <button className="press" aria-label="Close" onClick={() => setFdicOpen(false)} style={{ flex: "none", background: "none", border: "none", display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", padding: 2 }}>
              <Icon name="close" size={18} />
            </button>
          </div>
        </div>
      </>}
    </div>);

};

/* a row's daily-change chip · shows both % and $ inline */
const RowDelta = ({ pct: p, value }) => {
  const dollars = Math.round(value * p / 100);
  const c = p >= 0 ? "var(--accent-pos)" : "var(--signal-neg)";
  return (
    <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(11), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: c, letterSpacing: "-0.005em", whiteSpace: "nowrap" }}>{perf(dollars, p)}</span>);

};

const Group = ({ title, total, live, neg, tag, delta, children }) => {
  const [open, setOpen] = useState(true);
  const showNeg = neg || total < 0;
  return (
    <section style={{ padding: "16px 0 0" }}>
      <button className="press" onClick={() => setOpen((o) => !o)} style={{ width: "100%", background: "none", border: "none", cursor: "pointer", padding: window.__YOSHI_WEB ? "0 18px 9px 0" : "0 18px 8px", display: "flex", alignItems: "center", gap: 8, textAlign: "left" }}>
        <Icon name="down" size={window.__YOSHI_WEB ? 16 : 15} color={window.__YOSHI_WEB ? "var(--ink-2)" : "var(--ink-3)"} style={{ flex: "none", transform: open ? "rotate(180deg)" : "none", transition: "transform 180ms ease" }} />
        <span style={window.__YOSHI_WEB ? { fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--accent)" } : { fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, letterSpacing: "-0.015em" }}>{title}</span>
        {live && <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--accent)", letterSpacing: "0.02em" }}>● {live}</span>}
        {tag && <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), color: "var(--ink-3)", letterSpacing: "0.08em", textTransform: "uppercase" }}>{tag}</span>}
        <span style={{ marginLeft: "auto", marginRight: window.__YOSHI_WEB ? 28 : 0, display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 2 }}>
          <Money value={Math.abs(total)} size={window.__YOSHI_WEB ? 21 : 16} weight={window.__YOSHI_WEB ? 600 : 500} prefix={showNeg ? "−$" : "$"} color={showNeg ? "var(--signal-neg)" : "var(--ink)"} />
          {delta}
        </span>
      </button>
      {open && <div className="acct-card" style={window.__YOSHI_WEB ? { marginTop: 4, border: "1px solid var(--rule-2)", borderRadius: 12, background: "var(--bg-card)", overflow: "hidden" } : { borderTop: "1px solid var(--rule)" }}>{children}</div>}
    </section>);

};

/* group-level performance line under the headline total — matches RowDelta style */
const GroupFlow = ({ text, pos = true }) =>
<span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(11), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: pos ? "var(--accent-pos)" : "var(--signal-neg)", letterSpacing: "-0.005em", whiteSpace: "nowrap" }}>{text}</span>;

/* top-level tier header — separates Yoshi-held accounts from external/linked ones */
const Tier = ({ title, tag }) =>
<div style={{ padding: "24px 18px 0", display: "flex", alignItems: "baseline", gap: 8 }}>
    <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 700, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--ink-2)" }}>{title}</span>
    {tag && <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(9.5), color: "var(--ink-3)", letterSpacing: "0.08em", textTransform: "uppercase" }}>{tag}</span>}
  </div>;


const Row = ({ bar, logo, name, sub, value, valueColor, chip, onClick, push, reauth }) => {
  const web = !!window.__YOSHI_WEB;
  const cols = logo ? "30px 1fr auto 16px" : web ? "1fr auto 16px" : "3px 1fr auto 16px";
  return (
<button className="press" onClick={onClick} style={{
  width: "100%", textAlign: "left", background: "none", border: "none",
  display: "grid", gridTemplateColumns: cols, gap: 12, padding: "15px 18px",
  borderBottom: "1px solid var(--rule)", alignItems: "center"
}}>
    {logo ?
      <span style={{ width: 30, height: 30, flex: "none", borderRadius: 999, display: "grid", placeItems: "center", background: "var(--bg-2)", border: "1px solid var(--rule-2)", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 700, letterSpacing: "-0.01em" }}>{logo}</span> :
      !web ?
      <div style={{ background: bar, width: 3, height: 30 }} /> : null}
    <div style={{ minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, letterSpacing: "-0.005em" }}>{name}</div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sub}</div>
    </div>
    <div style={{ textAlign: "right" }}>
      {reauth ?
    <span role="button" tabIndex={0} className="press" onClick={(e) => {e.stopPropagation();reauth();}}
    style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--signal-warn)", background: "color-mix(in srgb, var(--signal-warn) 14%, transparent)", border: "1px solid color-mix(in srgb, var(--signal-warn) 38%, transparent)", borderRadius: 999, padding: "5px 11px", cursor: "pointer", whiteSpace: "nowrap" }}>
        <Icon name="connect" size={12.5} color="var(--signal-warn)" /> Re-auth
      </span> :
    <>
        <Money value={value} size={window.__YOSHI_WEB ? 13 : 12} color={valueColor || "var(--ink)"} />
        {chip && <div style={{ marginTop: 2 }}>{chip}</div>}
      </>}
    </div>
    <Icon name="back" size={16} color="var(--ink-3)" style={{ transform: "scaleX(-1)", justifySelf: "end" }} />
  </button>
  );
};


/* ============================================================
   Holding detail
   ============================================================ */
const RANGES = ["1D", "1W", "1M", "6M", "YTD", "1Y", "5Y", "ALL"];

const buildSeries = (h, range) => {
  const yearReturn = typeof securityYearReturn === "function" ? securityYearReturn(h) : h.perf?.["1Y"] ?? 17;
  const cfg = {
    "1D": [30, h.dch, 0.0011],
    "1W": [28, h.perf?.["1W"] ?? +(h.dch * 1.5).toFixed(1), 0.004],
    "1M": [32, h.perf?.["1M"] ?? h.dch * 4.5, 0.006],
    "6M": [40, yearReturn * 0.55, 0.01],
    "YTD": [44, yearReturn * 0.7, 0.011],
    "1Y": [52, yearReturn, 0.012],
    "5Y": [60, (Math.pow(1 + yearReturn / 100, 5) - 1) * 100, 0.018],
    "ALL": [64, (Math.pow(1 + yearReturn / 100, 7) - 1) * 100, 0.02]
  }[range] || [32, h.perf?.["1M"] ?? h.dch * 4.5, 0.006];
  const [len, movePct, vol] = cfg;
  const start = h.last / (1 + movePct / 100);
  return { data: series((h.seed || 1) + len, len, start, h.last, vol), movePct };
};

const txFor = (h) => {
  const buy = (qty, px, when, note) => ({
    side: "BUY",
    icon: "trade",
    title: `Bought ${h.ticker}`,
    detail: `${qty}${h.kind === "crypto" ? "" : " sh"} @ ${usd(px, h.kind === "crypto" ? 0 : 2)}${note ? " · " + note : ""}`,
    when,
    net: -(Number(qty) * px)
  });
  if (h.kind === "crypto") return [
  buy((h.shares * 0.3).toFixed(4), h.last * 0.94, "Apr 19"),
  buy((h.shares * 0.4).toFixed(4), h.avg * 1.1, "Mar 02"),
  buy((h.shares * 0.3).toFixed(4), h.avg, "Jan 14")];

  return [
  buy(Math.round(h.shares * 0.2), h.last * 0.97, "Fri", "auto-invest"),
  buy(Math.round(h.shares * 0.3), h.avg * 1.12, "Apr 04"),
  { side: "DIV", icon: "down", title: `Dividend · ${h.ticker}`, detail: "Reinvested", when: "Mar 28", net: h.value * 0.003 },
  buy(Math.round(h.shares * 0.5), h.avg, "Feb 11")];

};

/* Yoshi's bespoke, position-aware take for a holding — shown on its detail
   page, with a one-tap follow-up into chat. Specific by ticker, with sensible
   fallbacks by asset kind + whether the position is at a gain. */
function yoshiTake(h) {
  const t = h.ticker,gain = h.pl >= 0,paper = String(h.id).startsWith("pp-");
  const named = {
    NVDA: ["Your standout winner — and now your largest single position. I'd consider trimming a slice to lock in some of that gain and keep one name from steering the whole portfolio.", `trim some ${t} to lock gains`],
    AAPL: ["Steady ballast that's doing its job. Nothing here needs a decision today — hold and let it compound.", `review my ${t} position`],
    MSFT: ["Quietly compounding. A reasonable place to add on a dip; otherwise just hold.", `set up a buy-the-dip rule for ${t}`],
    VOO: ["Your low-cost index core. Keep the auto-invest running — this is the boring engine that does most of the work.", `review my ${t} auto-invest`],
    VTI: ["Total-market index, and it's in your Roth — exactly where it belongs, since the gains compound tax-free. Let it ride.", `review my Roth allocation`],
    BTC: ["Up strongly from your cost, and your most volatile sleeve. I'd size it deliberately so a rough week doesn't rattle you — trimming toward a target weight is the clean move.", `set a target weight for ${t}`],
    ETH: ["Held at a gain but choppier than Bitcoin. Fine as a smaller satellite; I wouldn't chase it higher from here.", `rebalance my crypto`],
    SOL: ["A big run on a high-beta name. Taking a little profit into your cash buffer locks some of it in without trying to call the top.", `take some ${t} profit into cash`]
  };
  if (named[t]) return { take: named[t][0], cue: named[t][1] };
  if (paper) return { take: `This is a paper-trade position — no real money at risk. A good place to test a thesis on ${t} before you put real capital behind it.`, cue: `talk through my ${t} thesis` };
  if (h.kind === "crypto") return gain ?
  { take: `${t} is up from your cost, and it's a volatile sleeve. Worth sizing on purpose rather than adding on momentum — I can set a target weight.`, cue: `set a target weight for ${t}` } :
  { take: `${t} is below your cost. If the thesis still holds, waiting is fine; if not, I can harvest the loss to offset gains.`, cue: `harvest the loss on ${t}` };
  if (h.kind === "etf") return { take: `${t} is a diversified index position — a sensible core you can keep adding to on a schedule.`, cue: `automate adding to ${t}` };
  return gain ?
  { take: `${t} is working for you — up from your cost. No need to touch it unless it's grown into too large a slice of the portfolio.`, cue: `check whether ${t} is overweight` } :
  { take: `${t} is below your cost. If you still believe the story, holding is reasonable; otherwise I can put the loss to work at tax time.`, cue: `weigh holding vs. selling ${t}` };
}

const SECURITY_DETAIL_AUDIT = [
  "Equity fundamentals: company profile, valuation, profitability, per-share and market profile",
  "Equity events: earnings, analyst view, corporate actions, distributions, estimates and splits",
  "ETF exposures: allocation, sector, country, region and top holdings",
  "ETF profile: sponsor, type, index, fund flows, trailing returns and volatility",
  "News: source, timestamp, sentiment and preview copy"
];

const SECURITY_POSITION_TABS = ["overview", "details"];
const SECURITY_FUNDAMENTAL_TABS = {
  stock: ["overview", "events"],
  etf: ["overview", "exposure", "etf", "events"],
  crypto: ["overview"]
};

const SECURITY_TAB_LABELS = {
  overview: "Overview",
  details: "Details",
  events: "Events",
  exposure: "Exposure",
  etf: "Details"
};

const equitySecurityDetail = (h) => ({
  kindLabel: "Equity",
  companyProfile: [
    ["Sector", h.ticker === "NVDA" ? "Electronic Technology" : "Technology"],
    ["Industry", h.ticker === "NVDA" ? "Semiconductors" : "Software and Services"],
    ["Market cap", h.ticker === "NVDA" ? "$4.82T" : "$3.48T"],
    ["P/E ratio", h.fund?.find(([k]) => k === "P/E ratio")?.[1] || "31.4"],
    ["Dividend yield", h.ticker === "NVDA" ? "0.02%" : "0.71%"]
  ],
  description: h.ticker === "NVDA"
    ? "NVIDIA designs graphics processors, accelerated computing platforms and related software used across gaming, data centers, automotive systems and enterprise AI infrastructure."
    : `${h.name} is a large-cap operating company. Yoshi tracks fundamentals, estimates and events here so a single security can be reviewed without leaving the app.`,
  valuation: [["Enterprise value", h.ticker === "NVDA" ? "$5.01T" : "$3.55T"], ["Price/book", h.ticker === "NVDA" ? "24.66" : "12.40"], ["Book value/share", h.ticker === "NVDA" ? "$8.07" : "$31.20"]],
  profitability: [["Revenue", h.ticker === "NVDA" ? "$253.49B" : "$198.27B"], ["Net income", h.ticker === "NVDA" ? "$159.61B" : "$72.18B"], ["Gross margin", h.ticker === "NVDA" ? "74.15%" : "61.22%"], ["Net margin", h.ticker === "NVDA" ? "71.46%" : "36.41%"]],
  perShare: [["EPS", h.ticker === "NVDA" ? "$6.53" : "$11.84"], ["Total debt", h.ticker === "NVDA" ? "$12.81B" : "$46.20B"]],
  marketProfile: [["Beta", h.ticker === "NVDA" ? "2.11" : "1.08"], ["Employees", h.ticker === "NVDA" ? "42,000" : "221,000"]],
  earnings: [["Q1", "05/20/2026"], ["Q2", "08/26/2026"], ["Q3", "11/19/2025"], ["Q4", "02/25/2026"]],
  analystView: [["Consensus", "Buy"], ["Analysts", h.ticker === "NVDA" ? "67" : "49"], ["Target price", h.ticker === "NVDA" ? "$310.62" : usd(h.last * 1.18)], ["High target", h.ticker === "NVDA" ? "$743.10" : usd(h.last * 1.65)], ["Low target", h.ticker === "NVDA" ? "$180.00" : usd(h.last * 0.82)], ["Estimate rows", "6"]],
  corporateActions: [["Annual General Meeting", "2026-06-24", "—", "EIAG1398783_30DAC726"], ["Distribution", "2026-06-04", "—", "EIDI4815764_30DAC726"], ["Distribution", "2026-03-11", "—", "EIDI4634331_30DAC726"]],
  distributions: [["2026-06-04", "2026-06-04", "2026-06-26", "0.25"], ["2026-03-11", "2026-03-11", "2026-04-01", "0.01"], ["2025-12-04", "2025-12-04", "2025-12-26", "0.01"], ["2025-09-11", "2025-09-11", "2025-10-02", "0.01"]],
  estimates: [["EPS", "2026FY", "8.938635", "49", "8.12", "9.85"], ["Sales", "2026FY", "392058.904863", "58", "357215", "415524.773224"], ["EPS", "2026Q2", "2.075352", "40", "2.01", "2.20"]],
  splits: [],
  news: (h.news || []).map(([src, head, when]) => ({ source: src, headline: head, when, tone: h.dch >= 0 ? "Soft" : "Watch", summary: `${head}. Yoshi tracks this alongside your position, analyst view and event calendar.` }))
});

const ETF_DETAIL_BY_TICKER = {
  VTI: { sponsor: "Vanguard", etfType: "Total market equity", index: "CRSP US Total Market Index", indexProvider: "CRSP", expense: "0.03%", secYield: "1.31%", ttmYield: "1.37%", category: "US Fund Large Blend", globalCategory: "US Equity", style: "Large Blend", description: "VTI seeks to track the performance of the broad US equity market across large-, mid- and small-cap stocks." },
  VOO: { sponsor: "Vanguard", etfType: "S&P 500 equity", index: "S&P 500 Index", indexProvider: "S&P Dow Jones", expense: "0.03%", secYield: "1.27%", ttmYield: "1.31%", category: "US Fund Large Blend", globalCategory: "US Equity", style: "Large Blend", description: "VOO seeks to track the performance of the S&P 500 Index before fees and expenses." },
  SPY: { sponsor: "State Street Global Advisors", etfType: "S&P 500 equity", index: "S&P 500 Index", indexProvider: "S&P Dow Jones", expense: "0.09%", secYield: "1.20%", ttmYield: "1.25%", category: "US Fund Large Blend", globalCategory: "US Equity", style: "Large Blend", description: "SPY seeks to track the performance of the S&P 500 Index and is used as broad US large-cap exposure." },
  QQQ: { sponsor: "Invesco", etfType: "Nasdaq 100 equity", index: "Nasdaq-100 Index", indexProvider: "Nasdaq", expense: "0.20%", secYield: "0.58%", ttmYield: "0.62%", category: "US Fund Large Growth", globalCategory: "US Equity", style: "Large Growth", description: "QQQ seeks to track the Nasdaq-100 Index, with high exposure to technology and communication services." }
};

const etfSecurityDetail = (h) => {
  const meta = ETF_DETAIL_BY_TICKER[h.ticker] || ETF_DETAIL_BY_TICKER.VTI;
  return {
    kindLabel: "ETF",
    companyProfile: [["Sector", "Miscellaneous"], ["Industry", "Investment Trusts or Mutual Funds"], ["Market cap", "—"], ["P/E ratio", "—"], ["Dividend yield", meta.ttmYield]],
    fund: [["Expense ratio", meta.expense], ["SEC yield", meta.secYield], ["TTM yield", meta.ttmYield], ["Duration", "—"], ["Fund category", meta.category], ["Global category", meta.globalCategory], ["Style", meta.style]],
    etfProfile: [["Sponsor", meta.sponsor], ["ETF type", meta.etfType], ["Asset class", "Equities (Stocks)"], ["Linked index", meta.index], ["Index provider", meta.indexProvider], ["Inception", h.ticker === "SPY" ? "1993-01-22" : "2001-05-24"], ["Listing", h.ticker === "SPY" ? "1993-01-22" : "2001-05-24"], ["Replication", "Physical"], ["Domicile", "US"], ["Base currency", "USD"], ["Prospectus", "sec.gov/Archives/edgar"]],
    description: meta.description,
    exposures: [
      { title: "Asset allocation", asOf: "Mar. 31, 2026", rows: [["U.S. stocks", "97.42%", "var(--accent)"], ["International stocks", "1.82%", "var(--alloc-cash)"], ["Other", "0.76%", "var(--signal-neg)"]] },
      { title: "Sector", rows: [["Technology", "32.14%", "var(--accent)"], ["Financials", "13.48%", "var(--alloc-cash)"], ["Health care", "11.26%", "var(--signal-neg)"], ["Consumer discretionary", "10.42%", "var(--alloc-crypto)"], ["Industrials", "8.71%", "var(--accent-pos)"]] },
      { title: "Country", asOf: "Mar. 31, 2026", rows: [["United States", "96.44%", "var(--accent)"], ["Canada", "1.02%", "var(--alloc-cash)"], ["United Kingdom", "0.74%", "var(--signal-neg)"], ["Other", "1.80%", "var(--ink-3)"]] },
      { title: "Region", rows: [["North America", "97.46%", "var(--accent)"], ["Europe", "1.12%", "var(--alloc-cash)"], ["Asia", "0.61%", "var(--signal-neg)"], ["Other", "0.81%", "var(--ink-3)"]] }
    ],
    topHoldings: [["Apple Inc", "AAPL", "Technology", "United States", "175.6M", "6.04%"], ["Microsoft Corp", "MSFT", "Technology", "United States", "82.1M", "5.77%"], ["NVIDIA Corp", "NVDA", "Semiconductors", "United States", "65.2M", "5.31%"], ["Amazon.com Inc", "AMZN", "Consumer", "United States", "58.8M", "3.42%"], ["Meta Platforms", "META", "Communication", "United States", "28.1M", "2.54%"]],
    fundFlows: [["2026-06-24", "1,074,012,781", "-9,820,000", "283,219,146", "2,933,706,934", "4,376,225,848"], ["2026-06-23", "1,074,012,781", "13,085,750", "283,219,014", "2,933,707,041", "4,470,868,675"], ["2026-06-22", "1,083,189,031", "22,262,000", "292,395,264", "2,942,883,292", "4,563,530,846"]],
    trailingReturns: [["Since inception", "0.037", "2026-06-24", "2026-06-25 10:41:03"], ["10 Year", "0.126", "2026-06-24", "2026-06-25 10:41:03"], ["5 Year", "0.197", "2026-06-24", "2026-06-25 10:41:03"], ["3 Year", "0.287", "2026-06-24", "2026-06-25 10:41:03"], ["1 Year", "0.132", "2026-06-24", "2026-06-25 10:41:03"], ["YTD", "-0.038", "2026-06-24", "2026-06-25 10:41:03"]],
    volatility: [["5 Year", "0.297", "—", "2026-06-24"], ["3 Year", "0.350", "—", "2026-06-24"], ["1 Year", "0.429", "—", "2026-06-24"], ["3 Month", "—", "474,590,307", "2026-06-24"], ["1 Month", "—", "524,552,729", "2026-06-24"]],
    earnings: [["Q1", "—"], ["Q2", "—"], ["Q3", "—"], ["Q4", "—"]],
    analystView: [["Consensus", "—"], ["Analysts", "—"], ["Target price", "—"], ["High target", "—"], ["Low target", "—"], ["Estimate rows", "0"]],
    corporateActions: [["Distribution", "2025-12-22", "—", "EIDI3845336_6BCBBE1A"], ["Distribution", "2024-12-23", "—", "EIDI3287802_6BCBBE1A"], ["Issuer Name Change", "2022-09-28", "—", "EIIS69852_6BCBBE1A"]],
    distributions: [["2025-12-29", "2025-12-29", "2025-12-30", "—"], ["2025-12-22", "2025-12-22", "2025-12-26", "3.1661"], ["2024-12-23", "2024-12-23", "2024-12-24", "0.6142"], ["2023-12-18", "2023-12-19", "2023-12-22", "3.258"]],
    estimates: [],
    splits: [],
    news: (h.news || []).length ? h.news.map(([src, head, when]) => ({ source: src, headline: head, when, tone: "Soft", summary: `${head}. Yoshi pairs this with ETF profile, exposure and flow data.` })) : []
  };
};

const cryptoSecurityDetail = (h) => ({
  kindLabel: "Crypto",
  companyProfile: h.fund || [["Network", h.name], ["Holdings", `${h.shares}`], ["Cost basis", usd(h.avg)]],
  news: (h.news || []).map(([src, head, when]) => ({ source: src, headline: head, when, tone: "Soft", summary: `${head}. Yoshi tracks crypto market context separately from ETF and equity fundamentals.` }))
});

const securityDetailData = (h) => h.kind === "etf" ? etfSecurityDetail(h) : h.kind === "crypto" ? cryptoSecurityDetail(h) : equitySecurityDetail(h);
const securityAccountLabel = (h) => h.group === "crypto" ? "Crypto" : h.ticker === "VTI" ? "Roth IRA" : String(h.id).startsWith("sch-") ? "Schwab Brokerage" : String(h.id).startsWith("pp-") ? "Test drive 1" : "Brokerage";
const securityAccountSub = (h) => h.group === "crypto" ? "Spot account" : h.ticker === "VTI" ? "Retirement" : "Individual Taxable";
const securityAccountHoldingRows = (h) => [{
  id: `${h.id || h.ticker}-account-position`,
  account: securityAccountLabel(h),
  sub: securityAccountSub(h),
  shares: h.shares,
  value: h.value,
  dayAbs: h.dayAbs,
  dch: h.dch,
  avg: h.avg,
  pl: h.pl,
  plPct: h.plPct,
  kind: h.kind,
  weight: 100
}];

const DetailSection = ({ title, children, sub }) =>
<section style={{ padding: "16px 18px 0" }}>
    <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 10 }}>
      <Eyebrow color="var(--ink-2)" style={{ fontSize: typeSize(10.5) }}>{title}</Eyebrow>
      {sub && <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{sub}</span>}
    </div>
    {children}
  </section>;

const hasUsefulFundamentalValue = (value, label) => {
  const text = String(value ?? "").trim();
  if (!text) return false;
  if (["-", "—", "--", "n/a", "na", "null", "undefined"].includes(text.toLowerCase())) return false;
  if (/estimate rows/i.test(String(label || "")) && Number(text.replace(/,/g, "")) === 0) return false;
  return true;
};

const cleanFactRows = (rows = []) => rows.filter(([label, value]) => hasUsefulFundamentalValue(value, label));
const cleanTableRows = (rows = []) => rows.filter((row) => row.slice(1).some((cell) => hasUsefulFundamentalValue(cell)));
const cleanBreakdownRows = (rows = []) => rows.filter(([label, value]) => hasUsefulFundamentalValue(label) && hasUsefulFundamentalValue(value));

const FactGrid = ({ rows, cols = 2 }) => {
  const usable = cleanFactRows(rows);
  if (!usable.length) return null;
  return (
    <div style={{ display: "grid", gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`, gap: "0 14px" }}>
      {usable.map(([label, value], i) =>
      <div key={label + i} style={{ minWidth: 0, padding: "10px 0", borderBottom: "1px solid var(--rule)" }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{label}</div>
          <div style={{ marginTop: 4, fontFamily: /[0-9$%]/.test(String(value)) ? "var(--f-mono)" : "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, color: "var(--ink)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div>
        </div>
      )}
    </div>);
};

const MiniTable = ({ columns, rows = [], strongFirst = false }) => {
  rows = cleanTableRows(rows);
  if (!rows.length) return null;
  return (
    <div className={columns.length > 4 ? "hcar" : undefined} style={{ border: "1px solid var(--rule)", borderRadius: 12, overflowX: columns.length > 4 ? "auto" : "hidden", overflowY: "hidden", background: "var(--bg-card)" }}>
    <div style={{ minWidth: columns.length > 4 ? columns.length * 92 : "auto" }}>
      <div style={{ display: "grid", gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`, padding: "10px 12px", borderBottom: "1px solid var(--rule)", color: "var(--ink-3)", fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 700 }}>
        {columns.map((c) => <span key={c} style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{c}</span>)}
      </div>
      {rows.map((row, i) =>
      <div key={i} style={{ display: "grid", gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`, padding: "11px 12px", borderBottom: i === rows.length - 1 ? "none" : "1px solid var(--rule)", gap: 8, alignItems: "baseline", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-2)" }}>
          {row.map((cell, j) => <span key={j} style={{ minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontWeight: strongFirst && j === 0 ? 700 : 500, color: strongFirst && j === 0 ? "var(--ink)" : "var(--ink-2)", fontFamily: /[0-9$%]/.test(String(cell)) ? "var(--f-mono)" : "var(--f-display)" }}>{cell || "—"}</span>)}
        </div>
      )}
    </div>
  </div>);
};

const renderDetailSection = ({ title, rows = [], cols = 2, sub, description }) => {
  const usable = cleanFactRows(rows);
  if (!usable.length && !description) return null;
  return (
    <DetailSection title={title} sub={sub}>
      <FactGrid rows={usable} cols={cols} />
      {description && <p style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), lineHeight: 1.55, color: "var(--ink-2)", margin: usable.length ? "14px 0 0" : 0 }}>{description}</p>}
    </DetailSection>);
};

const renderTableSection = ({ title, columns, rows = [], strongFirst = false, sub }) => {
  const usable = cleanTableRows(rows);
  if (!usable.length) return null;
  return (
    <DetailSection title={title} sub={sub}>
      <MiniTable columns={columns} rows={usable} strongFirst={strongFirst} />
    </DetailSection>);
};

const newsSummaryText = (h, items) => {
  if (!items.length) return `No recent headlines were returned for ${h.ticker}.`;
  const lead = items[0]?.headline || `${h.ticker} is in focus`;
  const extra = items.length > 1 ? ` ${items.length - 1} more headline${items.length === 2 ? "" : "s"} add context around recent movement.` : "";
  return `${lead.replace(/[.?!]$/, "")}.${extra}`;
};

const SecurityNewsSummaryCard = ({ h, detail }) => {
  const [expanded, setExpanded] = useState(false);
  const items = detail.news || [];
  return (
    <div data-security-news-summary="expandable-links" style={{ border: "1px solid var(--rule-2)", borderRadius: 12, background: "transparent", position: "relative", overflow: "hidden" }}>
      <div style={{ padding: "13px 14px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <Icon name="doc" size={15} color="var(--ink-2)" stroke={1.6} />
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)" }}>News summary</span>
        </div>
        <p style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), lineHeight: 1.5, margin: "9px 0 0", color: "var(--ink)" }}>{newsSummaryText(h, items)}</p>
        {items.length ? (
          <>
            <button className="press" onClick={() => setExpanded((open) => !open)} style={{ display: "inline-flex", alignItems: "center", gap: 5, marginTop: 11, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, color: "var(--ink-2)" }}>
              {expanded ? "Hide links" : "See more"} <Icon name="back" size={14} color="var(--ink-2)" style={{ transform: expanded ? "rotate(90deg)" : "scaleX(-1)" }} />
            </button>
            {expanded &&
              <div style={{ marginTop: 12, borderTop: "1px solid var(--rule)" }}>
                {items.map((item, i) =>
                <a key={i} href="#" onClick={(e) => e.preventDefault()} style={{ display: "block", padding: "11px 0", borderBottom: i === items.length - 1 ? "none" : "1px solid var(--rule)", textDecoration: "none", color: "inherit" }}>
                    <span style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
                      <span style={{ minWidth: 0, flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 700, lineHeight: 1.35, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{item.headline}</span>
                      <Icon name="external" size={13} color="var(--ink-3)" stroke={1.6} style={{ flex: "none" }} />
                    </span>
                    <span style={{ display: "flex", gap: 8, marginTop: 5, fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>
                      <span>{item.source}</span><span>{item.when}</span><span>{item.tone}</span>
                    </span>
                  </a>
                )}
              </div>}
          </>
        ) : null}
      </div>
    </div>);
};

const SecurityEventsPanel = ({ detail }) =>
<>
    {renderDetailSection({ title: "Earnings", rows: detail.earnings || [], cols: 4 })}
    {renderDetailSection({ title: "Analyst view", rows: detail.analystView || [] })}
    {renderTableSection({ title: "Corporate action summaries", columns: ["Type", "Date", "Status", "Description"], rows: detail.corporateActions || [] })}
    {renderTableSection({ title: "Distributions", columns: ["Ex-date", "Record", "Pay", "Amount"], rows: detail.distributions || [] })}
    {renderTableSection({ title: "Analyst estimates", columns: ["Type", "Period", "Value", "Analysts", "Low", "High"], rows: detail.estimates || [] })}
    {renderTableSection({ title: "Splits", columns: ["Date", "Ratio", "Status"], rows: detail.splits || [] })}
  </>;

const ExposureBreakdownCard = ({ breakdown }) =>
<div style={{ border: "1px solid var(--rule-2)", borderRadius: 12, background: "var(--bg-card)", padding: 12 }}>
    <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 12 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 700 }}>{breakdown.title}</div>
      {breakdown.asOf && <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{breakdown.asOf}</span>}
    </div>
    {cleanBreakdownRows(breakdown.rows || []).map(([label, value, color]) =>
    <div key={label} style={{ display: "grid", gridTemplateColumns: "10px 1fr auto", gap: 8, alignItems: "center", padding: "6px 0" }}>
        <span style={{ width: 8, height: 8, borderRadius: 999, background: color }} />
        <span style={{ minWidth: 0, fontFamily: "var(--f-display)", fontSize: typeSize(12), color: "var(--ink-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{label}</span>
        <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), fontWeight: 700 }}>{value}</span>
      </div>
    )}
  </div>;

const SecurityExposuresPanel = ({ detail }) => {
  const breakdowns = (detail.exposures || []).filter((breakdown) => cleanBreakdownRows(breakdown.rows || []).length);
  return (
    <>
      {breakdowns.length ? <DetailSection title="Exposure breakdowns">
        <div style={{ display: "grid", gap: 10 }}>{breakdowns.map((breakdown) => <ExposureBreakdownCard key={breakdown.title} breakdown={breakdown} />)}</div>
      </DetailSection> : null}
      {renderTableSection({ title: "Top holdings", columns: ["Holding", "Symbol", "Industry", "Country", "Shares", "Weight"], rows: detail.topHoldings || [], strongFirst: true })}
    </>);
};

const SecurityEtfPanel = ({ detail }) =>
<>
    {renderDetailSection({ title: "ETF profile", rows: detail.etfProfile || [], description: detail.description })}
    {renderTableSection({ title: "Fund flows", columns: ["Date", "YTD", "1M", "3M", "1Y", "AUM"], rows: detail.fundFlows || [] })}
    {renderTableSection({ title: "Trailing returns", columns: ["Period", "Return", "As of", "Updated"], rows: detail.trailingReturns || [] })}
    {renderTableSection({ title: "Volatility", columns: ["Period", "Volatility", "Avg daily volume", "As of"], rows: detail.volatility || [] })}
  </>;

const securityFundamentalTabsFor = (h) => SECURITY_FUNDAMENTAL_TABS[h.kind === "etf" ? "etf" : h.kind === "crypto" ? "crypto" : "stock"];

const securityDetailModel = (security) => {
  const held = typeof securityHolding === "function" ? securityHolding(security) : HOLDINGS.find((x) => x.id === security?.id || x.ticker === security?.ticker);
  const source = held || security || {};
  const market = MARKET.find((m) => m.id === source.id || m.ticker === source.ticker) || source;
  const last = source.last ?? market.last ?? 100;
  const dch = source.dch ?? market.dch ?? 0;
  const yearReturn = typeof securityYearReturn === "function" ? securityYearReturn(source) : source.perf?.["1Y"] ?? 17;
  const perfValue = source.perf || { "1W": +(dch * 1.5).toFixed(1), "1M": +(dch * 4.5).toFixed(1), "1Y": yearReturn };
  const shares = held?.shares ?? 0;
  const avg = held?.avg ?? last;
  const value = held?.value ?? shares * last;
  const cost = held?.cost ?? shares * avg;
  const positionDayAbs = held?.dayAbs ?? (value ? value * dch / 100 : last * dch / 100);
  return {
    ...market,
    ...source,
    ticker: source.ticker || market.ticker || "SEC",
    name: source.name || market.name || "Security",
    kind: source.kind || market.kind || "stock",
    last,
    dch,
    perf: perfValue,
    seed: source.seed ?? Math.round(Math.abs(shares || last) + last),
    shares,
    avg,
    value,
    cost,
    pl: held?.pl ?? value - cost,
    plPct: held?.plPct ?? (cost ? (value - cost) / cost * 100 : 0),
    dayAbs: positionDayAbs,
    news: source.news || market.news || [[market.kind === "crypto" ? "CoinDesk" : "Markets", `${source.name || market.name || "This security"} in focus as volume picks up`, "1h"]],
    hasPosition: !!held
  };
};

const SecurityPillTabs = ({ tabs, value, onChange, marker, wrap = false }) =>
<div data-security-pill-tabs={marker} data-security-pill-style="studio" data-security-pill-layout={wrap ? "wrap" : "inline"} className={wrap ? undefined : "hcar"} style={{ display: "flex", gap: 7, flexWrap: wrap ? "wrap" : "nowrap", overflowX: wrap ? "visible" : "auto", paddingBottom: 2, minWidth: 0 }}>
    {tabs.map((tab) => {
      const on = value === tab;
      return <button key={tab} className="press" onClick={() => onChange(tab)} style={{ flex: "none", padding: "7px 13px", borderRadius: 999, cursor: "pointer", background: on ? "var(--accent)" : "color-mix(in srgb, var(--ink) 8%, var(--bg-2))", border: "none", color: on ? "var(--accent-ink)" : "var(--ink-2)", fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 600 }}>{SECURITY_TAB_LABELS[tab]}</button>;
    })}
  </div>;

const SecurityPriceGraphSection = ({ h }) => {
  const [range, setRange] = useState("1M");
  const [scrub, setScrub] = useState(null);
  const { data, movePct } = useMemo(() => buildSeries(h, range), [h, range]);
  const accent = data.length > 1 && data[data.length - 1] >= data[0] ? "var(--accent-pos)" : "var(--signal-neg)";
  const price = scrub != null ? scrub : h.last;
  // $ + % move over the range: 1D trusts the official day change, other
  // ranges derive the dollar move from the series behind the chart
  const moveAbs = range === "1D" ? h.last - h.last / (1 + h.dch / 100) : data.length > 1 ? data[data.length - 1] - data[0] : 0;
  const movePctShown = range === "1D" ? h.dch : movePct;
  return (
    <section style={{ padding: "12px 20px 0" }}>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
        <Money value={price} size={28} weight={500} />
        <Delta abs={moveAbs} pct={movePctShown} size={12.5} />
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>{range === "1D" ? "Today" : range}</span>
      </div>
      <div style={{ marginTop: 12 }}>
        <Chart data={data} height={120} accent={accent} fillColor={accent} scrub onScrub={(idx) => setScrub(idx == null ? null : data[idx])} padY={4} range={range} />
      </div>
      <div style={{ display: "flex", gap: 3, marginTop: 10 }}>
        {RANGES.map((r) => {
          const on = r === range;
          return <button key={r} className="press" onClick={() => {setRange(r);setScrub(null);}} style={{ flex: 1, padding: "5px 0", background: on ? "var(--bg-2)" : "transparent", border: "none", borderRadius: 7, fontFamily: "var(--f-mono)", fontSize: typeSize(11), fontWeight: 500, color: on ? "var(--ink)" : "var(--ink-3)", cursor: "pointer" }}>{r}</button>;
        })}
      </div>
    </section>);
};

const SecurityStat = ({ label, value, color = "var(--ink)", right, noBorder }) =>
<div style={{ padding: "10px 0", borderBottom: noBorder ? "none" : "1px dashed var(--rule)", textAlign: right ? "right" : "left" }}>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)" }}>{label}</div>
    <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(14), fontWeight: 500, color, marginTop: 3, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>{value}</div>
  </div>;

const SecurityActivityRow = ({ activity, last }) => {
  const positive = activity.net >= 0;
  return (
    <div style={{ display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 11, alignItems: "center", padding: "11px 0", borderBottom: last ? "none" : "1px solid var(--rule)" }}>
      <span style={{ width: 26, height: 26, borderRadius: 999, display: "grid", placeItems: "center", background: "var(--bg-2)", color: "var(--ink-2)", flex: "none" }}>
        <Icon name={activity.icon || "trade"} size={15} color="var(--ink-2)" stroke={1.5} />
      </span>
      <span style={{ minWidth: 0 }}>
        <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, letterSpacing: "-0.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{activity.title}</span>
        <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{activity.detail}</span>
      </span>
      <span style={{ textAlign: "right", whiteSpace: "nowrap" }}>
        <span style={{ display: "block", fontFamily: "var(--f-mono)", fontSize: typeSize(12), fontWeight: 500, color: positive ? "var(--accent-pos)" : "var(--ink)", fontVariantNumeric: "tabular-nums" }}>{signed(activity.net, 0)}</span>
        <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 3 }}>{activity.when}</span>
      </span>
    </div>);
};

const SecurityActivityList = ({ h }) =>
<div data-security-activity-list="stream-style" style={{ borderTop: "1px solid var(--rule)", marginTop: 12 }}>
    {txFor(h).map((activity, index) => <SecurityActivityRow key={index} activity={activity} last={index === txFor(h).length - 1} />)}
  </div>;

const ACCOUNT_HOLDING_COLUMNS = [
  ["Shares", 74, (row) => ({ main: row.kind === "crypto" ? `${row.shares}` : `${row.shares}` })],
  ["Value", 102, (row) => ({ main: usd(row.value, 2) })],
  ["Today", 86, (row) => ({ main: signed(row.dayAbs, 0), sub: pct(row.dch), color: row.dch >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" })],
  ["%Port", 74, (row) => ({ main: row.weight.toFixed(2) + "%" })],
  ["Cost basis", 92, (row) => ({ main: usd(row.avg, row.kind === "crypto" ? 0 : 2), color: "var(--ink-2)" })],
  ["Return", 94, (row) => ({ main: signed(row.pl, 0), sub: pct(row.plPct), color: row.pl >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" })]
];
const ACCOUNT_HOLDING_ACCOUNT_WIDTH = 110;
const ACCOUNT_HOLDING_GRID_COLUMNS = [ACCOUNT_HOLDING_ACCOUNT_WIDTH, ...ACCOUNT_HOLDING_COLUMNS.map(([, width]) => width)].map((width) => `${width}px`).join(" ");

const SecurityAccountHoldingsTable = ({ h }) => {
  const rows = securityAccountHoldingRows(h);
  const W = ACCOUNT_HOLDING_ACCOUNT_WIDTH + ACCOUNT_HOLDING_COLUMNS.reduce((sum, [, width]) => sum + width, 0);
  const grid = { display: "grid", gridTemplateColumns: ACCOUNT_HOLDING_GRID_COLUMNS, alignItems: "center", width: W };
  const accountPin = { position: "sticky", left: 0, zIndex: 2, background: "var(--bg)", minWidth: 0, boxSizing: "border-box", paddingRight: 12 };
  const cell = { minWidth: 0, boxSizing: "border-box", textAlign: "right", paddingLeft: 10 };
  return (
    <div data-security-account-holdings-table="holdings-style" className="hcar" style={{ overflowX: "auto", overflowY: "hidden" }}>
      <div style={{ width: W }}>
        <div data-security-account-holdings-header="aligned-grid" style={{ ...grid, padding: "0 0 7px", borderBottom: "1px solid var(--rule)" }}>
          <div data-security-account-pin="surface-match" style={accountPin}>
            <Eyebrow color="var(--ink-3)" style={{ fontSize: typeSize(9.5) }}>Account</Eyebrow>
          </div>
          {ACCOUNT_HOLDING_COLUMNS.map(([label]) =>
          <div key={label} style={cell}>
              <Eyebrow color="var(--ink-3)" style={{ fontSize: typeSize(9.5) }}>{label}</Eyebrow>
            </div>
          )}
        </div>
        {rows.map((row, ri) =>
        <div key={row.id} data-security-account-holdings-row="aligned-grid" style={{ ...grid, borderBottom: ri === rows.length - 1 ? "none" : "1px solid var(--rule)", padding: "12px 0", textAlign: "left" }}>
            <div data-security-account-pin="surface-match" style={{ ...accountPin, display: "flex", flexDirection: "column", justifyContent: "center" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 700, letterSpacing: "-0.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{row.account}</span>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{row.sub}</span>
            </div>
            {ACCOUNT_HOLDING_COLUMNS.map(([label, _width, format]) => {
              const cellValue = format(row);
              return (
                <div key={label} style={cell}>
                  <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12.5), fontWeight: 500, color: cellValue.color || "var(--ink)", fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>{cellValue.main}</div>
                  {cellValue.sub && <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10.5), color: cellValue.color || "var(--ink-3)", marginTop: 1, fontVariantNumeric: "tabular-nums" }}>{cellValue.sub}</div>}
                </div>);

            })}
          </div>
        )}
      </div>
    </div>);

};

const SecurityPositionSection = ({ h }) => {
  const [tab, setTab] = useState("overview");
  if (!h.hasPosition) return null;
  return (
    <section data-security-position-tabs="overview-details" style={{ margin: "18px 20px 0" }}>
      <div data-security-position-surface="transparent" style={{ border: "1px solid var(--rule-2)", borderRadius: 12, background: "transparent", overflow: "hidden" }}>
        <div style={{ padding: "12px 14px 8px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
          <Eyebrow>Your position</Eyebrow>
          <SecurityPillTabs tabs={SECURITY_POSITION_TABS} value={tab} onChange={setTab} marker="position" />
        </div>
        {tab === "overview" ? (
          <div style={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr) auto", gap: 0, padding: "0 14px 12px" }}>
            <SecurityStat label="Value" value={usd(h.value)} />
            <SecurityStat label="Unrealized P/L" value={perf(h.pl, h.plPct)} color={h.pl >= 0 ? "var(--accent-pos)" : "var(--signal-neg)"} right />
            <SecurityStat label={h.kind === "crypto" ? "Holdings" : "Shares"} value={`${h.shares}`} noBorder />
            <SecurityStat label="Avg cost" value={usd(h.avg)} right noBorder />
          </div>
        ) : (
          <div style={{ padding: "0 14px 14px", display: "grid", gap: 12 }}>
            <SecurityAccountHoldingsTable h={h} />
            <SecurityActivityList h={h} />
          </div>
        )}
      </div>
    </section>);
};

const SecurityFundamentalsOverview = ({ h, detail }) =>
<>
    {renderDetailSection({ title: "Overview", rows: detail.companyProfile || [], description: detail.description })}
    {h.kind === "etf" ? renderDetailSection({ title: "Fund", rows: detail.fund || [] }) : h.kind === "stock" ? <>
      {renderDetailSection({ title: "Valuation", rows: detail.valuation || [] })}
      {renderDetailSection({ title: "Profitability", rows: detail.profitability || [] })}
      {renderDetailSection({ title: "Per-share and balance sheet", rows: detail.perShare || [] })}
      {renderDetailSection({ title: "Market profile", rows: detail.marketProfile || [] })}
    </> : null}
  </>;

const SecurityFundamentalsSection = ({ h, detail }) => {
  const tabs = securityFundamentalTabsFor(h);
  const [tab, setTab] = useState("overview");
  const active = tabs.includes(tab) ? tab : "overview";
  return (
    <section data-security-fundamental-tabs="type-aware" style={{ paddingTop: 18 }}>
      <div data-security-fundamental-tab-header="stacked-wrap" style={{ padding: "0 20px", display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 10 }}>
        <Eyebrow>Fundamentals</Eyebrow>
        <SecurityPillTabs tabs={tabs} value={active} onChange={setTab} marker="fundamentals" wrap />
      </div>
      {active === "overview" && <SecurityFundamentalsOverview h={h} detail={detail} />}
      {active === "events" && <SecurityEventsPanel detail={detail} />}
      {active === "exposure" && <SecurityExposuresPanel detail={detail} />}
      {active === "etf" && <SecurityEtfPanel detail={detail} />}
    </section>);
};

const SecurityDetailHierarchy = ({ security, nav, onTrade }) => {
  const h = securityDetailModel(security);
  const detail = securityDetailData(h);
  if (onTrade) void onTrade;
  return (
    <div data-security-detail-hierarchy="shared-mobile" className="scroll">
      <SecurityPriceGraphSection h={h} />
      <SecurityPositionSection h={h} />
      <section style={{ margin: "16px 20px 0" }}>
        <SecurityNewsSummaryCard h={h} detail={detail} />
      </section>
      <SecurityFundamentalsSection h={h} detail={detail} />
      <div style={{ height: 8 }} />
    </div>);
};

const HoldingDetail = ({ id, nav }) => {
  const h = HOLDINGS.find((x) => x.id === id);

  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      <div style={{ flex: "none", padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 10, minHeight: 44 }}>
        <button className="press" onClick={() => nav.pop()} aria-label="Back to holdings" style={{ background: "none", border: "none", padding: 4, margin: "0 -4px", color: "var(--ink)", display: "flex", flex: "none" }}>
          <Icon name="back" size={22} />
        </button>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{h.ticker}</div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{h.name}</div>
        </div>
        <button className="press" onClick={() => nav.clearStack ? nav.clearStack() : nav.tab("accounts")} aria-label="Close to Accounts" style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", padding: 4, flex: "none" }}>
          <Icon name="close" size={19} />
        </button>
      </div>

      <SecurityDetailHierarchy security={h} nav={nav} onTrade={() => nav.sheet({ type: "trade", id: h.id, returnToAccounts: true })} />

      <div style={{ flex: "none", padding: "12px 20px 18px", borderTop: "1px solid var(--rule)", background: "color-mix(in srgb, var(--bg) 92%, transparent)", backdropFilter: "blur(12px)" }}>
        <Btn full kind="primary" onClick={() => nav.sheet({ type: "trade", id: h.id, returnToAccounts: true })}>Trade {h.ticker}</Btn>
      </div>
    </div>);

};

const Stat = ({ label, value, color = "var(--ink)", right, noBorder }) =>
<div style={{ padding: "10px 0", borderBottom: noBorder ? "none" : "1px dashed var(--rule)", textAlign: right ? "right" : "left" }}>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)" }}>{label}</div>
    <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(14), fontWeight: 500, color, marginTop: 3, fontVariantNumeric: "tabular-nums" }}>{value}</div>
  </div>;


/* ============================================================
   Account detail · holdings table (h-scroll) + transactions
   ============================================================ */
/* which holdings live in each Yoshi account. Sums stay consistent with the
   group totals shown on the Accounts list. */
const ACCOUNTS = {
  cash: { name: "Cash", sub: "Yoshi ••8841", cash: true, routing: "021000021", account: "1500240048841" },
  brokerage: { name: "Brokerage", sub: "Yoshi · Individual Taxable", ids: ["nvda", "aapl", "msft", "voo"] },
  roth: { name: "Roth IRA", sub: "Yoshi · Retirement", ids: ["vti"] },
  crypto: { name: "Crypto", sub: "Yoshi · Spot ••8841", ids: ["btc", "eth", "sol"] },
  paper: { name: "Test drive 1", sub: "Yoshi · Simulated", ids: ["pp-tsla", "pp-amzn", "pp-spy"] },
  schwab: { name: "Schwab", displayName: "Schwab Brokerage", displaySub: "Schwab •5520", sub: "Brokerage · Connected", ids: ["sch-spy", "sch-qqq"] },
  coinbase: { name: "Coinbase", displayName: "Coinbase Crypto", displaySub: "Coinbase •3097", sub: "Crypto · Needs re-auth", ids: ["cb-doge"] },
  chase: { name: "Chase", displayName: "Chase Checking", displaySub: "Chase •4417", sub: "Checking · Connected", cash: true, totalLabel: "Balance", note: "External",
    accounts: [{ id: "chase-chk", name: "Chase Checking", sub: "••4471", apr: "", value: 8210.32, bar: "var(--rule-2)" }], txns: CHASE_TXNS }
};
const accountDisplayName = (acct) => acct.displayName || acct.name;
const accountDisplaySub = (acct) => acct.displaySub || acct.sub;
const acctValue = (ids) => ids.reduce((s, id) => {const h = HOLDINGS.find((x) => x.id === id);return s + (h ? h.value : 0);}, 0);
const YOSHI_INVESTMENT_ACCOUNTS = [ACCOUNTS.brokerage, ACCOUNTS.roth, ACCOUNTS.crypto].filter(Boolean);
const EXTERNAL_INVESTMENT_ACCOUNTS = [ACCOUNTS.schwab, ACCOUNTS.coinbase].filter(Boolean);

/* combined holdings view across Yoshi and external investment accounts. */
const ALL_HOLDINGS_ACCT = {
  name: "All holdings",
  sub: "Across your Yoshi and external accounts",
  ids: [...new Set([...YOSHI_INVESTMENT_ACCOUNTS.flatMap((account) => account.ids || []), ...EXTERNAL_INVESTMENT_ACCOUNTS.flatMap((account) => account.ids || [])])],
  combined: true
};
const ALL_HOLDINGS_SWITCH_ACCOUNTS = [ALL_HOLDINGS_ACCT, ...YOSHI_INVESTMENT_ACCOUNTS, ...EXTERNAL_INVESTMENT_ACCOUNTS];
const allHoldingsStartFor = (acct) => {
  if (acct.combined || acct.name === ALL_HOLDINGS_ACCT.name) return ALL_HOLDINGS_ACCT;
  return acct;
};
/* holdings flagged as recently added (for the "Recently added" filter) */
const RECENT_ADDS = ["nvda", "voo", "sol", "eth"];

/* build a full holding record (so HoldingDetail works) for paper + external accounts */
const mkH = (id, ticker, name, kind, shares, avg, last, dch) => {
  const value = shares * last,cost = shares * avg;
  return { id, ticker, name, kind, shares, avg, last, dch, value, cost, pl: value - cost, plPct: (value - cost) / cost * 100, dayAbs: value * dch / 100,
    perf: { "1W": +(dch * 1.5).toFixed(1), "1M": +(dch * 3).toFixed(1), "1Y": +(dch * 9).toFixed(1) }, seed: Math.round(Math.abs(shares) + last),
    fund: [["Asset class", kind === "crypto" ? "Crypto" : kind === "etf" ? "ETF" : "Equity"], ["Day range", "—"], ["52w range", "—"]],
    news: [["Markets", name + " moved with the broader tape.", "2h"]] };
};
const EXTRA_HOLDINGS = [
mkH("sch-spy", "SPY", "SPDR S&P 500 ETF", "etf", 28, 540.10, 597.10, 0.64),
mkH("sch-qqq", "QQQ", "Invesco QQQ Trust", "etf", 13, 470.00, 512.33, 0.88),
mkH("cb-doge", "DOGE", "Dogecoin", "crypto", 4400, 0.31, 0.42, 5.60),
mkH("pp-tsla", "TSLA", "Tesla Inc", "stock", 120, 360.00, 342.18, -1.84),
mkH("pp-amzn", "AMZN", "Amazon.com Inc", "stock", 50, 180.00, 201.40, 1.22),
mkH("pp-spy", "SPY", "SPDR S&P 500 ETF", "etf", 60, 560.00, 597.10, 0.64)];

EXTRA_HOLDINGS.forEach((h) => {if (!HOLDINGS.find((x) => x.id === h.id)) HOLDINGS.push(h);});

const CHASE_TXNS = [
{ id: "h1", icon: "down", title: "Payroll", detail: "Acme Corp · ACH", amt: 3120.00, when: "Jun 1" },
{ id: "h2", icon: "receipt", title: "Groceries", detail: "Whole Foods", amt: -184.22, when: "May 30" },
{ id: "h3", icon: "swap", title: "Transfer to Yoshi", detail: "External ••8841", rail: "Standard ACH", type: "External transfer", amt: -2000.00, when: "May 28" },
{ id: "h4", icon: "card", title: "Utilities", detail: "ConEd · autopay", amt: -142.05, when: "May 26" }];

const DEBT_TXNS = [
{ id: "d1", icon: "down", title: "Payment received", detail: "From Checking · autopay", amt: 300.00, when: "Jun 1" },
{ id: "d2", icon: "receipt", title: "Purchase", detail: "Travel · airline", amt: -642.18, when: "May 27" },
{ id: "d3", icon: "receipt", title: "Purchase", detail: "Dining", amt: -88.40, when: "May 24" },
{ id: "d4", icon: "bank", title: "Interest charged", detail: "Cycle finance charge", amt: -41.90, when: "May 22" }];


/* synthetic per-account transaction ledger, Stream-style */
const acctTxns = (holdings) => {
  const rows = [];
  holdings.forEach((h) => {
    const cr = h.kind === "crypto";
    const unit = cr ? "" : " sh";
    rows.push({ id: h.id + "-1", side: "BUY", ticker: h.ticker, qty: cr ? (h.shares * 0.3).toFixed(4) : Math.round(h.shares * 0.2), px: h.last * 0.97, when: "Fri", order: 5, note: "auto-invest" });
    rows.push({ id: h.id + "-2", side: "BUY", ticker: h.ticker, qty: cr ? (h.shares * 0.4).toFixed(4) : Math.round(h.shares * 0.3), px: h.avg * 1.08, when: "Apr 04", order: 30 });
    if (!cr) rows.push({ id: h.id + "-d", side: "DIV", ticker: h.ticker, amt: h.value * 0.003, when: "Mar 28", order: 34, note: "reinvested" });
    rows.push({ id: h.id + "-3", side: "BUY", ticker: h.ticker, qty: cr ? (h.shares * 0.3).toFixed(4) : Math.round(h.shares * 0.5), px: h.avg, when: "Feb 11", order: 60 });
  });
  return rows.sort((a, b) => a.order - b.order);
};

const accountTxnTitle = (t) => {
  if (t.title) return t.title;
  if (t.side === "DIV") return `Dividend · ${t.ticker}`;
  return `${t.side === "BUY" ? "Bought" : "Sold"} ${t.ticker}`;
};
const accountTxnDetail = (t) => {
  if (t.detail) return t.detail;
  const isDiv = t.side === "DIV";
  const isAuto = t.note === "auto-invest";
  return isDiv ? `${usd(t.amt)} ${t.note || ""}`.trim() : `${t.qty}${t.ticker && !String(t.qty).includes(".") ? " sh" : ""} @ ${usd(t.px, 2)}${t.note && !isAuto ? " · " + t.note : ""}`;
};
const accountTxnAmount = (t) => t.amt != null ? t.amt : t.side === "DIV" ? t.amt : -(Number(t.qty) * t.px);
const accountTxnCategory = (t) => {
  if (t.category) return t.category;
  if (t.side === "DIV") return "Income";
  if (t.side) return "Investment";
  const hay = `${t.title || ""} ${t.detail || ""}`.toLowerCase();
  if (t.icon === "swap") return "Transfer";
  if (t.icon === "down" || t.amt > 0) return "Income";
  if (/whole foods|grocer/.test(hay)) return "Groceries";
  if (/travel|airline/.test(hay)) return "Travel";
  if (/dining/.test(hay)) return "Dining";
  if (/rent|utility|utilities|coned|autopay|bill/.test(hay)) return "Bills";
  return t.amt >= 0 ? "Income" : "Spending";
};
const toAccountTxnDetail = (t, external) => ({
  id: t.id,
  icon: t.icon || (t.side === "DIV" ? "down" : "trade"),
  title: accountTxnTitle(t),
  detail: accountTxnDetail(t),
  when: t.when,
  net: accountTxnAmount(t),
  rail: t.rail,
  type: t.type,
  category: accountTxnCategory(t),
  accountScope: external ? "external" : "yoshi"
});

/* a compact search bar with an inline filter icon → dropdown of filter options */
const AcctToolbar = ({ q, setQ, placeholder, filters, filter, setFilter }) => {
  const [open, setOpen] = useState(false);
  const active = filter !== "all";
  const activeLabel = (filters.find((f) => f[0] === filter) || [])[1];
  return (
    <div style={{ padding: "10px 18px 4px", position: "relative" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "8px 12px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 10 }}>
        <Icon name="search" size={15} color="var(--ink-3)" stroke={1.5} />
        <input value={q} onChange={(e) => setQ(e.target.value)} placeholder={placeholder}
        style={{ flex: 1, minWidth: 0, border: "none", background: "transparent", outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(13.5) }} />
        {q && <button className="press" onClick={() => setQ("")} style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", padding: 0 }}><Icon name="close" size={15} /></button>}
        <span style={{ width: 1, height: 18, background: "var(--rule-2)", flex: "none" }} />
        <button className="press" onClick={() => setOpen((o) => !o)} aria-label="Filter" style={{ position: "relative", background: "none", border: "none", display: "flex", padding: 1, color: active || open ? "var(--accent)" : "var(--ink-3)", cursor: "pointer" }}>
          <Icon name="filter" size={17} stroke={1.6} />
          {active && <span style={{ position: "absolute", top: -2, right: -2, width: 6, height: 6, borderRadius: 999, background: "var(--accent)", border: "1.5px solid var(--bg-2)" }} />}
        </button>
      </div>

      {active &&
      <div style={{ display: "flex", marginTop: 8 }}>
        <button className="press" onClick={() => setFilter("all")} style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "4px 10px", borderRadius: 999, background: "color-mix(in srgb, var(--accent) 12%, var(--bg-2))", border: "1px solid color-mix(in srgb, var(--accent) 40%, var(--rule-2))", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, color: "var(--ink)", cursor: "pointer" }}>
          {activeLabel}<Icon name="close" size={12} color="var(--ink-3)" />
        </button>
      </div>}

      {open &&
      <>
        <div onClick={() => setOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 30 }} />
        <div style={{ position: "absolute", right: 18, top: "calc(100% - 2px)", zIndex: 31, minWidth: 188, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 12px 28px -14px rgba(0,0,0,0.35)", overflow: "hidden" }}>
          <div style={{ padding: "9px 13px 7px", borderBottom: "1px solid var(--rule)" }}><Eyebrow style={{ fontSize: typeSize(9.5) }}>Filter by</Eyebrow></div>
          {filters.map(([k, l], i) =>
          <button key={k} className="press" onClick={() => {setFilter(k);setOpen(false);}} style={{ width: "100%", display: "flex", alignItems: "center", gap: 8, padding: "9px 13px", background: k === filter ? "var(--bg-2)" : "none", border: "none", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer", textAlign: "left" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, color: "var(--ink)" }}>{l}</span>
              {k === filter && <Icon name="check" size={14} stroke={2} color="var(--accent)" style={{ marginLeft: "auto" }} />}
            </button>
          )}
        </div>
      </>}
    </div>);

};

/* horizontally-scrollable holdings table. First column (the holding) is pinned;
   scroll right to reveal % of portfolio, avg cost, and total return. */
const HoldingsTable = ({ holdings, total, nav }) => {
  const NUMCOLS = [
  ["Value", 84, (h) => ({ main: usd(h.value, 2) }), (h) => h.value],
  ["Today", 84, (h) => ({ main: signed(h.dayAbs, 0), sub: pct(h.dch), color: h.dch >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" }), (h) => h.dayAbs],
  ["%Port", 84, (h) => ({ main: (h.value / total * 100).toFixed(2) + "%" }), (h) => h.value],
  ["Cost basis", 92, (h) => ({ main: usd(h.avg, h.kind === "crypto" ? 0 : 2), color: "var(--ink-2)" }), (h) => h.avg],
  ["Return", 84, (h) => ({ main: signed(h.pl, 0), sub: pct(h.plPct), color: h.pl >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" }), (h) => h.pl]];

  // sortable: default Value, highest → lowest. Tapping a header toggles
  // direction; tapping a new column starts it descending (text starts A→Z).
  const [sort, setSort] = useState({ key: "Value", dir: "desc" });
  const onSort = (key) => setSort((s) => s.key === key ? { key, dir: s.dir === "desc" ? "asc" : "desc" } : { key, dir: key === "Holding" ? "asc" : "desc" });
  const sorted = useMemo(() => {
    const acc = sort.key === "Holding" ? null : (NUMCOLS.find((c) => c[0] === sort.key) || [])[3];
    const arr = [...holdings].sort((a, b) =>
    acc ? acc(a) - acc(b) : String(a.ticker).localeCompare(String(b.ticker)));
    return sort.dir === "desc" ? arr.reverse() : arr;
  }, [holdings, sort, total]);

  const web = !!window.__YOSHI_WEB;
  const SEAM = web ? 24 : 20,COLW = web ? 104 : 84,HOLDW = web ? 92 : 72; // edge padding, uniform numeric width, narrower holding column
  const last = NUMCOLS.length - 1;
  const W = SEAM + HOLDW + NUMCOLS.reduce((s, c, i) => s + (i === last ? COLW + SEAM : COLW), 0);
  const stick = { position: "sticky", left: 0, zIndex: 2, background: "var(--bg)", width: SEAM + HOLDW, flex: "none", boxSizing: "border-box", paddingLeft: SEAM };
  const cell = (i) => web
    ? { flex: "1 1 0", minWidth: COLW, boxSizing: "border-box", textAlign: "right", paddingLeft: 8, paddingRight: i === last ? SEAM : 0 }
    : { flex: "none", boxSizing: "border-box", textAlign: "right", width: i === last ? COLW + SEAM : COLW, paddingLeft: 8, paddingRight: i === last ? SEAM : 0 };
  const headerHeight = 58;
  const headerLabelStyle = { fontSize: typeSize(web ? 10.5 : 9.5), lineHeight: 1.35, textAlign: "center", whiteSpace: "normal" };
  const Caret = ({ k }) => {
    const active = sort.key === k;
    return <span aria-hidden="true" style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(8), lineHeight: 1, marginLeft: 5, color: active ? "var(--accent)" : "var(--ink-3)", opacity: active ? 1 : 0.3 }}>{active && sort.dir === "asc" ? "▲" : "▼"}</span>;
  };
  const headBtn = { background: "none", border: "none", padding: 0, margin: 0, cursor: "pointer", minHeight: headerHeight, display: "inline-flex", alignItems: "center", justifyContent: "center", font: "inherit", color: "inherit", textAlign: "center", WebkitTapHighlightColor: "transparent" };
  return (
    <div className="hcar" style={{ overflowX: "auto", overflowY: "hidden" }}>
      <div style={web ? { width: "100%", minWidth: W } : { width: W }}>
        {/* header · tap a column to sort high ↔ low */}
        <div style={{ display: "flex", alignItems: "center", padding: "0 0 7px", minHeight: headerHeight, borderBottom: "1px solid var(--rule)" }}>
          <div style={{ ...stick, display: "flex", alignItems: "center" }}>
            <button className="press" onClick={() => onSort("Holding")} style={{ ...headBtn, width: HOLDW }}>
              <Eyebrow color={sort.key === "Holding" ? "var(--accent)" : "var(--ink-3)"} style={headerLabelStyle}>Holding</Eyebrow><Caret k="Holding" />
            </button>
          </div>
          {NUMCOLS.map(([label], i) =>
          <div key={label} style={cell(i)}>
            <button className="press" onClick={() => onSort(label)} style={{ ...headBtn, width: "100%" }}>
              <Eyebrow color={sort.key === label ? "var(--accent)" : "var(--ink-3)"} style={headerLabelStyle}>{label}</Eyebrow><Caret k={label} />
            </button>
          </div>)}
        </div>
        {sorted.length === 0 ?
        <div style={{ padding: "26px 20px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>No holdings match.</div> :
        sorted.map((h, ri) =>
        <button key={h.id} className="press" onClick={() => nav.push({ type: "holding", id: h.id })} style={{ width: "100%", display: "flex", alignItems: "center", background: "none", border: "none", borderBottom: ri === sorted.length - 1 ? "none" : "1px solid var(--rule)", padding: "12px 0", cursor: "pointer", textAlign: "left" }}>
              <div style={{ ...stick, display: "flex", flexDirection: "column", justifyContent: "center" }}>
                <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(web ? 15 : 13.5), fontWeight: 700, letterSpacing: "-0.01em" }}>{h.ticker}</span>
                <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(web ? 11.5 : 10.5), color: "var(--ink-3)", marginTop: 3 }}>{h.kind === "crypto" ? h.shares : h.shares + " sh"}</span>
              </div>
              {NUMCOLS.map(([label, w, f], i) => {
            const c = f(h);
            return (
              <div key={label} style={cell(i)}>
                    <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(web ? 14 : 12.5), fontWeight: 500, color: c.color || "var(--ink)", fontVariantNumeric: "tabular-nums" }}>{c.main}</div>
                    {c.sub && <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(web ? 11.5 : 10.5), color: c.color || "var(--ink-3)", marginTop: 1, fontVariantNumeric: "tabular-nums" }}>{c.sub}</div>}
                  </div>);

          })}
            </button>
        )}
      </div>
    </div>);

};

const AcctTxnRow = ({ t, last, nav, external }) => {
  const isDiv = t.side === "DIV";
  const isAuto = t.note === "auto-invest";
  const title = accountTxnTitle(t);
  const detail = accountTxnDetail(t);
  const amt = accountTxnAmount(t);
  return (
    <button className="press" onClick={() => nav.sheet({ type: "txn", tx: toAccountTxnDetail(t, external) })} style={{ width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer", display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 11, alignItems: "center", padding: window.__YOSHI_WEB ? "11px 32px" : "11px 18px", borderBottom: last ? "none" : "1px solid var(--rule)" }}>
      <span style={{ width: 26, height: 26, flex: "none", display: "grid", placeItems: "center", color: "var(--ink-2)" }}>
        {isAuto ? <Logo size={18} /> : <Icon name={isDiv ? "bank" : "trade"} size={19} color="var(--ink-2)" stroke={1.5} />}
      </span>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{title}</div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{detail}</div>
      </div>
      <div style={{ textAlign: "right", whiteSpace: "nowrap" }}>
        <Money value={amt} size={12.5} sign color={amt >= 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
        <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(9.5), color: "var(--ink-3)", marginTop: 3 }}>{t.when}</div>
      </div>
    </button>);

};

/* synthetic recent cash activity */
const CASH_TXNS = [
{ id: "k1", icon: "down", title: "Pay day", detail: "Acme Corp · direct deposit", amt: 6240.00, when: "Jun 1" },
{ id: "k2", icon: "receipt", title: "Rent", detail: "Bill pay · Greystar", amt: -2800.00, when: "Jun 1" },
{ id: "k3", icon: "bank", title: "Interest earned", detail: "Reserve · 3.30% APY", amt: 256.12, when: "May 31" },
{ id: "k4", icon: "card", title: "Card autopay", detail: "Yoshi Card ••2291", amt: -1240.40, when: "May 28" },
{ id: "k5", icon: "swap", title: "Transfer to Brokerage", detail: "Yoshi · auto-invest", rail: "Instant", type: "Between accounts", amt: -1500.00, when: "May 27" },
{ id: "k6", icon: "down", title: "Zelle from Jordan", detail: "Received ••8841", amt: 320.00, when: "May 24" }];


const CashAccountDetail = ({ acct, nav }) => {
  const accounts = acct.accounts || CASH;
  const txlist = acct.txns || CASH_TXNS;
  const total = accounts.reduce((s, a) => s + a.value, 0);
  const yearly = accounts.reduce((s, a) => s + (/4\.80/.test(a.apr || "") ? a.value * 0.048 : 0), 0);
  const ytd = yearly * (5 / 12);
  const [tab, setTab] = useState("balances");
  const [q, setQ] = useState("");const [f, setF] = useState("all");
  const [shown, setShown] = useState(false);
  const [copied, setCopied] = useState("");
  const [nick, setNick] = useState(accountDisplayName(acct));
  const [editingNick, setEditingNick] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  const [apyInfo, setApyInfo] = useState(false);
  const [fdicOpen, setFdicOpen] = useState(false);
  const [hidden, toggleHidden] = useHiddenAccts();
  const isHidden = hidden.includes(acct.name);
  const external = !!acct.note; // linked / read-only accounts
  const canReveal = !!acct.account;
  useEffect(() => {setNick(accountDisplayName(acct));}, [acct]);
  const copy = (label, val) => {
    try {navigator.clipboard && navigator.clipboard.writeText(val);} catch (e) {}
    setCopied(label);setTimeout(() => setCopied(""), 1400);
  };
  const shownTx = txlist.filter((t) => {
    if (f === "in" && t.amt < 0) return false;
    if (f === "out" && t.amt >= 0) return false;
    const s = q.trim().toLowerCase();
    return !s || (t.title + " " + t.detail).toLowerCase().includes(s);
  });
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0, position: "relative" }}>
      <NavBar onBack={() => nav.pop()}
      title={nick}
      sub={accountDisplaySub(acct)}
      right={
      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
          {(external || canReveal) &&
          <button className="press" aria-label="Account options" onClick={() => setMenuOpen((o) => !o)} style={{ flex: "none", display: "grid", placeItems: "center", width: 30, height: 30, background: menuOpen ? "var(--bg-2)" : "none", border: "1px solid " + (menuOpen ? "var(--rule-2)" : "transparent"), borderRadius: 8, cursor: "pointer", color: "var(--ink-2)" }}><Icon name="more" size={18} /></button>}
          <button className="press" onClick={() => acct.debt ? nav.sheet({ type: "transfer", rail: "billpay" }) : nav.sheet({ type: "transfer", from: "cash" })} style={{ background: "none", border: "1px solid var(--rule-2)", borderRadius: 8, padding: "6px 12px", fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 600, color: "var(--ink)" }}>{acct.debt ? "Pay" : "Transfer"}</button>
        </div>} />

      {/* Yoshi cash · reveal numbers, rename, documents */}
      {menuOpen && canReveal &&
      <>
        <div onClick={() => setMenuOpen(false)} style={{ position: "absolute", inset: 0, zIndex: 30 }} />
        <div style={{ position: "absolute", top: 52, right: 14, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 244 }}>
          <button className="press" onClick={() => { setMenuOpen(false); setShown(true); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
            <Icon name="eye" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Reveal account & routing number</span>
          </button>
          <button className="press" onClick={() => { setEditingNick(true); setMenuOpen(false); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
            <Icon name="doc" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Edit nickname</span>
          </button>
          <button className="press" onClick={() => { setMenuOpen(false); nav.sheet({ type: "documents", acct: "savings" }); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
            <Icon name="receipt" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Documents</span>
          </button>
        </div>
      </>}

      {/* external-account actions · rename, or drop from totals & summaries */}
      {menuOpen && external &&
      <>
        <div onClick={() => setMenuOpen(false)} style={{ position: "absolute", inset: 0, zIndex: 30 }} />
        <div style={{ position: "absolute", top: 52, right: 14, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 232 }}>
          <button className="press" onClick={() => {setEditingNick(true);setMenuOpen(false);}} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
            <Icon name="doc" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Edit nickname</span>
          </button>
          <button className="press" onClick={() => {toggleHidden(acct.name);setMenuOpen(false);}} style={{ width: "100%", display: "flex", alignItems: "flex-start", gap: 10, padding: "12px 13px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
            <Icon name="eye" size={17} color="var(--ink-2)" stroke={1.6} style={{ marginTop: 1 }} />
            <span style={{ minWidth: 0 }}>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{isHidden ? "Show in totals & summary" : "Hide from totals & summary"}</span>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, lineHeight: 1.4 }}>{isHidden ? "Count this account again everywhere." : "Remains connected"}</span>
            </span>
          </button>
        </div>
      </>}

      <section style={{ flex: "none", padding: "14px 18px 12px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
          <Eyebrow style={{ margin: 0 }}>{acct.totalLabel || "Total cash"}</Eyebrow>
          {!acct.note && !acct.debt &&
          <span style={{ position: "relative", display: "inline-grid", lineHeight: 0 }}>
            <button className="press" aria-label="About FDIC insurance" onClick={() => setFdicOpen((o) => !o)} style={{ background: "none", border: "none", padding: 0, display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", lineHeight: 0 }}>
            <Icon name="info" size={12} stroke={1.5} />
          </button>
            {window.__YOSHI_WEB && <InfoPop open={fdicOpen} onClose={() => setFdicOpen(false)} title="FDIC insurance">{FDIC_DISCLOSURE}</InfoPop>}
          </span>}
        </div>
        <div style={{ marginTop: 5, display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" }}>
          <Money value={total} size={30} weight={500} />
          {isHidden &&
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, padding: "3px 9px" }}>Hidden from totals</span>}
        </div>
        <div style={{ marginTop: 6 }}>
          {acct.note ?
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontFamily: "var(--f-mono)", fontSize: typeSize(10.5), color: "var(--ink-3)", letterSpacing: "0.02em" }}>
            <span style={{ width: 5, height: 5, borderRadius: 999, background: "var(--ink-3)", flex: "none" }} />
            Synced {syncedAgo(acct.name)}
          </span> :
          <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 4 }}>
            <div>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: "var(--accent-pos)" }}>+{usd(ytd, 0)}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)", marginTop: 2 }}>Interest Jan–May</div>
            </div>
            <span style={{ width: 1, alignSelf: "stretch", background: "var(--rule)", flex: "none" }} />
            <div>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: "var(--ink)" }}>3.30%</div>
              <div style={{ display: "flex", alignItems: "center", gap: 4, marginTop: 2 }}>
                <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), color: "var(--ink-3)" }}>Current APY</span>
                <span style={{ position: "relative", display: "inline-grid", lineHeight: 0 }}>
                  <button className="press" aria-label="About your APY" onClick={() => setApyInfo((o) => !o)} style={{ background: "none", border: "none", padding: 0, display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", lineHeight: 0 }}>
                  <Icon name="info" size={12} stroke={1.5} />
                </button>
                  {window.__YOSHI_WEB && <InfoPop open={apyInfo} onClose={() => setApyInfo(false)} title="About your APY" width={274}>APY is an annual rate. Our current base rate is 3.30% and is subject to change. Interest accrues daily and is paid out monthly.</InfoPop>}
                </span>
              </div>
            </div>
          </div>}
        </div>
      </section>

      {/* APY disclosure · slide-up bottom sheet */}
      {apyInfo && !window.__YOSHI_WEB &&
      <>
        <div onClick={() => setApyInfo(false)} 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", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
          <div style={{ padding: "16px 18px max(26px,env(safe-area-inset-bottom))" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}>
              <Icon name="info" size={18} color="var(--ink-2)" stroke={1.5} />
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em" }}>About your APY</div>
              <button className="press" aria-label="Close" onClick={() => setApyInfo(false)} style={{ marginLeft: "auto", background: "none", border: "none", display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", padding: 2 }}>
                <Icon name="close" size={18} />
              </button>
            </div>
            <p style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), lineHeight: 1.6, color: "var(--ink-2)", margin: 0 }}>APY is an annual rate. Our current base rate as of today's date is 3.30% and is subject to change. Interest accrues daily and is paid out monthly. Payouts can vary month to month based on the APY account balance and the number of days in the month.</p>
          </div>
        </div>
      </>}

      {/* FDIC disclosure · slide-up bottom sheet */}
      {fdicOpen && !window.__YOSHI_WEB &&
      <>
        <div onClick={() => setFdicOpen(false)} 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", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
          <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))", display: "flex", gap: 12, alignItems: "flex-start" }}>
            <p style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(13.5), lineHeight: 1.6, color: "var(--ink-2)", margin: 0, display: "flex", gap: 9, alignItems: "flex-start" }}>
              <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} style={{ flex: "none", marginTop: 2 }} />
              <span>{FDIC_DISCLOSURE}</span>
            </p>
            <button className="press" aria-label="Close" onClick={() => setFdicOpen(false)} style={{ flex: "none", background: "none", border: "none", display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", padding: 2 }}>
              <Icon name="close" size={18} />
            </button>
          </div>
        </div>
      </>}

      {/* nickname editor · bottom sheet */}
      {editingNick &&
      <>
        <div onClick={() => setEditingNick(false)} 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", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
          <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>Edit nickname</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, color: "var(--ink-2)", marginBottom: 6 }}>Account nickname</div>
            <input autoFocus value={nick} onChange={(e) => setNick(e.target.value)} onKeyDown={(e) => {if (e.key === "Enter") setEditingNick(false);}}
              style={{ width: "100%", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, outline: "none", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, color: "var(--ink)" }} />
            <button className="press" onClick={() => setEditingNick(false)} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
          </div>
        </div>
      </>}

      {/* debt accounts · loan terms as a clean label/value ledger */}
      {acct.debt &&
      <section style={{ flex: "none", padding: "2px 18px 14px" }}>
        <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
          {[["Outstanding", acct.debt.outstanding], ["Original loan amount", acct.debt.original], ["Term length", acct.debt.term], ["Expected payoff", acct.debt.payoff], ["Amount paid to date", acct.debt.paid], ["Next payment minimum", acct.debt.nextMin]].map(([label, val], i) =>
          <div key={label} style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 14, padding: "11px 14px", borderTop: i ? "1px solid var(--rule)" : "none" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-2)" }}>{label}</span>
            <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: "var(--ink)", letterSpacing: "-0.005em", textAlign: "right" }}>{val}</span>
          </div>
          )}
        </div>
      </section>}

      {/* account numbers · slide-up bottom sheet */}
      {shown &&
      <>
        <div onClick={() => setShown(false)} 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", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
          <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>Account details</div>
            {[["Routing number", acct.routing], ["Account number", acct.account]].map(([label, val]) =>
            <div key={label} role="button" tabIndex={0} className="press" onClick={() => copy(label, val)} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 10, alignItems: "center", padding: "11px 0", borderBottom: "1px solid var(--rule)", cursor: "pointer" }}>
              <div style={{ minWidth: 0 }}>
                <Eyebrow style={{ fontSize: typeSize(9) }}>{label}</Eyebrow>
                <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(17), letterSpacing: "0.06em", marginTop: 3, color: "var(--ink)" }}>{val}</div>
              </div>
              <CopyActionLabel copied={copied === label} size={12} />
            </div>)}
            <button className="press" onClick={() => setShown(false)} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--ink)" }}>Done</button>
          </div>
        </div>
      </>}

      <div style={{ flex: "none" }}>
        <AcctToolbar q={q} setQ={setQ} placeholder="Search transactions" filter={f} setFilter={setF}
        filters={[["all", "All"], ["in", "Money in"], ["out", "Money out"]]} />
      </div>
      <div className="scroll" style={{ paddingTop: 6 }}>
        {shownTx.length ? shownTx.map((t, i) =>
        <button key={t.id} className="press" onClick={() => nav.sheet({ type: "txn", tx: toAccountTxnDetail(t, external) })} style={{ width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer", display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 11, alignItems: "center", padding: "11px 18px" }}>
            <span style={{ width: 26, height: 26, flex: "none", display: "grid", placeItems: "center", color: "var(--ink-2)" }}>
              <Icon name={t.icon} size={19} color="var(--ink-2)" stroke={1.5} />
            </span>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{t.title}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{t.detail}{t.rail ? " · " + t.rail : ""}</div>
            </div>
            <div style={{ textAlign: "right", whiteSpace: "nowrap" }}>
              <Money value={t.amt} size={12.5} sign color={t.amt >= 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
              <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(9.5), color: "var(--ink-3)", marginTop: 3 }}>{t.when}</div>
                </div>
              </button>
        ) : <div style={{ padding: "26px 18px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>No transactions match.</div>}
        <div style={{ height: 24 }} />
      </div>
    </div>);

};

/* deterministic "last synced" label for a linked account — different accounts
   pull at different times, so the time varies by account name but stays stable. */
const SYNC_TIMES = ["6 hours ago", "2 hours ago", "18 minutes ago", "yesterday", "4 hours ago", "1 hour ago", "11 hours ago", "40 minutes ago"];
const syncedAgo = (name) => { let h = 0; for (const c of String(name)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return SYNC_TIMES[h % SYNC_TIMES.length]; };

const AccountDetail = ({ acct, nav }) => {
  if (acct.cash) return <CashAccountDetail acct={acct} nav={nav} />;
  const YOSHI_INV = YOSHI_INVESTMENT_ACCOUNTS;
  const isYoshiInv = YOSHI_INV.some((a) => a.name === acct.name) || acct.combined || acct.opened;
  const external = !isYoshiInv && !acct.combined && acct.name !== ALL_HOLDINGS_ACCT.name; // linked / read-only brokerages
  const startAcct = allHoldingsStartFor(acct);
  const [curAcct, setCurAcct] = useState(startAcct);
  const [menuOpen, setMenuOpen] = useState(false);
  const [actionsOpen, setActionsOpen] = useState(false);
  const [nick, setNick] = useState(accountDisplayName(acct));
  const [editingNick, setEditingNick] = useState(false);
  const [hidden, toggleHidden] = useHiddenAccts();
  const isHidden = hidden.includes(acct.name);
  useEffect(() => {setCurAcct(startAcct);setNick(accountDisplayName(acct));}, [acct]);
  const SWITCH = (() => {
    const base = ALL_HOLDINGS_SWITCH_ACCOUNTS;
    return base.find((a) => a.name === curAcct.name) ? base : [curAcct, ...base];
  })();
  const holdings = curAcct.ids ? curAcct.ids.map((id) => HOLDINGS.find((h) => h.id === id)).filter(Boolean) : curAcct.holdings || [];
  const total = curAcct.valueOverride != null ? curAcct.valueOverride : holdings.reduce((s, h) => s + h.value, 0);
  const dayAbs = holdings.reduce((s, h) => s + h.dayAbs, 0);
  const dayPct = total && total !== dayAbs ? dayAbs / (total - dayAbs) * 100 : 0;
  const [tab, setTab] = useState("holdings");
  const [valRange, setValRange] = useState("Today");
  const [valRangeOpen, setValRangeOpen] = useState(false);
  const rangeWord = (r) => r === "Today" ? "today" : `past ${r === "1W" ? "week" : r === "1M" ? "month" : "year"}`;
  const rangeMenuLabel = (r) => r === "Today" ? "Today" : `Past ${r === "1W" ? "week" : r === "1M" ? "month" : "year"}`;
  // account-level change for the selected period: "Today" uses the live day
  // move; longer ranges aggregate each holding's period return into dollars.
  const valChange = useMemo(() => {
    if (valRange === "Today") return { abs: dayAbs, pct: dayPct };
    let abs = 0;
    for (const h of holdings) {
      const p = (h.perf && h.perf[valRange]) || 0;
      abs += h.value - h.value / (1 + p / 100);
    }
    const base = total - abs;
    return { abs, pct: base ? abs / base * 100 : 0 };
  }, [valRange, holdings, total, dayAbs, dayPct]);
  const [hq, setHq] = useState("");const [hf, setHf] = useState("all");
  const [tq, setTq] = useState("");const [tf, setTf] = useState("all");

  const shownHoldings = holdings.filter((h) => {
    if (hf === "gainers" && h.dch < 0) return false;
    if (hf === "losers" && h.dch >= 0) return false;
    if (hf === "stock" && h.kind !== "stock") return false;
    if (hf === "etf" && h.kind !== "etf") return false;
    if (hf === "crypto" && h.kind !== "crypto") return false;
    if (hf === "recent" && !RECENT_ADDS.includes(h.id)) return false;
    const s = hq.trim().toLowerCase();
    return !s || h.ticker.toLowerCase().includes(s) || h.name.toLowerCase().includes(s);
  });
  const kinds = [...new Set(holdings.map((h) => h.kind))];
  const hFilters = [["all", "All"], ["gainers", "Gainers"], ["losers", "Losers"],
  ...(kinds.includes("stock") ? [["stock", "Stocks"]] : []),
  ...(kinds.includes("etf") ? [["etf", "ETFs"]] : []),
  ...(kinds.includes("crypto") ? [["crypto", "Crypto"]] : []),
  ["recent", "Recently added"]];
  const allTxns = useMemo(() => acctTxns(holdings), [curAcct]);
  const shownTxns = allTxns.filter((t) => {
    if (tf === "buys" && t.side !== "BUY") return false;
    if (tf === "sells" && t.side !== "SELL") return false;
    if (tf === "div" && t.side !== "DIV") return false;
    const s = tq.trim().toLowerCase();
    return !s || t.ticker.toLowerCase().includes(s);
  });

  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0, position: "relative" }}>
      <NavBar onBack={() => nav.pop()} sub={accountDisplaySub(curAcct)}
      title={external ? nick :
      <button className="press" onClick={() => setMenuOpen((o) => !o)} style={{ display: "inline-flex", alignItems: "center", gap: 6, background: "none", border: "none", padding: 0, cursor: "pointer", maxWidth: "100%" }}>
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(17), fontWeight: 600, letterSpacing: "-0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", color: "var(--ink)" }}>{accountDisplayName(curAcct)}</span>
          <Icon name="down" size={16} color="var(--ink-3)" style={{ transform: menuOpen ? "rotate(180deg)" : "none", transition: "transform .15s", flex: "none" }} />
        </button>}
      right={
      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
          {!curAcct.combined && <button className="press" aria-label="Account options" onClick={() => setActionsOpen((o) => !o)} style={{ flex: "none", display: "grid", placeItems: "center", width: 30, height: 30, background: actionsOpen ? "var(--bg-2)" : "none", border: "1px solid " + (actionsOpen ? "var(--rule-2)" : "transparent"), borderRadius: 8, cursor: "pointer", color: "var(--ink-2)" }}><Icon name="more" size={18} /></button>}
          <button className="press" onClick={() => nav.tab("trade")} style={{ background: "var(--accent)", border: "none", borderRadius: 8, padding: "7px 13px", fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 600, color: "var(--accent-ink)" }}>Trade</button>
        </div>} />

      {/* account options (kebab) · external = rename / hide; Yoshi = rename / documents */}
      {actionsOpen && external &&
      <>
        <div onClick={() => setActionsOpen(false)} style={{ position: "absolute", inset: 0, zIndex: 30 }} />
        <div style={{ position: "absolute", top: 52, right: 14, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 232 }}>
          <button className="press" onClick={() => { setEditingNick(true); setActionsOpen(false); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
            <Icon name="doc" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Edit nickname</span>
          </button>
          <button className="press" onClick={() => { toggleHidden(acct.name); setActionsOpen(false); }} style={{ width: "100%", display: "flex", alignItems: "flex-start", gap: 10, padding: "12px 13px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
            <Icon name="eye" size={17} color="var(--ink-2)" stroke={1.6} style={{ marginTop: 1 }} />
            <span style={{ minWidth: 0 }}>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{isHidden ? "Show in totals & summary" : "Hide from totals & summary"}</span>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, lineHeight: 1.4 }}>{isHidden ? "Count this account again everywhere." : "Remains connected"}</span>
            </span>
          </button>
        </div>
      </>}

      {actionsOpen && !external &&
      <>
        <div onClick={() => setActionsOpen(false)} style={{ position: "absolute", inset: 0, zIndex: 30 }} />
        <div style={{ position: "absolute", top: 52, right: 14, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 224 }}>
          <button className="press" onClick={() => { setEditingNick(true); setActionsOpen(false); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
            <Icon name="doc" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Edit nickname</span>
          </button>
          <button className="press" onClick={() => { setActionsOpen(false); nav.sheet({ type: "documents", acct: { Brokerage: "brokerage", "Roth IRA": "roth", Crypto: "crypto" }[curAcct.name] }); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "12px 13px", background: "none", border: "none", borderTop: "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
            <Icon name="receipt" size={17} color="var(--ink-2)" stroke={1.6} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>Documents</span>
          </button>
        </div>
      </>}

      {menuOpen && !external &&
      <>
        <div onClick={() => setMenuOpen(false)} style={{ position: "absolute", inset: 0, zIndex: 30 }} />
        <div style={{ position: "absolute", top: 52, left: 16, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 210 }}>
          {SWITCH.map((a, i) => {
            const on = a.name === curAcct.name;
            return (
              <button key={a.name} className="press" onClick={() => {setCurAcct(a);setMenuOpen(false);setTab("holdings");}} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "11px 13px", background: on ? "var(--bg-2)" : "none", border: "none", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer", textAlign: "left" }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{accountDisplayName(a)}</div>
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{accountDisplaySub(a)}</div>
                </div>
                {on && <Icon name="check" size={15} color="var(--accent)" style={{ flex: "none" }} />}
              </button>);
          })}
        </div>
      </>}

      {/* value header */}
      <section style={{ flex: "none", padding: "14px 18px 12px" }}>
        <Eyebrow>Account value</Eyebrow>
        <div style={{ marginTop: 5, display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" }}>
          <Money value={total} size={30} weight={500} />
          {external && isHidden &&
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, padding: "3px 9px" }}>Hidden from totals</span>}
        </div>
        <div style={{ marginTop: 6, display: "flex", alignItems: "baseline", gap: 8, position: "relative" }}>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 500, fontVariantNumeric: "tabular-nums", color: valChange.pct >= 0 ? "var(--accent-pos)" : "var(--signal-neg)", letterSpacing: "-0.005em" }}>{perf(valChange.abs, valChange.pct)}</span>
          <button className="press" onClick={() => setValRangeOpen((o) => !o)} style={{ display: "inline-flex", alignItems: "center", gap: 4, background: "none", border: "none", padding: 0, cursor: "pointer" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)" }}>{rangeWord(valRange)}</span>
            <Icon name="down" size={13} color="var(--ink-3)" style={{ transform: valRangeOpen ? "rotate(180deg)" : "none", transition: "transform .15s" }} />
          </button>
          {valRangeOpen &&
          <>
            <div onClick={() => setValRangeOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 30 }} />
            <div style={{ position: "absolute", top: "calc(100% + 6px)", left: 0, zIndex: 31, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 10, boxShadow: "0 14px 30px -16px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 150 }}>
              {["Today", "1W", "1M", "1Y"].map((r, i) => {
                const on = r === valRange;
                return (
                  <button key={r} className="press" onClick={() => { setValRange(r); setValRangeOpen(false); }} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "10px 13px", background: on ? "var(--bg-2)" : "none", border: "none", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer", textAlign: "left" }}>
                    <span style={{ flex: 1, fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600 }}>{rangeMenuLabel(r)}</span>
                    {on && <Icon name="check" size={14} stroke={2} color="var(--accent)" />}
                  </button>);
              })}
            </div>
          </>}
        </div>
        {external &&
        <div style={{ marginTop: 9, display: "flex", alignItems: "center", gap: 6, fontFamily: "var(--f-mono)", fontSize: typeSize(10.5), color: "var(--ink-3)", letterSpacing: "0.02em" }}>
          <span style={{ width: 5, height: 5, borderRadius: 999, background: "var(--ink-3)", flex: "none" }} />
          Synced {syncedAgo(acct.name)}
        </div>}
      </section>

      {/* nickname editor · bottom sheet */}
      {editingNick &&
      <>
        <div onClick={() => setEditingNick(false)} 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", animation: "sheet-in 300ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}><span style={{ width: 36, height: 4, background: "var(--rule-2)", borderRadius: 999 }} /></div>
          <div style={{ padding: "14px 18px max(26px,env(safe-area-inset-bottom))" }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 16 }}>Edit nickname</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, color: "var(--ink-2)", marginBottom: 6 }}>Account nickname</div>
            <input autoFocus value={nick} onChange={(e) => setNick(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") setEditingNick(false); }}
              style={{ width: "100%", padding: "12px 13px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 10, outline: "none", fontFamily: "var(--f-display)", fontSize: typeSize(15), fontWeight: 600, color: "var(--ink)" }} />
            <button className="press" onClick={() => setEditingNick(false)} style={{ width: "100%", marginTop: 14, padding: "12px", background: "var(--accent)", border: "none", borderRadius: 10, fontFamily: "var(--f-display)", fontSize: typeSize(14), fontWeight: 600, cursor: "pointer", color: "var(--accent-ink)" }}>Save</button>
          </div>
        </div>
      </>}

      {/* tab toggle */}
      <div style={{ flex: "none", padding: "0 18px 4px" }}>
        <Segmented options={[{ value: "holdings", label: "Holdings" }, { value: "transactions", label: "Transactions" }]} value={tab} onChange={setTab} />
      </div>

      {tab === "holdings" ?
      <>
          <div style={{ flex: "none" }}>
            <AcctToolbar q={hq} setQ={setHq} placeholder="Search holdings" filter={hf} setFilter={setHf}
          filters={hFilters} />
          </div>
          <div className="scroll" style={{ paddingTop: 6 }}>
            <HoldingsTable holdings={shownHoldings} total={total} nav={nav} />
            <div style={{ height: 24 }} />
          </div>
        </> :

      <>
          <div style={{ flex: "none" }}>
            <AcctToolbar q={tq} setQ={setTq} placeholder="Search transactions" filter={tf} setFilter={setTf}
          filters={[["all", "All"], ["buys", "Buys"], ["sells", "Sells"], ["div", "Dividends"]]} />
          </div>
          <div className="scroll" style={{ paddingTop: 6 }}>
            {shownTxns.length ? shownTxns.map((t, i) => <AcctTxnRow key={t.id} t={t} last={i === shownTxns.length - 1} nav={nav} external={external} />) :
          <div style={{ padding: "26px 18px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)" }}>No transactions match.</div>}
            <div style={{ height: 24 }} />
          </div>
        </>
      }
    </div>);

};


Object.assign(window, { Accounts, AccountsExternalPanel, HoldingDetail, AccountDetail, SecurityDetailHierarchy, ACCOUNTS, ALL_HOLDINGS_ACCT });
