/* transfer.jsx, money movement flows rendered as overlays.
   TransferFlow: recipient → amount (custom keypad) → confirm → success.
   TradeSheet: buy/sell a holding → confirm → success.
   LinkSheet: Plaid-style institution picker.
   All share FlowSuccess (the seam draws, the check draws). */

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


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

};

/* ============================================================
   Trade sheet · search → explore → order entry → success
   ============================================================ */
const TradeSheet = ({ onClose, id, side: presetSide, nav, embedded }) => {
  // resolve the launch id against the market catalog; an unknown id (e.g. a
  // paper / linked holding's own id) drops to search instead of a blank sheet
  const launchSel = id && MARKET.find((x) => x.id === id) ? id : null;
  const [phase, setPhase] = useState(launchSel ? "entry" : "search");
  const [query, setQuery] = useState("");
  const [selId, setSelId] = useState(launchSel);
  const [side, setSide] = useState(presetSide || "BUY");
  const [amt, setAmt] = useState("");
  const [unit, setUnit] = useState("usd"); // SELL can switch to "shares"; BUY is always usd
  const [stopOn, setStopOn] = useState(false); // SELL only — stop (limit) order
  const [stop, setStop] = useState("");
  const [focus, setFocus] = useState("amt"); // which field the keypad edits: amt | stop
  const [sector, setSector] = useState(null);
  const sel = MARKET.find((x) => x.id === selId);
  const amtNum = parseFloat(amt || "0");
  const isShares = side === "SELL" && unit === "shares";
  const shares = sel ? isShares ? amtNum : amtNum / sel.last : 0;

  const key = (k) => {
    const maxDp = focus === "amt" && isShares ? 4 : 2;
    const upd = (prev) => k === "⌫" ? prev.slice(0, -1) : prev.includes(".") && prev.split(".")[1]?.length >= maxDp ? prev : prev + k;
    (focus === "stop" ? setStop : setAmt)(upd);
  };
  const chooseSide = (s) => {setSide(s);if (s === "BUY") {setUnit("usd");setStopOn(false);}setFocus("amt");};
  const explore = (mid) => {setSelId(mid);setPhase("detail");};
  const startOrder = (s) => {setAmt("");setStop("");setStopOn(false);setUnit("usd");setFocus("amt");if (s) setSide(s);setPhase("entry");};

  const body =
  <>
      {phase === "search" && <TradeSearch query={query} setQuery={setQuery} onPick={explore} onClose={onClose} nav={nav} embedded={embedded} onSeeAll={() => setPhase("holdings")} onSector={(area, name) => {setSector({ area, name });setPhase("sector");}} />}

      {phase === "sector" && sector &&
    <SectorDetail area={sector.area} name={sector.name} onBack={() => setPhase("search")} onClose={onClose} onPick={explore} nav={nav} />
    }

      {phase === "holdings" && <TradeHoldings onPick={explore} onBack={() => setPhase("search")} onClose={onClose} />}

      {phase === "detail" && sel &&
    <TradeDetail sel={sel} onBack={() => setPhase("search")} onClose={onClose} onTrade={startOrder} nav={nav} />
    }

      {phase === "entry" && sel &&
    <TradeEntry sel={sel} side={side} setSide={chooseSide} unit={unit} setUnit={setUnit}
    amt={amt} setAmt={setAmt} amtNum={amtNum} shares={shares} isShares={isShares}
    stopOn={stopOn} setStopOn={setStopOn} stop={stop} setStop={setStop} focus={focus} setFocus={setFocus}
    onKey={key} onBack={() => setPhase(id ? "search" : "detail")} onClose={onClose} onReview={() => setPhase("success")} nav={nav} />
    }

      {phase === "success" && sel &&
    <div style={{ padding: "10px 0 26px", minHeight: 380, display: "flex" }}>
          <FlowSuccess title={`${side === "BUY" ? "Bought" : "Sold"} ${sel.ticker}`} sub={`${shares.toFixed(4)} ${sel.kind === "crypto" ? sel.ticker : "sh"} at ${usd(sel.last)}. Settles ${sel.kind === "crypto" ? "instantly" : "T+1"}.`} onDone={onClose} />
        </div>
    }
    </>;


  /* Web: Trade is a two-pane page — browse on the left, the order ticket on the
     right. Picking a symbol on the left opens its ticket on the right.
     (Falls back to the single-column flow on narrow windows.) */
  if (window.__YOSHI_WEB && !embedded && window.innerWidth >= 1100) {
    const pickWeb = (mid) => { setSelId(mid); setPhase("detail"); };
    const left =
      phase === "sector" && sector
        ? <SectorDetail area={sector.area} name={sector.name} onBack={() => setPhase("search")} onClose={onClose} onPick={pickWeb} nav={nav} />
        : phase === "holdings"
          ? <TradeHoldings onPick={pickWeb} onBack={() => setPhase("search")} onClose={onClose} />
          : <TradeSearch web query={query} setQuery={setQuery} onPick={pickWeb} onClose={onClose} nav={nav} embedded={embedded} onSeeAll={() => setPhase("holdings")} onSector={(area, name) => { setSector({ area, name }); setPhase("sector"); }} />;
    const right =
      sel && phase === "success"
        ? <div style={{ flex: 1, display: "flex", padding: "10px 0 26px" }}>
            <FlowSuccess title={`${side === "BUY" ? "Bought" : "Sold"} ${sel.ticker}`} sub={`${shares.toFixed(4)} ${sel.kind === "crypto" ? sel.ticker : "sh"} at ${usd(sel.last)}. Settles ${sel.kind === "crypto" ? "instantly" : "T+1"}.`} onDone={onClose} />
          </div>
        : sel && phase === "detail"
        ? <TradeDetail sel={sel} onBack={() => { setSelId(null); setPhase("search"); }} onClose={onClose} onTrade={startOrder} nav={nav} />
        : <TradeEntry web sel={sel || null} side={side} setSide={chooseSide} unit={unit} setUnit={setUnit} onPickSymbol={pickWeb}
            amt={amt} setAmt={setAmt} amtNum={amtNum} shares={shares} isShares={isShares}
            stopOn={stopOn} setStopOn={setStopOn} stop={stop} setStop={setStop} focus={focus} setFocus={setFocus}
            onKey={key} onBack={() => (sel ? setPhase("detail") : setSelId(null))} onClose={onClose} onReview={() => setPhase("success")} nav={nav} />;
    return (
      <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)", display: "flex" }} data-screen-label="Trade">
        <div style={{ flex: "1 1 0", minWidth: 300, maxWidth: 750, position: "relative", display: "flex", flexDirection: "column", minHeight: 0, padding: "0 14px", boxSizing: "border-box", borderRight: "1px solid var(--rule)", background: "var(--bg)" }}>
          <div style={{ flex: "none", padding: "26px 20px 0", display: "flex", alignItems: "center" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: 21, fontWeight: 700, letterSpacing: "-0.025em" }}>Trade</span>
          </div>
          {left}
        </div>
        <div style={{ flex: "1 1 0", minWidth: 300, maxWidth: 460, position: "relative", display: "flex", flexDirection: "column", minHeight: 0, background: "var(--bg)" }}>
          {phase !== "detail" && <button className="press" onClick={onClose} aria-label="Close" style={{ position: "absolute", top: 8, right: 12, zIndex: 20, background: "none", border: "none", display: "flex", color: "var(--ink-3)", cursor: "pointer", padding: 4 }}><Icon name="close" size={19} /></button>}
          {right}
        </div>
      </div>
    );
  }

  /* Embedded mode: Trade is a first-class tab. Render inside the viewport
     so the persistent tab bar (a sibling) stays visible & tappable, exactly
     like Home / Accounts. No own StatusBar — the app's already shows above. */
  if (embedded) {
    return (
      <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column" }} data-screen-label="Trade">
        {body}
      </div>);

  }

  /* Overlay mode: launched from a holding's Buy/Sell — a focused modal flow. */
  return (
    <>
      <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Trade">
        {window.StatusBar && <window.StatusBar />}
        {body}
      </div>
    </>);

};

/* ---- search + explore ----------------------------------------------------- */
/* sector heat map · tiles tinted by move over the selected period, mono numbers */
const SECTOR_PERIODS = ["1D", "1W", "1M", "YTD"];
const SECTORS = [
["Technology", "tech", { "1D": 1.84, "1W": 3.20, "1M": 6.10, "YTD": 22.40 }],
["Financials", "fin", { "1D": 0.42, "1W": 1.10, "1M": 2.30, "YTD": 9.80 }],
["Comms", "comm", { "1D": 1.12, "1W": 0.80, "1M": 4.40, "YTD": 15.20 }],
["Energy", "ener", { "1D": -1.27, "1W": -2.40, "1M": 1.20, "YTD": -3.60 }],
["Healthcare", "heal", { "1D": -0.38, "1W": 0.60, "1M": -1.80, "YTD": 4.10 }],
["Consumer", "cons", { "1D": 0.61, "1W": -0.90, "1M": 2.10, "YTD": 7.30 }],
["Industrials", "indu", { "1D": 0.24, "1W": 1.40, "1M": 3.00, "YTD": 11.00 }],
["Materials", "mats", { "1D": -0.74, "1W": -1.20, "1M": -2.60, "YTD": 1.90 }],
["Utilities", "util", { "1D": -0.19, "1W": 0.30, "1M": -0.90, "YTD": 5.40 }]];
const SECTOR_AREAS = '"tech tech fin fin" "tech tech comm heal" "ener cons cons indu" "ener mats util util"';

/* Yoshi's shortlist per sector · used when a heat-map tile is tapped */
const SECTOR_PICKS = {
  tech: ["NVDA", "MSFT", "AAPL"],
  fin: ["JPM", "V", "BRK.B"],
  comm: ["GOOGL", "META", "NFLX"],
  heal: ["LLY", "UNH", "ABBV"],
  ener: ["XOM", "CVX", "NEE"],
  cons: ["AMZN", "COST", "WMT"],
  indu: ["CAT", "GE", "UBER"],
  mats: ["LIN", "FCX", "NEM"],
  util: ["NEE", "DUK", "SO"]
};

const SectorHeatmap = ({ onSector }) => {
  const [period, setPeriod] = useState("1D");
  const [open, setOpen] = useState(false);
  const maxAbs = Math.max(...SECTORS.map(([,, v]) => Math.abs(v[period]))) || 1;
  const period_theme = useTheme();
  const tint = (d) => {
    const mag = Math.min(Math.abs(d) / maxAbs, 1);
    const vivid = d >= 0 ? [31, 122, 68] : [178, 56, 44];
    const muted = [96, 100, 104];
    const t = 0.30 + mag * 0.70;
    const c = vivid.map((v, i) => Math.round(muted[i] + (v - muted[i]) * t));
    return `rgb(${c[0]}, ${c[1]}, ${c[2]})`;
  };
  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", margin: "20px 0 9px" }}>
        <Eyebrow style={{ margin: 0 }}>Sector heat map</Eyebrow>
        <div style={{ position: "relative", marginLeft: 8 }}>
          <button className="press" onClick={() => setOpen((o) => !o)} onBlur={() => setTimeout(() => setOpen(false), 120)} style={{ display: "inline-flex", alignItems: "center", gap: 3, padding: 0, background: "none", border: "none", cursor: "pointer", fontFamily: "var(--f-mono)", fontSize: 10, fontWeight: 600, letterSpacing: "0.08em", color: "var(--ink-3)" }}>
            {period}
            <Icon name="down" size={12} color="var(--ink-3)" stroke={1.5} />
          </button>
          {open &&
          <div style={{ position: "absolute", top: "calc(100% + 3px)", left: 0, zIndex: 6, background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 6, boxShadow: "0 10px 24px -14px rgba(0,0,0,0.35)", overflow: "hidden", minWidth: 58 }}>
              {SECTOR_PERIODS.map((p) =>
            <button key={p} className="press" onMouseDown={(e) => {e.preventDefault();setPeriod(p);setOpen(false);}} style={{ display: "block", width: "100%", textAlign: "left", padding: "6px 10px", background: p === period ? "var(--bg-2)" : "none", border: "none", cursor: "pointer", fontFamily: "var(--f-mono)", fontSize: 10.5, letterSpacing: "0.06em", color: p === period ? "var(--ink)" : "var(--ink-2)" }}>{p}</button>
            )}
            </div>
          }
        </div>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gridTemplateRows: "repeat(4, 52px)", gridTemplateAreas: SECTOR_AREAS, gap: 5 }}>
        {SECTORS.map(([name, area, vals]) => {
          const d = vals[period];
          return (
            <button key={name} className="press" onClick={() => onSector && onSector(name, area, d)} style={{ gridArea: area, padding: "8px 10px", background: tint(d), border: "none", borderRadius: 7, display: "flex", flexDirection: "column", justifyContent: "space-between", overflow: "hidden", cursor: "pointer", textAlign: "left" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: 11.5, fontWeight: 600, lineHeight: 1.1, letterSpacing: "-0.01em", color: "#fff", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{name}</span>
              <span style={{ fontFamily: "var(--f-mono)", fontSize: 11.5, lineHeight: 1, fontWeight: 500, color: "rgba(255,255,255,0.88)" }}>{pct(d)}</span>
            </button>);
        })}
      </div>
    </div>);
};

/* per-sector constituents · [ticker, name, last, dayChange%, notional$B]
   open / prev-close are derived deterministically from last + day change. */
