const { useState, useEffect, useRef, useMemo, useCallback } = React;
/* Snipe.Run — views part 1: Overview, Copy Traders, Trader Detail */

/* ============ REAL COPY CONFIG ============
 * On the live platform the user's copy list lives in the worker config
 * (GET/POST config → { traders:[wallets], ... }). This hook owns that list of
 * lowercased wallets and a toggle() that reads-modifies-writes the whole config
 * (push/pull the wallet, preserving every other field). No-ops on the demo. */
function _normWallet(w) { return (w || '').toString().trim().toLowerCase(); }
// Compact duration ("14d 06h" / "3h 12m" / "8m") for engine uptime.
function sniFmtDur(sec) {
  sec = Math.max(0, Math.floor(sec || 0));
  const d = Math.floor(sec / 86400), h = Math.floor((sec % 86400) / 3600), m = Math.floor((sec % 3600) / 60);
  if (d > 0) return d + 'd ' + String(h).padStart(2, '0') + 'h';
  if (h > 0) return h + 'h ' + String(m).padStart(2, '0') + 'm';
  return m + 'm';
}
// Real fill rate from engine stats (filled / submitted); '—' when not tracked.
function _fillRate(stats) {
  const f = +stats.fills, o = +stats.orders;
  if (o > 0 && !isNaN(f)) return (f / o * 100).toFixed(1) + '%';
  return '—';
}
function useCopyConfig() {
  const [wallets, setWallets] = useState([]);
  const [loading, setLoading] = useState(sniIsReal());
  const reload = useCallback(() => {
    if (!sniIsReal()) { setWallets([]); setLoading(false); return; }
    setLoading(true);
    sniApi('config')
      .then((d) => { setWallets(((d && d.traders) || []).map(_normWallet)); setLoading(false); })
      .catch(() => { setLoading(false); });
  }, []);
  useEffect(() => { reload(); }, [reload]);
  const isCopied = useCallback((w) => wallets.includes(_normWallet(w)), [wallets]);
  const toggle = useCallback((w, on) => {
    if (!sniIsReal() || !w) return Promise.resolve();
    const target = _normWallet(w);
    return sniApi('config').then((cfg) => {
      const cur = ((cfg && cfg.traders) || []).map(_normWallet);
      const has = cur.includes(target);
      const want = (on === undefined) ? !has : !!on;
      let next = cur;
      if (want && !has) next = [...cur, target];
      else if (!want && has) next = cur.filter((x) => x !== target);
      else { setWallets(cur); return; }
      const body = Object.assign({}, cfg || {}, { traders: next });
      return sniApi('config', { method: 'POST', body: JSON.stringify(body) })
        .then(() => setWallets(next))
        .catch(() => setWallets(cur));
    });
  }, []);
  return { wallets, loading, isCopied, toggle, reload };
}

