// app.jsx — DreamyStacks Live Trading Room
// Bento Soft layout (per Marketwise design handoff), wired to /api endpoints.

const { useState, useEffect, useMemo } = React;

// ───────────────────────────── helpers ─────────────────────────────

const fmt$ = (v) => '$' + Number(v || 0).toLocaleString('en-US', {
  minimumFractionDigits: 2, maximumFractionDigits: 2,
});
const fmt$0 = (v) => '$' + Number(v || 0).toLocaleString('en-US', {
  minimumFractionDigits: 0, maximumFractionDigits: 0,
});
const fmtPct = (v) => Number(v || 0).toFixed(2) + '%';
const fmtSigned = (v, d = 2) => (v >= 0 ? '+' : '−') + fmt$0(Math.abs(v)).slice(1);
const titleCase = (s) => (s || '').charAt(0).toUpperCase() + (s || '').slice(1).toLowerCase();

async function api(path) {
  try {
    const r = await fetch(path, { credentials: 'same-origin' });
    if (!r.ok) throw new Error(r.statusText);
    return await r.json();
  } catch (e) {
    console.warn('api', path, e);
    return null;
  }
}

function useApi(path, intervalMs = 15000) {
  const [data, setData] = useState(null);
  useEffect(() => {
    let alive = true;
    const tick = async () => {
      const d = await api(path);
      if (alive && d !== null) setData(d);
    };
    tick();
    const id = setInterval(tick, intervalMs);
    return () => { alive = false; clearInterval(id); };
  }, [path, intervalMs]);
  return data;
}

// ───────────────────────────── icons ─────────────────────────────