const SECTOR_MOVERS = {
  tech: [["NVDA", "NVIDIA Corp", 142.17, 2.31, 38.2], ["MSFT", "Microsoft Corp", 468.30, 1.12, 9.8], ["AVGO", "Broadcom Inc", 1712.40, 1.85, 6.1], ["AAPL", "Apple Inc", 214.05, -0.58, 11.4], ["ORCL", "Oracle Corp", 168.22, 0.94, 3.2]],
  fin: [["JPM", "JPMorgan Chase", 248.60, 0.42, 5.6], ["V", "Visa Inc", 312.85, 0.31, 3.1], ["BAC", "Bank of America", 44.20, 0.66, 4.0], ["BRK.B", "Berkshire Hathaway", 462.10, 0.18, 2.4], ["GS", "Goldman Sachs", 561.30, -0.22, 1.9]],
  comm: [["GOOGL", "Alphabet Inc", 178.22, 0.91, 6.8], ["META", "Meta Platforms", 612.05, 1.45, 8.3], ["NFLX", "Netflix Inc", 902.40, 1.88, 3.6], ["TMUS", "T-Mobile US", 224.10, 0.55, 1.7], ["DIS", "Walt Disney Co", 112.30, -0.40, 2.1]],
  ener: [["XOM", "Exxon Mobil", 108.40, -1.27, 4.2], ["CVX", "Chevron Corp", 152.18, -1.10, 3.0], ["COP", "ConocoPhillips", 98.60, -1.84, 2.2], ["SLB", "Schlumberger", 42.10, -2.05, 1.6], ["MPC", "Marathon Petroleum", 168.40, -0.92, 1.3]],
  heal: [["LLY", "Eli Lilly", 824.60, -0.38, 4.4], ["UNH", "UnitedHealth Grp", 512.30, -0.62, 3.1], ["JNJ", "Johnson & Johnson", 152.40, -0.30, 2.6], ["ABBV", "AbbVie Inc", 188.20, 0.24, 2.0], ["MRK", "Merck & Co", 98.10, -0.55, 1.8]],
  cons: [["AMZN", "Amazon.com Inc", 201.40, 1.22, 9.1], ["WMT", "Walmart Inc", 88.60, 0.36, 4.0], ["COST", "Costco Wholesale", 982.30, 0.74, 2.4], ["HD", "Home Depot", 412.50, 0.18, 2.1], ["MCD", "McDonald's Corp", 298.20, -0.22, 1.5]],
  indu: [["CAT", "Caterpillar Inc", 388.40, 0.24, 1.9], ["UBER", "Uber Technologies", 72.30, 1.40, 3.4], ["GE", "GE Aerospace", 198.60, 0.88, 2.2], ["BA", "Boeing Co", 182.10, -0.95, 2.6], ["HON", "Honeywell Intl", 224.50, 0.30, 1.4]],
  mats: [["LIN", "Linde plc", 462.30, -0.74, 1.6], ["FCX", "Freeport-McMoRan", 42.80, -1.20, 2.0], ["NEM", "Newmont Corp", 48.10, -1.60, 1.7], ["NUE", "Nucor Corp", 142.60, -0.88, 1.1], ["SHW", "Sherwin-Williams", 358.20, -0.40, 0.9]],
  util: [["NEE", "NextEra Energy", 78.30, 0.40, 1.9], ["SO", "Southern Co", 88.40, -0.25, 0.9], ["DUK", "Duke Energy", 116.20, -0.19, 0.8], ["D", "Dominion Energy", 56.10, -0.30, 0.7], ["AEP", "American Electric", 98.20, 0.12, 0.6]]
};
const notionalUsd = (b) => b >= 1 ? "$" + b.toFixed(1) + "B" : "$" + Math.round(b * 1000) + "M";

/* full-screen sector page · lists the sector's movers with price, open, prev
   close, day change and notional. Reached by tapping a heat-map tile. */
const SectorDetail = ({ area, name, onBack, onClose, onPick, nav }) => {
  const sector = SECTORS.find((s) => s[1] === area);
  const periods = sector ? sector[2] : { "1D": 0 };
  const rows = (SECTOR_MOVERS[area] || []).map(([ticker, nm, last, dch, notB]) => {
    const prev = last / (1 + dch / 100);
    const dayAbs = last - prev;
    const open = prev + dayAbs * 0.35; // opened partway between prev close and last
    return { ticker, nm, last, dch, prev, open, notB, dayAbs };
  });
  const movers = [...rows].sort((a, b) => Math.abs(b.dch) - Math.abs(a.dch));

  // scrollable table geometry — first column (the name) pinned
  const SEAM = 20,COLW = 82,NAMEW = 96;
  const COLS = [
  ["Day", (r) => ({ main: (r.dch >= 0 ? "+" : "−") + usd(Math.abs(r.dayAbs), 2), sub: pct(r.dch), color: r.dch >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" })],
  ["Last", (r) => ({ main: usd(r.last, 2) })],
  ["Open", (r) => ({ main: usd(r.open, 2), color: "var(--ink-2)" })],
  ["Prev close", (r) => ({ main: usd(r.prev, 2), color: "var(--ink-2)" })],
  ["Notional", (r) => ({ main: notionalUsd(r.notB), color: "var(--ink-2)" })]];

  const last = COLS.length - 1;
  const W = SEAM + NAMEW + COLS.reduce((s, _, i) => s + (i === last ? COLW + SEAM : COLW), 0);
  const stick = { position: "sticky", left: 0, zIndex: 2, background: "var(--bg)", width: SEAM + NAMEW, flex: "none", boxSizing: "border-box", paddingLeft: SEAM };
  const cell = (i) => ({ flex: "none", boxSizing: "border-box", textAlign: "right", width: i === last ? COLW + SEAM : COLW, paddingLeft: 8, paddingRight: i === last ? SEAM : 0 });

  return (
    <>
      <div style={{ padding: "8px 20px 0", display: "flex", alignItems: "center", gap: 10, flex: "none" }}>
        <button className="press" onClick={onBack} style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 0, margin: "0 -2px" }}><Icon name="back" size={20} /></button>
        <span style={{ fontFamily: "var(--f-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em" }}>{name}</span>
        <button className="press" onClick={onClose} style={{ marginLeft: "auto", background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={19} /></button>
      </div>

      {/* sector summary · move across periods */}
      <div style={{ flex: "none", padding: "12px 20px 4px" }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
          <span style={{ fontFamily: "var(--f-display)", fontSize: 12, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--ink-3)" }}>Today</span>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: 22, fontWeight: 500, letterSpacing: "-0.01em", color: periods["1D"] >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" }}>{pct(periods["1D"])}</span>
        </div>
        <div style={{ display: "flex", gap: 18, marginTop: 9 }}>
          {SECTOR_PERIODS.filter((p) => p !== "1D").map((p) =>
          <div key={p}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: 9.5, fontWeight: 600, letterSpacing: "0.08em", color: "var(--ink-3)" }}>{p}</div>
              <div style={{ fontFamily: "var(--f-mono)", fontSize: 12.5, marginTop: 2, color: periods[p] >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" }}>{pct(periods[p])}</div>
            </div>
          )}
        </div>
      </div>

      <Eyebrow style={{ margin: "16px 20px 0" }}>Sector movers</Eyebrow>
      <div className="scroll" style={{ padding: "6px 0 16px" }}>
        <div className="hcar" style={{ overflowX: "auto", overflowY: "hidden" }}>
          <div style={{ width: W }}>
            <div style={{ display: "flex", alignItems: "flex-end", padding: "0 0 7px", borderBottom: "1px solid var(--rule)" }}>
              <div style={{ ...stick }}><Eyebrow style={{ fontSize: 9.5 }}>Name</Eyebrow></div>
              {COLS.map(([label], i) => <div key={label} style={cell(i)}><Eyebrow style={{ fontSize: 9.5 }}>{label}</Eyebrow></div>)}
            </div>
            {movers.map((r, ri) => {
              // every mover opens its stock detail (same as picking a search result);
              // tickers outside the tradeable catalog get a synthesized instrument.
              const open = () => {
                let inst = MARKET.find((m) => m.ticker === r.ticker);
                if (!inst) {
                  inst = { id: "sx-" + r.ticker.toLowerCase().replace(/[^a-z0-9]/g, ""), ticker: r.ticker, name: r.nm, last: r.last, dch: r.dch, kind: "stock" };
                  if (!MARKET.find((m) => m.id === inst.id)) MARKET.push(inst);
                }
                onPick(inst.id);
              };
              return (
                <button key={r.ticker} className="press" onClick={open} style={{ width: "100%", display: "flex", alignItems: "center", background: "none", border: "none", borderBottom: ri === movers.length - 1 ? "none" : "1px solid var(--rule)", padding: "12px 0", textAlign: "left", cursor: "pointer" }}>
                <div style={{ ...stick, display: "flex", flexDirection: "column", justifyContent: "center" }}>
                  <span style={{ fontFamily: "var(--f-display)", fontSize: 13.5, fontWeight: 700, letterSpacing: "-0.01em" }}>{r.ticker}</span>
                  <span style={{ fontFamily: "var(--f-display)", fontSize: 10.5, color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: NAMEW - 4 }}>{r.nm}</span>
                </div>
                {COLS.map(([label, f], i) => {
                    const c = f(r);
                    return (
                      <div key={label} style={cell(i)}>
                      <div style={{ fontFamily: "var(--f-mono)", fontSize: 12.5, fontWeight: 500, color: c.color || "var(--ink)", fontVariantNumeric: "tabular-nums" }}>{c.main}</div>
                      {c.sub && <div style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: c.color || "var(--ink-3)", marginTop: 1, fontVariantNumeric: "tabular-nums" }}>{c.sub}</div>}
                    </div>);
                  })}
              </button>);
            })}
          </div>
        </div>
      </div>
    </>);
};

/* full-screen list of all holdings — reached via "See all" */
const TradeHoldings = ({ onPick, onBack, onClose }) => {
  const all = [...HOLDINGS].sort((a, b) => b.value - a.value).map((h) => MARKET.find((m) => m.id === h.id)).filter(Boolean);
  return (
    <>
      <div style={{ padding: "8px 20px 0", display: "flex", alignItems: "center", gap: 10, flex: "none" }}>
        <button className="press" onClick={onBack} style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 0, margin: "0 -2px" }}><Icon name="back" size={20} /></button>
        <span style={{ fontFamily: "var(--f-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em" }}>Your holdings</span>
        <button className="press" onClick={onClose} style={{ marginLeft: "auto", background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={19} /></button>
      </div>
      <div className="scroll" style={{ padding: "8px 20px 16px" }}>
        {all.map((m, i) => <InstrumentRow key={m.id} m={m} onPick={onPick} last={i === all.length - 1} />)}
      </div>
    </>);

};

const TradeSearch = ({ query, setQuery, onPick, onClose, nav, onSeeAll, embedded, onSector, web }) => {
  const q = query.trim().toLowerCase();
  const [focused, setFocused] = useState(false);
  const results = q ? MARKET.filter((m) => m.ticker.toLowerCase().includes(q) || m.name.toLowerCase().includes(q)) : [];
  const topHoldings = [...HOLDINGS].sort((a, b) => b.value - a.value).slice(0, 4).map((h) => MARKET.find((m) => m.id === h.id)).filter(Boolean);
  const allHoldings = [...HOLDINGS].sort((a, b) => b.value - a.value).map((h) => {const m = MARKET.find((x) => x.id === h.id);return m ? { ...m, posValue: h.value } : null;}).filter(Boolean);
  const moversSorted = [...MARKET].sort((a, b) => b.dch - a.dch);
  const gainers = moversSorted.slice(0, 3);
  const losers = moversSorted.slice(-3).reverse();
  // interleave biggest gainers and biggest losers so strong negatives surface up front
  const moversMixed = (() => {
    const up = moversSorted.filter((m) => m.dch >= 0);
    const down = moversSorted.filter((m) => m.dch < 0).sort((a, b) => a.dch - b.dch);
    const out = [];const max = Math.max(up.length, down.length);
    for (let i = 0; i < max; i++) {if (up[i]) out.push(up[i]);if (down[i]) out.push(down[i]);}
    return out;
  })();
  const askYoshi = () => {onClose();nav && nav.ask("Got any trade ideas for me?", "A few, based on your accounts. You're leaning heavy on NVDA, so moving some of it into VOO would spread out your risk. You've got $15k sitting idle in Savings that could earn about 5.1% in short-term Treasuries, and SOL has run up a lot if you want to take a little off the table. Want me to put any of these together for you to approve?");};

  return (
    <>
      {!web &&
      <div style={{ padding: "6px 16px 10px", minHeight: 44, display: "flex", alignItems: "center", gap: 10, flex: "none" }}>
        <YouButton nav={nav} />
        <span style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em" }}>Trade</span>
        {embedded ?
        <span style={{ marginLeft: "auto" }}><window.BellButton nav={nav} /></span> :
        <button className="press" onClick={onClose} style={{ marginLeft: "auto", background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={19} /></button>}
      </div>}

      {/* free-form search */}
      <div style={{ padding: "12px 20px 4px", flex: "none", position: "relative", zIndex: 5 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "11px 13px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 10 }}>
          <Icon name="trade" size={17} color="var(--ink-3)" stroke={1.5} />
          <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search any stock, ETF, or coin"
          onFocus={() => setFocused(true)} onBlur={() => setTimeout(() => setFocused(false), 120)}
          style={{ flex: 1, border: "none", background: "transparent", outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: 14.5 }} />
          {query && <button className="press" onClick={() => setQuery("")} style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", padding: 0 }}><Icon name="close" size={16} /></button>}
        </div>

        {/* quick-select dropdown · your holdings, shown on focus before typing */}
        {focused && !q &&
        <div style={{ position: "absolute", left: 20, right: 20, top: "calc(100% - 2px)", 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: "8px 13px 5px", borderBottom: "1px solid var(--rule)" }}><Eyebrow>Your holdings</Eyebrow></div>
            <div style={{ maxHeight: 234, overflowY: "auto", overscrollBehavior: "contain" }}>
              {allHoldings.map((m, i) =>
            <button key={m.id} className="press" onMouseDown={(e) => {e.preventDefault();onPick(m.id);}} style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 10, padding: "9px 13px", background: "none", border: "none", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer" }}>
                  <span style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 700, minWidth: 46 }}>{m.ticker}</span>
                  <span style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", flex: 1, minWidth: 0, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{m.name}</span>
                  <span style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 1 }}>
                    <span style={{ fontFamily: "var(--f-mono)", fontSize: 12, color: "var(--ink)", fontVariantNumeric: "tabular-nums" }}>{usd(m.posValue, 0)}</span>
                    <span style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: m.dch >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" }}>{pct(m.dch)}</span>
                  </span>
                </button>
            )}
            </div>
          </div>
        }
      </div>

      <div className="scroll" style={{ padding: "8px 20px 16px" }}>
        {q ?
        results.length ? results.map((m) => <InstrumentRow key={m.id} m={m} onPick={onPick} />) :

        <>
                <div style={{ padding: "26px 0 14px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: 13, color: "var(--ink-3)" }}>No matches for "{query}".</div>
                <YoshiPrompt onClick={askYoshi} sub={`Ask about ${query.toUpperCase()}, or anything else`} />
              </> :


        <>
            {/* impactful Yoshi entry point */}
            <YoshiPrompt onClick={askYoshi} sub="Trade ideas, strategies, what to buy" />

            {/* explore · market movers as scrollable green/red performance pills */}
            <Eyebrow style={{ margin: "18px 0 8px" }}>Market movers</Eyebrow>
            <div className="hcar" style={{ display: "flex", gap: 8, overflowX: "auto", paddingBottom: 2, margin: "0 -20px", padding: "0 20px 2px" }}>
              {moversMixed.map((m) => {
              const up = m.dch >= 0;
              return (
                <button key={m.id} className="press" onClick={() => onPick(m.id)} style={{
                  flex: "none", display: "inline-flex", alignItems: "center", gap: 7, padding: "8px 13px", borderRadius: 999, cursor: "pointer",
                  background: up ? "color-mix(in srgb, var(--accent-pos) 12%, var(--bg))" : "color-mix(in srgb, var(--signal-neg) 12%, var(--bg))",
                  border: "1px solid " + (up ? "color-mix(in srgb, var(--accent-pos) 38%, transparent)" : "color-mix(in srgb, var(--signal-neg) 38%, transparent)")
                }}>
                    <span style={{ fontFamily: "var(--f-display)", fontSize: 12.5, fontWeight: 700, letterSpacing: "-0.01em", color: "var(--ink)" }}>{m.ticker}</span>
                    <span style={{ fontFamily: "var(--f-mono)", fontSize: 11.5, fontWeight: 500, fontVariantNumeric: "tabular-nums", color: up ? "var(--accent-pos)" : "var(--signal-neg)" }}>{m.dch >= 0 ? "+" : "−"}{Math.abs(m.dch).toFixed(2)}%</span>
                  </button>);

            })}
            </div>

            {/* explore · the user's largest positions — studio-style carousel,
                chevron taps out to the brokerage holdings + transactions detail */}
            <button className="press" onClick={() => {onClose();nav.push({ type: "account", acct: window.ALL_HOLDINGS_ACCT || window.ACCOUNTS.brokerage });}} style={{ width: "100%", background: "none", border: "none", display: "flex", alignItems: "center", justifyContent: "space-between", margin: "18px 0 9px", padding: 0, cursor: "pointer", textAlign: "left" }}>
              <Eyebrow>Top holdings</Eyebrow>
              <Icon name="back" size={16} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
            </button>
            <div>
              {allHoldings.slice(0, 3).map((m, i) => {
              const dayAbs = m.last * m.dch / 100;
              const up = m.dch >= 0;
              return (
                <button key={m.id} className="press" onClick={() => onPick(m.id)} style={{ width: "100%", display: "grid", gridTemplateColumns: "1fr auto", gap: 12, alignItems: "center", background: "none", border: "none", borderBottom: i === 2 ? "none" : "1px solid var(--rule)", padding: "11px 0", cursor: "pointer", textAlign: "left" }}>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontFamily: "var(--f-display)", fontSize: 13.5, fontWeight: 700, letterSpacing: "-0.01em" }}>{m.ticker}</div>
                      <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{m.name}</div>
                    </div>
                    <div style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                      <div style={{ fontFamily: "var(--f-mono)", fontSize: 13, fontVariantNumeric: "tabular-nums", color: "var(--ink)" }}>{usd(m.last, 2)}</div>
                      <div style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, fontVariantNumeric: "tabular-nums", marginTop: 3, color: up ? "var(--accent-pos)" : "var(--signal-neg)" }}>{up ? "+" : "−"}{usd(Math.abs(dayAbs), 2)} ({Math.abs(m.dch).toFixed(1)}%)</div>
                    </div>
                  </button>);

            })}
            </div>

            {/* explore · sector heat map */}
            <SectorHeatmap onSector={(name, area) => onSector(area, name)} />

            {/* explore · AI news brief across the whole market, cited */}
            <Eyebrow style={{ margin: "18px 0 0" }}>Market news brief</Eyebrow>
            <window.NewsBrief data={MARKET_NEWS} hideMeta hideSources />
          </>
        }
      </div>
    </>);

};