/* ============ OVERVIEW ============ */
function Overview({ ctx }) {
  const { bot, setBot, balance, copied, positions, onNav, paper } = ctx;
  // Real engine telemetry (the user's own isolated worker) overlays the design's demo data.
  const live = useLiveData();
  const real = sniIsReal();
  const livePos = sniMapPositions(live);
  const showPos = real ? livePos : positions;
  const stats = sniStats(live);
  const w = sniWorker(live);
  const realizedReal = +stats.realized_pnl || 0;
  const unrealReal = livePos.reduce((a, p) => a + (+p.pnl || 0), 0);
  const winsR = +stats.wins || 0, lossR = +stats.losses || 0;
  const winRateReal = (winsR + lossR) ? (winsR / (winsR + lossR) * 100) : 0;
  // Paper bankroll = the running worker's bankroll, else the user's plan capital
  // (so a paused paper engine still shows e.g. Free $10k, not $0).
  const planCap = (live && live.plan && live.plan.paper_capital != null) ? +live.plan.paper_capital : 0;
  const bankrollReal = (w && typeof w.bankroll === 'number') ? w.bankroll : (paper ? planCap : 0);
  const portfolioReal = bankrollReal + realizedReal + unrealReal;
  const [tf, setTf] = useState('1W');
  // Real equity line = cumulative realized PnL from actual closed trades (flat/empty
  // when none). Never the demo uptrend for a logged-in user.
  const series = useMemo(() => {
    if (!real) return genSeries('overview' + tf, 64, true);
    const cl = (live && live.closes) ? live.closes.slice().sort((a, b) => (+a.closed_ts || 0) - (+b.closed_ts || 0)) : [];
    if (!cl.length) return null;
    let cum = 0; const out = [0];
    cl.forEach((c) => { cum += +c.realized || 0; out.push(cum); });
    return out;
  }, [real, live, tf]);
  const totalPnl = showPos.reduce((a, p) => a + p.pnl, 0);
  const todayPnl = 18420.55;

  return (
    <div className="content-inner" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <AISuggestBanner ctx={ctx} />
      <BotControl bot={bot} setBot={setBot} paper={paper} />

      {/* KPIs */}
      <div className="grid g-4">
        <KPI k="Portfolio value" icon="wallets">
          {real
            ? <span className="mono">{money(portfolioReal)}</span>
            : <span className="mono"><LivePnl start={balance} interval={1800} /></span>}
          {real
            ? <KPISub><span className="tiny mono">bankroll {moneyShort(bankrollReal)}</span></KPISub>
            : <KPISub kind="up"><Chip kind="up">▲ 8.2%</Chip> 7d</KPISub>}
        </KPI>
        <KPI k="Today's PnL" icon="pulse">
          {real
            ? <span className={'mono ' + ((realizedReal + unrealReal) >= 0 ? 'up' : 'down')}>{(realizedReal + unrealReal) >= 0 ? '+' : ''}{money(realizedReal + unrealReal)}</span>
            : <span className="mono up"><LivePnl start={todayPnl} interval={1400} /></span>}
          {real
            ? <KPISub><span className="tiny">{money(realizedReal)} realized · {money(unrealReal)} open</span></KPISub>
            : <KPISub kind="up"><Chip kind="up">▲ 12.8%</Chip> realized + open</KPISub>}
        </KPI>
        <KPI k="Win rate (30d)" icon="trophy">
          <span>{real ? ((winsR + lossR) ? winRateReal.toFixed(1) + '%' : '—') : '73.4%'}</span>
          <KPISub><div className="winbar" style={{ width: 120, marginTop: 2 }}><i style={{ width: (real ? winRateReal : 73) + '%' }} /></div></KPISub>
        </KPI>
        <KPI k="Active copies" icon="copy">
          <span className="mono">{real ? showPos.length : (copied.length || 4)}<span className="tiny" style={{ marginLeft: 6 }}>{real ? 'positions' : 'traders'}</span></span>
          <KPISub kind="up"><span className="live-dot" /> {real ? (sniRunning(live) ? 'engine live' : 'engine paused') : 'mirroring live'}</KPISub>
        </KPI>
      </div>

      {/* chart + side */}
      <div className="grid g-split">
        <div className="card card-pad">
          <div className="between" style={{ marginBottom: 14 }}>
            <div>
              <div className="tiny mono" style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}>Copied portfolio · realized PnL</div>
              <div style={{ fontFamily: 'var(--display)', fontWeight: 700, fontSize: 35, marginTop: 6 }} className={(real ? (realizedReal >= 0 ? 'up' : 'down') : 'up') + ' mono'}>
                {real
                  ? ((realizedReal >= 0 ? '+' : '') + money(realizedReal))
                  : <LivePnl start={248137.42} interval={1500} />}
              </div>
            </div>
            <div className="seg">
              {['1D', '1W', '1M', 'ALL'].map((t) => (
                <button key={t} className={tf === t ? 'on' : ''} onClick={() => setTf(t)}>{t}</button>
              ))}
            </div>
          </div>
          {series
            ? <Sparkline data={series} h={210} />
            : <div className="empty" style={{ height: 210, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>No realized P/L yet — your closed trades will chart here.</div>}
        </div>

        <div className="card card-pad" style={{ display: 'flex', flexDirection: 'column' }}>
          <div className="between" style={{ marginBottom: 6 }}>
            <div className="sec-title">Live activity</div>
            <button className="b b-ghost b-sm" onClick={() => onNav('activity')}>View all <Icon name="chevron" size={13} /></button>
          </div>
          <ActivityFeed live={live} real={real} />
        </div>
      </div>

      {/* open positions */}
      <div className="card">
        <div className="between card-pad" style={{ paddingBottom: 12 }}>
          <div className="sec-title">Open positions <span className="tiny" style={{ marginLeft: 6 }}>{showPos.length}</span></div>
          <button className="b b-ghost b-sm" onClick={() => onNav('positions')}>View all <Icon name="chevron" size={14} /></button>
        </div>
        {showPos.length
          ? <PositionsTable rows={showPos.slice(0, 4)} />
          : <div className="empty">No open positions yet — your engine fills this as it copies trades.</div>}
      </div>
    </div>
  );
}

function KPI({ k, icon, children }) {
  const arr = React.Children.toArray(children);
  return (
    <div className="card kpi">
      <div className="kpi-k"><Icon name={icon} size={14} /> {k}</div>
      <div className="kpi-v">{arr[0]}</div>
      {arr[1]}
    </div>
  );
}
function KPISub({ kind, children }) { return <div className="kpi-sub" style={{ color: kind === 'up' ? 'var(--green)' : 'var(--tx-3)' }}>{children}</div>; }

function BotControl({ bot, setBot, paper }) {
  const live = useLiveData();
  const real = sniIsReal();
  const stats = sniStats(live);
  const trades = +stats.trades || 0;
  const running = sniRunning(live);
  // Real uptime from the worker's start time (epoch secs), else '—'. Demo keeps its mock.
  const w = sniWorker(live);
  const startTs = w && (+w.started_ts || +w.start_ts || 0);
  const upStr = (real && running && startTs > 0) ? sniFmtDur(Date.now() / 1000 - startTs) : '—';
  return (
    <div className="bot-control" style={paper ? { background: 'linear-gradient(120deg, rgba(255,194,75,0.07), var(--panel))', borderColor: 'rgba(255,194,75,0.28)' } : null}>
      <div className={`bot-orb ${bot ? 'on' : 'off'}`}>
        <Icon name={bot ? 'bolt' : 'pause'} size={26} style={{ color: bot ? 'var(--green)' : 'var(--tx-3)' }} />
      </div>
      <div style={{ flex: 1 }}>
        <div className="between">
          <div>
            <div style={{ fontFamily: 'var(--display)', fontWeight: 700, fontSize: 21 }}>
              Snipe Engine {bot ? <span className="up">· Running</span> : <span style={{ color: 'var(--tx-3)' }}>· Paused</span>}
              {paper && <span className="chip chip-amber" style={{ display: 'inline-flex', alignItems: 'center', gap: 5, marginLeft: 10, verticalAlign: '2px' }}><Icon name="flask" size={11} /> PAPER</span>}
            </div>
            <div className="tiny" style={{ marginTop: 4 }}>
              {paper
                ? 'Paper mode · simulated fills against live prices — no real funds at risk'
                : (bot
                  ? (real ? 'Live mode · mirroring your copied traders on-chain · priority lane' : 'Mirroring 4 traders across 2 wallets · priority lane')
                  : 'Bot is paused — no trades will be copied')}
            </div>
          </div>
          <button className={`b ${bot ? 'b-red' : 'b-green'} b-lg`} onClick={() => setBot(!bot)}>
            <Icon name={bot ? 'pause' : 'play'} size={16} /> {bot ? 'Stop bot' : 'Start bot'}
          </button>
        </div>
        <div className="row" style={{ marginTop: 14, gap: 24 }}>
          <MiniMetric k="Uptime" v={real ? upStr : '14d 06h'} />
          <MiniMetric k="Latency" v={<LiveMs />} accent />
          <MiniMetric k="Copies today" v={real ? trades : <LiveCount start={312} />} />
          <MiniMetric k="Fill rate" v={real ? (running ? _fillRate(stats) : '—') : '99.2%'} accent />
          <MiniMetric k="Gas spent (24h)" v={real ? (paper ? '$0.00' : '—') : '$42.18'} />
        </div>
      </div>
    </div>
  );
}
function MiniMetric({ k, v, accent }) {
  return (<div><div className="tiny mono" style={{ textTransform: 'uppercase', letterSpacing: '0.04em' }}>{k}</div>
    <div className="mono" style={{ fontWeight: 700, fontSize: 17, marginTop: 3, color: accent ? 'var(--green)' : 'var(--tx)' }}>{v}</div></div>);
}
function LiveMs() {
  const live = useLiveData();
  const [v, setV] = useState(9);
  useInterval(() => setV((Math.random() * 6 + 7)), 1100);
  const real = sniLatency(live);
  if (sniIsReal()) {
    // Real users: latency is the engine's measured round-trip. A paused engine has
    // none — show '—', never an animated fake.
    return (sniRunning(live) && real > 0) ? <>{real}ms</> : <>—</>;
  }
  return <>{v.toFixed(0)}ms</>;
}
function LiveCount({ start }) { const [v, setV] = useState(start); useInterval(() => setV((p) => p + (Math.random() > 0.5 ? 1 : 0)), 1600); return <>{v}</>; }

function ActivityFeed({ live, real }) {
  const [rows, setRows] = useState(() => Array.from({ length: 6 }, () => makeFeed()));
  useInterval(() => { if (!real) setRows((r) => [makeFeed(), ...r].slice(0, 7)); }, 2300);
  if (real) {
    const items = sniMapActivity(live).slice(0, 7);
    if (!items.length) return <div className="tiny" style={{ marginTop: 14, color: 'var(--tx-3)' }}>Waiting for the first copy… your engine is watching the tape.</div>;
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 9, marginTop: 12, overflow: 'hidden' }}>
        {items.map((r, i) => (
          <div key={r.id} className="feed-row" style={{ display: 'flex', alignItems: 'center', gap: 9, fontFamily: 'var(--mono)', fontSize: 13.5, opacity: 1 - i * 0.04 }}>
            <span className={`chip ${r.type === 'sl' ? 'chip-down' : 'chip-up'}`} style={{ fontSize: 10.5 }}>{r.type === 'fill' ? 'BUY' : (r.type === 'sl' ? 'LOSS' : 'WIN')}</span>
            <span style={{ color: 'var(--tx)', fontWeight: 600 }}>{r.trader || 'copy'}</span>
            <span style={{ color: 'var(--tx-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{r.market}</span>
            <span className="up" style={{ marginLeft: 'auto' }}>{r.t}</span>
          </div>
        ))}
      </div>
    );
  }
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 9, marginTop: 12, overflow: 'hidden' }}>
      {rows.map((r, i) => (
        <div key={r.id} className="feed-row" style={{ display: 'flex', alignItems: 'center', gap: 9, fontFamily: 'var(--mono)', fontSize: 13.5, opacity: 1 - i * 0.04 }}>
          <span className={`chip ${r.buy ? 'chip-up' : 'chip-down'}`} style={{ fontSize: 10.5 }}>{r.buy ? 'BUY' : 'SELL'}</span>
          <span style={{ color: 'var(--tx)', fontWeight: 600 }}>{r.t}</span>
          <span style={{ color: 'var(--tx-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{r.m}</span>
          <span className="up" style={{ marginLeft: 'auto' }}>{r.ms}ms</span>
        </div>
      ))}
    </div>
  );
}
let _fid = 0;
function makeFeed() {
  const t = TRADERS[Math.floor(Math.random() * TRADERS.length)].name;
  const m = MARKETS[Math.floor(Math.random() * MARKETS.length)].q;
  return { id: _fid++, buy: Math.random() > 0.42, t, m: m.length > 26 ? m.slice(0, 26) + '…' : m, ms: (Math.random() * 50 + 7).toFixed(0) };
}

