/* Vector + BRYPTOS — crypto workbench (interactive)
   Real interactivity:
     - Live tick: BTC/ETH spot prices drift, chart endpoint moves, KPIs flash
     - Sortable + searchable crypto basket
     - Timeframe switcher on price chart (7d/30d/90d)
     - Macro row click → detail chart panel
     - Greeks ATM strike slider — recomputes Δ/Γ/Θ/ν live
     - Position book — adjustable qty with live net Δ
     - Multi-step sandbox execution with log streaming
     - Working glossary search + sidebar quick-lookup
   Tweaks: accent · density · sparkline · live tick · panels · default view */

const { useState, useEffect, useMemo, useRef, useCallback } = React;

/* ============================================================
   TWEAK DEFAULTS — host parses this block, rewrites on changes
   ============================================================ */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "amber",
  "density": "regular",
  "sparkline": "area",
  "liveTick": true,
  "tickRateMs": 1500,
  "showOnChain": true,
  "showBasket": true,
  "showPositionBook": true,
  "defaultView": "overview",
  "precision": 2,
  "gainColor": "#22c55e",
  "lossColor": "#ef4444"
}/*EDITMODE-END*/;

/* Accent presets — applied as CSS vars on body */
const ACCENTS = {
  amber:  { color: '#fcd34d', main: '#f59e0b', glow: 'rgba(245,158,11,0.40)' },
  cyan:   { color: '#67e8f9', main: '#22d3ee', glow: 'rgba(34,211,238,0.40)' },
  green:  { color: '#86efac', main: '#22c55e', glow: 'rgba(34,197,94,0.40)' },
  purple: { color: '#d8b4fe', main: '#a855f7', glow: 'rgba(168,85,247,0.40)' },
};

/* ---------- ICONS ---------- */
const ICONS = {
  chart:    'M3 3v18h18M7 14l3-3 4 4 5-6',
  bitcoin:  'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM8 8h5a3 3 0 0 1 0 6M8 14h6a3 3 0 0 1 0 6H8M8 4v4M8 16v4',
  sigma:    'M19 4H5l7 8-7 8h14',
  layers:   'M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5',
  book:     'M4 19.5A2.5 2.5 0 0 1 6.5 17H20V2H6.5A2.5 2.5 0 0 0 4 4.5v15zM4 19.5V21h16',
  beaker:   'M9 2v4l-5 9a4 4 0 0 0 4 6h8a4 4 0 0 0 4-6L15 6V2M9 2h6',
  brain:    'M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2zM14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2z',
  play:     'M5 3l14 9-14 9V3z',
  pause:    'M6 4h4v16H6zM14 4h4v16h-4z',
  search:   'M21 21l-4.35-4.35M10 17a7 7 0 1 1 0-14 7 7 0 0 1 0 14z',
  x:        'M18 6L6 18M6 6l12 12',
};
const Icon = ({ name, color = 'currentColor', size = 14 }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
    <path d={ICONS[name] || ICONS.chart} />
  </svg>
);

/* ---------- DATA ---------- */
function genSeries(seed, n, base, vol) {
  let v = base;
  const out = [];
  for (let i = 0; i < n; i++) {
    const r = Math.sin(seed + i * 0.31) * 0.5 + Math.cos(seed * 1.3 + i * 0.07);
    v = v * (1 + (r / 200) * vol);
    out.push(+v.toFixed(2));
  }
  return out;
}
function rescaleTo(series, target) {
  const last = series[series.length - 1];
  const f = target / last;
  return series.map((v) => +(v * f).toFixed(2));
}
const BTC_INIT = 71248.50;
const ETH_INIT =  3812.74;
const BTC_BASE = rescaleTo(genSeries(3, 90, 62000, 3.5), BTC_INIT);
const ETH_BASE = rescaleTo(genSeries(7, 90, 3400, 4.0), ETH_INIT);

const CRYPTO_BASKET_INIT = [
  { sym: 'BTC',  name: 'Bitcoin',     px: BTC_INIT, ch1d: 2.13, ch7d: 5.84, vol: 32.4e9, mcap: 1.41e12 },
  { sym: 'ETH',  name: 'Ethereum',    px: ETH_INIT, ch1d: 1.82, ch7d: 3.12, vol: 14.2e9, mcap: 460e9 },
  { sym: 'SOL',  name: 'Solana',      px:   183.42, ch1d: 4.21, ch7d: 8.91, vol: 3.8e9,  mcap: 88e9 },
  { sym: 'AVAX', name: 'Avalanche',   px:    42.13, ch1d: -1.42,ch7d: 2.10, vol: 480e6,  mcap: 16e9 },
  { sym: 'LINK', name: 'Chainlink',   px:    18.92, ch1d: 0.83, ch7d: 1.92, vol: 320e6,  mcap: 11e9 },
  { sym: 'ARB',  name: 'Arbitrum',    px:     1.13, ch1d: -3.21,ch7d: -1.21,vol: 180e6,  mcap: 4.5e9 },
  { sym: 'DOGE', name: 'Dogecoin',    px:     0.16, ch1d: 1.32, ch7d: 4.13, vol: 1.2e9,  mcap: 23e9 },
  { sym: 'MATIC',name: 'Polygon',     px:     0.92, ch1d: -0.84,ch7d: -2.31,vol: 280e6,  mcap: 8.2e9 },
];

const ONCHAIN = [
  { name: 'Active addresses',     val: '1.12M',     ch: '+4.2%', good: true },
  { name: 'Hash rate (EH/s)',     val: '628',       ch: '+1.8%', good: true },
  { name: 'Miners revenue (24h)', val: '$54.2M',    ch: '+2.1%', good: true },
  { name: 'Mempool size',         val: '124k',      ch: '-8.4%', good: false },
  { name: 'NVT ratio',            val: '42.3',      ch: '+2.0%', good: null  },
  { name: 'MVRV-Z',               val: '2.41',      ch: '+0.12', good: null  },
];

const STRIKES = [64000, 67000, 70000, 71000, 72000, 75000, 80000];

/* Compute Greeks given an ATM strike (Black-Scholes-ish proxies, kept smooth) */
function computeGreeks(atm, strikes = STRIKES) {
  // Bell-shape gamma/vega centered on ATM; smooth delta cdf-style
  return strikes.map((K) => {
    const m = Math.log(K / atm) * 6;             // moneyness in std-ish units
    const cdf = 1 / (1 + Math.exp(m));           // smooth approx to N(d1)
    const bell = Math.exp(-(m * m) / 2);         // peak at-the-money
    return {
      strike: K,
      Δ: +cdf.toFixed(2),
      Γ: +(0.00026 * bell).toFixed(5),
      Θ: +(-49 * bell - 30).toFixed(1),
      ν: +(168 * bell + 70).toFixed(0),
    };
  });
}