/* the impactful "chat with Yoshi" entry point */
const YoshiPrompt = ({ onClick, sub }) => {
  const logo = window.__resources && window.__resources[useTheme() === "bone" ? "logoWhite" : "logoBlack"] || "assets/logo-mark-white.png";
  return (
    <button className="press" onClick={onClick} style={{
      width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 12, padding: "13px 14px",
      background: "color-mix(in srgb, var(--accent) 12%, var(--bg-card))", border: "1px solid var(--accent)", borderRadius: 12, cursor: "pointer"
    }}>
      <span style={{ width: 36, height: 36, flex: "none", borderRadius: 999, background: "var(--accent)", display: "grid", placeItems: "center" }}>
        <img src={logo} alt="" style={{ height: 18, width: "auto" }} />
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 14, fontWeight: 600, letterSpacing: "-0.01em" }}>Ask Yoshi</div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 11.5, color: "var(--ink-2)", marginTop: 3 }}>{sub}</div>
      </div>
      <Icon name="back" size={16} color="var(--accent)" style={{ transform: "scaleX(-1)" }} />
    </button>);

};

/* tiny axis-less sparkline · seeded walk, color by direction */
const RowSpark = ({ seed, up, w = 54, h = 22 }) => {
  const n = 18;
  let s = (Number(seed) || 1) % 233280,v = 50,min = Infinity,max = -Infinity;
  const pts = [];
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280;
    v += (s / 233280 - 0.5) * 13 + (up ? 1.2 : -1.2);
    pts.push(v);
    if (v < min) min = v;
    if (v > max) max = v;
  }
  const span = max - min || 1;
  const d = pts.map((p, i) => `${i ? "L" : "M"}${(i / (n - 1) * w).toFixed(1)} ${(h - (p - min) / span * h).toFixed(1)}`).join(" ");
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: "block", overflow: "visible" }} aria-hidden="true">
      <path d={d} fill="none" stroke={up ? "var(--accent-pos)" : "var(--signal-neg)"} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>);

};

const InstrumentRow = ({ m, onPick, last }) =>
<button className="press" onClick={() => onPick(m.id)} style={{
  width: "100%", textAlign: "left", background: "none", border: "none", borderBottom: last ? "none" : "1px solid var(--rule)",
  display: "flex", alignItems: "center", gap: 10, padding: "11px 0", cursor: "pointer"
}}>
    <span style={{ fontFamily: "var(--f-display)", fontSize: 14, fontWeight: 600, letterSpacing: "-0.01em", minWidth: 52 }}>{m.ticker}</span>
    <span style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", minWidth: 0, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{m.name}</span>
  </button>;


/* ---- instrument detail · the "explore" view ------------------------------- */
const TD_RANGES = ["1D", "1M", "6M", "YTD", "1Y", "5Y", "All"];
const yrReturn = (m) => {const h = HOLDINGS.find((x) => x.id === m.id);return h ? h.perf["1Y"] : m.kind === "crypto" ? 72 : m.kind === "etf" ? 17 : 31;};
const tdSeries = (m, range) => {
  const seed = (m.ticker.charCodeAt(0) + (m.ticker.charCodeAt(1) || 0)) * 7;
  const yr = yrReturn(m);
  const cfg = {
    "1D": [30, m.dch, 0.0012],
    "1M": [32, m.dch * 4.5, 0.007],
    "6M": [40, yr * 0.55, 0.011],
    "YTD": [44, yr * 0.7, 0.012],
    "1Y": [54, yr, 0.014],
    "5Y": [60, (Math.pow(1 + yr / 100, 5) - 1) * 100, 0.02],
    "All": [64, (Math.pow(1 + yr / 100, 7) - 1) * 100, 0.022]
  }[range];
  const [len, movePct, vol] = cfg;
  const start = m.last / (1 + movePct / 100);
  return { data: series(seed + len, len, start, m.last, vol), movePct };
};
const tdStats = (m) => {
  const h = HOLDINGS.find((x) => x.id === m.id);
  if (h) return h.fund;
  const cap = m.last * (m.kind === "crypto" ? 19e6 : 2.4e9);
  return [["Asset class", m.kind === "crypto" ? "Crypto" : m.kind === "etf" ? "ETF" : "Equity"], ["Market cap", "$" + (cap / 1e9).toFixed(0) + "B"], ["Day range", `${usd(m.last * 0.99, 2)} – ${usd(m.last * 1.01, 2)}`], ["1Y return", pct(yrReturn(m))]];
};
const tdNews = (m) => {
  const h = HOLDINGS.find((x) => x.id === m.id);
  if (h && h.news) return h.news;
  return [[m.kind === "crypto" ? "CoinDesk" : "Bloomberg", `${m.name} in focus as volume picks up`, "1h"], ["Reuters", `Analysts revisit ${m.ticker} after the move`, "4h"]];
};

