// Markets view — brokers, exchanges, crypto exchanges, individual assets.
// Exposes window.MarketsPages (keyed page factory).

(function() {
  const { useState, useEffect, useMemo } = React;
  const C = window.Charts;

  // ---------------- seed market data ----------------
  const rand = (seed) => { let s = seed; return () => { s = (s * 9301 + 49297) % 233280; return s / 233280; }; };

  // Generate OHLC series with trend and volatility
  function makeOHLC(n, start, vol, drift, seed) {
    const r = rand(seed);
    const out = [];
    let p = start;
    for (let i = 0; i < n; i++) {
      const o = p;
      const ch = (r() - 0.5) * vol + drift;
      const c = Math.max(0.01, o + ch);
      const h = Math.max(o, c) + r() * vol * 0.4;
      const l = Math.min(o, c) - r() * vol * 0.4;
      out.push({ o, h, l, c, v: Math.round(1e6 + r() * 5e6) });
      p = c;
    }
    return out;
  }
  function makeLine(n, start, vol, drift, seed) {
    return makeOHLC(n, start, vol, drift, seed).map(b => b.c);
  }

  const ASSETS = {
    AAPL:  { name: 'Apple Inc.',       kind: 'equity', last: 214.82, chg: +1.42, chgPct: +0.66, vol: '54.2M', mcap: '$3.28T',  pe: 32.4, div: '0.48%' },
    NVDA:  { name: 'Nvidia Corp.',     kind: 'equity', last: 928.14, chg: +18.2, chgPct: +2.00, vol: '42.8M', mcap: '$2.31T',  pe: 68.1, div: '0.02%' },
    MSFT:  { name: 'Microsoft Corp.',  kind: 'equity', last: 438.21, chg: -2.41, chgPct: -0.55, vol: '22.1M', mcap: '$3.25T',  pe: 37.2, div: '0.72%' },
    TSLA:  { name: 'Tesla Inc.',       kind: 'equity', last: 188.14, chg: -4.32, chgPct: -2.25, vol: '84.2M', mcap: '$598B',   pe: 58.4, div: '—'     },
    GOOGL: { name: 'Alphabet Inc.',    kind: 'equity', last: 169.28, chg: +0.84, chgPct: +0.50, vol: '18.4M', mcap: '$2.09T',  pe: 28.1, div: '0.47%' },
    AMZN:  { name: 'Amazon.com Inc.',  kind: 'equity', last: 184.68, chg: +1.12, chgPct: +0.61, vol: '28.2M', mcap: '$1.92T',  pe: 52.2, div: '—'     },
    META:  { name: 'Meta Platforms',   kind: 'equity', last: 484.32, chg: +6.21, chgPct: +1.30, vol: '14.8M', mcap: '$1.23T',  pe: 28.9, div: '0.42%' },
    SPY:   { name: 'S&P 500 ETF',      kind: 'etf',    last: 528.41, chg: +1.82, chgPct: +0.35, vol: '72.1M', mcap: '$520B',   pe: 24.1, div: '1.38%' },
    QQQ:   { name: 'Nasdaq-100 ETF',   kind: 'etf',    last: 457.28, chg: +2.14, chgPct: +0.47, vol: '42.3M', mcap: '$270B',   pe: 28.9, div: '0.56%' },
    BTC:   { name: 'Bitcoin',          kind: 'crypto', last: 67412,  chg: +812,  chgPct: +1.22, vol: '$28.4B', mcap: '$1.33T', pe: null, div: '—'     },
    ETH:   { name: 'Ethereum',         kind: 'crypto', last: 3312,   chg: +48,   chgPct: +1.47, vol: '$14.8B', mcap: '$398B',  pe: null, div: '—'     },
    SOL:   { name: 'Solana',           kind: 'crypto', last: 162.4,  chg: -2.8,  chgPct: -1.70, vol: '$2.1B',  mcap: '$76B',   pe: null, div: '—'     },
    EURUSD:{ name: 'Euro / US Dollar', kind: 'fx',     last: 1.0684, chg: -0.0014, chgPct: -0.13, vol: '$148B', mcap: '—',     pe: null, div: '—'     },
    ES:    { name: 'S&P 500 Futures',  kind: 'futures',last: 5284.25, chg: +18.5, chgPct: +0.35, vol: '1.8M',  mcap: '—',       pe: null, div: '—'     },
    US10Y: { name: '10yr Treasury',    kind: 'bond',   last: 4.324,  chg: +0.018, chgPct: +0.42, vol: '—',     mcap: '—',       pe: null, div: '—'     },
  };

  // Price histories
  const HISTORY = {
    AAPL: makeOHLC(120, 195, 4, 0.15, 101),
    NVDA: makeOHLC(120, 620, 22, 2.6, 102),
    MSFT: makeOHLC(120, 410, 6, 0.22, 103),
    TSLA: makeOHLC(120, 230, 8, -0.35, 104),
    GOOGL: makeOHLC(120, 155, 3, 0.11, 105),
    AMZN: makeOHLC(120, 170, 4, 0.12, 106),
    META: makeOHLC(120, 430, 8, 0.45, 107),
    SPY: makeOHLC(120, 510, 2, 0.15, 108),
    QQQ: makeOHLC(120, 432, 3, 0.22, 109),
    BTC: makeOHLC(120, 58000, 1800, 78, 110),
    ETH: makeOHLC(120, 2900, 85, 3.4, 111),
    SOL: makeOHLC(120, 170, 9, -0.08, 112),
    EURUSD: makeOHLC(120, 1.082, 0.006, -0.00012, 113),
    ES:   makeOHLC(120, 5100, 28, 1.5, 114),
    US10Y: makeOHLC(120, 4.2,  0.06, 0.001, 115),
  };

  // Watchlist feed used in markets/overview
  const WATCHLIST = ['AAPL','NVDA','MSFT','TSLA','GOOGL','AMZN','META','SPY','QQQ','BTC','ETH','EURUSD','ES','US10Y'];

  const fmtPrice = (sym, v) => {
    const a = ASSETS[sym];
    if (!a) return v;
    if (a.kind === 'crypto') return '$' + v.toLocaleString('en-US', { maximumFractionDigits: 2 });
    if (a.kind === 'fx')     return v.toFixed(4);
    if (a.kind === 'bond')   return v.toFixed(3) + '%';
    if (a.kind === 'futures')return v.toLocaleString('en-US', { minimumFractionDigits: 2 });
    return '$' + v.toFixed(2);
  };
  const fmtDelta = (sym, v) => {
    const a = ASSETS[sym];
    if (!a) return v;
    const sign = v >= 0 ? '+' : '−';
    const absV = Math.abs(v);
    if (a.kind === 'fx' || a.kind === 'bond') return sign + absV.toFixed(4);
    if (a.kind === 'crypto') return sign + '$' + absV.toFixed(0);
    return sign + '$' + absV.toFixed(2);
  };

  // ---------------- Hero / tabs ----------------
  const MARKETS_TABS = [
    { id: 'markets/overview',  label: 'Overview' },
    { id: 'markets/watchlist', label: 'Watchlist' },
    { id: 'markets/ib',        label: 'IB' },
    { id: 'markets/schwab',    label: 'Schwab' },
    { id: 'markets/fidelity',  label: 'Fidelity' },
    { id: 'markets/coinbase',  label: 'Coinbase' },
    { id: 'markets/binance',   label: 'Binance' },
    { id: 'markets/nasdaq',    label: 'Nasdaq' },
    { id: 'markets/cme',       label: 'CME' },
    { id: 'markets/cboe',      label: 'CBOE' },
  ];

  function MarketsHero({ title, sub, activePage, goto }) {
    return (
      <>
        <div className="reports-hero">
          <div className="crumb mono" style={{ fontSize: 11, color: 'var(--fg-2)', letterSpacing: '0.14em', textTransform: 'uppercase', marginBottom: 8 }}>
            workspace <span style={{opacity:0.4}}>›</span> markets <span style={{opacity:0.4}}>›</span> {title.toLowerCase()}
          </div>
          <h1>{title} <span style={{ color: 'var(--fg-3)', fontStyle: 'italic', fontSize: 22 }}>· live · 16:04 ET</span></h1>
          <div className="sub">{sub}</div>
          <div className="reports-controls">
            <div className="period-pick">
              {['1D','5D','1M','3M','YTD','1Y','5Y','MAX'].map(p => (
                <button key={p} className={p==='3M'?'on':''}>{p}</button>
              ))}
            </div>
            <div style={{ flex: 1 }} />
            <button className="btn">Add symbol</button>
            <button className="btn">Create alert</button>
            <button className="btn primary">Trade ▸</button>
          </div>
        </div>
        <nav className="reports-tabs">
          {MARKETS_TABS.map(s => (
            <a key={s.id}
               className={activePage === s.id ? 'on' : ''}
               onClick={(e) => { e.preventDefault(); goto && goto(s.id); }}
               href={'#' + s.id}>{s.label}</a>
          ))}
        </nav>
      </>
    );
  }

  function Section({ id, title, note, children }) {
    return (
      <section id={id} className="report-section">
        <div className="report-section-head">
          <div className="title">{title}</div>
          {note && <div className="note">{note}</div>}
        </div>
        {children}
      </section>
    );
  }
  function Card({ title, right, children, flush }) {
    return (
      <div className="report-card">
        <div className="report-card-head">
          <span>{title}</span>
          {right && <span className="right">{right}</span>}
        </div>
        <div className={'report-card-body' + (flush ? ' flush' : '')}>{children}</div>
      </div>
    );
  }

  // ---------------- Overview page ----------------
  function Overview({ goto }) {
    const indexes = ['SPY','QQQ','ES','US10Y'];
    const sectors = [
      { name: 'Technology', pct: +1.82, color: 'var(--cat-cyan)' },
      { name: 'Communication', pct: +0.94, color: 'var(--cat-violet)' },
      { name: 'Cons. Disc.', pct: -0.41, color: 'var(--cat-magenta)' },
      { name: 'Industrials', pct: +0.22, color: 'var(--cat-lime)' },
      { name: 'Health Care', pct: -0.84, color: 'var(--cat-red)' },
      { name: 'Financials',  pct: +0.62, color: 'var(--cat-teal)' },
      { name: 'Energy', pct: -1.24, color: 'var(--cat-amber)' },
      { name: 'Utilities', pct: +0.08, color: 'var(--cat-blue)' },
      { name: 'Materials', pct: -0.18, color: 'var(--fg-3)' },
      { name: 'Real Estate', pct: -0.52, color: 'var(--cat-violet)' },
      { name: 'Staples', pct: +0.32, color: 'var(--cat-lime)' },
    ];

    return (
      <div className="reports-body">
        <div className="kpi-grid">
          {indexes.map(sym => {
            const a = ASSETS[sym];
            const h = HISTORY[sym].slice(-30).map(b => b.c);
            const up = a.chg >= 0;
            return (
              <div key={sym} className="kpi" onClick={() => goto && goto('markets/sym/' + sym)} style={{ cursor: 'pointer' }}>
                <div className="label">{sym} · {a.name}</div>
                <div className="value">{fmtPrice(sym, a.last)}</div>
                <div className="delta" style={{ color: up ? 'var(--pos)' : 'var(--neg)' }}>
                  {fmtDelta(sym, a.chg)} · {a.chgPct >= 0 ? '+' : ''}{a.chgPct.toFixed(2)}%
                </div>
                <div className="spark-line"><C.Spark data={h} w={160} h={28} color={up ? 'var(--pos)' : 'var(--neg)'} /></div>
              </div>
            );
          })}
        </div>

        <Section id="heatmap" title="Sector heatmap" note="GICS · today">
          <div className="sector-heatmap">
            {sectors.map((s, i) => {
              const bg = s.pct >= 0
                ? `color-mix(in oklch, var(--pos) ${Math.min(60, Math.abs(s.pct)*30)}%, var(--bg-2))`
                : `color-mix(in oklch, var(--neg) ${Math.min(60, Math.abs(s.pct)*30)}%, var(--bg-2))`;
              return (
                <div key={i} className="sector-tile" style={{ background: bg }}>
                  <div className="name">{s.name}</div>
                  <div className="pct">{s.pct >= 0 ? '+' : ''}{s.pct.toFixed(2)}%</div>
                </div>
              );
            })}
          </div>
        </Section>

        <div className="report-grid-2">
          <Card title="Market movers · today" right="top gains / losses">
            <table className="pnl-table">
              <thead><tr><th>Sym</th><th>Last</th><th>Chg</th><th>%</th><th>Vol</th></tr></thead>
              <tbody>
                {WATCHLIST.map(sym => {
                  const a = ASSETS[sym];
                  return (
                    <tr key={sym} className="sub" style={{ cursor: 'pointer' }} onClick={() => goto && goto('markets/sym/' + sym)}>
                      <td><span className="mono" style={{ fontSize: 12 }}>{sym}</span> <span style={{color:'var(--fg-2)', fontSize:11}}>{a.name}</span></td>
                      <td className="num">{fmtPrice(sym, a.last)}</td>
                      <td className="num" style={{ color: a.chg >= 0 ? 'var(--pos)' : 'var(--neg)' }}>{fmtDelta(sym, a.chg)}</td>
                      <td className="num" style={{ color: a.chg >= 0 ? 'var(--pos)' : 'var(--neg)' }}>{a.chgPct >= 0 ? '+' : ''}{a.chgPct.toFixed(2)}%</td>
                      <td className="num" style={{color:'var(--fg-2)'}}>{a.vol}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </Card>
          <Card title="Portfolio · connected brokers" right="aggregated">
            <table className="pnl-table">
              <tbody>
                <tr className="sub"><td>Interactive Brokers</td><td className="num">$184,210</td><td className="num" style={{color:'var(--pos)'}}>+$1,823</td></tr>
                <tr className="sub"><td>Schwab</td><td className="num">$62,400</td><td className="num" style={{color:'var(--pos)'}}>+$412</td></tr>
                <tr className="sub"><td>Fidelity (401k)</td><td className="num">$128,420</td><td className="num" style={{color:'var(--neg)'}}>−$148</td></tr>
                <tr className="sub"><td>Robinhood</td><td className="num">$8,420</td><td className="num" style={{color:'var(--pos)'}}>+$84</td></tr>
                <tr className="sub"><td>Coinbase</td><td className="num">$41,233</td><td className="num" style={{color:'var(--pos)'}}>+$3,241</td></tr>
                <tr className="sub"><td>BTC wallet</td><td className="num">$32,510</td><td className="num" style={{color:'var(--pos)'}}>+$812</td></tr>
                <tr className="total"><td>Total</td><td className="num">$457,193</td><td className="num" style={{color:'var(--pos)'}}>+$6,224 · +1.38%</td></tr>
              </tbody>
            </table>
            <div className="mono" style={{ fontSize: 10, color: 'var(--fg-3)', padding: '8px 4px 0', letterSpacing: '0.08em' }}>
              asset allocation · 62% equities · 16% crypto · 14% fx · 8% fixed income
            </div>
          </Card>
        </div>

        <Section id="news" title="Market news" note="ai-surfaced · relevant to your positions">
          <div className="news-list">
            {[
              ['16:02', 'NVDA', 'NVIDIA to ship Rubin architecture accelerators by late 2026, TSMC confirms', '+2.00% · beat consensus', 'pos'],
              ['15:48', 'AAPL', 'Apple iPhone 16 pre-orders pace ahead of prior year in Asia', '+0.66% · catalyst', 'pos'],
              ['15:21', 'TSLA', 'Tesla Q1 deliveries miss estimates, Model 2 delayed to 2027', '−2.25% · affects position', 'neg'],
              ['14:58', 'Fed',  'Powell signals no cuts at June FOMC; rates to stay 5.25–5.50%', '10Y +4bp', 'neg'],
              ['14:40', 'BTC',  'SEC approves second wave of spot BTC ETFs, inflows resume', '+1.22%', 'pos'],
              ['14:15', 'EURUSD','ECB minutes hint at July cut, EUR weakens on dovish shift', '−0.13%', 'neg'],
            ].map((n, i) => (
              <div key={i} className="news-item">
                <span className="ts mono">{n[0]}</span>
                <span className={'badge ' + n[4]}>{n[1]}</span>
                <span className="head">{n[2]}</span>
                <span className={'meta ' + n[4]}>{n[3]}</span>
              </div>
            ))}
          </div>
        </Section>
      </div>
    );
  }

  // ---------------- Watchlist page ----------------
  function Watchlist({ goto }) {
    return (
      <div className="reports-body">
        <Section id="watchlist" title="Watchlist" note="14 symbols · sorted by % change">
          <table className="pnl-table watchlist-table">
            <thead><tr><th>Sym</th><th>Name</th><th>Last</th><th>Chg</th><th>%</th><th>Vol</th><th>Mkt cap</th><th>P/E</th><th>Spark · 30d</th></tr></thead>
            <tbody>
              {WATCHLIST.map(sym => {
                const a = ASSETS[sym];
                const h = HISTORY[sym].slice(-30).map(b => b.c);
                return (
                  <tr key={sym} className="sub" style={{ cursor: 'pointer' }} onClick={() => goto && goto('markets/sym/' + sym)}>
                    <td><span className="mono" style={{ fontSize: 12, color: 'var(--fg-0)' }}>{sym}</span></td>
                    <td style={{color:'var(--fg-2)'}}>{a.name}</td>
                    <td className="num">{fmtPrice(sym, a.last)}</td>
                    <td className="num" style={{ color: a.chg >= 0 ? 'var(--pos)' : 'var(--neg)' }}>{fmtDelta(sym, a.chg)}</td>
                    <td className="num" style={{ color: a.chg >= 0 ? 'var(--pos)' : 'var(--neg)' }}>{a.chgPct >= 0 ? '+' : ''}{a.chgPct.toFixed(2)}%</td>
                    <td className="num" style={{color:'var(--fg-2)'}}>{a.vol}</td>
                    <td className="num" style={{color:'var(--fg-2)'}}>{a.mcap}</td>
                    <td className="num" style={{color:'var(--fg-2)'}}>{a.pe ?? '—'}</td>
                    <td><C.Spark data={h} w={120} h={24} color={a.chg >= 0 ? 'var(--pos)' : 'var(--neg)'} /></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </Section>
      </div>
    );
  }

  // ---------------- Broker page ----------------
  function BrokerPage({ title, data, goto }) {
    return (
      <div className="reports-body">
        <div className="kpi-grid">
          <div className="kpi"><div className="label">Total value</div><div className="value">{data.total}</div><div className="delta" style={{color:'var(--pos)'}}>{data.totalDelta}</div></div>
          <div className="kpi"><div className="label">Day P&L</div><div className="value">{data.dayPL}</div><div className="delta" style={{color: data.dayPLClass}}>{data.dayPLPct}</div></div>
          <div className="kpi"><div className="label">Buying power</div><div className="value">{data.bp}</div><div className="delta">margin · {data.margin}</div></div>
          <div className="kpi"><div className="label">Positions</div><div className="value">{data.positions}</div><div className="delta">{data.openOrders} open orders</div></div>
        </div>

        <div className="report-grid-2">
          <Card title="Positions" right={title}>
            <table className="pnl-table">
              <thead><tr><th>Sym</th><th>Qty</th><th>Avg cost</th><th>Last</th><th>Market value</th><th>Unrealized</th><th>% P/L</th></tr></thead>
              <tbody>
                {data.positions_list.map((p, i) => (
                  <tr key={i} className="sub" style={{ cursor: 'pointer' }} onClick={() => goto && goto('markets/sym/' + p[0])}>
                    <td><span className="mono" style={{fontSize:12}}>{p[0]}</span></td>
                    <td className="num">{p[1]}</td>
                    <td className="num">${p[2].toFixed(2)}</td>
                    <td className="num">${p[3].toFixed(2)}</td>
                    <td className="num">${(p[1]*p[3]).toLocaleString('en-US',{maximumFractionDigits:2})}</td>
                    <td className="num" style={{ color: (p[3]-p[2]) >= 0 ? 'var(--pos)' : 'var(--neg)' }}>${((p[3]-p[2])*p[1]).toLocaleString('en-US',{maximumFractionDigits:2})}</td>
                    <td className="num" style={{ color: (p[3]-p[2]) >= 0 ? 'var(--pos)' : 'var(--neg)' }}>{((p[3]-p[2])/p[2]*100).toFixed(2)}%</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <Card title="Allocation">
              <C.Donut
                slices={data.alloc}
                w={240} h={200} center={{ label: 'equity', value: data.total }}
              />
            </Card>
            <Card title="Recent orders">
              <table className="pnl-table">
                <thead><tr><th>Time</th><th>Sym</th><th>Side</th><th>Qty</th><th>Price</th><th>Status</th></tr></thead>
                <tbody>
                  {data.orders.map((o, i) => (
                    <tr key={i} className="sub">
                      <td className="mono" style={{fontSize:11}}>{o[0]}</td>
                      <td><span className="mono" style={{fontSize:12}}>{o[1]}</span></td>
                      <td style={{ color: o[2] === 'BUY' ? 'var(--pos)' : 'var(--neg)' }}>{o[2]}</td>
                      <td className="num">{o[3]}</td>
                      <td className="num">${o[4].toFixed(2)}</td>
                      <td><span className="chip" style={{ color: o[5] === 'filled' ? 'var(--pos)' : 'var(--cat-amber)' }}>{o[5]}</span></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </Card>
          </div>
        </div>

        <Section id="perf" title="Performance · 90d" note="vs. S&P 500 benchmark">
          <Card title="Equity curve">
            <C.LineChart
              series={[
                { name: 'portfolio', data: data.curve, color: 'var(--accent)', width: 1.8 },
                { name: 'S&P 500',   data: data.bench, color: 'var(--fg-3)',   width: 1.2, dash: '4 3' },
              ]}
              w={960} h={280} labels={Array.from({length: data.curve.length}, (_,i) => i%10===0 ? 'd'+i : '')}
              yFmt={v => '$' + Math.round(v/1000) + 'k'} area={false}
            />
          </Card>
        </Section>
      </div>
    );
  }

  // Curve generator for brokers
  function curve(start, end, len, vol, seed) {
    const r = rand(seed);
    const pts = [];
    for (let i = 0; i < len; i++) {
      const base = start + (end - start) * (i / (len - 1));
      pts.push(base + (r() - 0.5) * vol);
    }
    return pts;
  }

  const IB_DATA = {
    total: '$184,210', totalDelta: '+$1,823 · +1.00%',
    dayPL: '+$1,823', dayPLClass: 'var(--pos)', dayPLPct: '+1.00% today',
    bp: '$42,840', margin: '12% used',
    positions: 18, openOrders: 3,
    positions_list: [
      ['NVDA', 80, 620.4, 928.14],
      ['AAPL', 140, 182.4, 214.82],
      ['MSFT', 60, 380.1, 438.21],
      ['SPY', 120, 488.2, 528.41],
      ['QQQ', 40, 388.4, 457.28],
      ['GOOGL', 85, 142.8, 169.28],
      ['META', 22, 384.2, 484.32],
      ['AMZN', 48, 148.2, 184.68],
    ],
    alloc: [
      { label: 'Tech',      value: 82, color: 'var(--cat-cyan)' },
      { label: 'ETFs',      value: 28, color: 'var(--cat-violet)' },
      { label: 'Comm.',     value: 14, color: 'var(--cat-lime)' },
      { label: 'Cash',      value:  6, color: 'var(--fg-3)' },
    ],
    orders: [
      ['15:58', 'NVDA', 'BUY',  10, 928.14, 'filled'],
      ['15:42', 'SPY',  'SELL', 20, 528.40, 'filled'],
      ['15:20', 'AAPL', 'BUY',  25, 214.80, 'filled'],
      ['14:31', 'MSFT', 'BUY',  20, 437.20, 'working'],
      ['14:10', 'META', 'BUY',   8, 484.00, 'filled'],
    ],
    curve: curve(168000, 184210, 90, 2400, 201),
    bench: curve(168000, 179000, 90, 1800, 202),
  };

  const SCHWAB_DATA = {
    total: '$62,400', totalDelta: '+$412 · +0.66%',
    dayPL: '+$412', dayPLClass: 'var(--pos)', dayPLPct: '+0.66% today',
    bp: '$18,200', margin: '—',
    positions: 9, openOrders: 1,
    positions_list: [
      ['SPY',  60, 478.4, 528.41],
      ['VTI',  50, 248.2, 265.42],
      ['BND',  80, 72.4,  71.8],
      ['QQQ',  20, 401.2, 457.28],
      ['JEPI', 40, 54.8,  56.1],
    ],
    alloc: [
      { label: 'ETFs',  value: 50, color: 'var(--cat-violet)' },
      { label: 'Bonds', value: 10, color: 'var(--cat-blue)' },
      { label: 'Income',value:  2, color: 'var(--cat-lime)' },
    ],
    orders: [
      ['15:48', 'SPY',  'BUY',  10, 528.31, 'filled'],
      ['14:12', 'BND',  'BUY',  20, 71.80, 'filled'],
      ['10:02', 'VTI',  'BUY',  10, 265.20, 'working'],
    ],
    curve: curve(58000, 62400, 90, 600, 203),
    bench: curve(58000, 60800, 90, 500, 204),
  };

  const FIDELITY_DATA = {
    total: '$128,420', totalDelta: '−$148 · −0.12%',
    dayPL: '−$148', dayPLClass: 'var(--neg)', dayPLPct: '−0.12% today',
    bp: '$0', margin: 'cash account',
    positions: 6, openOrders: 0,
    positions_list: [
      ['FXAIX', 340, 162.4, 184.2],
      ['FSPGX', 180, 22.1,  26.8],
      ['FXNAX', 220, 10.4,  10.1],
      ['FXAIX', 90,  178.2, 184.2],
    ],
    alloc: [
      { label: 'Index',  value: 88, color: 'var(--cat-violet)' },
      { label: 'Growth', value: 18, color: 'var(--cat-lime)' },
      { label: 'Bond',   value: 22, color: 'var(--cat-blue)' },
    ],
    orders: [
      ['Apr 15', '401k', 'BUY',  '—', 0, 'filled'],
      ['Apr 01', '401k', 'BUY',  '—', 0, 'filled'],
      ['Mar 15', '401k', 'BUY',  '—', 0, 'filled'],
    ],
    curve: curve(118000, 128420, 90, 1400, 205),
    bench: curve(118000, 126000, 90, 1000, 206),
  };

  const COINBASE_DATA = {
    total: '$41,233', totalDelta: '+$3,241 · +8.53%',
    dayPL: '+$3,241', dayPLClass: 'var(--pos)', dayPLPct: '+8.53% today',
    bp: '$12,400', margin: 'stablecoin',
    positions: 4, openOrders: 2,
    positions_list: [
      ['BTC', 0.48, 58000, 67412],
      ['ETH', 3.2,  2900, 3312],
      ['SOL', 24,   170,  162.4],
      ['USDC', 8200, 1, 1],
    ],
    alloc: [
      { label: 'BTC',   value: 32, color: 'var(--cat-amber)' },
      { label: 'ETH',   value: 10, color: 'var(--cat-cyan)' },
      { label: 'SOL',   value:  4, color: 'var(--cat-violet)' },
      { label: 'USDC',  value:  8, color: 'var(--fg-3)' },
    ],
    orders: [
      ['15:52', 'BTC', 'BUY',  0.02, 67400, 'filled'],
      ['14:20', 'ETH', 'SELL', 0.5,  3310,  'filled'],
      ['13:08', 'SOL', 'BUY',  10,   162.2, 'working'],
    ],
    curve: curve(32000, 41233, 90, 1600, 207),
    bench: curve(32000, 38000, 90, 1200, 208),
  };

  const BINANCE_DATA = {
    total: '$14,820', totalDelta: '+$212 · +1.45%',
    dayPL: '+$212', dayPLClass: 'var(--pos)', dayPLPct: '+1.45% today',
    bp: '$4,200', margin: '—',
    positions: 5, openOrders: 1,
    positions_list: [
      ['BTC', 0.12, 59000, 67412],
      ['ETH', 1.4,  2800,  3312],
      ['BNB', 12,   340,   418],
      ['SOL', 8,    168,   162.4],
    ],
    alloc: [
      { label: 'BTC',  value: 8, color: 'var(--cat-amber)' },
      { label: 'ETH',  value: 4, color: 'var(--cat-cyan)' },
      { label: 'BNB',  value: 5, color: 'var(--cat-lime)' },
      { label: 'SOL',  value: 1, color: 'var(--cat-violet)' },
    ],
    orders: [
      ['15:58', 'BTC', 'BUY', 0.01, 67380, 'filled'],
      ['15:12', 'BNB', 'BUY', 2,    418,   'filled'],
    ],
    curve: curve(13000, 14820, 90, 300, 209),
    bench: curve(13000, 14200, 90, 220, 210),
  };

  // ---------------- Exchange pages ----------------
  function ExchangePage({ title, spec, goto }) {
    return (
      <div className="reports-body">
        <div className="kpi-grid">
          {spec.kpis.map((k, i) => (
            <div className="kpi" key={i}>
              <div className="label">{k.label}</div>
              <div className="value">{k.value}</div>
              <div className="delta" style={{ color: k.deltaClass || 'var(--fg-2)' }}>{k.delta}</div>
            </div>
          ))}
        </div>

        <div className="report-grid-2">
          <Card title="Most active · today" right={title}>
            <table className="pnl-table">
              <thead><tr><th>Sym</th><th>Last</th><th>Chg%</th><th>Volume</th></tr></thead>
              <tbody>
                {spec.actives.map((r, i) => (
                  <tr key={i} className="sub" style={{ cursor: 'pointer' }} onClick={() => goto && goto('markets/sym/' + r[0])}>
                    <td><span className="mono" style={{fontSize:12}}>{r[0]}</span> <span style={{color:'var(--fg-2)',fontSize:11}}>{r[1]}</span></td>
                    <td className="num">${r[2]}</td>
                    <td className="num" style={{ color: r[3] >= 0 ? 'var(--pos)' : 'var(--neg)' }}>{r[3] >= 0 ? '+' : ''}{r[3].toFixed(2)}%</td>
                    <td className="num">{r[4]}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>
          <Card title="Session stats">
            <table className="pnl-table">
              <tbody>
                {spec.session.map((r, i) => (
                  <tr key={i} className="sub"><td>{r[0]}</td><td className="num" style={{ color: r[2] || 'var(--fg-0)' }}>{r[1]}</td></tr>
                ))}
              </tbody>
            </table>
          </Card>
        </div>

        <Section id="advance-decline" title="Advance / decline">
          <Card title="Breadth">
            <C.BarChart
              data={spec.breadth}
              w={820} h={200} labels={['9:30','10','11','12','13','14','15','16']}
              color="var(--pos)" negColor="var(--neg)"
              yFmt={v => v}
            />
          </Card>
        </Section>
      </div>
    );
  }

  const NASDAQ_SPEC = {
    kpis: [
      { label: 'NASDAQ Composite', value: '18,421.36', delta: '+124.8 · +0.68%', deltaClass: 'var(--pos)' },
      { label: 'Volume', value: '4.28B sh', delta: '+12% vs 20d avg', deltaClass: 'var(--pos)' },
      { label: 'Advancers / Decliners', value: '2,184 / 1,402', delta: '1.56 ratio', deltaClass: 'var(--pos)' },
      { label: 'New 52w highs', value: '142', delta: 'vs 28 lows', deltaClass: 'var(--pos)' },
    ],
    actives: [
      ['NVDA', 'Nvidia',    928.14, +2.00, '42.8M'],
      ['TSLA', 'Tesla',     188.14, -2.25, '84.2M'],
      ['AAPL', 'Apple',     214.82, +0.66, '54.2M'],
      ['MSFT', 'Microsoft', 438.21, -0.55, '22.1M'],
      ['META', 'Meta',      484.32, +1.30, '14.8M'],
      ['GOOGL','Alphabet',  169.28, +0.50, '18.4M'],
      ['AMZN', 'Amazon',    184.68, +0.61, '28.2M'],
      ['AVGO', 'Broadcom', 1384.20, +1.82, '4.2M'],
    ],
    session: [
      ['Open', '18,296.56'],
      ['High', '18,432.18', 'var(--pos)'],
      ['Low', '18,288.42', 'var(--neg)'],
      ['Close', '18,421.36'],
      ['Prev close', '18,296.56'],
      ['VIX', '14.82', 'var(--pos)'],
    ],
    breadth: [ 124, 218, 142, 182, 284, 242, 326, 418 ],
  };

  const NYSE_SPEC = {
    kpis: [
      { label: 'S&P 500', value: '5,284.25', delta: '+18.5 · +0.35%', deltaClass: 'var(--pos)' },
      { label: 'NYSE Vol.', value: '3.42B sh', delta: '+4% vs 20d', deltaClass: 'var(--pos)' },
      { label: 'A/D ratio', value: '1.32', delta: '1,842 / 1,398', deltaClass: 'var(--pos)' },
      { label: 'TICK', value: '+482', delta: 'buying pressure', deltaClass: 'var(--pos)' },
    ],
    actives: [
      ['F', 'Ford', 12.40, -0.82, '62M'],
      ['BAC', 'Bank of America', 37.80, +0.45, '42M'],
      ['T', 'AT&T', 16.82, +0.12, '38M'],
      ['GE', 'GE Aerospace', 158.42, +1.20, '8M'],
      ['KO', 'Coca-Cola', 62.18, +0.28, '14M'],
    ],
    session: [
      ['Open', '5,265.75'],
      ['High', '5,292.40', 'var(--pos)'],
      ['Low',  '5,260.10', 'var(--neg)'],
      ['Close','5,284.25'],
      ['Put/Call', '0.82'],
      ['VIX',  '14.82', 'var(--pos)'],
    ],
    breadth: [ 82, 124, 168, 218, 284, 212, 264, 312 ],
  };

  const CME_SPEC = {
    kpis: [
      { label: 'ES · S&P 500 Futures', value: '5,284.25', delta: '+18.5 · +0.35%', deltaClass: 'var(--pos)' },
      { label: 'NQ · Nasdaq Futures',  value: '18,420.75', delta: '+124.5 · +0.68%', deltaClass: 'var(--pos)' },
      { label: 'CL · Crude Oil',       value: '$78.42',    delta: '−0.84 · −1.06%',  deltaClass: 'var(--neg)' },
      { label: 'GC · Gold',            value: '$2,384.60', delta: '+12.4 · +0.52%',  deltaClass: 'var(--pos)' },
    ],
    actives: [
      ['ES',  'S&P 500 Futures',      5284.25, +0.35, '1.8M'],
      ['NQ',  'Nasdaq Futures',       18420.75, +0.68, '842k'],
      ['CL',  'WTI Crude',            78.42, -1.06, '420k'],
      ['GC',  'Gold',                 2384.6, +0.52, '184k'],
      ['ZN',  '10Y Treasury Note',    110.24, -0.18, '1.2M'],
      ['6E',  'EUR/USD Futures',      1.0684, -0.13, '124k'],
      ['BTC', 'Bitcoin Futures',      67412,  +1.22, '24k'],
    ],
    session: [
      ['Open interest', '12.4M'],
      ['Globex session', 'active'],
      ['Settlement', '15:00 CT'],
      ['Daily limit ES', '±7%'],
      ['Tick size ES', '0.25 pt = $12.50'],
    ],
    breadth: [ 148, 228, 182, 224, 318, 262, 348, 412 ],
  };

  const CBOE_SPEC = {
    kpis: [
      { label: 'VIX',  value: '14.82', delta: '−0.62 · −4.02%', deltaClass: 'var(--pos)' },
      { label: 'Total options vol.', value: '42.8M contracts', delta: '+8% vs 20d', deltaClass: 'var(--pos)' },
      { label: 'Put/Call ratio', value: '0.82', delta: 'bullish', deltaClass: 'var(--pos)' },
      { label: 'SPX volume', value: '2.84M', delta: '+12%', deltaClass: 'var(--pos)' },
    ],
    actives: [
      ['SPX',  'S&P 500 Index',    5284.25, +0.35, '2.84M'],
      ['NDX',  'Nasdaq 100',       18420.75, +0.68, '842k'],
      ['VIX',  'Volatility Index', 14.82, -4.02, '624k'],
      ['RUT',  'Russell 2000',     2084.40, +1.22, '184k'],
      ['SPY',  'SPY options',      528.41, +0.35, '4.2M'],
      ['QQQ',  'QQQ options',      457.28, +0.47, '2.8M'],
    ],
    session: [
      ['Session', 'regular'],
      ['Market makers', '28 active'],
      ['SPX settlement', 'a.m. Friday'],
      ['Weekly expiries', 'M/W/F'],
    ],
    breadth: [ 220, 182, 148, 214, 242, 268, 302, 284 ],
  };

  // ---------------- Symbol page ----------------
  function SymbolPage({ sym, goto }) {
    const asset = ASSETS[sym];
    if (!asset) {
      return <div className="reports-body"><div style={{padding: 'var(--pad-x)'}}>Unknown symbol · {sym}</div></div>;
    }
    const hist = HISTORY[sym];
    const up = asset.chg >= 0;

    // Build options chain
    const spot = asset.last;
    const strikes = [];
    for (let i = -6; i <= 6; i++) {
      const stepPct = asset.kind === 'fx' ? 0.005 : 0.025;
      strikes.push(+(spot * (1 + i * stepPct)).toFixed(asset.kind === 'fx' ? 4 : 2));
    }

    return (
      <div className="reports-body">
        <Card title={'Price · ' + sym} right="3M · candles · daily">
          <C.Candles data={hist.slice(-60)} w={1200} h={360} labels={hist.slice(-60).map((_,i)=> i%10===0 ? 'd'+i : '')} />
        </Card>
        <div className="report-grid-2">
          <Card title="Volume · 60d">
            <C.BarChart
              data={hist.slice(-60).map(b => b.v/1e6)}
              w={600} h={180} labels={hist.slice(-60).map((_,i)=> i%10===0 ? 'd'+i : '')}
              color="var(--fg-3)"
              yFmt={v => v.toFixed(0)+'M'}
            />
          </Card>
          <Card title="Key stats">
            <table className="pnl-table">
              <tbody>
                <tr className="sub"><td>Open</td><td className="num">{fmtPrice(sym, hist[hist.length-1].o)}</td></tr>
                <tr className="sub"><td>Day range</td><td className="num">{fmtPrice(sym, Math.min(...hist.slice(-1).map(b=>b.l)))} — {fmtPrice(sym, Math.max(...hist.slice(-1).map(b=>b.h)))}</td></tr>
                <tr className="sub"><td>52-week range</td><td className="num">{fmtPrice(sym, Math.min(...hist.map(b=>b.l)))} — {fmtPrice(sym, Math.max(...hist.map(b=>b.h)))}</td></tr>
                <tr className="sub"><td>Volume</td><td className="num">{asset.vol}</td></tr>
                <tr className="sub"><td>Market cap</td><td className="num">{asset.mcap}</td></tr>
                <tr className="sub"><td>P/E</td><td className="num">{asset.pe ?? '—'}</td></tr>
                <tr className="sub"><td>Dividend</td><td className="num">{asset.div}</td></tr>
                <tr className="sub"><td>Beta</td><td className="num">{(0.8 + Math.random()*0.6).toFixed(2)}</td></tr>
              </tbody>
            </table>
          </Card>
        </div>

        {(asset.kind === 'equity' || asset.kind === 'etf' || asset.kind === 'futures') && (
          <Section id="options" title={'Options chain · ' + sym} note="monthly · 17 May 2026 expiry · 14 DTE">
            <Card title="Calls · Strike · Puts" flush>
              <table className="pnl-table options-chain">
                <thead>
                  <tr>
                    <th colSpan={4} style={{ textAlign: 'center', color: 'var(--pos)' }}>CALLS</th>
                    <th style={{ textAlign: 'center', color: 'var(--fg-0)' }}>STRIKE</th>
                    <th colSpan={4} style={{ textAlign: 'center', color: 'var(--neg)' }}>PUTS</th>
                  </tr>
                  <tr>
                    <th className="num">Last</th><th className="num">Bid</th><th className="num">Ask</th><th className="num">IV</th>
                    <th className="num" style={{textAlign:'center'}}></th>
                    <th className="num">Last</th><th className="num">Bid</th><th className="num">Ask</th><th className="num">IV</th>
                  </tr>
                </thead>
                <tbody>
                  {strikes.map((k, i) => {
                    const itm = k < spot;
                    const otm = k > spot;
                    const atm = Math.abs(k - spot) < spot * 0.012;
                    const cIV = (0.28 + Math.abs(k - spot) / spot * 0.4).toFixed(3);
                    const pIV = (0.30 + Math.abs(k - spot) / spot * 0.4).toFixed(3);
                    const cPrice = Math.max(0.05, spot - k + 2.4 + Math.random()*1.2);
                    const pPrice = Math.max(0.05, k - spot + 2.4 + Math.random()*1.2);
                    return (
                      <tr key={k} className={'chain-row ' + (atm ? 'atm' : '')}>
                        <td className="num" style={{ color: itm ? 'var(--pos)' : 'var(--fg-2)' }}>{cPrice.toFixed(2)}</td>
                        <td className="num">{(cPrice - 0.05).toFixed(2)}</td>
                        <td className="num">{(cPrice + 0.05).toFixed(2)}</td>
                        <td className="num" style={{ color: 'var(--fg-2)' }}>{cIV}</td>
                        <td className="num strike" style={{ textAlign: 'center', color: atm ? 'var(--accent)' : 'var(--fg-0)', fontWeight: atm ? 500 : 400 }}>{k.toFixed(2)}</td>
                        <td className="num" style={{ color: otm ? 'var(--neg)' : 'var(--fg-2)' }}>{pPrice.toFixed(2)}</td>
                        <td className="num">{(pPrice - 0.05).toFixed(2)}</td>
                        <td className="num">{(pPrice + 0.05).toFixed(2)}</td>
                        <td className="num" style={{ color: 'var(--fg-2)' }}>{pIV}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </Card>
          </Section>
        )}

        {asset.kind === 'equity' && (
          <div className="report-grid-2">
            <Card title="Fundamentals · TTM">
              <table className="pnl-table">
                <tbody>
                  <tr className="sub"><td>Revenue</td><td className="num">$394.3B</td></tr>
                  <tr className="sub"><td>Net income</td><td className="num">$96.1B</td></tr>
                  <tr className="sub"><td>EPS (diluted)</td><td className="num">$6.42</td></tr>
                  <tr className="sub"><td>Gross margin</td><td className="num">46.2%</td></tr>
                  <tr className="sub"><td>Operating margin</td><td className="num">30.1%</td></tr>
                  <tr className="sub"><td>Net margin</td><td className="num">24.4%</td></tr>
                  <tr className="sub"><td>Free cash flow</td><td className="num">$110.2B</td></tr>
                  <tr className="sub"><td>Shares outstanding</td><td className="num">15.3B</td></tr>
                </tbody>
              </table>
            </Card>
            <Card title="Analyst coverage">
              <table className="pnl-table">
                <tbody>
                  <tr className="sub"><td>Consensus</td><td className="num" style={{color:'var(--pos)'}}>BUY</td></tr>
                  <tr className="sub"><td>Price target (mean)</td><td className="num">$232.40</td></tr>
                  <tr className="sub"><td>Target high / low</td><td className="num">$275 / $180</td></tr>
                  <tr className="sub"><td>Upgrades / downgrades 30d</td><td className="num">8 / 2</td></tr>
                  <tr className="sub"><td>Strong Buy</td><td className="num" style={{color:'var(--pos)'}}>22 analysts</td></tr>
                  <tr className="sub"><td>Buy</td><td className="num" style={{color:'var(--pos)'}}>14</td></tr>
                  <tr className="sub"><td>Hold</td><td className="num" style={{color:'var(--fg-2)'}}>6</td></tr>
                  <tr className="sub"><td>Sell</td><td className="num" style={{color:'var(--neg)'}}>2</td></tr>
                </tbody>
              </table>
            </Card>
          </div>
        )}

        {asset.kind === 'crypto' && (
          <div className="report-grid-2">
            <Card title="On-chain metrics">
              <table className="pnl-table">
                <tbody>
                  <tr className="sub"><td>Circulating supply</td><td className="num">19.68M BTC</td></tr>
                  <tr className="sub"><td>Hash rate</td><td className="num">624 EH/s</td></tr>
                  <tr className="sub"><td>Active addresses (24h)</td><td className="num">814k</td></tr>
                  <tr className="sub"><td>Transaction volume</td><td className="num">$18.2B</td></tr>
                  <tr className="sub"><td>Mempool size</td><td className="num">42 MB</td></tr>
                  <tr className="sub"><td>Fees (24h)</td><td className="num">$1.82M</td></tr>
                </tbody>
              </table>
            </Card>
            <Card title="Exchange flows · 24h">
              <C.BarChart
                data={[-42,-28,12,-18,32,-48,18,-22,62,-14,38,-28]}
                w={420} h={180} labels={['00','02','04','06','08','10','12','14','16','18','20','22']}
                color="var(--pos)" negColor="var(--neg)"
                yFmt={v => v + 'M'}
              />
            </Card>
          </div>
        )}

        <Section id="news" title="News" note="related to this symbol">
          <div className="news-list">
            {[
              ['16:02', sym, asset.name + ' beats Q1 estimates, guidance raised', '+' + asset.chgPct.toFixed(2) + '%', 'pos'],
              ['15:21', sym, 'Analyst upgrade from Morgan Stanley · $232 target', 'positive', 'pos'],
              ['14:40', 'Macro', 'Fed minutes signal patient approach on cuts', 'mixed', 'neg'],
              ['13:15', sym, 'Insider purchase · 120k shares by CFO', 'insider', 'pos'],
            ].map((n, i) => (
              <div key={i} className="news-item">
                <span className="ts mono">{n[0]}</span>
                <span className={'badge ' + n[4]}>{n[1]}</span>
                <span className="head">{n[2]}</span>
                <span className={'meta ' + n[4]}>{n[3]}</span>
              </div>
            ))}
          </div>
        </Section>
      </div>
    );
  }

  // ---------------- Router wrapper ----------------
  function makePage(renderer, title, sub, pageId) {
    return function({ goto }) {
      return (
        <div className="reports">
          <MarketsHero title={title} sub={sub} activePage={pageId} goto={goto} />
          {renderer({ goto })}
        </div>
      );
    };
  }

  function makeBrokerPage(title, data, pageId) {
    return function({ goto }) {
      return (
        <div className="reports">
          <MarketsHero title={title} sub={data.total + ' · ' + data.positions + ' positions · ' + data.openOrders + ' open orders'} activePage={pageId} goto={goto} />
          <BrokerPage title={title} data={data} goto={goto} />
        </div>
      );
    };
  }

  function makeExchangePage(title, spec, pageId, sub) {
    return function({ goto }) {
      return (
        <div className="reports">
          <MarketsHero title={title} sub={sub} activePage={pageId} goto={goto} />
          <ExchangePage title={title} spec={spec} goto={goto} />
        </div>
      );
    };
  }

  function makeSymbolPage(sym) {
    return function({ goto }) {
      const a = ASSETS[sym];
      return (
        <div className="reports">
          <MarketsHero title={sym + ' · ' + a.name} sub={fmtPrice(sym, a.last) + ' · ' + (a.chgPct >= 0 ? '+' : '') + a.chgPct.toFixed(2) + '% today · ' + a.vol + ' vol'} activePage={'markets/sym/' + sym} goto={goto} />
          <SymbolPage sym={sym} goto={goto} />
        </div>
      );
    };
  }

  // Export
  window.MarketsPages = {
    'markets/overview':  makePage(Overview, 'Markets', 'live tape · 14 symbols · 5 brokers · 4 exchanges · 2 crypto exchanges', 'markets/overview'),
    'markets/watchlist': makePage(Watchlist, 'Watchlist', 'your tracked symbols · sortable · customizable', 'markets/watchlist'),
    'markets/ib':        makeBrokerPage('Interactive Brokers', IB_DATA, 'markets/ib'),
    'markets/schwab':    makeBrokerPage('Charles Schwab', SCHWAB_DATA, 'markets/schwab'),
    'markets/fidelity':  makeBrokerPage('Fidelity', FIDELITY_DATA, 'markets/fidelity'),
    'markets/robinhood': makeBrokerPage('Robinhood', { ...SCHWAB_DATA, total: '$8,420', positions: 4, openOrders: 0 }, 'markets/robinhood'),
    'markets/etrade':    makeBrokerPage('E*TRADE', { ...SCHWAB_DATA, total: '$18,420', positions: 5, openOrders: 1 }, 'markets/etrade'),
    'markets/tasty':     makeBrokerPage('tastytrade', { ...IB_DATA, total: '$24,800', positions: 8, openOrders: 4 }, 'markets/tasty'),
    'markets/coinbase':  makeBrokerPage('Coinbase Pro', COINBASE_DATA, 'markets/coinbase'),
    'markets/binance':   makeBrokerPage('Binance', BINANCE_DATA, 'markets/binance'),
    'markets/kraken':    makeBrokerPage('Kraken', { ...BINANCE_DATA, total: '$4,820' }, 'markets/kraken'),
    'markets/onchain':   makeBrokerPage('On-chain wallets', { ...COINBASE_DATA, total: '$32,510' }, 'markets/onchain'),
    'markets/nyse':      makeExchangePage('NYSE', NYSE_SPEC, 'markets/nyse', 'New York Stock Exchange · session open · primary listings'),
    'markets/nasdaq':    makeExchangePage('Nasdaq', NASDAQ_SPEC, 'markets/nasdaq', 'Nasdaq Stock Market · electronic · tech-heavy'),
    'markets/cme':       makeExchangePage('CME Group', CME_SPEC, 'markets/cme', 'Chicago Mercantile · futures · 24h session'),
    'markets/cboe':      makeExchangePage('CBOE', CBOE_SPEC, 'markets/cboe', 'Chicago Board Options · SPX · VIX · index options'),
    'markets/lse':       makeExchangePage('LSE · London', { ...NYSE_SPEC, kpis: [{ label: 'FTSE 100', value: '8,142.82', delta: '+42.4 · +0.52%', deltaClass: 'var(--pos)' }, ...NYSE_SPEC.kpis.slice(1)] }, 'markets/lse', 'London Stock Exchange · GMT session'),
    'markets/sym/AAPL':  makeSymbolPage('AAPL'),
    'markets/sym/NVDA':  makeSymbolPage('NVDA'),
    'markets/sym/SPY':   makeSymbolPage('SPY'),
    'markets/sym/TSLA':  makeSymbolPage('TSLA'),
    'markets/sym/BTC':   makeSymbolPage('BTC'),
    'markets/sym/ETH':   makeSymbolPage('ETH'),
    'markets/fx_eurusd': makeSymbolPage('EURUSD'),
    'markets/futures_es':makeSymbolPage('ES'),
    'markets/bonds_10y': makeSymbolPage('US10Y'),
  };
})();