function PositionsTable({ rows, onClose }) {
  const [sell, setSell] = useState(null);
  return (
    <>
    <table className="tbl">
      <thead><tr>
        <th>Market</th><th>Side</th><th>Copied from</th><th className="th-num">Entry</th><th className="th-num">Now</th><th className="th-num">Size</th><th className="th-num">PnL</th>{onClose && <th></th>}
      </tr></thead>
      <tbody>
        {rows.map((p, i) => (
          <tr key={i}>
            <td style={{ fontWeight: 600, maxWidth: 220, verticalAlign: 'middle' }}>{p.mkt}</td>
            <td style={{ verticalAlign: 'middle' }}><span className={`chip ${p.side === 'YES' ? 'chip-up' : 'chip-down'}`}>{p.side}</span></td>
            <td style={{ verticalAlign: 'middle' }}><div style={{ display: 'flex', alignItems: 'center', gap: 8, lineHeight: 1 }}><Avatar name={p.via} size={24} /><span className="tiny" style={{ color: 'var(--tx-2)' }}>{p.via}</span></div></td>
            <td className="td-num muted" style={{ verticalAlign: 'middle' }}>{(p.entry * 100).toFixed(0)}¢</td>
            <td className="td-num" style={{ verticalAlign: 'middle' }}>{(p.cur * 100).toFixed(0)}¢</td>
            <td className="td-num" style={{ verticalAlign: 'middle' }}>{moneyShort(p.size)}</td>
            <td className="td-num" style={{ color: p.pnl >= 0 ? 'var(--green)' : 'var(--red)', fontWeight: 700, verticalAlign: 'middle' }}>{p.pnl >= 0 ? '+' : ''}{moneyShort(p.pnl)}<div className="tiny" style={{ color: p.pnl >= 0 ? 'var(--green)' : 'var(--red)' }}>{pct(p.pnlPct)}</div></td>
            {onClose && <td style={{ verticalAlign: 'middle' }}><button className="b b-red b-sm" onClick={() => setSell({ p, i })}><Icon name="upload" size={13} /> Sell</button></td>}
          </tr>
        ))}
      </tbody>
    </table>
    {sell && <SellPositionModal item={sell.p} onClose={() => setSell(null)} onConfirm={() => { onClose(sell.i); setSell(null); }} />}
    </>
  );
}

function SellPositionModal({ item: p, onClose, onConfirm }) {
  const shares = p.size / p.entry;
  const value = shares * p.cur;
  const fee = value * 0.001;
  const proceeds = value - fee;
  const [done, setDone] = useState(false);
  return (
    <Modal title="Sell position" onClose={onClose} width={460}>
      {done ? (
        <div style={{ textAlign: 'center', padding: '18px 0' }}>
          <div style={{ width: 54, height: 54, borderRadius: '50%', background: 'var(--green)', display: 'grid', placeItems: 'center', margin: '0 auto 14px' }}><Icon name="check" size={26} style={{ color: '#04140a' }} /></div>
          <div style={{ fontFamily: 'var(--display)', fontSize: 20, fontWeight: 700 }}>Sold · {money(proceeds, 0)} returned</div>
          <div className="mono tiny" style={{ marginTop: 6 }}>{p.mkt} · 7ms</div>
          <button className="b b-green b-block b-lg" style={{ marginTop: 18 }} onClick={onConfirm}>Done</button>
        </div>
      ) : (<>
        <div style={{ padding: 14, background: 'var(--bg-1)', border: '1px solid var(--line)', borderRadius: 12, marginBottom: 16 }}>
          <div style={{ fontWeight: 700 }}>{p.mkt}</div>
          <div className="between" style={{ marginTop: 8 }}><span className={`chip ${p.side === 'YES' ? 'chip-up' : 'chip-down'}`}>{p.side} · {(p.cur * 100).toFixed(0)}¢</span><span className="tiny mono">via {p.via}</span></div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: '2px 0 14px', borderBottom: '1px solid var(--line)', marginBottom: 14 }}>
          <Row k="Shares held" v={shares.toFixed(0)} />
          <Row k="Entry → Now" v={`${(p.entry * 100).toFixed(0)}¢ → ${(p.cur * 100).toFixed(0)}¢`} />
          <Row k="Cost basis" v={money(p.size, 0)} />
          <Row k="Current value" v={money(value, 0)} accent />
          <Row k="Unrealized P&L" v={`${p.pnl >= 0 ? '+' : ''}${money(p.pnl, 0)} · ${pct(p.pnlPct)}`} />
          <Row k="Sell fee (0.1%)" v={money(fee, 2)} />
          <Row k="You receive" v={money(proceeds, 0)} accent />
        </div>
        <button className="b b-green b-block b-lg" onClick={() => setDone(true)}><Icon name="upload" size={16} /> Sell now · {money(proceeds, 0)}</button>
        <div className="tiny" style={{ textAlign: 'center', marginTop: 10 }}>Market sell at the current price — settles instantly to your wallet.</div>
      </>)}
    </Modal>
  );
}