const TradeDetail = ({ sel, onBack, onClose, onTrade, nav }) => {
  const [range, setRange] = useState("1M");
  const [scrub, setScrub] = useState(null);
  const { data, movePct } = useMemo(() => tdSeries(sel, range), [sel, range]);
  const accent = data.length > 1 && data[data.length - 1] >= data[0] ? "var(--accent-pos)" : "var(--signal-neg)";
  const price = scrub != null ? scrub : sel.last;
  const yr = yrReturn(sel);
  const rating = yr > 25 ? "Buy" : yr > 8 ? "Hold" : "Mixed";

  return (
    <>
      <div style={{ padding: "8px 20px 0", display: "flex", alignItems: "center", gap: 10, flex: "none" }}>
        <button className="press" onClick={onBack} style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 0, margin: "0 -2px" }}><Icon name="back" size={20} /></button>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em" }}>{sel.ticker}</span>
          </div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sel.name}</div>
        </div>
        <button className="press" onClick={onClose} style={{ marginLeft: "auto", background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={19} /></button>
      </div>

      <div className="scroll" style={{ padding: "12px 20px 0" }}>
        {/* price */}
        <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
          <Money value={price} size={28} weight={500} />
          <span style={{ fontFamily: "var(--f-mono)", fontSize: 12.5, color: accent }}>{range === "1D" ? `${pct(sel.dch)} today` : `${movePct >= 0 ? "+" : ""}${movePct.toFixed(1)}% ${range.toLowerCase()}`}</span>
        </div>

        {/* chart + range */}
        <div style={{ marginTop: 12 }}>
          <Chart data={data} height={120} accent={accent} fillColor={accent} scrub onScrub={(idx) => setScrub(idx == null ? null : data[idx])} padY={4} />
        </div>
        <div style={{ display: "flex", gap: 3, marginTop: 10 }}>
          {TD_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: 11, fontWeight: 500, color: on ? "var(--ink)" : "var(--ink-3)", cursor: "pointer" }}>{r}</button>;
          })}
        </div>

        {/* analysis */}
        <Eyebrow style={{ margin: "18px 0 7px" }}>Analysis</Eyebrow>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 9 }}>
          <span style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 600, color: "var(--accent)" }}>{rating}</span>
          <div style={{ flex: 1, height: 5, background: "var(--bg-2)", borderRadius: 999, overflow: "hidden" }}>
            <div style={{ width: `${Math.min(96, 50 + yr)}%`, height: "100%", background: "var(--accent)" }} />
          </div>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: "var(--ink-3)" }}>{18 + sel.ticker.length * 3} analysts</span>
        </div>

        {/* ask Yoshi about this name */}
        <button className="press" onClick={() => {onClose();nav && nav.ask(`What do you think about ${sel.ticker}?`, `${sel.ticker} (${sel.name}) is ${sel.dch >= 0 ? "up" : "down"} ${Math.abs(sel.dch).toFixed(2)}% today and ${yr >= 0 ? "up" : "down"} ${Math.abs(yr)}% over the year. Consensus reads ${rating}. Against your accounts it'd ${sel.held ? "add to a position you already hold" : "be a new name for you"}. Want me to size a starter position and draft it as a proposal?`);}}
        style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 11, padding: "11px 13px", background: "color-mix(in srgb, var(--accent) 12%, var(--bg-card))", border: "1px solid var(--accent)", borderRadius: 12, cursor: "pointer" }}>
          <span style={{ width: 32, height: 32, flex: "none", borderRadius: 999, background: "var(--accent)", display: "grid", placeItems: "center" }}>
            <img src={window.__resources && window.__resources[useTheme() === "bone" ? "logoWhite" : "logoBlack"] || "assets/logo-mark-white.png"} alt="" style={{ height: 16, width: "auto" }} />
          </span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 13.5, fontWeight: 600 }}>Ask Yoshi about {sel.ticker}</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-2)", marginTop: 3 }}>Is it a fit for your accounts?</div>
          </div>
          <Icon name="back" size={15} color="var(--accent)" style={{ transform: "scaleX(-1)" }} />
        </button>

        {/* stats */}
        <Eyebrow style={{ margin: "18px 0 4px" }}>Key stats</Eyebrow>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0 18px" }}>
          {tdStats(sel).map(([k, v]) =>
          <div key={k} style={{ display: "flex", justifyContent: "space-between", padding: "8px 0", borderBottom: "1px dashed var(--rule)" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: 11.5, color: "var(--ink-3)" }}>{k}</span>
              <span style={{ fontFamily: /[0-9]/.test(v) ? "var(--f-mono)" : "var(--f-display)", fontSize: 11.5, fontWeight: 500 }}>{v}</span>
            </div>
          )}
        </div>

        {/* news */}
        <Eyebrow style={{ margin: "18px 0 4px" }}>News</Eyebrow>
        {tdNews(sel).map(([src, head, when], i) =>
        <div key={i} style={{ padding: "11px 0", borderBottom: "1px solid var(--rule)" }}>
            <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: 9.5, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--accent)" }}>{src}</span>
              <span style={{ marginLeft: "auto", fontFamily: "var(--f-mono)", fontSize: 10, color: "var(--ink-3)" }}>{when}</span>
            </div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 500, marginTop: 3, lineHeight: 1.35 }}>{head}</div>
          </div>
        )}
        <div style={{ height: 8 }} />
      </div>

      {/* sticky trade footer */}
      <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={() => onTrade()}>Trade {sel.ticker}</Btn>
      </div>
    </>);

};

/* ---- order entry · mirrors the Trade form: account → symbol → action →
   amount (with % quick-fills) → confirm. Buy is USD-only; Sell can be USD or
   shares and can carry a stop (limit) order. Advanced order types live in
   automations. ------------------------------------------------------------- */
/* the Yoshi investment accounts a user can trade into/from, each with its own
   buying power. The picker on the Account row selects between them. */
const TRADE_ACCTS = [
  { name: "Brokerage", sub: "Yoshi · Individual ••2208", power: 10000.00 },
  { name: "Roth IRA", sub: "Yoshi · Retirement ••7731", power: 4291.28 },
  { name: "Crypto", sub: "Yoshi · Spot ••6614", power: 2150.00, crypto: true },
];

const TradeEntry = ({ sel, side, setSide, unit, setUnit, amt, setAmt, amtNum, shares, isShares, stopOn, setStopOn, stop, setStop, focus, setFocus, onKey, onBack, onClose, onReview, nav, web, onPickSymbol }) => {
  const [symQ, setSymQ] = useState("");
  const [gate, setGate] = useState(false);
  const symResults = symQ.trim() ? MARKET.filter((m) => m.ticker.toLowerCase().includes(symQ.trim().toLowerCase()) || m.name.toLowerCase().includes(symQ.trim().toLowerCase())).slice(0, 6) : [];
  const [padOpen, setPadOpen] = useState(false);
  const [acctIdx, setAcctIdx] = useState(0);
  const [acctOpen, setAcctOpen] = useState(false);
  // crypto symbols trade only in the Crypto account; stocks/ETFs only in the others
  const accts = TRADE_ACCTS.filter((a) => sel && sel.kind === "crypto" ? a.crypto : !a.crypto);
  const acct = accts[Math.min(acctIdx, accts.length - 1)];
  const held = sel ? HOLDINGS.find((h) => h.id === sel.id) : null;
  const posShares = held ? held.shares : 0;
  const posValue = held ? held.value : 0;
  // buying always draws from the single Yoshi cash account
  const avail = side === "BUY" || !sel ? CASH_TOTAL : isShares ? posShares : posValue;
  const availLabel = isShares && sel ? `${posShares} ${sel.kind === "crypto" ? sel.ticker : "sh"} available` : side === "BUY" || !sel ? `${usd(avail)} available in Yoshi Cash` : `${usd(avail)} available`;
  const dayAbs = sel ? sel.last - sel.last / (1 + sel.dch / 100) : 0;
  const stopNum = parseFloat(stop || "0");
  const canConfirm = !!sel && amtNum > 0 && (!stopOn || stopNum > 0);
  const openPad = (f) => {setFocus(f);setPadOpen(true);};
  const setPct = (f) => {setFocus("amt");setAmt(isShares ? String(+(avail * f).toFixed(4)) : String(+(avail * f).toFixed(2)));};
  const toAutomations = () => {if (onClose) onClose();if (nav && nav.studio) nav.studio("automations");};

  const fieldStyle = (active) => ({ display: "flex", alignItems: "center", gap: 8, minHeight: 50, padding: "0 14px", background: "var(--bg-card)", border: `1px solid ${active ? "var(--accent)" : "var(--rule-2)"}`, borderRadius: 12, cursor: "pointer" });

  return (
    <>
      {!web &&
      <div style={{ padding: "8px 20px 0", display: "flex", alignItems: "center", gap: 10, flex: "none" }}>
        <button className="press" onClick={onBack} style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 0, margin: "0 -2px" }}><Icon name="back" size={20} /></button>
        <span style={{ fontFamily: "var(--f-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em" }}>Trade</span>
      </div>}

      <div className="scroll" style={{ padding: web ? "26px 36px 0 20px" : "12px 20px 0" }}>
        {/* account · dropdown to pick which Yoshi account you trade into/from */}
        <Eyebrow>Account</Eyebrow>
        <div style={{ marginTop: web ? 22 : 7, background: "var(--bg-card)", border: `1px solid ${acctOpen ? "var(--accent)" : "var(--rule-2)"}`, borderRadius: 12, overflow: "hidden", transition: "border-color .12s" }}>
          <button className="press" onClick={() => setAcctOpen((o) => !o)} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", background: "none", border: "none", cursor: "pointer", textAlign: "left" }}>
            <span style={{ width: 34, height: 34, flex: "none", display: "grid", placeItems: "center", color: "var(--ink)" }}><window.Logo size={19} /></span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: 14, fontWeight: 600 }}>{acct.name}</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: 11.5, color: "var(--ink-3)", marginTop: 3 }}>{acct.sub}</div>
            </div>
            <span style={{ display: "flex", flex: "none", transform: acctOpen ? "rotate(180deg)" : "none", transition: "transform .15s", color: "var(--ink-3)" }}><Icon name="down" size={16} /></span>
          </button>
          {acctOpen &&
          <div style={{ borderTop: "1px solid var(--rule)" }}>
            {accts.map((a, i) =>
            <button key={a.name} className="press" onClick={() => {setAcctIdx(i);setAcctOpen(false);}} style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "11px 14px", background: i === acctIdx ? "var(--bg-2)" : "none", border: "none", borderTop: i === 0 ? "none" : "1px solid var(--rule)", cursor: "pointer", textAlign: "left" }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "var(--f-display)", fontSize: 13.5, fontWeight: 600 }}>{a.name}</div>
                <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", marginTop: 2 }}>{a.sub}</div>
              </div>
              {i === acctIdx && <Icon name="check" size={16} color="var(--accent)" style={{ flex: "none" }} />}
            </button>
            )}
          </div>}
        </div>

        {/* symbol */}
        <Eyebrow style={{ marginTop: 16 }}>Symbol</Eyebrow>
        {sel ? (
        <button className="press" onClick={onBack} style={{ width: "100%", marginTop: 7, display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, cursor: "pointer", textAlign: "left" }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 14, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sel.ticker} · {sel.name}</div>
          </div>
          <Icon name="close" size={16} color="var(--ink-3)" style={{ flex: "none" }} />
        </button>
        ) : (
        <div style={{ position: "relative", marginTop: 7 }}>
          <div style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", boxSizing: "border-box", background: "var(--bg-card)", border: `1px ${symQ ? "solid" : "dashed"} ${symQ ? "var(--accent)" : "var(--rule-2)"}`, borderRadius: 12 }}>
            <Icon name="search" size={16} color="var(--ink-3)" stroke={1.5} style={{ flex: "none" }} />
            {onPickSymbol
              ? <input value={symQ} onChange={(e) => setSymQ(e.target.value)} placeholder="Search any stock, ETF, or coin"
                  style={{ flex: 1, minWidth: 0, border: "none", background: "transparent", outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: 13.5 }} />
              : <div style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-display)", fontSize: 13.5, color: "var(--ink-3)" }}>Pick a symbol on the left — search, movers, or your holdings</div>}
          </div>
          {symResults.length > 0 &&
          <div style={{ position: "absolute", left: 0, right: 0, top: "calc(100% + 4px)", zIndex: 40, 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" }}>
            {symResults.map((m, i) =>
            <button key={m.id} className="press" onMouseDown={(e) => { e.preventDefault(); setSymQ(""); onPickSymbol(m.id); }} style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 10, padding: "9px 13px", background: "none", border: "none", borderTop: i ? "1px solid var(--rule)" : "none", cursor: "pointer" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 700, minWidth: 46 }}>{m.ticker}</span>
              <span style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", flex: 1, minWidth: 0, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{m.name}</span>
              <span style={{ fontFamily: "var(--f-mono)", fontSize: 12, fontVariantNumeric: "tabular-nums", flex: "none" }}>{usd(m.last, 2)}</span>
            </button>)}
          </div>}
        </div>
        )}

        {/* price */}
        {sel &&
        <div style={{ display: "flex", alignItems: "baseline", gap: 9, margin: "14px 2px 0", flexWrap: "wrap" }}>
          <span style={{ fontFamily: "var(--f-display)", fontSize: 11, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--ink-3)" }}>Price</span>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: 15, fontWeight: 500, fontVariantNumeric: "tabular-nums" }}>{usd(sel.last, 2)}</span>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: 12, color: sel.dch >= 0 ? "var(--accent-pos)" : "var(--signal-neg)" }}>{sel.dch >= 0 ? "+" : "−"}{usd(Math.abs(dayAbs), 2)} ({sel.dch >= 0 ? "+" : "−"}{Math.abs(sel.dch).toFixed(2)}%) today</span>
        </div>}

        {/* action */}
        <Eyebrow style={{ marginTop: 18 }}>Action</Eyebrow>
        <div style={{ marginTop: 7 }}>
          <Segmented options={[{ value: "BUY", label: "Buy" }, { value: "SELL", label: "Sell" }]} value={side} onChange={setSide} />
        </div>

        {/* sell-only · dollars or shares (buy is dollars only) */}
        {side === "SELL" &&
        <div style={{ marginTop: 10 }}>
          <Segmented options={[{ value: "usd", label: "Dollars" }, { value: "shares", label: "Shares" }]} value={unit} onChange={(u) => {setUnit(u);setAmt("");}} />
        </div>}

        {/* amount */}
        <Eyebrow style={{ marginTop: 18 }}>Amount <span style={{ textTransform: "none", letterSpacing: 0, color: "var(--ink-3)", fontWeight: 500 }}>({availLabel})</span></Eyebrow>
        <div onClick={() => openPad("amt")} style={{ ...fieldStyle(focus === "amt" && padOpen), marginTop: 7 }}>
          {!isShares && <span style={{ fontFamily: "var(--f-mono)", fontSize: 18, color: amt ? "var(--ink-3)" : "var(--rule-2)" }}>$</span>}
          <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-mono)", fontSize: 18, color: amt ? "var(--ink)" : "var(--rule-2)", fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap", overflow: "hidden" }}>{amt || "Enter amount"}</span>
          {sel && isShares && amt && <span style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", flex: "none" }}>{sel.kind === "crypto" ? sel.ticker : "sh"}</span>}
          {amt && <button className="press" onClick={(e) => {e.stopPropagation();setAmt("");}} style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", padding: 0, flex: "none" }}><Icon name="close" size={16} /></button>}
        </div>
        <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
          {[["25%", 0.25], ["50%", 0.5], ["75%", 0.75], ["Max", 1]].map(([lbl, f]) =>
          <button key={lbl} className="press" onClick={() => setPct(f)} style={{ flex: 1, padding: "7px 0", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 999, fontFamily: "var(--f-display)", fontSize: 12, fontWeight: 600, color: lbl === "Max" ? "var(--accent)" : "var(--ink-2)", cursor: "pointer" }}>{lbl}</button>
          )}
        </div>
        {sel && !isShares && amtNum > 0 &&
        <div style={{ fontFamily: "var(--f-mono)", fontSize: 11, color: "var(--ink-3)", marginTop: 7 }}>≈ {shares.toFixed(sel.kind === "crypto" ? 6 : 4)} {sel.kind === "crypto" ? sel.ticker : "sh"} at {usd(sel.last)}</div>}

        {/* sell-only · stop (limit) order. Buys cannot carry a stop. */}
        {side === "SELL" &&
        <>
          <div style={{ marginTop: 18, display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12 }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: 13.5, fontWeight: 600 }}>Add a stop loss</div>
              <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", marginTop: 2, lineHeight: 1.4 }}>Sell at market if the price falls to your stop.</div>
            </div>
            <button className="press" aria-label="Toggle stop loss" onClick={() => setStopOn((o) => {const n = !o;if (!n) setFocus("amt");return n;})} style={{ width: 34, height: 20, borderRadius: 999, background: stopOn ? "var(--accent)" : "var(--rule-2)", position: "relative", flex: "none", border: "none", cursor: "pointer", transition: "background .15s" }}>
              <span style={{ position: "absolute", top: 2, left: stopOn ? 16 : 2, width: 16, height: 16, borderRadius: 999, background: "var(--bg)", transition: "left .15s", boxShadow: "0 1px 2px rgba(0,0,0,0.2)" }} />
            </button>
          </div>
          {stopOn &&
          <div onClick={() => openPad("stop")} style={{ ...fieldStyle(focus === "stop" && padOpen), marginTop: 8 }}>
            <span style={{ fontFamily: "var(--f-mono)", fontSize: 16, color: stop ? "var(--ink-3)" : "var(--rule-2)" }}>$</span>
            <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-mono)", fontSize: 16, color: stop ? "var(--ink)" : "var(--rule-2)", fontVariantNumeric: "tabular-nums" }}>{stop || "Stop price"}</span>
            {stop && <button className="press" onClick={(e) => {e.stopPropagation();setStop("");}} style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)", padding: 0 }}><Icon name="close" size={16} /></button>}
          </div>}
        </>}

        {/* automations · the clear path to limit prices, recurring buys, and triggers */}
        <button className="press" onClick={toAutomations} style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 11, marginTop: 18, padding: "12px 13px", background: "color-mix(in srgb, var(--accent) 10%, var(--bg-card))", border: "1px solid var(--accent)", borderRadius: 12, cursor: "pointer" }}>
          <span style={{ width: 30, height: 30, flex: "none", display: "grid", placeItems: "center", borderRadius: 999, background: "var(--accent)" }}><Icon name="bolt" size={16} color="var(--accent-ink)" /></span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 600 }}>Need a limit price, recurring buy, or trigger?</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-2)", marginTop: 2, lineHeight: 1.4 }}>Set up an automation for Yoshi to run for you.</div>
          </div>
          <Icon name="back" size={15} color="var(--accent)" style={{ transform: "scaleX(-1)", flex: "none" }} />
        </button>

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

      {/* confirm + footer */}
      <div style={{ flex: "none", padding: "12px 20px 18px", borderTop: "1px solid var(--rule)" }}>
        <Btn full disabled={!canConfirm} onClick={() => setGate(true)}>Confirm {side === "BUY" ? "Buy" : "Sell"}</Btn>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 9.5, color: "color-mix(in srgb, var(--ink-3) 62%, transparent)", textAlign: "center", marginTop: 10, lineHeight: 1.5 }}>
          Executes as a market order. Verified with Passkey
          <br />
          Yoshi is a fintech company, not an FDIC-insured depository institution. Deposits in Yoshi Cash are FDIC-insured through Column N.A., Member FDIC. FDIC deposit insurance covers the failure of an insured depository institution. Certain conditions must be satisfied for pass-through FDIC insurance to apply.
        </div>
      </div>
      {gate && <PasskeyGate title={`${side === "BUY" ? "Buy" : "Sell"} ${sel.ticker}`} amount={isShares ? null : amtNum} detail={`${shares ? shares.toFixed(sel.kind === "crypto" ? 4 : 2) : ""} ${sel.kind === "crypto" ? sel.ticker : "sh"} · market order. Yoshi executes immediately.`} cta="Authorize with passkey" onSuccess={() => { setGate(false); onReview(); }} onCancel={() => setGate(false)} />}

      {/* keypad sheet — opens on a field tap */}
      {padOpen &&
      <>
        <div onClick={() => setPadOpen(false)} style={{ position: "absolute", inset: 0, zIndex: 40 }} />
        <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, zIndex: 41, background: "var(--bg)", borderTop: "1px solid var(--accent)", animation: "sheet-in 240ms cubic-bezier(0.16,1,0.30,1) both" }}>
          <div style={{ padding: "10px 20px 4px", display: "flex", alignItems: "center" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: 12, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--ink-3)" }}>{focus === "stop" ? "Stop price" : "Amount"}</span>
            <button className="press" onClick={() => setPadOpen(false)} style={{ marginLeft: "auto", background: "none", border: "none", color: "var(--accent)", fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 600, cursor: "pointer" }}>Done</button>
          </div>
          <Keypad onKey={onKey} />
          <div style={{ height: 8 }} />
        </div>
      </>}
    </>);
};