const MACRO = [
  { id: 'GDP',        name: 'US Real GDP YoY',       val: '2.81%',  ch: '+0.04', spark: genSeries(11, 30, 2.7, 0.4),  unit: '%' },
  { id: 'CPIAUCSL',   name: 'CPI inflation YoY',     val: '3.20%',  ch: '-0.02', spark: genSeries(13, 30, 3.4, 0.6),  unit: '%' },
  { id: 'UNRATE',     name: 'Unemployment rate',     val: '3.92%',  ch: '+0.01', spark: genSeries(17, 30, 3.85, 0.3), unit: '%' },
  { id: 'FEDFUNDS',   name: 'Federal funds rate',    val: '5.33%',  ch: '0.00',  spark: genSeries(19, 30, 5.33, 0.05),unit: '%' },
  { id: 'DGS10',      name: '10-year treasury yield',val: '4.31%',  ch: '+0.03', spark: genSeries(23, 30, 4.3, 0.8),  unit: '%' },
  { id: 'M2SL',       name: 'M2 money supply',       val: '21.0T',  ch: '+0.1%', spark: genSeries(29, 30, 21.0, 0.2), unit: 'T' },
  { id: 'DCOILWTICO', name: 'WTI crude oil',         val: '$78.42', ch: '+1.8%', spark: genSeries(31, 30, 76, 2.2),   unit: '$' },
  { id: 'DEXUSEU',    name: 'USD / EUR',             val: '1.08',   ch: '-0.2%', spark: genSeries(37, 30, 1.08, 0.4), unit: '' },
];

const GLOSSARY = [
  { term: 'Hebbian routing',   def: 'Adaptive agent ranking where successful routes reinforce their weights (fire-together-wire-together).' },
  { term: 'ATP envelope',      def: 'Artemis Transmission Protocol — versioned wrapper {agent, intent, payload, governance, trust_decay}.' },
  { term: 'Trust decay',       def: 'Per-agent score that drops on policy violation. Below trust_min, the agent is unrouted.' },
  { term: 'NVT ratio',         def: 'Network Value to Transactions — market cap divided by daily transaction volume.' },
  { term: 'MVRV-Z',            def: 'Z-score of market cap vs realized cap. >3 = euphoria, <0 = capitulation.' },
  { term: 'Hash rate',         def: 'Total computational power securing the network (EH/s for Bitcoin).' },
  { term: 'Mempool',           def: 'Queue of unconfirmed transactions waiting for block inclusion.' },
  { term: 'Miners revenue',    def: 'Block reward + fees collected per 24h. Proxy for network security cost.' },
  { term: 'Implied volatility',def: 'Market-derived expected volatility; the only unknown in Black-Scholes.' },
  { term: 'Delta (Δ)',         def: 'First-order price sensitivity. 0.50 ≈ at-the-money; 1.0 = deep ITM.' },
  { term: 'Gamma (Γ)',         def: 'Second-order: rate of change of delta. Peaks at-the-money near expiry.' },
  { term: 'Theta (Θ)',         def: 'Time decay; option value erosion per day. Negative for long options.' },
  { term: 'Vega (ν)',          def: 'Sensitivity to a 1% change in implied volatility.' },
  { term: 'Sharpe of quality', def: 'Risk-adjusted measure where "return" is task quality, not P&L.' },
];

const POSITIONS_INIT = [
  { side: 'LONG',  inst: 'BTC-Jun-72000-C', strike: 72000, qty: 5,  delta: 0.42 },
  { side: 'LONG',  inst: 'BTC-Jun-75000-C', strike: 75000, qty: 3,  delta: 0.24 },
  { side: 'SHORT', inst: 'BTC-Jun-80000-C', strike: 80000, qty: -2, delta: 0.10 },
  { side: 'LONG',  inst: 'BTC-Jun-67000-P', strike: 67000, qty: 4,  delta: -0.22 },
  { side: 'SHORT', inst: 'BTC-Jun-64000-P', strike: 64000, qty: -3, delta: -0.08 },
];

/* ---------- FMT helpers ---------- */
function fmtUsd(v, precision = 2) {
  if (v >= 1e12) return '$' + (v / 1e12).toFixed(2) + 'T';
  if (v >= 1e9)  return '$' + (v / 1e9).toFixed(2) + 'B';
  if (v >= 1e6)  return '$' + (v / 1e6).toFixed(1) + 'M';
  if (v >= 1000) return '$' + v.toLocaleString(undefined, { maximumFractionDigits: 0 });
  return '$' + v.toFixed(precision);
}
function fmt(v, unit = '$', precision = 2) {
  if (unit === '%') return v.toFixed(precision) + '%';
  if (unit === '')  return v.toFixed(precision);
  if (unit === 'T') return v.toFixed(1) + 'T';
  if (v >= 1000) return '$' + Math.round(v).toLocaleString();
  if (v >= 100)  return '$' + v.toFixed(1);
  return '$' + v.toFixed(precision);
}