/* ============ COPY TRADERS / LEADERBOARD ============ */
// Render a numeric metric, '—' when the real feed reports it null/undefined.
function _orDash(v, render) { return (v === null || v === undefined) ? '—' : render(v); }
// Map a real wallet kind ('external'|'snipe') onto the design's filter values.
function _kindOf(t) { return t.kind === 'snipe' ? 'snipe' : (t.kind === 'external' ? 'wallet' : (t.kind || 'wallet')); }

function CopyTraders({ ctx }) {
  const { onOpenTrader, copied, onUncopy } = ctx;
  const real = sniIsReal();
  const [sort, setSort] = useState(real ? 'pnl' : 'roi');
  const [cat, setCat] = useState('All');
  const [kind, setKind] = useState('All');
  const cats = ['All', 'Politics', 'Crypto', 'Sports', 'Macro'];

  // Real leaderboard from the platform; demo list otherwise (login backdrop).
  const { traders: realTraders } = useTraders('30d', 25);
  const cfg = useCopyConfig();
  const src = real ? realTraders : TRADERS;
  const copyCount = real ? cfg.wallets.length : (copied.length || 4);
  // Copied-state predicate: config wallets when real, the demo id-list otherwise.
  const isCopiedT = useCallback((t) => real ? cfg.isCopied(t.wallet) : copied.includes(t.id), [real, cfg, copied]);

  const profiles = useMemo(() => src.filter((t) => _kindOf(t) === 'snipe'), [src]);
  const list = useMemo(() => {
    let l = src.filter((t) => kind === 'All' || _kindOf(t) === kind);
    if (cat !== 'All') l = l.filter((t) => (t.tags || []).includes(cat));
    // Sort key per source: real exposes profit/volume always; roi/copiers only
    // when the feed provides them. Demo keeps its original roi/pnl/copiers keys.
    const keyFor = (k) => {
      if (!real) return k;                       // demo: roi|pnl|copiers
      if (k === 'volume') return 'volume';       // real: profit + volume are the live columns
      if (k === 'pnl') return 'profit';
      if (k === 'copiers') return 'copiers';
      if (k === 'roi') return 'roi';
      return 'profit';
    };
    const k = keyFor(sort);
    l = l.slice().sort((a, b) => {
      let av = a[k], bv = b[k];
      // ROI/copiers may be null on the real feed → push to bottom, keep order.
      if (av === null || av === undefined) av = -Infinity;
      if (bv === null || bv === undefined) bv = -Infinity;
      return bv - av;
    });
    return l;
  }, [src, sort, cat, kind, real]);

  return (
    <div className="content-inner" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div className="grid g-3">
        <StatMini k="Snipe profiles to follow" v={`${profiles.length}`} icon="user" accent />
        <StatMini k="Polymarket wallets tracked" v={real ? `${list.length}` : '12,480'} icon="pulse" />
        <StatMini k="Your copied traders" v={`${copyCount} active`} icon="copy" />
      </div>

      {/* Who you're copying right now — the engine mirrors these wallets' trades. */}
      {real && cfg.wallets.length > 0 && (
        <div className="card">
          <div className="between card-pad" style={{ paddingBottom: 12, flexWrap: 'wrap', gap: 8 }}>
            <div>
              <div className="sec-title" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>Currently copying <span className="chip chip-up">{cfg.wallets.length}</span></div>
              <div className="tiny" style={{ marginTop: 2 }}>Your engine mirrors these wallets' trades to your balance. Stop any to remove it from your bot.</div>
            </div>
          </div>
          <div>
            {cfg.wallets.map((w) => {
              const t = (realTraders || []).find((x) => _normWallet(x.wallet) === _normWallet(w));
              const nm = (t && t.name) || short(w);
              return (
                <div key={w} className="between" style={{ padding: '11px 16px', borderTop: '1px solid var(--line)' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 11, minWidth: 0, cursor: 'pointer' }} onClick={() => onOpenTrader(w)}>
                    <Avatar name={nm} size={34} />
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontWeight: 700, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{nm}</div>
                      <div className="mono tiny">{short(w)}{t && t.profit != null ? ' · ' + moneyShort(t.profit) + ' 30d' : ''}</div>
                    </div>
                  </div>
                  <button className="b b-red b-sm" onClick={() => cfg.toggle(w, false)}><Icon name="x" size={13} /> Stop</button>
                </div>
              );
            })}
          </div>
        </div>
      )}

      <AICopyMatch ctx={ctx} />

      {/* Featured Snipe profiles — internal, followable, eToro-style */}
      {profiles.length > 0 && <div className="card">
        <div className="between card-pad" style={{ paddingBottom: 12, flexWrap: 'wrap', gap: 10 }}>
          <div>
            <div className="sec-title" style={{ display: 'flex', alignItems: 'center', gap: 9 }}>Popular Snipe profiles <span className="chip chip-up">NEW</span></div>
            <div className="tiny" style={{ marginTop: 2 }}>Registered Snipe traders &amp; bots you can follow and copy — they earn a share when you copy them.</div>
          </div>
          <button className="b b-ghost b-sm" onClick={() => onNavToProfile(ctx)}><Icon name="user" size={13} /> Become a profile</button>
        </div>
        <div className="profile-rail">
          {profiles.map((p) => <ProfileCard key={p.id} p={p} real={real} copiedNow={isCopiedT(p)} onOpen={() => onOpenTrader(real ? p.wallet : p.id)} onToggle={() => real ? cfg.toggle(p.wallet) : (isCopiedT(p) ? onUncopy(p.id) : onOpenTrader(p.id))} />)}
        </div>
      </div>}

      <div className="card">
        <div className="between card-pad" style={{ paddingBottom: 14, flexWrap: 'wrap', gap: 12 }}>
          <div>
            <div className="sec-title">Leaderboard</div>
            <div className="tiny" style={{ marginTop: 2 }}>Snipe profiles &amp; Polymarket wallets · ranked on-chain</div>
          </div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            <div className="seg">
              <button className={kind === 'All' ? 'on' : ''} onClick={() => setKind('All')}>All</button>
              <button className={kind === 'snipe' ? 'on' : ''} onClick={() => setKind('snipe')}>Snipe</button>
              <button className={kind === 'wallet' ? 'on' : ''} onClick={() => setKind('wallet')}>Wallets</button>
            </div>
            {/* Category tags only exist for demo/internal profiles — external Polymarket
                wallets aren't categorized, so hide the filter in real mode (it would empty the list). */}
            {!real && <div className="seg seg-scroll">{cats.map((c) => <button key={c} className={cat === c ? 'on' : ''} onClick={() => setCat(c)}>{c}</button>)}</div>}
            <div className="seg">
              {real ? (<>
                <button className={sort === 'pnl' ? 'on' : ''} onClick={() => setSort('pnl')}>PnL</button>
                <button className={sort === 'volume' ? 'on' : ''} onClick={() => setSort('volume')}>Volume</button>
              </>) : (<>
                <button className={sort === 'roi' ? 'on' : ''} onClick={() => setSort('roi')}>ROI</button>
                <button className={sort === 'pnl' ? 'on' : ''} onClick={() => setSort('pnl')}>PnL</button>
                <button className={sort === 'copiers' ? 'on' : ''} onClick={() => setSort('copiers')}>Copiers</button>
              </>)}
            </div>
          </div>
        </div>
        <table className="tbl">
          <thead><tr>
            <th style={{ width: 40 }}>#</th><th>Trader</th>
            {!real && <th className="th-num">30d ROI</th>}
            {!real && <th className="th-num">Win rate</th>}
            <th className="th-num">{real ? '30d Profit' : '30d PnL'}</th><th className="th-num">{real ? '30d Volume' : 'Sharpe'}</th>
            {!real && <th className="th-num">Copiers</th>}<th style={{ width: 90 }}></th>
          </tr></thead>
          <tbody>
            {list.map((t, i) => {
              const isCopied = isCopiedT(t);
              const snipe = _kindOf(t) === 'snipe';
              const tid = real ? t.wallet : t.id;
              const sub = real
                ? (snipe ? '@' + (t.handle || (t.name || '').toLowerCase().replace(/\s+/g, '_')) : short(t.wallet)) + (((t.tags || []).length) ? ' · ' + t.tags.join(', ') : '')
                : (snipe ? '@' + t.handle : short(t.addr)) + ' · ' + t.tags.join(', ');
              return (
                <tr key={tid} style={{ cursor: 'pointer' }} onClick={() => onOpenTrader(tid)}>
                  <td className="mono" style={{ color: i < 3 ? 'var(--green)' : 'var(--tx-3)', fontWeight: 700, verticalAlign: 'middle' }}>{i + 1}</td>
                  <td style={{ verticalAlign: 'middle' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                      <Avatar name={t.name} size={38} />
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontWeight: 700, display: 'flex', alignItems: 'center', gap: 7, flexWrap: 'wrap' }}>{t.name}
                          {snipe ? <span className="chip chip-up" style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}>{t.verified && <Icon name="check" size={9} />} Snipe</span> : <span className="chip" style={{ background: 'var(--bg-2)', color: 'var(--tx-3)', border: '1px solid var(--line-2)' }}>Wallet</span>}
                          {t.badge === 'top' && <Icon name="trophy" size={13} style={{ color: 'var(--amber)' }} />}
                          {!real && t.streak >= 5 && <span className="chip chip-amber" style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}><Icon name="fire" size={10} />{t.streak}</span>}
                        </div>
                        <div className="mono tiny">{sub}</div>
                      </div>
                    </div>
                  </td>
                  {!real && <td className="td-num up" style={{ fontWeight: 700, verticalAlign: 'middle' }}>{pct(t.roi)}</td>}
                  {!real && <td className="td-num" style={{ verticalAlign: 'middle' }}>{t.win + '%'}</td>}
                  <td className="td-num up" style={{ verticalAlign: 'middle' }}>{real ? moneyShort(t.profit) : moneyShort(t.pnl)}</td>
                  <td className="td-num muted" style={{ verticalAlign: 'middle' }}>{real ? _orDash(t.volume, (v) => moneyShort(v)) : t.sharpe}</td>
                  {!real && <td className="td-num muted" style={{ verticalAlign: 'middle' }}>{t.copiers.toLocaleString()}</td>}
                  <td onClick={(e) => e.stopPropagation()} style={{ verticalAlign: 'middle' }}>
                    {real
                      ? <button className={`b ${isCopied ? 'b-red' : 'b-green'} b-sm`} onClick={() => cfg.toggle(t.wallet)}>{isCopied ? 'Stop' : 'Copy'}</button>
                      : (isCopied
                        ? <button className="b b-red b-sm" onClick={() => onUncopy(t.id)}>Stop</button>
                        : <button className="b b-green b-sm" onClick={() => onOpenTrader(t.id)}>Copy</button>)}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}
function onNavToProfile(ctx) { ctx.onNav('settings'); try { localStorage.setItem('snipe_settings_tab', 'profile'); } catch (e) {} }
function ProfileCard({ p, real, copiedNow, onOpen, onToggle }) {
  const isCopied = !!copiedNow;
  const handle = p.handle || (p.name || '').toLowerCase().replace(/\s+/g, '_');
  return (
    <div className="profile-card" onClick={onOpen}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 11 }}>
        <Avatar name={p.name} size={46} radius={13} />
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontWeight: 700, display: 'flex', alignItems: 'center', gap: 6 }}>{p.name}{p.verified && <Icon name="check" size={13} style={{ color: 'var(--green)' }} />}</div>
          <div className="mono tiny">@{handle}</div>
        </div>
        {p.badge === 'top' && <Icon name="trophy" size={14} style={{ color: 'var(--amber)' }} />}
      </div>
      <div className="pc-bio">{p.bio}</div>
      <div className="pc-stats">
        {real ? (<>
          <div><div className="tiny mono">30d ROI</div><div className="up mono" style={{ fontWeight: 700, fontSize: 15 }}>{_orDash(p.roi, (v) => pct(v))}</div></div>
          <div><div className="tiny mono">Profit</div><div className="up mono" style={{ fontWeight: 700, fontSize: 15 }}>{moneyShort(p.profit)}</div></div>
          <div><div className="tiny mono">Volume</div><div className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{_orDash(p.volume, (v) => moneyShort(v))}</div></div>
        </>) : (<>
          <div><div className="tiny mono">30d ROI</div><div className="up mono" style={{ fontWeight: 700, fontSize: 15 }}>{pct(p.roi)}</div></div>
          <div><div className="tiny mono">Win</div><div className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{p.win}%</div></div>
          <div><div className="tiny mono">Risk</div><div className="mono" style={{ fontWeight: 700, fontSize: 15, color: p.risk >= 7 ? 'var(--red)' : p.risk >= 5 ? 'var(--amber)' : 'var(--green)' }}>{p.risk}/10</div></div>
        </>)}
      </div>
      <div className="between" style={{ paddingTop: 11, borderTop: '1px solid var(--line)' }}>
        <span className="tiny" style={{ display: 'flex', alignItems: 'center', gap: 5 }}><Icon name="user" size={12} /> {real ? _orDash(p.copiers, (v) => v.toLocaleString() + ' copiers') : p.copiers.toLocaleString() + ' copiers'}</span>
        <button className={`b ${isCopied ? 'b-red' : 'b-green'} b-sm`} onClick={(e) => { e.stopPropagation(); onToggle(); }}>{isCopied ? 'Stop copy' : 'Copy'}</button>
      </div>
    </div>
  );
}
function StatMini({ k, v, icon, accent }) {
  return (<div className="card kpi"><div className="kpi-k"><Icon name={icon} size={14} /> {k}</div>
    <div className="kpi-v mono" style={{ fontSize: 27, color: accent ? 'var(--green)' : 'var(--tx)' }}>{v}</div></div>);
}