const PreviewLine = ({ label, value, hint, last }) =>
<div style={{ display: "flex", alignItems: "baseline", gap: 8, padding: "10px 13px", borderBottom: last ? "none" : "1px dashed var(--rule)" }}>
    <span style={{ fontFamily: "var(--f-display)", fontSize: 11.5, color: "var(--ink-2)" }}>{label}</span>
    {hint && <span style={{ fontFamily: "var(--f-display)", fontSize: 10, color: "var(--ink-3)" }}>· {hint}</span>}
    <span style={{ marginLeft: "auto", fontFamily: "var(--f-mono)", fontSize: 12.5, fontWeight: 500, fontVariantNumeric: "tabular-nums" }}>{value}</span>
  </div>;



/* ============================================================
   Link external account sheet
   ============================================================ */
const LinkSheet = ({ onClose }) =>
<>
    <div className="yo-focus-backdrop" onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)", zIndex: 300 }} />
    <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, zIndex: 301, background: "var(--bg)", borderTop: "1px solid var(--accent)", animation: "sheet-in 320ms 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: "10px 20px 4px", display: "flex", alignItems: "center" }}>
        <span style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em" }}>Link an account</span>
        <button className="press" onClick={onClose} style={{ marginLeft: "auto", background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={19} /></button>
      </div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: 12.5, color: "var(--ink-2)", padding: "4px 20px 0", lineHeight: 1.5 }}>Pick an institution to link with Plaid.</div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8, padding: "16px 20px 24px" }}>
        {[["Chase", "C"], ["BofA", "B"], ["Schwab", "S"], ["Fidelity", "F"], ["Wells", "W"], ["Coinbase", "₿"]].map(([n, g]) =>
      <button key={n} className="press" onClick={onClose} style={{ padding: "14px 6px", background: "var(--bg-card)", border: "1px solid var(--rule)", display: "flex", flexDirection: "column", alignItems: "center", gap: 7, cursor: "pointer" }}>
            <span style={{ width: 30, height: 30, background: "var(--ink)", color: "var(--bg-card)", display: "grid", placeItems: "center", fontFamily: "var(--f-display)", fontSize: 15, fontWeight: 700 }}>{g}</span>
            <span style={{ fontFamily: "var(--f-display)", fontSize: 11, fontWeight: 500 }}>{n}</span>
          </button>
      )}
      </div>
    </div>
  </>;


Object.assign(window, { TradeSheet, LinkSheet });

/* ============================================================
   Connect an external agent · MCP / CLI / API
   The portability promise made tangible: scoped, propose-only
   access an outside agent can wire up in one of three ways.
   ============================================================ */
/* The three ways an outside agent wires up to Yoshi. The config a connection
   shows is built from its own name + key, so the credential travels with it. */
const METHOD_OPTIONS = [{ value: "MCP", label: "MCP" }, { value: "SDK", label: "SDK" }, { value: "API", label: "API" }];
const METHOD_HINT = {
  MCP: "Paste Yoshi into your agent's MCP config, then authorize it inside the agent. The connection shows up in Active connections once you approve.",
  SDK: "Call Yoshi from your own code. SDKs authenticate with an API key, so there's nothing to register here.",
  API: "Call the REST API directly. Name the connection and mint its scoped key.",
};

// MCP config is identical for everyone — a server URL, authorized inside the
// agent over OAuth. No token to paste, no name to choose: the agent reports its
// own name back over the protocol.
const MCP_CONFIG = `{
  "mcpServers": {
    "yoshi": {
      "url": "https://prototype-agents.yoshi.invalid/mcp"
    }
  }
}`;

// The Yoshi SDK across languages — each authenticates with a scoped API key.
// The Yoshi SDK + CLI, installed with your package manager. Each authenticates
// with a scoped API key.
const SDK_MANAGERS = [
  { id: "pip", label: "pip", cmd: "pip install yoshi" },
  { id: "npm", label: "npm", cmd: "npm install @yoshi-ai/sdk" },
  { id: "brew", label: "Homebrew", cmd: "brew install yoshi-ai-dev/yoshi-cli/yoshi" },
  { id: "go", label: "Go", cmd: "go install github.com/yoshi-ai-dev/yoshi-cli/cmd/yoshi@latest" },
];
const OPENCLAW_SETUP = "mcporter auth https://prototype-agents.yoshi.invalid/mcp/";
const BUILD_SETUP = "npx create-yoshi-agent@latest";
const CHATGPT_APP_URL = "https://chatgpt.example.invalid/yoshi";

// Legacy multi-language snippets (unused).
const SDK_LANGS = [
  { value: "python", label: "Python" },
  { value: "cli", label: "CLI" },
  { value: "npm", label: "npm" },
  { value: "go", label: "Go" },
];
const SDK_CODE = {
  python: `$ pip install yoshi

from yoshi import Yoshi
client = Yoshi(api_key="<YOSHI_API_KEY>")
client.propose.trade(symbol="VTI", usd=500)`,
  cli: `$ npm i -g @yoshi/cli
$ export YOSHI_API_KEY=<YOSHI_API_KEY>
$ yoshi propose trade --symbol VTI --usd 500`,
  npm: `$ npm i @yoshi/sdk

import { Yoshi } from "@yoshi/sdk";
const yoshi = new Yoshi({ apiKey: "<YOSHI_API_KEY>" });
await yoshi.propose.trade({ symbol: "VTI", usd: 500 });`,
  go: `$ go get github.com/yoshi/yoshi-go

client := yoshi.New("<YOSHI_API_KEY>")
client.Propose.Trade(yoshi.Trade{Symbol: "VTI", USD: 500})`,
};

// A copy-paste API quickstart — the docs snippet that used to live here.
const API_DOCS = `$ curl https://api.prototype.yoshi.invalid/v1/propose/trade \\
    -H "Authorization: Bearer <YOSHI_API_KEY>" \\
    -d symbol=VTI -d usd=500`;
const API_DOCS_URL = "https://docs.prototype.yoshi.invalid/api";