/* ---------- CHARTS ---------- */
function SeriesChart({ series, color = '#fbbf24', height = 260, label, unit = '$', sparkStyle = 'area' }) {
  const w = 800, h = height;
  const pad = { l: 36, r: 8, t: 12, b: 24 };
  if (!series.length) return null;
  const min = Math.min(...series), max = Math.max(...series);
  const range = max - min || 1;
  const xs = series.length;
  const x = (i) => pad.l + (i / (xs - 1)) * (w - pad.l - pad.r);
  const y = (v) => pad.t + (h - pad.t - pad.b) * (1 - (v - min) / range);
  const path = series.map((v, i) => (i === 0 ? 'M' : 'L') + x(i) + ',' + y(v)).join(' ');
  const area = path + ` L${x(xs - 1)},${h - pad.b} L${x(0)},${h - pad.b} Z`;
  const safeColor = color.replace('#', '');

  return (
    <svg viewBox={`0 0 ${w} ${h}`} style={{ width: '100%', height, display: 'block' }}>
      <defs>
        <linearGradient id={`fill-${safeColor}-${height}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"  stopColor={color} stopOpacity="0.30" />
          <stop offset="100%" stopColor={color} stopOpacity="0.00" />
        </linearGradient>
      </defs>
      {[0.25, 0.5, 0.75].map((p, i) => (
        <line key={i} x1={pad.l} y1={pad.t + (h - pad.t - pad.b) * p} x2={w - pad.r} y2={pad.t + (h - pad.t - pad.b) * p}
              stroke="rgba(139,148,158,0.10)" strokeDasharray="2 4" />
      ))}
      {[max, min + range / 2, min].map((v, i) => (
        <text key={i} x={pad.l - 6} y={pad.t + (h - pad.t - pad.b) * (i * 0.5) + 4} fill="#6e7681" fontSize="10" fontFamily="JetBrains Mono" textAnchor="end">
          {fmt(v, unit)}
        </text>
      ))}
      {sparkStyle === 'area' && <path d={area} fill={`url(#fill-${safeColor}-${height})`} />}
      <path d={path} fill="none" stroke={color} strokeWidth="1.6" />
      <text x={pad.l} y={pad.t + 2} fontSize="10" fontFamily="JetBrains Mono" fill="#8b949e" letterSpacing="0.10em">{label}</text>
    </svg>
  );
}

function Spark({ series, color, style = 'area' }) {
  const w = 80, h = 22;
  const min = Math.min(...series), max = Math.max(...series), r = max - min || 1;
  const pts = series.map((v, i) => `${(i / (series.length - 1)) * w},${h - ((v - min) / r) * h}`).join(' ');
  const safeColor = color.replace('#', '');
  return (
    <svg className="macro-spark" viewBox={`0 0 ${w} ${h}`}>
      {style === 'area' && (
        <>
          <defs>
            <linearGradient id={`sp-${safeColor}`} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={color} stopOpacity="0.40" />
              <stop offset="100%" stopColor={color} stopOpacity="0" />
            </linearGradient>
          </defs>
          <polygon points={`0,${h} ${pts} ${w},${h}`} fill={`url(#sp-${safeColor})`} />
        </>
      )}
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.4" />
    </svg>
  );
}

/* ============================================================
   Live ticking — single source of truth for spot prices
   ============================================================ */
function useLiveTick(enabled, ms) {
  const [btc, setBtc] = useState({ px: BTC_INIT, series: BTC_BASE, dir: 0 });
  const [eth, setEth] = useState({ px: ETH_INIT, series: ETH_BASE, dir: 0 });
  const [basket, setBasket] = useState(CRYPTO_BASKET_INIT);
  const [tickN, setTickN] = useState(0);

  useEffect(() => {
    if (!enabled) return;
    const id = setInterval(() => {
      setTickN((n) => n + 1);
      setBtc((b) => {
        const drift = (Math.random() - 0.49) * 0.0025;
        const px = +(b.px * (1 + drift)).toFixed(2);
        const dir = drift > 0 ? 1 : -1;
        return { px, series: [...b.series.slice(1), px], dir };
      });
      setEth((e) => {
        const drift = (Math.random() - 0.49) * 0.003;
        const px = +(e.px * (1 + drift)).toFixed(2);
        const dir = drift > 0 ? 1 : -1;
        return { px, series: [...e.series.slice(1), px], dir };
      });
      setBasket((bk) => bk.map((row) => {
        const drift = (Math.random() - 0.49) * 0.004;
        const px = +(row.px * (1 + drift)).toFixed(row.px > 100 ? 2 : 4);
        const ch1d = +(row.ch1d + drift * 100).toFixed(2);
        return { ...row, px, ch1d, lastDir: drift > 0 ? 1 : -1 };
      }));
    }, ms);
    return () => clearInterval(id);
  }, [enabled, ms]);
  return { btc, eth, basket, tickN };
}

/* ============================================================
   OVERVIEW — KPIs + chart + on-chain + basket
   ============================================================ */
function Overview({ tweaks, accent, live }) {
  const [coin, setCoin] = useState('BTC');
  const [tf, setTf] = useState('90d');
  const [sortBy, setSortBy] = useState({ key: 'mcap', dir: -1 });
  const [search, setSearch] = useState('');

  // Slice timeframe out of the running series
  const tfDays = tf === '7d' ? 7 : tf === '30d' ? 30 : 90;
  const baseSeries = coin === 'BTC' ? live.btc.series : live.eth.series;
  const series = baseSeries.slice(-tfDays);
  const last = series[series.length - 1], first = series[0];
  const ch = ((last - first) / first) * 100;
  const spot = coin === 'BTC' ? live.btc.px : live.eth.px;
  const dir  = coin === 'BTC' ? live.btc.dir : live.eth.dir;

  // Basket — filter + sort
  const filtered = useMemo(() => {
    const s = search.trim().toLowerCase();
    let arr = live.basket.filter((r) => !s || r.sym.toLowerCase().includes(s) || r.name.toLowerCase().includes(s));
    arr = [...arr].sort((a, b) => {
      const av = a[sortBy.key], bv = b[sortBy.key];
      if (typeof av === 'string') return sortBy.dir * av.localeCompare(bv);
      return sortBy.dir * (av - bv);
    });
    return arr;
  }, [live.basket, search, sortBy]);

  function toggleSort(key) {
    setSortBy((s) => s.key === key ? { key, dir: -s.dir } : { key, dir: -1 });
  }
  const arr = (k) => sortBy.key === k ? (sortBy.dir < 0 ? '▼' : '▲') : '';

  return (
    <>
      <div className="kpis">
        <div className="kpi">
          <div className="lbl">BTC spot</div>
          <div className={'val ' + (live.btc.dir > 0 ? 'flash-up' : live.btc.dir < 0 ? 'flash-down' : '')} key={'btc-' + live.btc.px}>
            {fmtUsd(live.btc.px, 0)}
          </div>
          <div className={'delta ' + (live.btc.dir >= 0 ? 'pos' : 'neg')}>
            {live.btc.dir >= 0 ? '▲' : '▼'} {Math.abs(((live.btc.px - BTC_INIT) / BTC_INIT) * 100).toFixed(2)}% · session
          </div>
        </div>
        <div className="kpi">
          <div className="lbl">ETH spot</div>
          <div className={'val ' + (live.eth.dir > 0 ? 'flash-up' : live.eth.dir < 0 ? 'flash-down' : '')} key={'eth-' + live.eth.px}>
            {fmtUsd(live.eth.px, 0)}
          </div>
          <div className={'delta ' + (live.eth.dir >= 0 ? 'pos' : 'neg')}>
            {live.eth.dir >= 0 ? '▲' : '▼'} {Math.abs(((live.eth.px - ETH_INIT) / ETH_INIT) * 100).toFixed(2)}% · session
          </div>
        </div>
        <div className="kpi"><div className="lbl">BTC dominance</div><div className="val">52.1%</div><div className="delta pos">▲ 0.34pp · 7d</div></div>
        <div className="kpi"><div className="lbl">Crypto market cap</div><div className="val">$2.71T</div><div className="delta pos">▲ $84B · 7d</div></div>
      </div>

      <div className="grid-2">
        <div className="card">
          <div className="card-head">
            <h3>{coin}-USD · {tf}</h3>
            <div style={{ display: 'flex', gap: 8 }}>
              <div className="seg">
                {['BTC', 'ETH'].map((c) => (
                  <button key={c} className={coin === c ? 'on' : ''} onClick={() => setCoin(c)}>{c}</button>
                ))}
              </div>
              <div className="seg">
                {['7d', '30d', '90d'].map((t) => (
                  <button key={t} className={tf === t ? 'on' : ''} onClick={() => setTf(t)}>{t}</button>
                ))}
              </div>
            </div>
          </div>
          <div className="chart-shell">
            <SeriesChart series={series}
              color={coin === 'BTC' ? accent.main : '#a855f7'}
              label={`${tf} · ${ch >= 0 ? '+' : ''}${ch.toFixed(1)}% · spot ${fmtUsd(spot, 0)} ${dir > 0 ? '▲' : dir < 0 ? '▼' : ''}`}
              sparkStyle={tweaks.sparkline} />
          </div>
        </div>

        {tweaks.showOnChain && (
          <div className="card">
            <div className="card-head"><h3>On-chain metrics</h3><span className="tag">BTC · 24h</span></div>
            <div>
              {ONCHAIN.map((m) => (
                <div key={m.name} className="macro-row" style={{ gridTemplateColumns: '1fr auto auto', padding: '12px 14px' }}>
                  <span className="macro-name">{m.name}</span>
                  <span style={{ color: '#f0f6fc' }}>{m.val}</span>
                  <span className={m.good === null ? '' : m.good ? 'pos' : 'neg'} style={{ minWidth: 60, textAlign: 'right' }}>{m.ch}</span>
                </div>
              ))}
            </div>
          </div>
        )}
      </div>

      {tweaks.showBasket && (
        <div className="card">
          <div className="card-head">
            <h3>Top crypto · spot &amp; flow</h3>
            <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
              <input className="search-input" placeholder="filter…" value={search} onChange={(e) => setSearch(e.target.value)} />
              <span className="tag">{filtered.length} of {live.basket.length}</span>
            </div>
          </div>
          <table>
            <thead>
              <tr>
                <th className="sortable" onClick={() => toggleSort('sym')}>Symbol <span className="arr">{arr('sym')}</span></th>
                <th className="sortable" onClick={() => toggleSort('name')}>Name <span className="arr">{arr('name')}</span></th>
                <th className="sortable" onClick={() => toggleSort('px')}>Price <span className="arr">{arr('px')}</span></th>
                <th className="sortable" onClick={() => toggleSort('ch1d')}>24h <span className="arr">{arr('ch1d')}</span></th>
                <th className="sortable" onClick={() => toggleSort('ch7d')}>7d <span className="arr">{arr('ch7d')}</span></th>
                <th className="sortable" onClick={() => toggleSort('vol')}>Vol 24h <span className="arr">{arr('vol')}</span></th>
                <th className="sortable" onClick={() => toggleSort('mcap')}>Market cap <span className="arr">{arr('mcap')}</span></th>
              </tr>
            </thead>
            <tbody>
              {filtered.map((r) => (
                <tr key={r.sym} className={'hover-able ' + (r.lastDir === 1 ? 'tr-flash' : r.lastDir === -1 ? 'tr-flash neg' : '')}>
                  <td style={{ color: accent.color, fontWeight: 600 }}>{r.sym}</td>
                  <td style={{ color: '#cbd5e1' }}>{r.name}</td>
                  <td>${r.px.toLocaleString(undefined, { minimumFractionDigits: r.px > 100 ? 2 : 4, maximumFractionDigits: r.px > 100 ? 2 : 4 })}</td>
                  <td className={r.ch1d >= 0 ? 'pos' : 'neg'}>{r.ch1d >= 0 ? '+' : ''}{r.ch1d.toFixed(2)}%</td>
                  <td className={r.ch7d >= 0 ? 'pos' : 'neg'}>{r.ch7d >= 0 ? '+' : ''}{r.ch7d.toFixed(2)}%</td>
                  <td>{fmtUsd(r.vol, 1)}</td>
                  <td>{fmtUsd(r.mcap, 1)}</td>
                </tr>
              ))}
              {filtered.length === 0 && (
                <tr><td colSpan={7} style={{ textAlign: 'center', color: '#8b949e', fontStyle: 'italic' }}>no matches for "{search}"</td></tr>
              )}
            </tbody>
          </table>
        </div>
      )}
    </>
  );
}

/* ============================================================
   GREEKS — strike slider + editable position book
   ============================================================ */
function Greeks({ tweaks, accent, live }) {
  const [atm, setAtm] = useState(71000);
  const [positions, setPositions] = useState(POSITIONS_INIT);

  const greeks = useMemo(() => computeGreeks(atm), [atm]);
  const greeksByGreek = useMemo(() => ({
    Δ: greeks.map((g) => g.Δ),
    Γ: greeks.map((g) => g.Γ),
    Θ: greeks.map((g) => g.Θ),
    ν: greeks.map((g) => g.ν),
  }), [greeks]);

  function cellColor(v, min, max) {
    const t = (v - min) / (max - min || 1);
    const r = Math.round(34 + (245 - 34) * Math.abs(t - 0.5) * 2);
    const g = Math.round(197 - (197 - 158) * Math.abs(t - 0.5) * 2);
    const b = Math.round(94  - (94  -  11) * Math.abs(t - 0.5) * 2);
    return `rgba(${r},${g},${b},${0.10 + Math.abs(t - 0.5) * 0.6})`;
  }

  // Recompute each position's per-contract delta from the curve
  const positionsWithLiveDelta = positions.map((p) => {
    const found = greeks.reduce((best, g) => Math.abs(g.strike - p.strike) < Math.abs(best.strike - p.strike) ? g : best, greeks[0]);
    const isCall = p.inst.endsWith('-C');
    return { ...p, delta: isCall ? found.Δ : -(1 - found.Δ) };
  });
  const netDelta = positionsWithLiveDelta.reduce((s, p) => s + p.delta * p.qty, 0);

  function bumpQty(idx, dir) {
    setPositions((arr) => arr.map((p, i) => i === idx ? { ...p, qty: p.qty + dir } : p));
  }

  return (
    <>
      <div className="kpis">
        <div className="kpi"><div className="lbl">Implied vol · 30d ATM</div><div className="val">52.4%</div><div className="delta neg">▼ 1.8pp · 24h</div></div>
        <div className="kpi"><div className="lbl">Open interest</div><div className="val">$24.1B</div><div className="delta pos">▲ $1.2B</div></div>
        <div className="kpi"><div className="lbl">Put/Call ratio</div><div className="val">0.83</div><div className="delta neg">▼ 0.04</div></div>
        <div className="kpi">
          <div className="lbl">Net Δ · book</div>
          <div className="val" style={{ color: netDelta >= 0 ? '#86efac' : '#fca5a5' }}>{netDelta >= 0 ? '+' : ''}{netDelta.toFixed(2)}</div>
          <div className="delta" style={{ color: '#8b949e' }}>{positions.length} positions · ATM {fmtUsd(atm, 0)}</div>
        </div>
      </div>

      <div className="grid-2">
        <div className="card">
          <div className="card-head"><h3>Greeks heatmap · BTC calls (Jun expiry)</h3><span className="tag">Δ · Γ · Θ · ν</span></div>
          <div className="greeks-bar">
            <span>ATM strike</span>
            <input type="range" min="64000" max="80000" step="500" value={atm} onChange={(e) => setAtm(+e.target.value)} />
            <span style={{ color: accent.color, fontFamily: 'var(--font-mono)' }}>{fmtUsd(atm, 0)}</span>
            <span style={{ marginLeft: 'auto' }}>spot {fmtUsd(live.btc.px, 0)}</span>
          </div>
          <div className="greeks-grid">
            <div className="cell axis"></div>
            {STRIKES.map((s) => <div key={s} className="cell header">${(s/1000).toFixed(0)}k</div>)}
            {Object.entries(greeksByGreek).map(([g, vals]) => {
              const min = Math.min(...vals), max = Math.max(...vals);
              return (
                <React.Fragment key={g}>
                  <div className="cell axis">{g}</div>
                  {vals.map((v, i) => (
                    <div key={i} className="cell" style={{ background: cellColor(v, min, max), color: '#f0f6fc' }}>
                      {Math.abs(v) >= 1 ? v.toFixed(1) : v.toFixed(4)}
                    </div>
                  ))}
                </React.Fragment>
              );
            })}
          </div>
        </div>

        {tweaks.showPositionBook && (
          <div className="card">
            <div className="card-head">
              <h3>Position book</h3>
              <span className="tag" style={{ color: netDelta >= 0 ? '#86efac' : '#fca5a5' }}>net Δ · {netDelta >= 0 ? '+' : ''}{netDelta.toFixed(2)}</span>
            </div>
            <table>
              <thead><tr><th>Side</th><th>Inst</th><th>Qty</th><th>Δ/ct</th><th>Δ ttl</th></tr></thead>
              <tbody>
                {positionsWithLiveDelta.map((p, i) => (
                  <tr key={p.inst}>
                    <td style={{ color: p.qty >= 0 ? '#86efac' : '#fca5a5' }}>{p.qty >= 0 ? 'LONG' : 'SHORT'}</td>
                    <td style={{ fontSize: 11 }}>{p.inst}</td>
                    <td>
                      <span className="qty-cell">
                        <button onClick={() => bumpQty(i, -1)}>−</button>
                        <span style={{ minWidth: 24, textAlign: 'center', color: p.qty >= 0 ? '#86efac' : '#fca5a5' }}>{p.qty >= 0 ? '+' : ''}{p.qty}</span>
                        <button onClick={() => bumpQty(i, +1)}>+</button>
                      </span>
                    </td>
                    <td>{p.delta.toFixed(2)}</td>
                    <td style={{ color: p.delta * p.qty >= 0 ? '#86efac' : '#fca5a5' }}>{(p.delta * p.qty).toFixed(2)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>

      <div className="grid-2">
        <div className="card">
          <div className="card-head"><h3>Delta surface</h3><span className="tag">strike × spot {fmtUsd(live.btc.px, 0)}</span></div>
          <div className="chart-shell"><SeriesChart series={greeksByGreek.Δ.map((v) => v * 100)} color="#22d3ee" height={200} label="Δ × 100 across strikes" unit="" sparkStyle={tweaks.sparkline} /></div>
        </div>
        <div className="card">
          <div className="card-head"><h3>Vega surface</h3><span className="tag">strike × dte</span></div>
          <div className="chart-shell"><SeriesChart series={greeksByGreek.ν} color="#a855f7" height={200} label="ν across strikes" unit="" sparkStyle={tweaks.sparkline} /></div>
        </div>
      </div>
    </>
  );
}

/* ============================================================
   MACRO — clickable rows expand a detail panel
   ============================================================ */
function Macro({ tweaks, accent }) {
  const [selected, setSelected] = useState(null);
  const sel = MACRO.find((m) => m.id === selected);

  return (
    <>
      <div className="kpis">
        <div className="kpi"><div className="lbl">DXY (USD index)</div><div className="val">104.21</div><div className="delta pos">▲ 0.32 · 1d</div></div>
        <div className="kpi"><div className="lbl">VIX</div><div className="val">14.83</div><div className="delta pos">▲ 0.42 · 1d</div></div>
        <div className="kpi"><div className="lbl">SPY</div><div className="val">$520.18</div><div className="delta pos">▲ 0.31% · 1d</div></div>
        <div className="kpi"><div className="lbl">Yield curve · 10y-2y</div><div className="val">+0.42</div><div className="delta pos">▲ 0.08bp</div></div>
      </div>
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-head">
          <h3>FRED economic series</h3>
          <span className="tag">click a row to chart · {sel ? sel.id : '—'}</span>
        </div>
        <div>
          {MACRO.map((m) => (
            <div key={m.id}
              className="macro-row hover-able"
              style={{ cursor: 'pointer', background: selected === m.id ? 'rgba(245,158,11,0.08)' : undefined }}
              onClick={() => setSelected(selected === m.id ? null : m.id)}>
              <span className="macro-name">{m.name}<span className="id" style={{ marginLeft: 8 }}>· {m.id}</span></span>
              <Spark series={m.spark} color={m.ch.startsWith('-') ? '#ef4444' : '#22c55e'} style={tweaks.sparkline} />
              <span style={{ color: '#f0f6fc', textAlign: 'right' }}>{m.val}</span>
              <span className={m.ch.startsWith('-') ? 'neg' : 'pos'} style={{ textAlign: 'right' }}>{m.ch.startsWith('+') || m.ch.startsWith('-') ? m.ch : '+' + m.ch}</span>
            </div>
          ))}
        </div>
        {sel && (
          <div className="macro-detail">
            <SeriesChart series={sel.spark} color={accent.main} height={220} label={`${sel.id} · ${sel.name}`} unit={sel.unit} sparkStyle={tweaks.sparkline} />
          </div>
        )}
      </div>
      <div className="grid-2">
        <div className="card">
          <div className="card-head"><h3>Inflation vs FFR (24m)</h3><span className="tag">CPIAUCSL · FEDFUNDS</span></div>
          <div className="chart-shell"><SeriesChart series={MACRO[1].spark.concat(MACRO[1].spark)} color="#fbbf24" height={200} label="CPI YoY %" unit="%" sparkStyle={tweaks.sparkline} /></div>
        </div>
        <div className="card">
          <div className="card-head"><h3>10y treasury yield</h3><span className="tag">DGS10</span></div>
          <div className="chart-shell"><SeriesChart series={MACRO[4].spark.concat(MACRO[4].spark)} color="#67e8f9" height={200} label="DGS10 %" unit="%" sparkStyle={tweaks.sparkline} /></div>
        </div>
      </div>
    </>
  );
}

/* ============================================================
   SANDBOX — multi-step execution + log streaming
   ============================================================ */
function Sandbox({ tweaks, accent, live }) {
  const DEFAULT_CODE = `# BRYPTOS sandbox · python · cached frames in scope
# 'btc' · 'macro' · 'greeks' available

import numpy as np
import pandas as pd

# Pull last 30 BTC daily closes
window = btc.tail(30)
returns = window.pct_change().dropna()

# Volatility-weighted z-score signal
sigma  = returns.rolling(7).std().iloc[-1]
z      = (returns.iloc[-1] - returns.mean()) / sigma
signal = "LONG" if z > 0.5 else "SHORT" if z < -0.5 else "FLAT"

# Render the close + signal markers
plot_signal(window, z=z, signal=signal)
print(f"z={z:.2f}  σ={sigma:.4f}  → {signal}")`;
  const [code, setCode] = useState(DEFAULT_CODE);
  const [running, setRunning] = useState(false);
  const [done, setDone] = useState(false);
  const [log, setLog] = useState([]);
  const [signal, setSignal] = useState(null);
  const [lastMs, setLastMs] = useState(null);
  const tRef = useRef(null);

  // Recompute the signal from the live BTC series so it stays in sync.
  function compute() {
    const win = live.btc.series.slice(-30);
    const rets = win.map((v, i) => (i ? (v - win[i - 1]) / win[i - 1] : 0)).slice(1);
    const mean = rets.reduce((s, x) => s + x, 0) / rets.length;
    const last7 = rets.slice(-7);
    const m7 = last7.reduce((s, x) => s + x, 0) / last7.length;
    const v7 = Math.sqrt(last7.reduce((s, x) => s + (x - m7) ** 2, 0) / last7.length);
    const z = (rets[rets.length - 1] - mean) / (v7 || 1e-9);
    const sig = z > 0.5 ? 'LONG' : z < -0.5 ? 'SHORT' : 'FLAT';
    return { z, sigma: v7, signal: sig };
  }

  function run() {
    if (running) return;
    setRunning(true); setDone(false); setLog([]); setSignal(null);
    const t0 = performance.now();
    const steps = [
      { delay: 120, line: { kind: 'info', text: '[exec] parsing strategy.py — 18 statements, 0 syntax errors' } },
      { delay: 180, line: { kind: 'info', text: '[exec] loading frame · btc (90×4)  ·  macro (8×30)  ·  greeks (7×4)' } },
      { delay: 220, line: { kind: 'info', text: '[exec] window = btc.tail(30)  →  shape (30,)' } },
      { delay: 200, line: { kind: 'info', text: '[exec] returns = window.pct_change().dropna()  →  shape (29,)' } },
      { delay: 240, line: { kind: 'info', text: '[exec] sigma = returns.rolling(7).std().iloc[-1]' } },
      { delay: 180, line: { kind: 'info', text: '[exec] z = (returns.iloc[-1] - returns.mean()) / sigma' } },
    ];
    let cum = 0;
    steps.forEach((s) => {
      cum += s.delay;
      setTimeout(() => setLog((l) => [...l, s.line]), cum);
    });
    setTimeout(() => {
      const r = compute();
      setSignal(r);
      setLog((l) => [
        ...l,
        { kind: 'out',  text: `z=${r.z.toFixed(2)}  σ=${r.sigma.toFixed(4)}  →  ${r.signal}` },
        { kind: 'ok',   text: `[exec] completed · ${(performance.now() - t0).toFixed(1)}ms · 0 warnings` },
      ]);
      setRunning(false); setDone(true);
      setLastMs(+(performance.now() - t0).toFixed(1));
    }, cum + 280);
  }

  function reset() {
    setCode(DEFAULT_CODE); setLog([]); setSignal(null); setDone(false); setLastMs(null);
  }

  function hl(text) {
    return text
      .replace(/&/g, '&amp;').replace(/</g, '&lt;')
      .replace(/(#[^\n]*)/g, '<span class="cm">$1</span>')
      .replace(/\b(import|from|as|def|return|if|elif|else|for|in|while|with|class|lambda|None|True|False)\b/g, '<span class="k">$1</span>')
      .replace(/(['"])([^'"]*)\1/g, '<span class="s">$1$2$1</span>')
      .replace(/\b(\d+\.?\d*)\b/g, '<span class="n">$1</span>')
      .replace(/\b([a-zA-Z_]+)\(/g, '<span class="fn">$1</span>(');
  }

  return (
    <>
      <div className="kpis" style={{ marginBottom: 14 }}>
        <div className="kpi"><div className="lbl">Runtime</div><div className="val" style={{ fontSize: 16 }}>python · 3.13</div><div className="delta" style={{ color: '#67e8f9' }}>● kernel-sandboxed</div></div>
        <div className="kpi"><div className="lbl">Frames in scope</div><div className="val" style={{ fontSize: 16 }}>btc · macro · greeks</div><div className="delta" style={{ color: '#8b949e' }}>cached · 24h ttl</div></div>
        <div className="kpi"><div className="lbl">Policy</div><div className="val" style={{ fontSize: 16, color: '#fcd34d' }}>review</div><div className="delta">no fs · no net</div></div>
        <div className="kpi">
          <div className="lbl">Last run</div>
          <div className="val" style={{ fontSize: 16 }}>{lastMs == null ? '—' : `${lastMs}ms`}</div>
          <div className={'delta ' + (done ? 'pos' : '')}>{done ? '✓ ' + (signal?.signal || '') : '—'}</div>
        </div>
      </div>

      <div className="sandbox">
        <div className="card">
          <div className="card-head">
            <h3>Editor · strategy.py</h3>
            <div style={{ display: 'flex', gap: 6 }}>
              <button className="run-btn" style={{ background: '#21262d', color: '#cbd5e1', border: '1px solid rgba(139,148,158,0.20)' }} onClick={reset} disabled={running}>Reset</button>
              <button className="run-btn" onClick={run} disabled={running}>
                <Icon name="play" size={11} color="#052e16" /> {running ? 'Running…' : 'Run'}
              </button>
            </div>
          </div>
          {/* Use a textarea overlay for actual editing — but cheap: show highlighted preview */}
          <div className="editor" dangerouslySetInnerHTML={{ __html: hl(code) }} />
        </div>
        <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>
          <div className="card-head">
            <h3>Output</h3>
            <span className="tag">stdout · matplotlib</span>
          </div>
          <div className="sb-log">
            {log.length === 0 && !running && <div style={{ color: '#8b949e', letterSpacing: '0.12em', textTransform: 'uppercase' }}>● press run</div>}
            {log.map((l, i) => (
              <div key={i} className={'l-' + l.kind}>{l.text}</div>
            ))}
          </div>
          {done && signal && (
            <div className="output-chart" style={{ minHeight: 0, padding: '0 14px 14px' }}>
              <SeriesChart
                series={live.btc.series.slice(-30)}
                color={signal.signal === 'LONG' ? '#22c55e' : signal.signal === 'SHORT' ? '#ef4444' : '#fbbf24'}
                height={180}
                label={`BTC last 30 ticks · z=${signal.z.toFixed(2)} · ${signal.signal}`}
                sparkStyle={tweaks.sparkline}
              />
            </div>
          )}
        </div>
      </div>
    </>
  );
}

/* ============================================================
   GLOSSARY — search + highlight
   ============================================================ */
function GlossaryView() {
  const [q, setQ] = useState('');
  const needle = q.trim().toLowerCase();
  const filt = GLOSSARY.filter((g) => !needle || (g.term + g.def).toLowerCase().includes(needle));

  function highlight(text) {
    if (!needle) return text;
    const re = new RegExp('(' + needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'ig');
    return text.split(re).map((s, i) => i % 2 === 1 ? <mark key={i}>{s}</mark> : s);
  }

  return (
    <>
      <div style={{ marginBottom: 14, maxWidth: 480, display: 'flex', gap: 8 }}>
        <input
          style={{ flex: 1, padding: '10px 14px', background: '#161b22', border: '1px solid rgba(139,148,158,0.20)', borderRadius: 7, color: '#f0f6fc', fontFamily: 'var(--font-sans)', fontSize: 14, outline: 'none' }}
          placeholder="Search glossary…"
          value={q}
          onChange={(e) => setQ(e.target.value)}
          autoFocus
        />
        {q && (
          <button
            onClick={() => setQ('')}
            style={{ padding: '0 12px', background: 'transparent', border: '1px solid rgba(139,148,158,0.20)', color: '#cbd5e1', borderRadius: 7, cursor: 'pointer' }}>
            ✕
          </button>
        )}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        {filt.map((g) => (
          <div key={g.term} className="card" style={{ padding: 14 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 12 }}>
              <span style={{ color: '#fcd34d', fontWeight: 600, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>{highlight(g.term)}</span>
              <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: '#8b949e', letterSpacing: '0.10em', textTransform: 'uppercase' }}>def</span>
            </div>
            <p style={{ margin: '8px 0 0', color: '#cbd5e1', fontSize: 13, lineHeight: 1.55 }}>{highlight(g.def)}</p>
          </div>
        ))}
        {filt.length === 0 && (
          <div style={{ gridColumn: '1 / -1', textAlign: 'center', color: '#8b949e', padding: 32, fontStyle: 'italic' }}>
            no terms match "{q}"
          </div>
        )}
      </div>
    </>
  );
}

/* ============================================================
   APP ROOT — wires Tweaks + lifts live state to top
   ============================================================ */
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const accent = ACCENTS[t.accent] || ACCENTS.amber;
  const live = useLiveTick(t.liveTick, t.tickRateMs);

  const [view, setView] = useState(t.defaultView);
  const [qlookup, setQlookup] = useState('');

  // Apply density class to body
  useEffect(() => {
    document.body.classList.remove('dens-compact', 'dens-regular', 'dens-comfy');
    document.body.classList.add('dens-' + t.density);
    document.body.style.setProperty('--accent-amber', accent.color);
  }, [t.density, t.accent, accent.color]);

  const ViewName = {
    overview: 'Crypto Overview', greeks: 'Options · Greeks', macro: 'Macro · FRED',
    sandbox: 'Strategy Sandbox', glossary: 'Glossary',
  }[view];
  const ViewDesc = {
    overview: 'BTC/ETH spot, on-chain metrics, and the top-cap basket. Sort columns, filter, watch ticks land.',
    greeks: 'Live Greeks heatmap + position book. Drag the ATM strike to recompute the curve; net Δ updates instantly.',
    macro: 'Eight Fed/Treasury series with sparklines. Click any row to drill into the full chart.',
    sandbox: 'Run governed Python against the live BTC frame. The z-score signal re-evaluates each tick.',
    glossary: 'Searchable term reference. Matches highlight inline.',
  }[view];

  // Sidebar lookup: filter glossary live
  const lookupHits = qlookup ? GLOSSARY.filter((g) => (g.term + g.def).toLowerCase().includes(qlookup.toLowerCase())).slice(0, 5) : GLOSSARY.slice(0, 3);

  return (
    <div className="app">
      <div className="topbar">
        <div className="brand">
          <span className="glyph" style={{ background: `linear-gradient(135deg, ${accent.color}, ${accent.main})` }}>
            <Icon name="bitcoin" size={18} color="#1f2937" />
          </span>
          <div>
            <div className="name"><span className="b" style={{ color: accent.color }}>B</span>RYPTOS <span style={{ color: '#8b949e', fontWeight: 400 }}>· on Vector</span></div>
            <div className="sub">CRYPTO · OPTIONS · MACRO</div>
          </div>
        </div>
        <div className="topbar-right">
          <span className="chip">
            <span className="dot" style={{ background: t.liveTick ? '#22c55e' : '#8b949e', boxShadow: t.liveTick ? '0 0 8px #22c55e' : 'none', animation: t.liveTick ? 'pulse 2s infinite' : 'none' }}></span>
            {t.liveTick ? `Live · ${(1000/t.tickRateMs).toFixed(1)}Hz · IEX + CMC + FRED` : 'Paused'}
          </span>
          <span className="status-pill"><span style={{ width: 6, height: 6, borderRadius: '50%', background: '#22c55e', boxShadow: '0 0 8px #22c55e' }}></span> Descope · session valid</span>
          <div className="user-chip">
            <span className="av" style={{ background: `linear-gradient(135deg, ${accent.color}, ${accent.main})` }}>PP</span>
            <span style={{ fontFamily: 'var(--font-sans)' }}>Prinston P.</span>
          </div>
        </div>
      </div>

      <aside className="sidebar">
        <div>
          <div className="nav-title">BRYPTOS</div>
          {[
            { id: 'overview', label: 'Crypto overview', icon: 'bitcoin', badge: t.liveTick ? 'live' : 'paused' },
            { id: 'greeks',   label: 'Options · Greeks', icon: 'sigma' },
            { id: 'macro',    label: 'Macro · FRED',     icon: 'layers' },
            { id: 'sandbox',  label: 'Strategy sandbox', icon: 'beaker' },
            { id: 'glossary', label: 'Glossary',         icon: 'book', badge: GLOSSARY.length },
          ].map((n) => (
            <div key={n.id} className={'nav-item ' + (view === n.id ? 'active' : '')} onClick={() => setView(n.id)} style={view === n.id ? { borderColor: accent.color + '4D', background: accent.glow.replace('0.40', '0.10'), color: accent.color, boxShadow: `0 0 16px -6px ${accent.glow}` } : null}>
              <Icon name={n.icon} /> {n.label}
              {n.badge != null && <span className="nav-badge" style={{ background: accent.color + '2E', color: accent.color }}>{n.badge}</span>}
            </div>
          ))}
        </div>

        <div>
          <div className="nav-title">Vector</div>
          <div className="nav-item"><Icon name="chart" /> Strategies</div>
          <div className="nav-item"><Icon name="layers" /> Portfolio</div>
          <div className="nav-item"><Icon name="brain" /> Signals</div>
        </div>

        <div className="glossary">
          <h5>● Quick lookup</h5>
          <input placeholder="lookup a term…" value={qlookup} onChange={(e) => setQlookup(e.target.value)} />
          {lookupHits.length === 0 ? (
            <div style={{ color: '#8b949e', fontStyle: 'italic', padding: 4 }}>no match · try Δ, NVT, ATP…</div>
          ) : lookupHits.map((g) => (
            <div className="gl-item" key={g.term}>
              <span className="term">{g.term}</span>
              <div className="def">{g.def}</div>
            </div>
          ))}
        </div>
      </aside>

      <main>
        <div className="page-head">
          <div>
            <span className="eb" style={{ color: accent.color }}>● {view}</span>
            <h1>{ViewName}</h1>
            <p>{ViewDesc}</p>
          </div>
        </div>

        {view === 'overview' && <Overview tweaks={t} accent={accent} live={live} />}
        {view === 'greeks'   && <Greeks tweaks={t} accent={accent} live={live} />}
        {view === 'macro'    && <Macro tweaks={t} accent={accent} />}
        {view === 'sandbox'  && <Sandbox tweaks={t} accent={accent} live={live} />}
        {view === 'glossary' && <GlossaryView />}
      </main>

      <TweaksPanel title="BRYPTOS Tweaks">
        <TweakSection label="Appearance">
          <TweakColor label="Accent" value={t.accent}
            options={[
              { value: 'amber',  label: '#fcd34d' },
              { value: 'cyan',   label: '#67e8f9' },
              { value: 'green',  label: '#86efac' },
              { value: 'purple', label: '#d8b4fe' },
            ].map((o) => o.label)}
            onChange={(hex) => {
              const id = Object.entries(ACCENTS).find(([k, v]) => v.color === hex)?.[0] || 'amber';
              setTweak('accent', id);
            }} />
          <TweakRadio label="Density" value={t.density}
            options={['compact', 'regular', 'comfy']}
            onChange={(v) => setTweak('density', v)} />
          <TweakRadio label="Chart fill" value={t.sparkline}
            options={['line', 'area']}
            onChange={(v) => setTweak('sparkline', v)} />
        </TweakSection>

        <TweakSection label="Live data">
          <TweakToggle label="Tick prices" value={t.liveTick}
            onChange={(v) => setTweak('liveTick', v)} />
          <TweakSlider label="Tick rate" value={t.tickRateMs} min={400} max={4000} step={100} unit="ms"
            onChange={(v) => setTweak('tickRateMs', v)} />
        </TweakSection>

        <TweakSection label="Panels">
          <TweakToggle label="On-chain metrics" value={t.showOnChain}
            onChange={(v) => setTweak('showOnChain', v)} />
          <TweakToggle label="Crypto basket"    value={t.showBasket}
            onChange={(v) => setTweak('showBasket', v)} />
          <TweakToggle label="Position book"    value={t.showPositionBook}
            onChange={(v) => setTweak('showPositionBook', v)} />
        </TweakSection>

        <TweakSection label="Defaults">
          <TweakSelect label="Default view" value={t.defaultView}
            options={[
              { value: 'overview', label: 'Overview' },
              { value: 'greeks',   label: 'Options · Greeks' },
              { value: 'macro',    label: 'Macro · FRED' },
              { value: 'sandbox',  label: 'Sandbox' },
              { value: 'glossary', label: 'Glossary' },
            ]}
            onChange={(v) => setTweak('defaultView', v)} />
          <TweakSlider label="Precision" value={t.precision} min={0} max={4} step={1} unit=" dp"
            onChange={(v) => setTweak('precision', v)} />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