const Icon = {
  Dashboard: ({ s = 16 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="3" width="7" height="9" rx="1.5" />
      <rect x="14" y="3" width="7" height="5" rx="1.5" />
      <rect x="14" y="12" width="7" height="9" rx="1.5" />
      <rect x="3" y="16" width="7" height="5" rx="1.5" />
    </svg>
  ),
  Award: ({ s = 16 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="9" r="6" /><path d="M8.21 13.89 7 22l5-3 5 3-1.21-8.12" />
    </svg>
  ),
  Bank: ({ s = 16 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 21h18" /><path d="M3 10h18" /><path d="M5 6 12 3l7 3" />
      <path d="M4 10v11" /><path d="M20 10v11" /><path d="M8 14v3" /><path d="M12 14v3" /><path d="M16 14v3" />
    </svg>
  ),
  ChevronRight: ({ s = 12 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6" /></svg>
  ),
  Settings: ({ s = 16 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" /></svg>
  ),
  Sun: ({ s = 16 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="4" /><path d="M12 2v2" /><path d="M12 20v2" /><path d="m4.93 4.93 1.41 1.41" /><path d="m17.66 17.66 1.41 1.41" /><path d="M2 12h2" /><path d="M20 12h2" /><path d="m4.93 19.07 1.41-1.41" /><path d="m17.66 6.34 1.41-1.41" /></svg>
  ),
  Moon: ({ s = 16 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" /></svg>
  ),
  Plus: ({ s = 13 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></svg>
  ),
  Search: ({ s = 14 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
  ),
  Bell: ({ s = 14 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" /><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" /></svg>
  ),
  More: ({ s = 14 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
  ),
  Menu: ({ s = 20 }) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>
  ),
  Dot: ({ color = 'currentColor' }) => <span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: 999, background: color, marginRight: 6 }} />,
};

// ───────────────────────────── sidebar ─────────────────────────────

function Sidebar({ active, setActive, collapsed, setCollapsed, closeMobNav }) {
  const [open, setOpen] = useState({ ftmo: true, ibkr: false });
  const toggle = (gid) => setOpen(o => ({ ...o, [gid]: !o[gid] }));

  const groups = [
    { id: 'ftmo', label: 'FTMO Demo', icon: <Icon.Award s={18} />, children: [
      { id: 'dashboard', label: 'Dashboard' },
      { id: 'portfolio', label: 'Portfolio' },
      { id: 'strategy',  label: 'Strategy' },
      { id: 'journal',   label: 'Journal' },
      { id: 'positions', label: 'Positions' },
    ]},
    { id: 'ibkr', label: 'IBKR', icon: <Icon.Bank s={18} />, soon: true, children: [
      { id: 'tfsa',   label: 'TFSA' },
      { id: 'margin', label: 'Margin' },
    ]},
    { id: 'signal', label: 'Signal Trader', icon: <Icon.Bell s={18} />, href: '/signal-trader', children: [] },
  ];

  const onGroupClick = (gid) => {
    const g = groups.find(x => x.id === gid);
    if (g && g.href) { window.location.href = g.href; return; }
    if (collapsed) { setCollapsed(false); setOpen(o => ({ ...o, [gid]: true })); }
    else toggle(gid);
  };

  return (
    <aside className="sb">
      <div className="sb-brand">
        <button className="sb-brand-av" onClick={() => setCollapsed(!collapsed)}
                title={collapsed ? 'Expand' : 'Collapse'}>FB</button>
        {!collapsed && <span style={{ fontSize: 13.5, fontWeight: 600, marginLeft: 4 }}>DreamyStacks</span>}
      </div>

      <div className="sb-section">Accounts</div>
      {groups.map(g => {
        const isOpen = open[g.id];
        const childActive = g.children.some(c => c.id === active);
        return (
          <div className="sb-group" key={g.id}>
            <button
              className={`sb-item sb-group-head ${childActive && (collapsed || !isOpen) ? 'active' : ''}`}
              onClick={() => onGroupClick(g.id)}>
              <span className="sb-item-icon">{g.icon}</span>
              <span className="sb-item-label">
                {g.label}
                {g.soon && !collapsed && (
                  <span style={{
                    fontSize: 9, marginLeft: 6, padding: '1px 6px', borderRadius: 999,
                    background: 'var(--bg-3)', color: 'var(--ink-3)', fontWeight: 600,
                  }}>SOON</span>
                )}
              </span>
              {!g.href && <span className={`sb-caret ${isOpen ? 'open' : ''}`}><Icon.ChevronRight s={12} /></span>}
            </button>
            {isOpen && !collapsed && (
              <div className="sb-sub">
                {g.children.map(c => (
                  <button key={c.id}
                          className={`sb-subitem ${active === c.id ? 'active' : ''}`}
                          onClick={() => { setActive(c.id); closeMobNav && closeMobNav(); }}
                          disabled={g.soon}
                          style={g.soon ? { opacity: 0.55, cursor: 'not-allowed' } : {}}>
                    <span className="sb-subitem-label">{c.label}</span>
                  </button>
                ))}
              </div>
            )}
          </div>
        );
      })}

      <div className="sb-spacer" />
      <div className="sb-foot">
        <button className="sb-item">
          <span className="sb-item-icon"><Icon.Settings s={18} /></span>
          <span className="sb-item-label">Settings</span>
        </button>
        <div className="sb-acc">
          <div className="sb-acc-av">FB</div>
          {!collapsed && (
            <div className="sb-acc-meta" style={{ flex: 1 }}>
              <div className="sb-acc-name">Fred B.</div>
              <div className="sb-acc-id">·dreamystacks</div>
            </div>
          )}
        </div>
      </div>
    </aside>
  );
}

// ───────────────────────────── theme toggle ─────────────────────────────

function ThemeToggle({ mode, onChange }) {
  return (
    <div className="theme-toggle">
      {['light', 'dark'].map(m => (
        <button key={m} onClick={() => onChange(m)}
                className={mode === m ? 'active' : ''}
                aria-label={titleCase(m) + ' theme'} aria-pressed={mode === m}>
          {m === 'light' ? <Icon.Sun s={14} /> : <Icon.Moon s={14} />}
          <span className="tt-label">{titleCase(m)}</span>
        </button>
      ))}
    </div>
  );
}

// ───────────────────────────── equity sparkline ─────────────────────────────

function EquityLine({ dates, equity, h = 200, accent }) {
  if (!dates || dates.length < 2) {
    return <div style={{ height: h, display: 'grid', placeItems: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
      Waiting for first closed trade…
    </div>;
  }
  const min = Math.min(...equity), max = Math.max(...equity);
  const r = max - min || 1;
  const w = 600;
  const pts = equity.map((v, i) => [(i / (equity.length - 1)) * w, h - 18 - ((v - min) / r) * (h - 40)]);
  let p = `M ${pts[0][0]} ${pts[0][1]}`;
  for (let i = 1; i < pts.length; i++) {
    const [x1, y1] = pts[i - 1], [x2, y2] = pts[i], cx = (x1 + x2) / 2;
    p += ` C ${cx} ${y1}, ${cx} ${y2}, ${x2} ${y2}`;
  }
  const grad = `url(#eqg)`;
  return (
    <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} style={{ display: 'block' }}>
      <defs>
        <linearGradient id="eqg" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.30" />
          <stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={p + ` L ${w} ${h} L 0 ${h} Z`} fill={grad} />
      <path d={p} fill="none" stroke={accent} strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

// ───────────────────────────── dashboard page ─────────────────────────────

const STATUS_PILL = {
  LIVE:   { bg: 'rgba(16,167,114,0.14)', col: 'var(--pos)', label: 'LIVE' },
  SHADOW: { bg: 'rgba(245,158,11,0.16)', col: '#a16207',    label: 'SHADOW' },
  OFF:    { bg: 'var(--bg-3)',           col: 'var(--ink-3)', label: 'OFF' },
};

function StatusPill({ status }) {
  const s = STATUS_PILL[status] || STATUS_PILL.OFF;
  return (
    <span style={{
      fontSize: 10.5, padding: '3px 9px', borderRadius: 999,
      background: s.bg, color: s.col, fontWeight: 700, letterSpacing: '0.04em',
    }}>{s.label}</span>
  );
}

function DashboardPage({ accent }) {
  const account  = useApi('/api/account', 5000);
  const ftmo     = useApi('/api/ftmo', 5000);
  const equity   = useApi('/api/equity', 30000);
  const positions = useApi('/api/positions', 5000);
  const live     = useApi('/api/live_state', 30000);
  const pairs    = useApi('/api/pair_positions', 10000);

  const balance     = account?.balance ?? 50000;
  const equityNow   = account?.equity ?? 50000;
  const floating    = account?.floating_pnl ?? 0;
  const freeMargin  = account?.free_margin ?? 50000;
  const connected   = account?.connected ?? false;

  const initial     = ftmo?.initial_balance ?? 50000;
  const profitProgress = ftmo?.profit_progress_pct ?? 0;
  const profitTarget   = ftmo?.profit_target_usd ?? 5000;
  const profitEarned   = ftmo?.current_profit_usd ?? 0;
  const profitRemain   = Math.max(0, profitTarget - profitEarned);
  const dailyUsedPct   = ftmo?.daily_loss_used_pct ?? 0;
  const dailyUsed      = ftmo?.daily_loss_used_usd ?? 0;
  const dailyLimit     = ftmo?.daily_loss_limit_usd ?? 2250;
  const totalDdPct     = ftmo?.total_dd_used_pct ?? 0;
  const totalDd        = ftmo?.total_dd_used_usd ?? 0;
  const totalDdLimit   = ftmo?.total_dd_limit_usd ?? 4500;
  const tradingDays    = ftmo?.trading_days ?? 0;

  const strats = live?.strategies ?? [];
  const openPairs = pairs?.open ?? [];
  const closedPairs = pairs?.closed_recent ?? [];

  return (
    <>
      <div className="topbar">
        <div className="topbar-l">
          <h1>Live Trading Room</h1>
          <div className="sub">
            FTMO-Demo · account <code>{(live?.ftmo_demo?.account_id || '').slice(0, 8)}…</code>
            {' · '}
            <span style={{ color: connected ? 'var(--pos)' : 'var(--neg)' }}>
              {connected ? '● connected' : '● disconnected'}
            </span>
            {' · bot '}
            <span style={{ color: live?.bot_active ? 'var(--pos)' : 'var(--neg)' }}>
              {live?.bot_active ? 'running' : 'stopped'}
            </span>
          </div>
        </div>
        <div className="topbar-r">
          <button className="icon-btn" aria-label="Search"><Icon.Search /></button>
          <button className="icon-btn" aria-label="Notifications"><Icon.Bell /></button>
        </div>
      </div>

      <div className="content">
        <div className="bento">
          {/* Hero — total equity */}
          <div className="b-card hero span-7">
            <div style={{ position: 'relative', zIndex: 1 }}>
              <div className="hero-eyebrow">Total equity</div>
              <div className="hero-val">{fmt$(equityNow)}</div>
              <div className="hero-delta">
                <b>{fmtSigned(equityNow - initial)}</b>
                &nbsp;·&nbsp;
                <b>{((equityNow - initial) / initial * 100).toFixed(2)}%</b>
                &nbsp;since account start
              </div>
            </div>
            <div style={{ height: 24 }} />
            <div className="hero-stats">
              <div><div className="lbl">Balance</div><div className="val">{fmt$0(balance)}</div></div>
              <div><div className="lbl">Free margin</div><div className="val">${(freeMargin / 1000).toFixed(1)}k</div></div>
              <div><div className="lbl">Floating</div><div className="val">{fmtSigned(floating)}</div></div>
              <div><div className="lbl">Trading days</div><div className="val">{tradingDays}</div></div>
            </div>
          </div>

          {/* FTMO progress */}
          <div className="b-card span-5">
            <div className="b-h">
              <div><h3 className="b-title">FTMO progress</h3><div className="b-sub">Phase 1 demo · no real risk</div></div>
            </div>
            <div className="obj-list">
              {[
                { name: 'Profit target', stat: fmtPct(profitProgress / 10),
                  pct: profitProgress, sub: fmt$0(profitEarned) + ' earned',
                  sub2: fmt$0(profitRemain) + ' to go', kind: 'pos' },
                { name: 'Daily loss',  stat: fmtPct(dailyUsedPct),
                  pct: (dailyUsedPct / (ftmo?.daily_loss_limit_pct || 4.5)) * 100,
                  sub: fmt$0(dailyUsed) + ' used',
                  sub2: fmt$0(dailyLimit - dailyUsed) + ' buffer', kind: 'warn' },
                { name: 'Overall loss', stat: fmtPct(totalDdPct),
                  pct: (totalDdPct / (ftmo?.total_dd_limit_pct || 9)) * 100,
                  sub: fmt$0(totalDd) + ' used',
                  sub2: fmt$0(totalDdLimit - totalDd) + ' buffer', kind: '' },
              ].map((o, i) => (
                <div key={i}>
                  <div className="obj-h"><span className="obj-name">{o.name}</span><span className="obj-stat">{o.stat}</span></div>
                  <div className="obj-bar"><div className={`obj-fill ${o.kind}`} style={{ width: Math.min(100, Math.max(0, o.pct)) + '%' }} /></div>
                  <div className="obj-meta"><span>{o.sub}</span><span>{o.sub2}</span></div>
                </div>
              ))}
            </div>
          </div>

          {/* Equity curve */}
          <div className="b-card span-8">
            <div className="b-h">
              <div><h3 className="b-title">Equity curve</h3><div className="b-sub">Cumulative closed-trade P&amp;L</div></div>
            </div>
            <EquityLine
              dates={equity?.dates}
              equity={equity?.equity}
              h={240}
              accent={accent}
            />
          </div>

          {/* Active strategies */}
          <div className="b-card span-4">
            <div className="b-h">
              <div><h3 className="b-title">Active strategies</h3><div className="b-sub">{strats.filter(s => s.status === 'LIVE').length} live · {strats.filter(s => s.status === 'SHADOW').length} shadow</div></div>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {strats.map(s => (
                <div key={s.key} style={{
                  display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                  padding: '10px 12px', borderRadius: 10, background: 'var(--bg-3)',
                }}>
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontSize: 13, fontWeight: 600 }}>{s.label}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {s.symbols.length ? s.symbols.join(', ') : '—'}
                    </div>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'ui-monospace,monospace' }}>${s.risk_usd.toFixed(0)}</span>
                    <StatusPill status={s.status} />
                  </div>
                </div>
              ))}
            </div>
          </div>

          {/* Open positions (single-leg) */}
          <div className="b-card span-7">
            <div className="b-h">
              <div>
                <h3 className="b-title">Open positions</h3>
                <div className="b-sub">{(positions || []).length} active · floating {fmtSigned(floating)}</div>
              </div>
            </div>
            <div className="pos-list">
              {(positions || []).length === 0 && (
                <div style={{ padding: '24px 8px', textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
                  No open single-leg positions
                </div>
              )}
              {(positions || []).map((p, i) => (
                <div key={i} className="pos-card">
                  <div className="pos-sym-side">
                    <span className={`pos-side ${p.side === 'LONG' ? 'buy' : 'sell'}`}>{p.side === 'LONG' ? 'BUY' : 'SELL'}</span>
                    <span className="pos-sym">{p.symbol}</span>
                  </div>
                  <div>
                    <div className="pos-meta">{p.lots} lots · @ {p.entry_price?.toFixed(5)} · {p.duration_min}m</div>
                    <div className="pos-meta" style={{ marginTop: 2 }}>
                      SL {p.stop_loss?.toFixed(5) || '—'} · TP {p.take_profit?.toFixed(5) || '—'}
                    </div>
                  </div>
                  <div className={`pos-pnl ${p.unrealized_pnl >= 0 ? 'pos' : 'neg'}`}>{fmtSigned(p.unrealized_pnl)}</div>
                </div>
              ))}
            </div>
          </div>

          {/* Open pairs */}
          <div className="b-card span-5">
            <div className="b-h">
              <div>
                <h3 className="b-title">Open pair positions</h3>
                <div className="b-sub">{openPairs.length} active · max {live?.max_concurrent_pairs ?? 3} concurrent</div>
              </div>
            </div>
            {openPairs.length === 0 && (
              <div style={{ padding: '24px 8px', textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
                No open pair positions
              </div>
            )}
            {openPairs.map((p, i) => (
              <div key={i} style={{
                padding: 12, borderRadius: 10, background: 'var(--bg-3)', marginBottom: 8,
              }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{p.spread_name}</span>
                  <span style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'ui-monospace,monospace' }}>id {p.pair_id} · {p.bars_held} bars</span>
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, fontSize: 11.5 }}>
                  <div>
                    <span className={`pos-side ${p.direction_a === 'long' ? 'buy' : 'sell'}`}>{p.direction_a.toUpperCase()}</span>
                    {' '}{p.symbol_a} · {p.lot_size_a.toFixed(2)} lots
                  </div>
                  <div>
                    <span className={`pos-side ${p.direction_b === 'long' ? 'buy' : 'sell'}`}>{p.direction_b.toUpperCase()}</span>
                    {' '}{p.symbol_b} · {p.lot_size_b.toFixed(2)} lots
                  </div>
                </div>
                <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 6, fontFamily: 'ui-monospace,monospace' }}>
                  z entry {p.entry_z?.toFixed(2)} · stop {p.stop_z?.toFixed(2)} · exit {p.exit_z?.toFixed(2)} · β {p.hedge_ratio?.toFixed(3)}
                </div>
              </div>
            ))}
          </div>

          {/* Recent closed pairs */}
          <div className="b-card span-12">
            <div className="b-h">
              <div>
                <h3 className="b-title">Recent pair activity</h3>
                <div className="b-sub">Last 20 records · open / closed / partial-fill forensics</div>
              </div>
            </div>
            <table>
              <thead>
                <tr>
                  <th>When</th><th>Spread</th><th>Legs</th>
                  <th style={{ textAlign: 'right' }}>R</th>
                  <th style={{ textAlign: 'right' }}>P&amp;L</th>
                  <th>Reason</th>
                </tr>
              </thead>
              <tbody>
                {closedPairs.length === 0 && (
                  <tr><td colSpan="6" style={{ textAlign: 'center', color: 'var(--ink-3)', padding: 20 }}>
                    No pair activity yet
                  </td></tr>
                )}
                {closedPairs.map((p, i) => {
                  const opened = p.opened_at ? new Date(p.opened_at) : null;
                  const r = p.r_realized;
                  const pnl = p.pnl_usd;
                  const isForensic = (p.exit_reason || '').startsWith('leg_a_failed') ||
                                       (p.exit_reason || '').startsWith('partial_fill');
                  return (
                    <tr key={i}>
                      <td style={{ color: 'var(--ink-3)' }}>{opened ? opened.toISOString().slice(5, 16).replace('T', ' ') : '—'}</td>
                      <td><span className="pos-sym">{p.spread_name}</span></td>
                      <td style={{ fontSize: 11.5 }}>
                        <span className={`pos-side ${p.direction_a === 'long' ? 'buy' : 'sell'}`}>{(p.direction_a || '').toUpperCase()}</span> {p.symbol_a}
                        {' / '}
                        <span className={`pos-side ${p.direction_b === 'long' ? 'buy' : 'sell'}`}>{(p.direction_b || '').toUpperCase()}</span> {p.symbol_b}
                      </td>
                      <td style={{ textAlign: 'right' }} className={r > 0 ? 'pos' : r < 0 ? 'neg' : ''}>
                        {r != null ? (r >= 0 ? '+' : '') + r.toFixed(2) + 'R' : '—'}
                      </td>
                      <td style={{ textAlign: 'right' }} className={pnl > 0 ? 'pos' : pnl < 0 ? 'neg' : ''}>
                        <b>{pnl != null ? fmtSigned(pnl) : '—'}</b>
                      </td>
                      <td style={{
                        fontSize: 11, color: isForensic ? 'var(--neg)' : 'var(--ink-3)',
                        fontFamily: 'ui-monospace,monospace',
                      }}>{p.exit_reason || '—'}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </>
  );
}

// ───────────────────────────── strategy page ─────────────────────────────

const STRATEGY_DESC = {
  'BOS+FVG': '4H BOS + 15M FVG triple-confirmation reversal — only AUDJPY survived the 2026-05 audit.',
  'KL_S5':   'Koroush "S5" level breakout on 4H — halted 2026-05-26 after lookahead fix (all 3 pairs Tier 3/0).',
  'ARB':     'Asian range → London breakout with EMA50 trend filter. Halted 2026-05-26 after EMA lookahead fix.',
  'LNYR':    'London-NY reversal — fades the London move with z-thresholded entry. GBPUSD-only.',
  'PAIRS':   'Statsmodels-coint mean-reversion on cointegrated spreads. AUDUSD/NZDUSD, EURJPY/GBPJPY, EURUSD/USDCHF.',
};

function StrategyPage({ accent }) {
  const live = useApi('/api/live_state', 15000);
  const kpis = useApi('/api/strategies_live', 15000);
  const pairs = useApi('/api/pair_positions', 15000);
  const [report, setReport] = useState(null);

  if (!live) {
    return <div className="topbar"><h1>Strategy</h1></div>;
  }
  const strats = live.strategies || [];
  const kpiMap = new Map((kpis?.strategies || []).map(s => [s.key, s]));

  return (
    <>
      <div className="topbar">
        <div className="topbar-l">
          <h1>Strategy</h1>
          <div className="sub">Active playbooks · live state read from .env + signals.db</div>
        </div>
        <div className="topbar-r">
          <button className="btn" onClick={() => setReport('trust-report')}>Backtest report</button>
          <button className="btn" onClick={() => setReport('trade-explorer')} style={{ marginLeft: 8 }}>Trade explorer</button>
        </div>
      </div>
      <div className="content">
        {report && (
          <div className="bento">
            <div className="b-card span-12">
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                <strong>{report === 'trust-report' ? 'Backtest Trust Report' : 'Trade Explorer'} <span style={{ color: 'var(--ink-3)', fontWeight: 400 }}>· preview (illustrative data)</span></strong>
                <button className="btn" onClick={() => setReport(null)}>✕ Close</button>
              </div>
              <iframe title="report" src={`/static/strategy/${report}.html`} style={{ width: '100%', height: '82vh', border: 'none', borderRadius: 12, background: '#0c0e14' }} />
            </div>
          </div>
        )}
        <div className="bento">
          {strats.map(s => {
            const kpi = kpiMap.get(s.key) || { n_closed: 0, win_rate_pct: null, pnl_usd: 0, last_close: null };
            const desc = STRATEGY_DESC[s.key] || '';
            const lastClose = kpi.last_close ? new Date(kpi.last_close).toISOString().slice(0, 10) : '—';
            return (
              <div key={s.key} className="b-card span-6">
                <div className="b-h">
                  <div>
                    <h3 className="b-title">{s.label}</h3>
                    <div className="b-sub">{desc}</div>
                  </div>
                  <StatusPill status={s.status} />
                </div>

                {/* Per-strategy KPI row */}
                <div className="perf-grid">
                  <div className="perf-cell"><div className="perf-lbl">Closed trades</div><div className="perf-val">{kpi.n_closed}</div></div>
                  <div className="perf-cell"><div className="perf-lbl">Win rate</div><div className="perf-val">{kpi.win_rate_pct != null ? kpi.win_rate_pct.toFixed(1) + '%' : '—'}</div></div>
                  <div className="perf-cell"><div className="perf-lbl">Risk/trade</div><div className="perf-val">${s.risk_usd.toFixed(0)}</div></div>
                </div>

                {/* Symbol/spread list */}
                <div style={{ marginTop: 12, padding: '12px 14px', borderRadius: 10, background: 'var(--bg-3)' }}>
                  <div style={{ fontSize: 10.5, color: 'var(--ink-3)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Targets</div>
                  {s.symbols.length === 0 ? (
                    <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>— none configured —</div>
                  ) : (
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                      {s.symbols.map(sym => (
                        <span key={sym} style={{
                          fontFamily: 'ui-monospace,monospace', fontSize: 11.5, fontWeight: 600,
                          padding: '4px 10px', borderRadius: 6, background: 'var(--bg)', border: '1px solid var(--line)',
                        }}>{sym}</span>
                      ))}
                    </div>
                  )}
                </div>

                {/* Total P&L */}
                <div style={{
                  marginTop: 12, padding: '12px 14px', borderRadius: 10, background: 'var(--bg-3)',
                  display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                }}>
                  <div>
                    <div style={{ fontSize: 11.5, color: 'var(--ink-3)', fontWeight: 500 }}>Total P&amp;L (closed)</div>
                    <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>Last close · {lastClose}</div>
                  </div>
                  <span style={{
                    fontSize: 18, fontWeight: 700, fontFamily: 'ui-monospace,monospace',
                    color: kpi.pnl_usd >= 0 ? 'var(--pos)' : 'var(--neg)',
                  }}>
                    {fmtSigned(kpi.pnl_usd)}
                  </span>
                </div>
              </div>
            );
          })}

          {/* Pair sub-cards */}
          {(kpis?.strategies || []).filter(s => s.kind === 'pair').map(s => (
            <div key={s.key} className="b-card span-4">
              <div className="b-h">
                <div>
                  <h3 className="b-title">{s.label}</h3>
                  <div className="b-sub">Pair sub-stream</div>
                </div>
              </div>
              <div className="perf-grid">
                <div className="perf-cell"><div className="perf-lbl">Closed</div><div className="perf-val">{s.n_closed}</div></div>
                <div className="perf-cell"><div className="perf-lbl">WR</div><div className="perf-val">{s.win_rate_pct != null ? s.win_rate_pct.toFixed(0) + '%' : '—'}</div></div>
                <div className="perf-cell">
                  <div className="perf-lbl">P&amp;L</div>
                  <div className={`perf-val ${s.pnl_usd >= 0 ? 'pos' : 'neg'}`}>{fmtSigned(s.pnl_usd)}</div>
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

// ───────────────────────────── stub pages ─────────────────────────────

function ComingSoonPage({ title, sub, hint }) {
  return (
    <>
      <div className="topbar">
        <div className="topbar-l">
          <h1>{title}</h1>
          <div className="sub">{sub}</div>
        </div>
      </div>
      <div className="content">
        <div className="b-card" style={{ padding: 36, textAlign: 'center' }}>
          <div style={{ fontSize: 13, color: 'var(--ink-3)', fontWeight: 500, marginBottom: 8 }}>Coming soon</div>
          <div style={{ fontSize: 18, fontWeight: 600 }}>{title}</div>
          <div style={{ marginTop: 12, fontSize: 13, color: 'var(--ink-3)', maxWidth: 480, margin: '12px auto 0' }}>{hint}</div>
        </div>
      </div>
    </>
  );
}

function PositionsPage({ accent }) {
  const positions = useApi('/api/positions', 5000);
  const pairs     = useApi('/api/pair_positions', 5000);
  const open = positions || [];
  const openPairs = pairs?.open || [];
  return (
    <>
      <div className="topbar">
        <div className="topbar-l">
          <h1>Positions</h1>
          <div className="sub">{open.length} single-leg + {openPairs.length} pair</div>
        </div>
      </div>
      <div className="content">
        <div className="bento">
          <div className="b-card span-12">
            <div className="b-h"><h3 className="b-title">Single-leg ({open.length})</h3></div>
            <div className="pos-list">
              {open.length === 0 && <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)' }}>None open</div>}
              {open.map((p, i) => (
                <div key={i} className="pos-card">
                  <div className="pos-sym-side"><span className={`pos-side ${p.side === 'LONG' ? 'buy' : 'sell'}`}>{p.side === 'LONG' ? 'BUY' : 'SELL'}</span><span className="pos-sym">{p.symbol}</span></div>
                  <div>
                    <div className="pos-meta">{p.lots} lots · @ {p.entry_price?.toFixed(5)} · {p.duration_min}m</div>
                  </div>
                  <div className={`pos-pnl ${p.unrealized_pnl >= 0 ? 'pos' : 'neg'}`}>{fmtSigned(p.unrealized_pnl)}</div>
                </div>
              ))}
            </div>
          </div>
          <div className="b-card span-12">
            <div className="b-h"><h3 className="b-title">Pairs ({openPairs.length})</h3></div>
            <div className="pos-list">
              {openPairs.length === 0 && <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)' }}>None open</div>}
              {openPairs.map((p, i) => (
                <div key={i} className="pos-card">
                  <div className="pos-sym-side"><span className="pos-sym">{p.spread_name}</span></div>
                  <div>
                    <div className="pos-meta">
                      <span className={`pos-side ${p.direction_a === 'long' ? 'buy' : 'sell'}`}>{p.direction_a === 'long' ? 'BUY' : 'SELL'}</span> {p.symbol_a} {p.lot_size_a} · <span className={`pos-side ${p.direction_b === 'long' ? 'buy' : 'sell'}`}>{p.direction_b === 'long' ? 'BUY' : 'SELL'}</span> {p.symbol_b} {p.lot_size_b}
                    </div>
                    <div className="pos-meta">entry z {p.entry_z?.toFixed(2)} · {p.bars_held} bars · #{p.pair_id}</div>
                  </div>
                  <div className="pos-pnl" style={{ color: 'var(--ink-3)' }}>spread</div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </>
  );
}

function JournalPage() {
  const recent = useApi('/api/signals/history', 30000);
  const rows = recent || [];
  return (
    <>
      <div className="topbar">
        <div className="topbar-l">
          <h1>Journal</h1>
          <div className="sub">Recent signal records · {rows.length} entries</div>
        </div>
      </div>
      <div className="content">
        <div className="bento">
          <div className="b-card span-12">
            <table>
              <thead>
                <tr>
                  <th>Time</th><th>Strategy</th><th>Symbol</th><th>Side</th>
                  <th style={{ textAlign: 'right' }}>Entry</th>
                  <th>Decision</th>
                  <th style={{ textAlign: 'right' }}>P&amp;L</th>
                  <th>Close reason</th>
                </tr>
              </thead>
              <tbody>
                {rows.length === 0 && <tr><td colSpan="8" style={{ textAlign: 'center', color: 'var(--ink-3)', padding: 20 }}>No signal records yet</td></tr>}
                {rows.map((r, i) => (
                  <tr key={i}>
                    <td style={{ color: 'var(--ink-3)' }}>{r.signal_time ? r.signal_time.slice(5, 16).replace('T', ' ') : ''}</td>
                    <td>{r.strategy}</td>
                    <td><span className="pos-sym">{r.symbol}</span></td>
                    <td><span className={`pos-side ${r.side === 'LONG' ? 'buy' : 'sell'}`}>{r.side}</span></td>
                    <td style={{ textAlign: 'right' }}>{r.actual_entry_price?.toFixed(5) || r.entry_price?.toFixed(5)}</td>
                    <td style={{ color: 'var(--ink-3)' }}>{r.hitl_decision || '—'}</td>
                    <td style={{ textAlign: 'right' }} className={r.pnl_usd > 0 ? 'pos' : r.pnl_usd < 0 ? 'neg' : ''}>{r.pnl_usd != null ? fmtSigned(r.pnl_usd) : '—'}</td>
                    <td style={{ color: 'var(--ink-3)', fontSize: 11 }}>{r.close_reason || '—'}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </>
  );
}

// ───────────────────────────── app shell ─────────────────────────────

const ACCENT = '#3b82f6';

function _initialTab() {
  // Derive initial tab from URL path so /strategy, /journal, /positions work.
  const path = (typeof window !== 'undefined' ? window.location.pathname : '/').replace(/\/$/, '');
  if (path === '/portfolio') return 'portfolio';
  if (path === '/strategy')  return 'strategy';
  if (path === '/journal')   return 'journal';
  if (path === '/positions') return 'positions';
  if (path === '/tfsa')      return 'tfsa';
  if (path === '/margin')    return 'margin';
  return 'dashboard';
}

function App() {
  const [active, setActive] = useState(_initialTab());
  const [collapsed, setCollapsed] = useState(false);
  const [theme, setTheme] = useState('light');
  const [mobNavOpen, setMobNavOpen] = useState(false);

  // Keep the URL in sync with tab changes (no full reload).
  useEffect(() => {
    const path = active === 'dashboard' ? '/' : '/' + active;
    if (window.location.pathname !== path) {
      window.history.replaceState({}, '', path);
    }
  }, [active]);

  // Close the mobile drawer whenever the active tab changes.
  useEffect(() => { setMobNavOpen(false); }, [active]);

  let Page;
  switch (active) {
    case 'portfolio': Page = <PortfolioPage accent={ACCENT} />; break;
    case 'strategy':  Page = <StrategyPage  accent={ACCENT} />; break;
    case 'journal':   Page = <JournalPage   accent={ACCENT} />; break;
    case 'positions': Page = <PositionsPage accent={ACCENT} />; break;
    case 'tfsa':      Page = <ComingSoonPage title="IBKR · TFSA"
                              sub="Personal account · not yet connected"
                              hint="Add IBKR account credentials to .env (IBKR_TFSA_USER, IBKR_TFSA_TOKEN) and restart. Fred will populate when ready." />; break;
    case 'margin':    Page = <ComingSoonPage title="IBKR · Margin"
                              sub="Personal margin account · placeholder"
                              hint="Same wiring as TFSA. IBKR Web API integration via ibapi or ib_insync — design lives in this slot when configured." />; break;
    default:          Page = <DashboardPage accent={ACCENT} />;
  }

  return (
    <div className="v2" data-mode={theme} data-collapsed={collapsed}
         data-mobnav={mobNavOpen ? 'open' : 'closed'}
         style={{ '--acc': ACCENT, width: '100%', display: 'flex', minHeight: '100dvh' }}>
      <button className="mobnav-toggle" aria-label="Open navigation menu"
              aria-expanded={mobNavOpen} onClick={() => setMobNavOpen(true)}>
        <Icon.Menu s={20} />
      </button>
      <div className="mob-scrim" onClick={() => setMobNavOpen(false)} aria-hidden="true" />
      <Sidebar collapsed={collapsed} setCollapsed={setCollapsed}
               active={active} setActive={setActive}
               closeMobNav={() => setMobNavOpen(false)} />
      <div className="main">
        <div className="main-card" style={{ position: 'relative' }}>
          <div className="theme-toggle-pos">
            <ThemeToggle mode={theme} onChange={setTheme} />
          </div>
          <div className="page-wrap">{Page}</div>
        </div>
      </div>
    </div>
  );
}

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

// ───────────────────────────── portfolio page ─────────────────────────────

function ParamGrid({ params }) {
  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 10 }}>
      {Object.entries(params || {}).map(([k, v]) => (
        <span key={k} style={{
          fontFamily: "ui-monospace,monospace", fontSize: 11,
          padding: "4px 9px", borderRadius: 6, background: "var(--bg)",
          border: "1px solid var(--line)",
        }}>
          <span style={{ color: "var(--ink-3)" }}>{k.replace(/_/g, " ")}</span>{" "}
          <b>{String(v)}</b>
        </span>
      ))}
    </div>
  );
}

function ConfigRow({ label, value, sub }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline",
                  padding: "9px 0", borderBottom: "1px solid var(--line)" }}>
      <span style={{ fontSize: 12.5, color: "var(--ink-2)" }}>{label}</span>
      <span style={{ textAlign: "right" }}>
        <span style={{ fontSize: 13, fontWeight: 600, fontFamily: "ui-monospace,monospace" }}>{value}</span>
        {sub && <div style={{ fontSize: 10.5, color: "var(--ink-3)", fontWeight: 400 }}>{sub}</div>}
      </span>
    </div>
  );
}

function SectionLabel({ text }) {
  return (
    <div className="b-card span-12" style={{ background: "transparent", boxShadow: "none", padding: "6px 2px 0" }}>
      <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.06em",
                    textTransform: "uppercase", color: "var(--ink-3)" }}>{text}</div>
    </div>
  );
}

function PortfolioPage({ accent }) {
  const portfolio = useApi("/api/portfolio", 15000);
  const ftmo      = useApi("/api/ftmo", 5000);

  if (!portfolio) {
    return <div className="topbar"><div className="topbar-l"><h1>Portfolio</h1></div></div>;
  }
  const cfg = portfolio.config || {};
  const m = portfolio.metrics || {};
  const insts = portfolio.instruments || [];
  const pairs = insts.filter(i => i.kind === "pair");
  const single = insts.filter(i => i.kind === "single_leg");
  const bands = cfg.dd_bands || [];
  const gm = cfg.kl_grade_multipliers || {};

  const ddPct = ftmo?.total_dd_used_pct ?? 0;
  const curBand = bands.find(b => ddPct >= b.lo_pct && (b.hi_pct == null || ddPct < b.hi_pct));
  const curMult = curBand ? (curBand.mult == null ? "HALT" : curBand.mult.toFixed(2) + "×") : "1.00×";
  const dailyHead = (ftmo?.daily_loss_limit_usd ?? 0) - (ftmo?.daily_loss_used_usd ?? 0);
  const ddHead = (ftmo?.total_dd_limit_usd ?? 0) - (ftmo?.total_dd_used_usd ?? 0);

  const metric = (lbl, val, sub) => (
    <div className="perf-cell">
      <div className="perf-lbl">{lbl}</div>
      <div className="perf-val">{val}</div>
      {sub && <div style={{ fontSize: 10.5, color: "var(--ink-3)" }}>{sub}</div>}
    </div>
  );

  return (
    <>
      <div className="topbar">
        <div className="topbar-l">
          <h1>Portfolio</h1>
          <div className="sub">Live parameters &amp; risk config · read from .env + portfolio_guard</div>
        </div>
      </div>
      <div className="content">
        <div className="bento">

          <div className="b-card span-7">
            <div className="b-h"><div><h3 className="b-title">Live exposure</h3>
              <div className="b-sub">Concurrency, drawdown band &amp; headroom — live</div></div></div>
            <div className="perf-grid" style={{ gridTemplateColumns: "repeat(3, 1fr)" }}>
              {metric("Open pairs", (m.open_pairs ?? 0) + " / " + (m.max_pairs ?? 3))}
              {metric("Open single-leg", (m.open_single_leg ?? 0) + " / " + (m.max_single_leg ?? 5))}
              {metric("DD-band size", curMult, "at " + ddPct.toFixed(2) + "% DD")}
              {metric("Daily-loss buffer", fmt$0(dailyHead))}
              {metric("Total-DD buffer", fmt$0(ddHead))}
              {metric("HITL", cfg.hitl_enabled ? "ON" : "OFF (auto)")}
            </div>
          </div>

          <div className="b-card span-5">
            <div className="b-h"><div><h3 className="b-title">Risk configuration</h3>
              <div className="b-sub">PortfolioRiskGuard + FTMO limits</div></div></div>
            <div>
              <ConfigRow label="Max concurrent pairs" value={cfg.max_concurrent_pairs} />
              <ConfigRow label="Max concurrent positions" value={cfg.max_concurrent_positions}
                         sub={cfg.grade_a_reserved_slots + " reserved for Grade A"} />
              <ConfigRow label="JPY correlation penalty" value={cfg.jpy_penalty?.multiplier + "×"}
                         sub={"≥ " + cfg.jpy_penalty?.threshold + " same-dir JPY"} />
              <ConfigRow label="KL grade multipliers" value={"A " + gm.A + " · B " + gm.B + " · C " + gm.C} />
              <ConfigRow label="FTMO daily / total limit"
                         value={cfg.ftmo?.daily_loss_pct + "% / " + cfg.ftmo?.total_dd_pct + "%"}
                         sub={"on " + fmt$0(cfg.ftmo?.initial_balance)} />
            </div>
          </div>

          <div className="b-card span-12">
            <div className="b-h"><div><h3 className="b-title">Drawdown sizing ladder</h3>
              <div className="b-sub">Risk multiplier by total-drawdown band · 8% = hard halt</div></div></div>
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
              {bands.map((b, i) => {
                const active = curBand === b;
                const halt = b.mult == null;
                return (
                  <div key={i} style={{
                    flex: "1 1 140px", padding: "12px 14px", borderRadius: 10,
                    background: active ? "rgba(59,130,246,0.10)" : "var(--bg-3)",
                    border: active ? ("1px solid " + accent) : "1px solid transparent",
                  }}>
                    <div style={{ fontSize: 11, color: "var(--ink-3)" }}>
                      {b.lo_pct}%{b.hi_pct == null ? "+" : "–" + b.hi_pct + "%"} DD
                    </div>
                    <div style={{ fontSize: 18, fontWeight: 700, fontFamily: "ui-monospace,monospace",
                                  color: halt ? "var(--neg)" : "var(--ink)" }}>
                      {halt ? "HALT" : b.mult.toFixed(2) + "×"}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

          {pairs.length > 0 && <SectionLabel text={"Pair spreads · " + pairs.length} />}
          {pairs.map(p => (
            <div key={p.name} className="b-card span-4">
              <div className="b-h"><div><h3 className="b-title" style={{ fontSize: 15 }}>{p.name}</h3>
                <div className="b-sub">{p.strategy} · {p.tier || "—"} · ${p.risk_usd.toFixed(0)}/trade</div></div>
                <StatusPill status={p.status} /></div>
              <ParamGrid params={p.params} />
            </div>
          ))}

          {single.length > 0 && <SectionLabel text={"Single-leg · " + single.length} />}
          {single.map(s => (
            <div key={s.strategy + s.name} className="b-card span-4">
              <div className="b-h"><div><h3 className="b-title" style={{ fontSize: 15 }}>{s.name}</h3>
                <div className="b-sub">{s.strategy} · ${s.risk_usd.toFixed(0)}/trade</div></div>
                <StatusPill status={s.status} /></div>
              <ParamGrid params={s.params} />
            </div>
          ))}

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