const CodeBlock = ({ code }) => {
  const [copied, setCopied] = useState(false);
  const copy = () => { try { navigator.clipboard && navigator.clipboard.writeText(code); } catch (_) {} setCopied(true); setTimeout(() => setCopied(false), 1600); };
  return (
    <div style={{ margin: "12px 20px 0", background: "var(--terminal-fill)" }}>
      <div style={{ display: "flex", justifyContent: "flex-end", padding: "8px 8px 0" }}>
        <button className="press" onClick={copy} style={{ background: "color-mix(in srgb, var(--terminal-ink) 14%, transparent)", border: "none", color: "var(--terminal-ink)", fontFamily: "var(--f-display)", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", padding: "5px 9px", cursor: "pointer" }}>{copied ? "Copied" : "Copy"}</button>
      </div>
      <pre style={{ margin: 0, padding: "6px 15px 14px", color: "var(--terminal-ink)", fontFamily: "var(--f-mono)", fontSize: 11.5, lineHeight: 1.7, whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{code}</pre>
    </div>
  );
};

/* small muted explainer line used across the setup panels */
const SetupNote = ({ children }) =>
  <div style={{ fontFamily: "var(--f-display)", fontSize: 11.5, color: "var(--ink-3)", lineHeight: 1.5, margin: "10px 20px 0" }}>{children}</div>;

/* A direct shortcut into the ChatGPT Yoshi app — gets MCP users closest to done. */
const ChatGptShortcut = () =>
  <a href={CHATGPT_APP_URL} target="_blank" rel="noopener noreferrer" className="press"
    style={{ display: "flex", alignItems: "center", gap: 11, margin: "12px 20px 0", padding: "12px 13px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", borderRadius: 12, textDecoration: "none", color: "inherit", cursor: "pointer" }}>
    <span style={{ width: 32, height: 32, flex: "none", border: "1px solid var(--rule-2)", borderRadius: 9, display: "grid", placeItems: "center", color: "var(--ink)" }}>
      <Icon name="message" size={18} stroke={1.5} />
    </span>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 600, letterSpacing: "-0.01em" }}>Open the Yoshi app in ChatGPT</div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", marginTop: 2 }}>Authorize in a tap, no config to paste</div>
    </div>
    <Icon name="back" size={15} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
  </a>;

/* MCP setup · copy the config, authorize inside the agent. No name, no commit —
   the connection lands in Active connections once the user approves. */
const McpSetup = ({ shortcut }) =>
  <>
    <CodeBlock code={MCP_CONFIG} />
  </>;

/* SDK setup · pick a language, copy the snippet. SDKs authenticate with an API
   key, so there's no registration step. */
const SdkSetup = () => {
  const [lang, setLang] = useState("python");
  return (
    <>
      <div style={{ padding: "12px 20px 0" }}>
        <Segmented size="sm" options={SDK_LANGS} value={lang} onChange={setLang} />
      </div>
      <CodeBlock code={SDK_CODE[lang]} />
      <SetupNote>SDKs authenticate with a scoped API key. Create one under the API tab, then drop it in.</SetupNote>
    </>
  );
};

/* The API quickstart docs, with a copy action and a link to the full reference. */
const ApiDocs = () =>
  <>
    <div style={{ padding: "18px 20px 0" }}><Eyebrow>API reference</Eyebrow></div>
    <CodeBlock code={API_DOCS} />
    <a href={API_DOCS_URL} target="_blank" rel="noopener noreferrer" className="press"
      style={{ display: "inline-flex", alignItems: "center", gap: 6, margin: "10px 20px 0", fontFamily: "var(--f-display)", fontSize: 11.5, fontWeight: 600, color: "var(--accent)", textDecoration: "none" }}>
      View full API reference <Icon name="back" size={13} color="var(--accent)" style={{ transform: "scaleX(-1)" }} />
    </a>
  </>;

const AGENT_PROVIDERS = [
{ id: "chatgpt", name: "ChatGPT", icon: "message" },
{ id: "claude", name: "Claude", icon: "robot" },
{ id: "perplexity", name: "Perplexity", icon: "search" },
{ id: "cursor", name: "Cursor", icon: "terminal" },
{ id: "openclaw", name: "OpenClaw", icon: "bolt" },
{ id: "hermes", name: "Hermes Agent", icon: "send" }];

const PROVIDER_IDS = AGENT_PROVIDERS.map((p) => p.id);
const PROVIDER_ICON = Object.fromEntries(AGENT_PROVIDERS.map((p) => [p.id, p.icon]));
const ICON_FOR_VIA = { MCP: "connect", SDK: "terminal", API: "key" };
const iconForConn = (c) => c.icon || PROVIDER_ICON[c.id] || ICON_FOR_VIA[c.via] || "connect";

// A connection is one authorized link to Yoshi. MCP connections are named by
// the service that reports in over the protocol (ChatGPT, OpenClaw, …); API
// connections are named by you when you mint the key. Some are authorized
// entirely outside the app and simply appear here once they land.
const AGENT_CONNECTIONS_SEED = [
{ id: "chatgpt", name: "ChatGPT", via: "MCP", scope: "Propose-only", last: "Active now", status: "active" },
{ id: "claude", name: "Claude", via: "MCP", scope: "Propose-only", last: "Active now", status: "active" },
{ id: "openclaw", name: "OpenClaw", via: "MCP", scope: "Propose-only", last: "Connected May 30", status: "active" },
{ id: "k_ray", name: "Raycast agent", via: "API", scope: "Propose-only", last: "Used 2h ago", status: "active", custom: true, token: "ys_demo_7f3a9c2b6d14e8f0a7c92" },
{ id: "k_scripts", name: "Personal scripts", via: "API", scope: "Read-only", last: "Used May 28", status: "active", custom: true, token: "ys_demo_2b8e44170c9ad3f6b8e41d" }];


const maskKey = (t) => {
  const i = t.indexOf("_demo_");
  const prefix = i >= 0 ? t.slice(0, i + 6) : t.slice(0, 8);
  return prefix + "••••••••" + t.slice(-4);
};
const genToken = () => {
  const hex = "0123456789abcdef";
  let s = "ys_demo_";
  for (let i = 0; i < 28; i++) s += hex[Math.floor(Math.random() * 16)];
  return s;
};

/* API key UI for one connection: mint the key, reveal it once, then it stays
   masked. The key belongs to the connection, not a separate registry. */
const ApiKeyArea = ({ token, fresh, onCreate, onRegenerate, ctaLabel }) => {
  const [copied, setCopied] = useState(false);
  const copy = (val) => { try { navigator.clipboard && navigator.clipboard.writeText(val); } catch (_) {} setCopied(true); setTimeout(() => setCopied(false), 1600); };

  // a key was just minted this session — revealed in full, exactly once
  if (fresh)
    return (
      <div style={{ margin: "12px 20px 0", border: "1px solid var(--accent)", background: "color-mix(in srgb, var(--accent) 7%, var(--bg-card))", padding: "12px 13px" }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 11.5, fontWeight: 600, color: "var(--ink-2)", lineHeight: 1.45 }}>Key created. Copy it now; you won't see it in full again.</div>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 10 }}>
          <code style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-mono)", fontSize: 11.5, color: "var(--ink)", wordBreak: "break-all", lineHeight: 1.4 }}>{fresh}</code>
          <button className="press" onClick={() => copy(fresh)} style={{ flex: "none", background: "var(--ink)", color: "var(--bg)", border: "none", fontFamily: "var(--f-display)", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", padding: "6px 10px", cursor: "pointer" }}>{copied ? "Copied" : "Copy"}</button>
        </div>
      </div>
    );

  // an existing key, kept masked for safety
  if (token)
    return (
      <div style={{ margin: "12px 20px 0", border: "1px solid var(--rule-2)", background: "var(--bg-card)", padding: "12px 13px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
          <Icon name="key" size={15} color="var(--ink-3)" stroke={1.5} />
          <code style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-mono)", fontSize: 11.5, color: "var(--ink-2)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{maskKey(token)}</code>
          <button className="press" onClick={onRegenerate} style={{ flex: "none", background: "none", border: "1px solid var(--rule-2)", borderRadius: 999, padding: "4px 11px", fontFamily: "var(--f-display)", fontSize: 10.5, fontWeight: 600, color: "var(--ink)", cursor: "pointer", whiteSpace: "nowrap" }}>Regenerate</button>
        </div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 10.5, color: "var(--ink-3)", marginTop: 8, lineHeight: 1.45 }}>Hidden for safety. Regenerate to issue a new key; the old one stops working.</div>
      </div>
    );

  // no key yet
  return (
    <div style={{ margin: "12px 20px 0", border: "1px dashed var(--rule-2)", padding: "14px 13px", textAlign: "center" }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", lineHeight: 1.45, marginBottom: 11 }}>This connection authenticates with a scoped API key.</div>
      <Btn onClick={onCreate} style={{ padding: "9px 16px", fontSize: 12.5 }}>{ctaLabel || "Create key"} <Icon name="key" size={14} color="var(--accent-ink)" /></Btn>
    </div>
  );
};

/* Add a connection · choose how the agent talks to Yoshi. MCP and SDK are
   copy-only (they authorize elsewhere and land in Active connections later);
   API is the one type you actually create here, minting a named, scoped key. */
const AddConnectionView = ({ onCancel, onCreateApi }) => {
  const [via, setVia] = useState("MCP");
  const [name, setName] = useState("");
  const [token, setToken] = useState(null);
  const [fresh, setFresh] = useState(null);
  const named = name.trim().length > 0;
  const createKey = () => {
    if (!named || token) return;
    const t = genToken();
    setToken(t); setFresh(t);
    onCreateApi({ name: name.trim(), token: t });
  };

  return (
    <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 340, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Add connection">
      {window.StatusBar && <window.StatusBar />}
      <div style={{ flex: "none", padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 8, minHeight: 44 }}>
        <button className="press" onClick={onCancel} aria-label="Back" style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 6, margin: "0 -6px" }}><Icon name="back" size={20} /></button>
        <span style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em", flex: 1 }}>Add connection</span>
      </div>

      <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
        <div style={{ padding: "18px 20px 0" }}>
          <Eyebrow>Connect with</Eyebrow>
          <div style={{ marginTop: 8 }}>
            <Segmented options={METHOD_OPTIONS} value={via} onChange={(v) => setVia(v)} />
          </div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", marginTop: 9, lineHeight: 1.5 }}>{METHOD_HINT[via]}</div>
        </div>

        {via === "MCP" && <McpSetup shortcut />}
        {via === "SDK" && <SdkSetup />}
        {via === "API" &&
          <>
            <div style={{ padding: "18px 20px 0" }}>
              <Eyebrow>Name</Eyebrow>
              <input autoFocus value={name} disabled={!!token} onChange={(e) => setName(e.target.value)} placeholder="e.g. Raycast agent"
                style={{ width: "100%", marginTop: 8, padding: "12px 13px", background: "var(--bg-card)", border: "1px solid var(--rule-2)", outline: "none", color: token ? "var(--ink-3)" : "var(--ink)", fontFamily: "var(--f-display)", fontSize: 14.5 }} />
            </div>
            {!token ?
              <div style={{ padding: "14px 20px 0" }}>
                <Btn full disabled={!named} onClick={createKey}>Create API key <Icon name="key" size={14} color="var(--accent-ink)" /></Btn>
                {!named && <div style={{ fontFamily: "var(--f-display)", fontSize: 10.5, color: "var(--ink-3)", textAlign: "center", marginTop: 9 }}>Name the connection to create its key.</div>}
              </div> :
              <ApiKeyArea token={token} fresh={fresh} onCreate={createKey} onRegenerate={createKey} />}
            <ApiDocs />
            {token &&
              <div style={{ padding: "22px 20px 0" }}>
                <div style={{ fontFamily: "var(--f-display)", fontSize: 11.5, color: "var(--accent-pos)", textAlign: "center", marginBottom: 11 }}>Added to Active connections.</div>
                <Btn full kind="ghost" onClick={onCancel}>Done</Btn>
              </div>}
          </>}

        <div style={{ display: "flex", gap: 9, alignItems: "flex-start", margin: "22px 20px 0", padding: "10px 12px", background: "var(--bg-2)" }}>
          <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} style={{ marginTop: 1 }} />
          <span style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", lineHeight: 1.45 }}>Propose-only access. An agent never holds or moves your money on its own. Revoke anytime.</span>
        </div>
      </div>
    </div>
  );
};

/* A connected agent, opened by its edit pencil: rename it, grab its MCP / CLI /
   API setup, manage its key, or revoke it. */
const ConnectionDetailView = ({ conn, onBack, onRename, onRevoke, onSetToken }) => {
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(conn.name);
  const [fresh, setFresh] = useState(null);
  const [confirmRevoke, setConfirmRevoke] = useState(false);
  const mint = () => { const t = genToken(); onSetToken(conn.id, t); setFresh(t); };
  const saveName = () => { const n = draft.trim(); if (n) onRename(conn.id, n); setEditing(false); };

  return (
    <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 340, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Connection">
      {window.StatusBar && <window.StatusBar />}
      <div style={{ flex: "none", padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 8, minHeight: 44 }}>
        <button className="press" onClick={onBack} aria-label="Back" style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 6, margin: "0 -6px" }}><Icon name="back" size={20} /></button>
        <span style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em", flex: 1 }}>Connection</span>
      </div>

      <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
        {/* identity */}
        <div style={{ padding: "18px 20px 0", display: "flex", alignItems: "center", gap: 12 }}>
          <span style={{ width: 42, height: 42, flex: "none", border: "1px solid var(--rule-2)", borderRadius: 12, display: "grid", placeItems: "center", color: "var(--ink)" }}>
            <Icon name={iconForConn(conn)} size={21} stroke={1.5} />
          </span>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{conn.name}</div>
            <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: "var(--f-display)", fontSize: 11, fontWeight: 600, color: "var(--accent-pos)", marginTop: 3 }}>
              <span style={{ width: 5, height: 5, borderRadius: 999, background: "var(--accent-pos)" }} /> {conn.via} · {conn.scope}
            </span>
          </div>
        </div>

        {/* alias · only available now that the connection exists */}
        <div style={{ padding: "18px 20px 0" }}>
          <Eyebrow>Alias</Eyebrow>
          {editing ?
            <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
              <input autoFocus value={draft} onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") saveName(); }}
                style={{ flex: 1, minWidth: 0, padding: "10px 12px", background: "var(--bg-card)", border: "1px solid var(--accent)", outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: 14 }} />
              <Btn onClick={saveName} style={{ padding: "0 15px", fontSize: 12.5 }}>Save</Btn>
            </div>
            :
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 8, padding: "11px 13px", background: "var(--bg-card)", border: "1px solid var(--rule-2)" }}>
              <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-display)", fontSize: 14, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{conn.name}</span>
              <button className="press" onClick={() => { setDraft(conn.name); setEditing(true); }} style={{ display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "1px solid var(--rule-2)", borderRadius: 999, padding: "4px 11px", fontFamily: "var(--f-display)", fontSize: 10.5, fontWeight: 600, color: "var(--ink)", cursor: "pointer" }}>Rename <Icon name="pencil" size={12} color="var(--ink)" /></button>
            </div>}
        </div>

        {/* setup · scoped to how this connection actually talks to Yoshi */}
        <div style={{ padding: "20px 20px 0" }}>
          <Eyebrow>Setup</Eyebrow>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)", marginTop: 9, lineHeight: 1.5 }}>{METHOD_HINT[conn.via]}</div>
        </div>
        {conn.via === "MCP" && <McpSetup />}
        {conn.via === "API" &&
          <>
            <ApiKeyArea token={conn.token} fresh={fresh} onCreate={mint} onRegenerate={mint} ctaLabel="Create key" />
            <ApiDocs />
          </>}

        {/* revoke */}
        <div style={{ padding: "24px 20px 0" }}>
          {confirmRevoke ?
            <div style={{ border: "1px solid color-mix(in srgb, var(--signal-neg) 40%, transparent)", padding: "13px 14px" }}>
              <div style={{ fontFamily: "var(--f-display)", fontSize: 12.5, color: "var(--ink-2)", lineHeight: 1.45 }}>Revoke {conn.name}? It loses access immediately and any key stops working.</div>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 9, marginTop: 12 }}>
                <Btn kind="ghost" onClick={() => setConfirmRevoke(false)}>Keep</Btn>
                <Btn kind="danger" onClick={() => onRevoke(conn.id)}>Revoke</Btn>
              </div>
            </div>
            :
            <Btn kind="danger" full onClick={() => setConfirmRevoke(true)}>Revoke connection</Btn>}
        </div>
      </div>
    </div>
  );
};