/* ============ TRADER DETAIL ============ */
function TraderDetail({ ctx }) {
  const real = sniIsReal();
  if (real) return <RealTraderDetail ctx={ctx} />;
  return <DemoTraderDetail ctx={ctx} />;
}

/* Real trader detail — traderId is a wallet. Detail endpoint gives positions,
 * recent trades & computed 30d metrics; leaderboard row supplies name/profit/
 * roi/copiers/tags. Unknown metrics render '—' (never fabricated). */
function RealTraderDetail({ ctx }) {
  const { traderId, onBack } = ctx;
  const wallet = traderId;
  const { data, loading } = useTrader(wallet);
  const { traders } = useTraders('30d', 25);
  const cfg = useCopyConfig();
  const [showCopy, setShowCopy] = useState(false);

  const lb = useMemo(() => (traders || []).find((x) => _normWallet(x.wallet) === _normWallet(wallet)) || null, [traders, wallet]);
  const isCopied = cfg.isCopied(wallet);

  const name = (lb && lb.name) || (data && data.short) || short(wallet);
  const tags = (lb && lb.tags) || [];
  const badge = lb && lb.badge;
  const m = (data && data.metrics) || {};
  const positions = (data && data.positions) || [];
  const trades = (data && data.trades) || [];
  const roi = lb ? lb.roi : null;
  const profit = lb ? lb.profit : null;
  const copiers = lb ? lb.copiers : null;
  // Prefer the real 30d volume from the leaderboard (/volume endpoint) over the
  // recent-trades partial sum, which only covers the last ~50 activity rows.
  const volume = (lb && lb.volume != null) ? lb.volume : ((m.volume_30d !== undefined && m.volume_30d !== null) ? m.volume_30d : null);
  const win = (m.win !== undefined) ? m.win : null;
  const trader = { id: wallet, wallet, name, addr: wallet, roi: roi, profit: profit };

  return (
    <div className="content-inner" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <button className="b b-ghost b-sm" style={{ alignSelf: 'flex-start' }} onClick={onBack}><Icon name="chevron" size={14} style={{ transform: 'rotate(180deg)' }} /> Back to leaderboard</button>

      <div className="card card-pad">
        <div className="between" style={{ flexWrap: 'wrap', gap: 16 }}>
          <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
            <Avatar name={name} size={64} radius={18} />
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <h2 style={{ fontFamily: 'var(--display)', fontSize: 27, margin: 0 }}>{name}</h2>
                {badge === 'top' && <span className="chip chip-amber" style={{ display: 'inline-flex', gap: 4, alignItems: 'center' }}><Icon name="trophy" size={11} /> Top trader</span>}
              </div>
              <div className="mono tiny" style={{ marginTop: 5, display: 'flex', alignItems: 'center', gap: 8 }}>
                {short(wallet)} <Icon name="copyIcon" size={12} style={{ cursor: 'pointer' }} onClick={() => { try { navigator.clipboard.writeText(wallet); } catch (e) {} }} /> <Icon name="external" size={12} style={{ cursor: 'pointer' }} onClick={() => window.open('https://polygonscan.com/address/' + wallet, '_blank')} />
              </div>
              {tags.length > 0 && <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>{tags.map((tag) => <span key={tag} className="chip" style={{ background: 'var(--bg-2)', color: 'var(--tx-2)' }}>{tag}</span>)}</div>}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            <button className="b b-ghost"><Icon name="star" size={15} /> Watch</button>
            {isCopied && <button className="b b-red b-lg" onClick={() => cfg.toggle(wallet, false)}><Icon name="x" size={15} /> Stop copying</button>}
            <button className={`b ${isCopied ? 'b-ghost' : 'b-green'} b-lg`} onClick={() => setShowCopy(true)}>
              <Icon name="copy" size={16} /> {isCopied ? 'Manage copy' : 'Copy this trader'}
            </button>
          </div>
        </div>
        <div className="grid g-4" style={{ marginTop: 20 }}>
          <DetailStat k="30d ROI" v={_orDash(roi, (v) => pct(v))} up />
          <DetailStat k="Win rate" v={_orDash(win, (v) => v + '%')} />
          <DetailStat k="30d Profit" v={_orDash(profit, (v) => moneyShort(v))} up />
          <DetailStat k="30d Volume" v={_orDash(volume, (v) => moneyShort(v))} />
          <DetailStat k="Trades (30d)" v={_orDash(m.trades_30d, (v) => v.toLocaleString())} />
          <DetailStat k="Buys / Sells" v={(m.buys_30d !== undefined && m.buys_30d !== null) ? (m.buys_30d + ' / ' + (m.sells_30d != null ? m.sells_30d : '—')) : '—'} />
          <DetailStat k="Copiers" v={_orDash(copiers, (v) => v.toLocaleString())} />
          <DetailStat k="Last trade" v={m.last_trade_ts ? sniFmtAgo(m.last_trade_ts) + ' ago' : '—'} />
        </div>
      </div>

      <div className="grid g-split">
        <div className="card card-pad">
          <div className="between" style={{ marginBottom: 12 }}>
            <div className="sec-title">Equity curve</div>
            {roi !== null && roi !== undefined && <Chip kind="up">▲ {pct(roi)}</Chip>}
          </div>
          <div className="empty" style={{ height: 220, display: 'flex', alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: '0 18px', color: 'var(--tx-3)' }}>Polymarket doesn't publish a historical equity curve for external wallets. The profit, volume and open positions on this page are live and real.</div>
        </div>
        <div className="card card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div className="sec-title">Open positions <span className="tiny" style={{ marginLeft: 6 }}>{positions.length}</span></div>
          {positions.length
            ? <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
                {positions.slice(0, 6).map((p, i) => (
                  <div key={p.asset || i} className="between" style={{ background: 'var(--bg-1)', border: '1px solid var(--line)', borderRadius: 10, padding: '9px 11px', gap: 8 }}>
                    <div style={{ minWidth: 0 }}>
                      <div className="tiny" style={{ fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.market}</div>
                      <div className="mono tiny" style={{ color: 'var(--tx-3)' }}><span className={`chip ${p.side === 'YES' ? 'chip-up' : 'chip-down'}`} style={{ fontSize: 10 }}>{p.side}</span> {(p.entry * 100).toFixed(0)}¢ → {(p.cur * 100).toFixed(0)}¢</div>
                    </div>
                    <div className="mono" style={{ textAlign: 'right', color: (+p.pnl >= 0) ? 'var(--green)' : 'var(--red)', fontWeight: 700, fontSize: 13 }}>{(+p.pnl >= 0 ? '+' : '') + moneyShort(p.pnl)}<div className="tiny">{pct(+p.pnlPct || 0)}</div></div>
                  </div>
                ))}
              </div>
            : <div className="tiny" style={{ color: 'var(--tx-3)' }}>{loading ? 'Loading positions…' : 'No open positions.'}</div>}
        </div>
      </div>

      <div className="card">
        <div className="card-pad" style={{ paddingBottom: 10 }}><div className="sec-title">Recent trades</div></div>
        <table className="tbl">
          <thead><tr><th>Market</th><th>Side</th><th className="th-num">Price</th><th className="th-num">Size</th><th className="th-num">Value</th><th className="th-num">When</th></tr></thead>
          <tbody>
            {trades.length ? trades.slice(0, 12).map((r, i) => (
              <tr key={r.tx || i}>
                <td style={{ fontWeight: 600, maxWidth: 260, verticalAlign: 'middle' }}>{r.market}{r.outcome ? <span className="tiny" style={{ color: 'var(--tx-3)' }}> · {r.outcome}</span> : null}</td>
                <td style={{ verticalAlign: 'middle' }}><span className={`chip ${r.side === 'BUY' ? 'chip-up' : 'chip-down'}`}>{r.side}</span></td>
                <td className="td-num" style={{ verticalAlign: 'middle' }}>{(r.price !== null && r.price !== undefined) ? (r.price * 100).toFixed(0) + '¢' : '—'}</td>
                <td className="td-num" style={{ verticalAlign: 'middle' }}>{_orDash(r.size, (v) => moneyShort(v))}</td>
                <td className="td-num" style={{ verticalAlign: 'middle' }}>{_orDash(r.usdc, (v) => moneyShort(v))}</td>
                <td className="td-num muted" style={{ verticalAlign: 'middle' }}>{r.ts ? sniFmtAgo(r.ts) + ' ago' : '—'}</td>
              </tr>
            )) : (
              <tr><td colSpan={6} className="tiny" style={{ color: 'var(--tx-3)' }}>{loading ? 'Loading recent trades…' : 'No recent trades in this window.'}</td></tr>
            )}
          </tbody>
        </table>
      </div>

      {showCopy && <CopyModal trader={trader} onClose={() => setShowCopy(false)} onConfirm={() => { cfg.toggle(wallet, true); setShowCopy(false); }} alreadyCopied={isCopied} />}
    </div>
  );
}

function DemoTraderDetail({ ctx }) {
  const { traderId, onBack, copied, onCopyTrader, onUncopy } = ctx;
  const t = TRADERS.find((x) => x.id === traderId) || TRADERS[0];
  const series = useMemo(() => genSeries('t' + t.id, 70, true), [t.id]);
  const isCopied = copied.includes(t.id);
  const [showCopy, setShowCopy] = useState(false);
  const recent = useMemo(() => MARKETS.slice(0, 5).map((m, i) => ({
    m: m.q, side: i % 3 === 0 ? 'NO' : 'YES', price: m.yes, size: [8200, 12400, 4100, 6800, 9600][i], when: ['2m', '41m', '3h', '6h', '1d'][i], pnl: [820, 1240, -180, 540, 1960][i],
  })), [t.id]);

  return (
    <div className="content-inner" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <button className="b b-ghost b-sm" style={{ alignSelf: 'flex-start' }} onClick={onBack}><Icon name="chevron" size={14} style={{ transform: 'rotate(180deg)' }} /> Back to leaderboard</button>

      {/* hero */}
      <div className="card card-pad">
        <div className="between" style={{ flexWrap: 'wrap', gap: 16 }}>
          <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
            <Avatar name={t.name} size={64} radius={18} />
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <h2 style={{ fontFamily: 'var(--display)', fontSize: 27, margin: 0 }}>{t.name}</h2>
                {t.badge === 'top' && <span className="chip chip-amber" style={{ display: 'inline-flex', gap: 4, alignItems: 'center' }}><Icon name="trophy" size={11} /> Top trader</span>}
              </div>
              <div className="mono tiny" style={{ marginTop: 5, display: 'flex', alignItems: 'center', gap: 8 }}>
                {short(t.addr)} <Icon name="copyIcon" size={12} style={{ cursor: 'pointer' }} /> <Icon name="external" size={12} style={{ cursor: 'pointer' }} />
              </div>
              <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>{t.tags.map((tag) => <span key={tag} className="chip" style={{ background: 'var(--bg-2)', color: 'var(--tx-2)' }}>{tag}</span>)}</div>
            </div>
          </div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            <button className="b b-ghost"><Icon name="star" size={15} /> Watch</button>
            {isCopied && <button className="b b-red b-lg" onClick={() => onUncopy(t.id)}><Icon name="x" size={15} /> Stop copying</button>}
            <button className={`b ${isCopied ? 'b-ghost' : 'b-green'} b-lg`} onClick={() => setShowCopy(true)}>
              <Icon name="copy" size={16} /> {isCopied ? 'Manage copy' : 'Copy this trader'}
            </button>
          </div>
        </div>
        <div className="grid g-4" style={{ marginTop: 20 }}>
          <DetailStat k="30d ROI" v={pct(t.roi)} up />
          <DetailStat k="Win rate" v={t.win + '%'} />
          <DetailStat k="30d PnL" v={moneyShort(t.pnl)} up />
          <DetailStat k="Sharpe ratio" v={t.sharpe} />
          <DetailStat k="Total trades" v={t.trades.toLocaleString()} />
          <DetailStat k="Avg. hold" v={t.avgHold} />
          <DetailStat k="Copiers" v={t.copiers.toLocaleString()} />
          <DetailStat k="Win streak" v={t.streak + ' wins'} />
        </div>
      </div>

      <div className="grid g-split">
        <div className="card card-pad">
          <div className="between" style={{ marginBottom: 12 }}>
            <div className="sec-title">Equity curve (90d)</div>
            <Chip kind="up">▲ {pct(t.roi)}</Chip>
          </div>
          <Sparkline data={series} h={220} />
        </div>
        <div className="card card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div className="sec-title">If you'd copied $10k</div>
          <Donut value={Math.min(t.roi / 5, 95)} size={150} label={moneyShort(10000 + 10000 * t.roi / 100)} sub="projected 30d" />
          <div className="tiny" style={{ textAlign: 'center' }}>Based on this trader's last 30 days of realized performance. Not a guarantee of future results.</div>
        </div>
      </div>

      <div className="card">
        <div className="card-pad" style={{ paddingBottom: 10 }}><div className="sec-title">Recent trades</div></div>
        <table className="tbl">
          <thead><tr><th>Market</th><th>Side</th><th className="th-num">Price</th><th className="th-num">Size</th><th className="th-num">Result</th><th className="th-num">When</th></tr></thead>
          <tbody>
            {recent.map((r, i) => (
              <tr key={i}>
                <td style={{ fontWeight: 600, maxWidth: 260 }}>{r.m}</td>
                <td><span className={`chip ${r.side === 'YES' ? 'chip-up' : 'chip-down'}`}>{r.side}</span></td>
                <td className="td-num">{r.price}¢</td>
                <td className="td-num">{moneyShort(r.size)}</td>
                <td className="td-num" style={{ color: r.pnl >= 0 ? 'var(--green)' : 'var(--red)', fontWeight: 700 }}>{r.pnl >= 0 ? '+' : ''}{moneyShort(r.pnl)}</td>
                <td className="td-num muted">{r.when} ago</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {showCopy && <CopyModal trader={t} onClose={() => setShowCopy(false)} onConfirm={() => { onCopyTrader(t.id); setShowCopy(false); }} alreadyCopied={isCopied} />}
    </div>
  );
}
function DetailStat({ k, v, up }) {
  return (<div style={{ background: 'var(--bg-1)', border: '1px solid var(--line)', borderRadius: 12, padding: '14px 16px' }}>
    <div className="tiny mono" style={{ textTransform: 'uppercase', letterSpacing: '0.04em' }}>{k}</div>
    <div className="mono" style={{ fontWeight: 700, fontSize: 21, marginTop: 6, color: up ? 'var(--green)' : 'var(--tx)' }}>{v}</div>
  </div>);
}

/* ============ COPY MODAL ============ */
function CopyModal({ trader, onClose, onConfirm, alreadyCopied, initialAlloc }) {
  const [alloc, setAlloc] = useState(initialAlloc || 5000);
  const [maxTrade, setMaxTrade] = useState(15);
  const [sl, setSl] = useState(20);
  const [tp, setTp] = useState(50);
  const [proportional, setProportional] = useState(true);
  const [wallet, setWallet] = useState('w1');
  return (
    <Modal title={alreadyCopied ? `Manage copy · ${trader.name}` : `Copy ${trader.name}`} onClose={onClose} width={520}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 14, background: 'var(--bg-1)', border: '1px solid var(--line)', borderRadius: 12, marginBottom: 18 }}>
        <Avatar name={trader.name} size={42} />
        <div style={{ flex: 1 }}>
          <div style={{ fontWeight: 700 }}>{trader.name}</div>
          <div className="mono tiny">{short(trader.addr)}</div>
        </div>
        <div style={{ textAlign: 'right' }}><div className="up mono" style={{ fontWeight: 700 }}>{(trader.roi === null || trader.roi === undefined) ? '—' : pct(trader.roi)}</div><div className="tiny">30d ROI</div></div>
      </div>

      <div className="field">
        <label>Copy from wallet</label>
        <div className="seg" style={{ display: 'flex' }}>
          {WALLETS.map((w) => <button key={w.id} className={wallet === w.id ? 'on' : ''} style={{ flex: 1 }} onClick={() => setWallet(w.id)}>{w.label}</button>)}
        </div>
      </div>

      <div className="field">
        <label>Allocation · <span className="mono up">{money(alloc, 0)}</span></label>
        <input className="slider" type="range" min="500" max={Math.max(50000, Math.ceil(alloc / 1000) * 1000)} step="500" value={alloc} onChange={(e) => setAlloc(+e.target.value)} />
        <div className="hint">Total capital this trader can deploy on your behalf.</div>
      </div>

      <div className="grid g-2" style={{ gap: 14 }}>
        <div className="field"><label>Max per trade · <span className="mono">{maxTrade}%</span></label>
          <input className="slider" type="range" min="1" max="50" value={maxTrade} onChange={(e) => setMaxTrade(+e.target.value)} /></div>
        <div className="field"><label>Sizing</label>
          <div className="between" style={{ background: 'var(--bg-2)', border: '1px solid var(--line-2)', borderRadius: 10, padding: '8px 12px' }}>
            <span className="tiny">{proportional ? 'Proportional to trader' : 'Fixed per trade'}</span>
            <Toggle on={proportional} onChange={setProportional} />
          </div></div>
      </div>

      <div className="grid g-2" style={{ gap: 14 }}>
        <div className="field"><label>Stop-loss · <span className="mono down">−{sl}%</span></label>
          <input className="slider" type="range" min="5" max="80" value={sl} onChange={(e) => setSl(+e.target.value)} /></div>
        <div className="field"><label>Take-profit · <span className="mono up">+{tp}%</span></label>
          <input className="slider" type="range" min="10" max="300" step="5" value={tp} onChange={(e) => setTp(+e.target.value)} /></div>
      </div>

      <div style={{ background: 'rgba(255,194,75,0.06)', border: '1px solid rgba(255,194,75,0.2)', borderRadius: 10, padding: '11px 13px', display: 'flex', gap: 10, fontSize: 13.5, color: 'var(--tx-2)', margin: '4px 0 18px' }}>
        <Icon name="shield" size={16} style={{ color: 'var(--amber)', flexShrink: 0 }} />
        Copy trading carries risk of total loss. The engine mirrors real positions at market and cannot guarantee fills or prices.
      </div>

      <button className="b b-green b-block b-lg" onClick={onConfirm}>
        <Icon name="copy" size={16} /> {alreadyCopied ? `Update copy · ${money(alloc, 0)}` : `Copy now · ${money(alloc, 0)}`}
      </button>
    </Modal>
  );
}

Object.assign(window, { Overview, CopyTraders, TraderDetail, PositionsTable });