const ConnectAgentsSheetOld = ({ onClose, nav }) => {
  const [conns, setConns] = useState(AGENT_CONNECTIONS_SEED);
  const [view, setView] = useState(null);       // null | "add" | { detail: id }

  const connById = (id) => conns.find((c) => c.id === id);
  // API is the only type created in-app: minting its key registers it. MCP and
  // SDK connections aren't added here at all — they arrive once authorized.
  const addApi = ({ name, token }) => {
    const id = "c_" + Date.now();
    setConns((cs) => [...cs, { id, name, via: "API", scope: "Propose-only", last: "Active now", status: "active", custom: true, token }]);
  };
  const renameConn = (id, name) => setConns((cs) => cs.map((c) => c.id === id ? { ...c, name } : c));
  const setToken = (id, token) => setConns((cs) => cs.map((c) => c.id === id ? { ...c, token } : c));
  const revokeConn = (id) => { setConns((cs) => cs.filter((c) => c.id !== id)); setView(null); };

  const detailConn = view && view.detail ? connById(view.detail) : null;

  const ConnTile = ({ c }) => (
    <button className="press" onClick={() => setView({ detail: c.id })} style={{
      height: 104, position: "relative",
      display: "flex", flexDirection: "column", alignItems: "flex-start", justifyContent: "space-between", gap: 6, padding: "12px",
      background: "color-mix(in srgb, var(--accent-pos) 9%, var(--bg-card))",
      border: "1px solid color-mix(in srgb, var(--accent-pos) 50%, var(--rule-2))",
      borderRadius: 12, cursor: "pointer", textAlign: "left"
    }}>
      <span role="button" aria-label={`Edit ${c.name}`} title={`Edit ${c.name}`} onClick={(e) => { e.stopPropagation(); setView({ detail: c.id }); }}
        style={{ position: "absolute", top: 6, right: 6, width: 22, height: 22, borderRadius: 999, display: "grid", placeItems: "center", background: "var(--bg-2)", color: "var(--ink-2)", cursor: "pointer" }}>
        <Icon name="pencil" size={12} stroke={1.6} />
      </span>
      <span style={{ width: 30, height: 30, display: "grid", placeItems: "center", color: "var(--ink)", flex: "none" }}>
        <Icon name={iconForConn(c)} size={19} stroke={1.5} />
      </span>
      <div style={{ width: "100%", minWidth: 0 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 600, letterSpacing: "-0.01em", lineHeight: 1.15, overflowWrap: "break-word" }}>{c.name}</div>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontFamily: "var(--f-display)", fontSize: 10.5, fontWeight: 600, color: "var(--accent-pos)", marginTop: 2 }}>
          <span style={{ width: 5, height: 5, borderRadius: 999, background: "var(--accent-pos)" }} /> {c.via}
        </span>
      </div>
    </button>);

  return (
    <>
      <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Agents">
        {window.StatusBar && <window.StatusBar />}
        <div style={{ flex: "none", padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 10, minHeight: 44 }}>
          <YouButton nav={nav} />
          <span style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em", flex: 1 }}>Agents</span>
          <button className="press" onClick={onClose} style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={20} /></button>
        </div>

        <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 12.5, color: "var(--ink-2)", padding: "4px 20px 0", lineHeight: 1.5 }}>Connect Yoshi to your agents over MCP, an SDK, or an API key. Authorize Yoshi inside the agent and the connection turns up here, named by the service itself.</div>

          {/* Active connections — service-named MCP links + your own API keys, including
               anything authorized entirely outside the app. */}
          <div style={{ display: "flex", alignItems: "baseline", gap: 8, padding: "18px 20px 0" }}>
            <Eyebrow>Active connections</Eyebrow>
            <span style={{ fontFamily: "var(--f-mono)", fontSize: 12, fontWeight: 600, color: "var(--accent-pos)" }}>{conns.length}</span>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 9, padding: "12px 20px 4px" }}>
            {conns.map((c) => <ConnTile key={c.id} c={c} />)}

            {/* add tile */}
            <button className="press" onClick={() => setView("add")} style={{
              height: 104, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 8,
              background: "transparent", border: "1px dashed var(--rule-2)",
              borderRadius: 12, cursor: "pointer", color: "var(--ink-2)"
            }}>
              <Icon name="plus" size={22} stroke={1.5} color="var(--ink-2)" />
              <span style={{ fontFamily: "var(--f-display)", fontSize: 11.5, fontWeight: 600 }}>Add</span>
            </button>
          </div>

          {/* scoped-access note */}
          <div style={{ display: "flex", gap: 9, alignItems: "flex-start", margin: "20px 20px 0", padding: "10px 12px", background: "var(--bg-2)" }}>
            <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} style={{ marginTop: 1 }} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", lineHeight: 1.45 }}>Propose-only access. An agent never holds or moves your money on its own. Revoke any connection anytime.</span>
          </div>
        </div>
      </div>

      {/* add / detail overlays */}
      {view === "add" && <AddConnectionView onCancel={() => setView(null)} onCreateApi={addApi} />}
      {detailConn && <ConnectionDetailView conn={detailConn} onBack={() => setView(null)} onRename={renameConn} onRevoke={revokeConn} onSetToken={setToken} />}
    </>);

};

/* ============================================================
   Agents · connect methods (MCP / SDKs / API key) + agent apps
   ============================================================ */
const useCopy = () => {
  const [copied, setCopied] = useState(false);
  const copy = (val) => { try { navigator.clipboard && navigator.clipboard.writeText(val); } catch (_) {} setCopied(true); setTimeout(() => setCopied(false), 1600); };
  return [copied, copy];
};

const YOSHI_METHODS = [
  { id: "mcp", name: "Yoshi MCP", icon: "connect", desc: "prototype-agents.yoshi.invalid/mcp", action: "copy", copy: MCP_CONFIG, cta: "Copy setup" },
  { id: "build", name: "Build with Yoshi", icon: "easel", desc: "Scaffold an agent project", action: "copy", copy: BUILD_SETUP, cta: "Copy setup" },
  { id: "sdk", name: "Yoshi SDKs", icon: "terminal", desc: "pip · npm · Homebrew · Go", action: "sdk", cta: "View SDKs" },
];

const AGENT_APPS = [
  { id: "chatgpt", name: "ChatGPT", icon: "message", kind: "authorize", url: CHATGPT_APP_URL },
  { id: "claude", name: "Claude", icon: "robot", kind: "connected" },
  { id: "perplexity", name: "Perplexity", icon: "search", kind: "soon" },
  { id: "cursor", name: "Cursor", icon: "terminal", kind: "soon" },
  { id: "openclaw", name: "OpenClaw", icon: "bolt", kind: "copy", copy: OPENCLAW_SETUP },
];

const TileBtn = ({ children, onClick, href, tone = "default" }) => {
  const web = !!window.__YOSHI_WEB;
  const st = { display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6, width: "100%", padding: web ? "9px 12px" : "6px 8px", borderRadius: web ? 10 : 9, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: web ? 13 : 11, fontWeight: 600, whiteSpace: "nowrap", border: "1px solid var(--rule-2)", background: "color-mix(in srgb, var(--ink) 4%, var(--bg-card))", color: "var(--ink)", textDecoration: "none" };
  if (tone === "accent") { st.background = "var(--accent)"; st.color = "var(--accent-ink)"; st.border = "none"; }
  if (tone === "pos") { st.color = "var(--accent-pos)"; st.borderColor = "color-mix(in srgb, var(--accent-pos) 45%, var(--rule-2))"; }
  if (href) return <a className="press" href={href} target="_blank" rel="noopener noreferrer" style={st} onClick={(e) => e.stopPropagation()}>{children}</a>;
  return <button className="press" onClick={(e) => { e.stopPropagation(); onClick && onClick(); }} style={st}>{children}</button>;
};

const ConnectTile = ({ icon, name, desc, primary, onClick, children }) => {
  const web = !!window.__YOSHI_WEB;
  return (
  <div onClick={onClick} className={onClick ? "press" : undefined} style={{ position: "relative", display: "flex", flexDirection: "column", gap: web ? 10 : 6, padding: web ? 16 : 11, aspectRatio: "1 / 1", borderRadius: 14, overflow: "hidden", cursor: onClick ? "pointer" : "default", background: primary ? "color-mix(in srgb, var(--accent-pos) 9%, var(--bg-card))" : "var(--bg-card)", border: "1px solid " + (primary ? "color-mix(in srgb, var(--accent-pos) 45%, var(--rule-2))" : "var(--rule-2)") }}>
    {primary && <span style={{ position: "absolute", top: 0, left: 0, right: 0, height: 2, background: "var(--accent-pos)" }} />}
    <span style={{ width: web ? 30 : 24, height: web ? 30 : 24, flex: "none", display: "grid", placeItems: "center", color: "var(--ink)" }}>
      <Icon name={icon} size={web ? 24 : 19} stroke={1.5} />
    </span>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: web ? 16 : 13, fontWeight: 600, letterSpacing: "-0.01em", lineHeight: 1.15 }}>{name}</div>
    </div>
    {children}
  </div>
  );
};

const ChevRow = ({ label }) => (
  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", minHeight: window.__YOSHI_WEB ? 38 : 28, padding: "4px 0", boxSizing: "border-box", fontFamily: "var(--f-display)", fontSize: window.__YOSHI_WEB ? 13 : 11, fontWeight: 600, color: "var(--accent)" }}>
    <span>{label}</span>
    <Icon name="back" size={13} color="var(--accent)" style={{ transform: "scaleX(-1)" }} />
  </div>
);

const MethodTile = ({ m, onOpen }) => {
  const [copied, copy] = useCopy();
  const body = m.action === "copy"
    ? <TileBtn onClick={() => copy(m.copy)} tone={copied ? "pos" : "default"}>{copied ? "Copied" : m.cta}</TileBtn>
    : <ChevRow label={m.cta} />;
  return <ConnectTile icon={m.icon} name={m.name} desc={m.desc} onClick={m.action === "copy" ? undefined : () => onOpen(m.action)}>{body}</ConnectTile>;
};

const AppTile = ({ app }) => {
  const [copied, copy] = useCopy();
  const [soon, setSoon] = useState(false);
  let action;
  if (app.kind === "connected")
    action = (
      <div style={{ display: "flex", alignItems: "center", width: "100%" }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6, minHeight: window.__YOSHI_WEB ? 38 : 28, fontFamily: "var(--f-display)", fontSize: window.__YOSHI_WEB ? 13 : 11, fontWeight: 600, color: "var(--accent-pos)" }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: "var(--accent-pos)" }} /> Activated
        </span>
      </div>
    );
  else if (app.kind === "authorize")
    action = <TileBtn href={app.url}>Authorize</TileBtn>;
  else if (app.kind === "copy")
    action = <TileBtn onClick={() => copy(app.copy)} tone={copied ? "pos" : "default"}>{copied ? "Copied" : "Copy setup"}</TileBtn>;
  else
    action = <TileBtn onClick={() => { setSoon(true); setTimeout(() => setSoon(false), 1600); }}>{soon ? "Coming soon" : "Authorize"}</TileBtn>;
  return <ConnectTile icon={app.icon} name={app.name} primary={app.kind === "connected"}>{action}</ConnectTile>;
};

const SubHead = ({ title, onBack }) => (
  <div style={{ flex: "none", padding: "6px 16px 10px", borderBottom: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 8, minHeight: 44 }}>
    <button className="press" onClick={onBack} aria-label="Back" style={{ background: "none", border: "none", display: "flex", color: "var(--ink)", padding: 6, margin: "0 -6px" }}><Icon name="back" size={20} /></button>
    <span style={{ fontFamily: "var(--f-display)", fontSize: 17, fontWeight: 600, letterSpacing: "-0.02em", flex: 1 }}>{title}</span>
  </div>
);

const InstallRow = ({ m }) => {
  const [copied, copy] = useCopy();
  return (
    <div style={{ margin: "12px 20px 0", border: "1px solid var(--rule-2)", background: "var(--bg-card)" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "10px 12px 6px" }}>
        <Eyebrow style={{ flex: 1 }}>{m.label}</Eyebrow>
        <button className="press" onClick={() => copy(m.cmd)} style={{ flex: "none", background: copied ? "var(--accent-pos)" : "var(--bg-2)", color: copied ? "var(--bg)" : "var(--ink)", border: "none", fontFamily: "var(--f-display)", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", padding: "5px 9px", cursor: "pointer", borderRadius: 8 }}>{copied ? "Copied" : "Copy"}</button>
      </div>
      <pre style={{ margin: 0, padding: "0 13px 13px", color: "var(--ink)", fontFamily: "var(--f-mono)", fontSize: 12, lineHeight: 1.6, whiteSpace: "pre-wrap", wordBreak: "break-all" }}>$ {m.cmd}</pre>
    </div>
  );
};

const SdkView = ({ onBack }) => (
  <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 340, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Yoshi SDKs">
    {window.StatusBar && <window.StatusBar />}
    <SubHead title="Yoshi SDKs" onBack={onBack} />
    <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: 12.5, color: "var(--ink-2)", padding: "16px 20px 0", lineHeight: 1.5 }}>Install the Yoshi SDK and CLI with your package manager. Each authenticates with a scoped API key.</div>
      {SDK_MANAGERS.map((m) => <InstallRow key={m.id} m={m} />)}
      <SetupNote>Create a scoped key under Yoshi API key, then drop it into your environment as YOSHI_API_KEY.</SetupNote>
    </div>
  </div>
);

const ApiKeyView = ({ onBack }) => {
  const [token, setToken] = useState(null);
  const [fresh, setFresh] = useState(null);
  const mint = () => { const t = genToken(); setToken(t); setFresh(t); };
  return (
    <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 340, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Yoshi API key">
      {window.StatusBar && <window.StatusBar />}
      <SubHead title="Yoshi API key" onBack={onBack} />
      <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 12.5, color: "var(--ink-2)", padding: "16px 20px 0", lineHeight: 1.5 }}>Mint a scoped, propose-only key for your own scripts and SDK calls. You'll see it in full once; store it somewhere safe.</div>
        <ApiKeyArea token={token} fresh={fresh} onCreate={mint} onRegenerate={mint} ctaLabel="Create API key" />
        <ApiDocs />
        <div style={{ display: "flex", gap: 9, alignItems: "flex-start", margin: "22px 20px 0", padding: "10px 12px", background: "var(--bg-2)" }}>
          <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} style={{ marginTop: 1 }} />
          <span style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", lineHeight: 1.45 }}>Propose-only access. An agent never holds or moves your money on its own. Revoke anytime.</span>
        </div>
      </div>
    </div>
  );
};

/* the example live connection — created from a Copy setup and named by the
   user during the MCP handshake. */
const AGENT_CONNECTIONS_LIVE = [
  { id: "c1", name: "portfolio-copilot", sub: "Yoshi MCP · named at setup · connected May 28", live: true },
];

/* a live connection renders as a tile alongside the others — the user named
   it during the MCP handshake after tapping Copy setup */
const ConnectionTile = ({ c }) => (
  <ConnectTile icon="connect" name={c.name} primary>
    <div style={{ display: "flex", alignItems: "center", width: "100%" }}>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 6, minHeight: window.__YOSHI_WEB ? 38 : 28, fontFamily: "var(--f-mono)", fontSize: window.__YOSHI_WEB ? 11 : 10, fontWeight: 600, letterSpacing: "0.08em", color: "var(--accent-pos)" }}>
        <span className="yo-mk-pulse" style={{ width: 7, height: 7, borderRadius: 999, background: "var(--accent-pos)" }} />LIVE
      </span>
    </div>
  </ConnectTile>
);

const AgentConnectionsSection = () => (
  <>
    <div style={{ padding: "20px 20px 0" }}><Eyebrow>Agent connections</Eyebrow></div>
    <div style={{ margin: "10px 20px 0", border: "1px solid var(--rule-2)", borderRadius: 12, background: "var(--bg-card)", overflow: "hidden" }}>
      {AGENT_CONNECTIONS_LIVE.map((c, i) => (
        <div key={c.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 14px", borderTop: i ? "1px solid var(--rule)" : "none" }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "var(--f-mono)", fontSize: 13, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{c.name}</div>
            <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", marginTop: 3 }}>{c.sub}</div>
          </div>
          <span style={{ flex: "none", display: "inline-flex", alignItems: "center", gap: 6, fontFamily: "var(--f-mono)", fontSize: 10, fontWeight: 600, letterSpacing: "0.08em", color: "var(--accent-pos)" }}>
            <span className="yo-mk-pulse" style={{ width: 7, height: 7, borderRadius: 999, background: "var(--accent-pos)", opacity: 1 }} />LIVE
          </span>
        </div>
      ))}
    </div>
    <SetupNote>A connection lands here after the agent completes the handshake from a copied setup. You name it during setup.</SetupNote>
  </>
);

/* API keys — a flat registry at the bottom of the page. Generate, name, copy
   once, then rename or revoke any time. */
const API_KEYS_SEED = [
  { id: "k1", name: "Local scripts", token: "ys_demo_8c2e96b1d4f07a35ce12", created: "May 12" },
];
const maskToken = (t) => "ys_demo_••••••••" + String(t).slice(-4);

const ApiKeysSection = () => {
  const [keys, setKeys] = useState(API_KEYS_SEED);
  const [creating, setCreating] = useState(false);
  const [newName, setNewName] = useState("");
  const [freshId, setFreshId] = useState(null);
  const [renamingId, setRenamingId] = useState(null);
  const [renameVal, setRenameVal] = useState("");
  const [copiedId, setCopiedId] = useState(null);
  const copy = (id, val) => { try { navigator.clipboard && navigator.clipboard.writeText(val); } catch (_) {} setCopiedId(id); setTimeout(() => setCopiedId(null), 1600); };
  const create = () => {
    const nm = newName.trim(); if (!nm) return;
    const id = "k" + Date.now();
    setKeys((ks) => [...ks, { id, name: nm, token: genToken(), created: "Just now" }]);
    setFreshId(id); setCreating(false); setNewName("");
  };
  const saveRename = () => {
    const nm = renameVal.trim();
    if (nm) setKeys((ks) => ks.map((k) => k.id === renamingId ? { ...k, name: nm } : k));
    setRenamingId(null);
  };
  const txtBtn = { background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: "var(--f-display)", fontSize: 11, fontWeight: 600, color: "var(--ink-3)" };
  return (
    <>
      <div style={{ padding: "20px 20px 0", display: "flex", alignItems: "center" }}>
        <Eyebrow style={{ flex: 1 }}>API keys</Eyebrow>
        {!creating &&
        <button className="press" onClick={() => { setCreating(true); setFreshId(null); }} style={{ background: "var(--accent)", color: "var(--accent-ink)", border: "none", borderRadius: 8, padding: "6px 11px", fontFamily: "var(--f-display)", fontSize: 11.5, fontWeight: 600, cursor: "pointer" }}>Generate key</button>}
      </div>
      <div style={{ margin: "10px 20px 0", border: "1px solid var(--rule-2)", borderRadius: 12, background: "var(--bg-card)", overflow: "hidden" }}>
        {creating &&
        <div style={{ padding: "12px 14px", borderBottom: keys.length ? "1px solid var(--rule)" : "none" }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 11, fontWeight: 600, color: "var(--ink-2)", marginBottom: 6 }}>Name this key</div>
          <div style={{ display: "flex", gap: 8 }}>
            <input autoFocus value={newName} onChange={(e) => setNewName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") create(); }} placeholder="e.g. Trading bot"
              style={{ flex: 1, minWidth: 0, padding: "9px 11px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 9, outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: 13 }} />
            <button className="press" onClick={create} disabled={!newName.trim()} style={{ flex: "none", background: newName.trim() ? "var(--accent)" : "var(--rule-2)", color: newName.trim() ? "var(--accent-ink)" : "var(--ink-3)", border: "none", borderRadius: 9, padding: "0 14px", fontFamily: "var(--f-display)", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>Generate</button>
            <button className="press" onClick={() => { setCreating(false); setNewName(""); }} style={{ flex: "none", background: "none", border: "none", color: "var(--ink-3)", fontFamily: "var(--f-display)", fontSize: 12, cursor: "pointer" }}>Cancel</button>
          </div>
        </div>}
        {keys.length === 0 && !creating &&
        <div style={{ padding: "18px 14px", textAlign: "center", fontFamily: "var(--f-display)", fontSize: 12, color: "var(--ink-3)" }}>No keys yet. Generate one to call the API directly.</div>}
        {keys.map((k, i) => (
          <div key={k.id} style={{ padding: "12px 14px", borderTop: i || creating ? "1px solid var(--rule)" : "none" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              {renamingId === k.id ? (
                <input autoFocus value={renameVal} onChange={(e) => setRenameVal(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") saveRename(); if (e.key === "Escape") setRenamingId(null); }} onBlur={saveRename}
                  style={{ flex: 1, minWidth: 0, padding: "5px 9px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 8, outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: 13, fontWeight: 600 }} />
              ) : (
                <div style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-display)", fontSize: 13.5, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{k.name}</div>
              )}
              <button className="press" onClick={() => { setRenamingId(k.id); setRenameVal(k.name); }} style={txtBtn}>Rename</button>
              <button className="press" onClick={() => setKeys((ks) => ks.filter((x) => x.id !== k.id))} style={{ ...txtBtn, color: "var(--signal-neg)" }}>Revoke</button>
            </div>
            {freshId === k.id ? (
              <div style={{ marginTop: 8, padding: "9px 11px", background: "var(--bg-2)", border: "1px solid var(--accent)", borderRadius: 9 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <code style={{ flex: 1, minWidth: 0, fontFamily: "var(--f-mono)", fontSize: 11.5, color: "var(--ink)", wordBreak: "break-all" }}>{k.token}</code>
                  <button className="press" onClick={() => copy(k.id, k.token)} style={{ flex: "none", background: copiedId === k.id ? "var(--accent-pos)" : "var(--accent)", color: "var(--accent-ink)", border: "none", borderRadius: 8, padding: "5px 10px", fontFamily: "var(--f-display)", fontSize: 11, fontWeight: 600, cursor: "pointer" }}>{copiedId === k.id ? "Copied" : "Copy"}</button>
                </div>
                <div style={{ fontFamily: "var(--f-display)", fontSize: 10.5, color: "var(--ink-3)", marginTop: 6 }}>Copy it now. For your security, you won't see the full key again.</div>
              </div>
            ) : (
              <div style={{ fontFamily: "var(--f-mono)", fontSize: 11, color: "var(--ink-3)", marginTop: 4 }}>{maskToken(k.token)} · Created {k.created}</div>
            )}
          </div>
        ))}
      </div>
      <SetupNote>Keys are scoped and propose-only. An agent calling with your key can draft moves; only you can approve them.</SetupNote>
    </>
  );
};

const ConnectAgentsSheet = ({ onClose, nav }) => {
  const [view, setView] = useState(null);
  return (
    <>
      <div className="push-enter" style={{ position: "absolute", inset: 0, zIndex: 320, background: "var(--bg)", display: "flex", flexDirection: "column" }} data-screen-label="Agents">
        {window.StatusBar && <window.StatusBar />}
        <div style={{ flex: "none", padding: window.__YOSHI_WEB ? "26px 20px 6px" : "6px 16px 10px", borderBottom: window.__YOSHI_WEB ? "none" : "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 10, minHeight: 44 }}>
          {!window.__YOSHI_WEB && <YouButton nav={nav} />}
          <span style={{ fontFamily: "var(--f-display)", fontSize: window.__YOSHI_WEB ? 21 : 17, fontWeight: window.__YOSHI_WEB ? 700 : 600, letterSpacing: window.__YOSHI_WEB ? "-0.025em" : "-0.02em", flex: 1 }}>Agents</span>
          {!window.__YOSHI_WEB && <button className="press" onClick={onClose} style={{ background: "none", border: "none", display: "flex", color: "var(--ink-3)" }}><Icon name="close" size={20} /></button>}
        </div>

        <div className="scroll" style={{ overflowY: "auto", padding: "0 0 24px" }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 12.5, color: "var(--ink-2)", padding: "4px 20px 0", lineHeight: 1.5 }}>Connect Yoshi to your agents. Set it up yourself over MCP, an SDK, or an API key, or authorize a supported app.</div>

          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 9, padding: "16px 20px 4px" }}>
            {YOSHI_METHODS.map((m) => <MethodTile key={m.id} m={m} onOpen={setView} />)}
            {AGENT_APPS.map((a) => <AppTile key={a.id} app={a} />)}
            {AGENT_CONNECTIONS_LIVE.map((c) => <ConnectionTile key={c.id} c={c} />)}
          </div>

          <ApiKeysSection />

          <div style={{ display: "flex", gap: 9, alignItems: "flex-start", margin: "20px 20px 0", padding: "10px 12px", background: "var(--bg-2)" }}>
            <Icon name="shield" size={16} color="var(--ink-3)" stroke={1.5} style={{ marginTop: 1 }} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", lineHeight: 1.45 }}>Propose-only access. An agent never holds or moves your money on its own. Revoke any connection anytime.</span>
          </div>
        </div>
      </div>

      {view === "sdk" && <SdkView onBack={() => setView(null)} />}
      {view === "api" && <ApiKeyView onBack={() => setView(null)} />}
    </>);
};

Object.assign(window, { ConnectAgentsSheet });
