// Shared pedby.com components v2 — premium pass.
// Palette + type retained: Samofund green/pink/ink, Plus Jakarta + JetBrains Mono + Instrument Serif.

const PedbyLogo = ({ size = 20, mono = false }) => (
  <div style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'Plus Jakarta Sans', fontWeight: 800, fontSize: size, letterSpacing: '-0.03em', lineHeight: 1 }}>
    <img src="pedby-icon.png" alt="" style={{ width: size * 1.05, height: size * 1.05, borderRadius: '32%', display: 'block', flex: '0 0 auto' }} />
    <span style={{ color: mono ? 'currentColor' : '#16201A' }}>pedby</span>
  </div>
);

// Normalize whatever the creator typed into a real, safe href
const pedbySocialUrl = (type, v) => {
  if (!v) return null;
  v = String(v).trim();
  if (/^javascript:/i.test(v)) return null;
  if (/^https?:\/\//i.test(v)) return v;
  if (type === 'twitter') return 'https://x.com/' + v.replace(/^@/, '').replace(/^(x\.com\/|twitter\.com\/)/i, '');
  if (type === 'telegram') return 'https://t.me/' + v.replace(/^@/, '').replace(/^t\.me\//i, '');
  return 'https://' + v;
};

const SocialLinks = ({ links }) => {
  if (!links) return null;
  const items = [['website', 'Website'], ['twitter', 'X / Twitter'], ['telegram', 'Telegram'], ['discord', 'Discord']]
    .map(([k, label]) => links[k] ? { href: pedbySocialUrl(k, links[k]), label } : null)
    .filter(i => i && i.href);
  if (!items.length) return null;
  return (
    <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
      {items.map(({ href, label }) => (
        <a key={label} href={href} target="_blank" rel="noreferrer noopener" onClick={e => e.stopPropagation()} style={{ textDecoration: 'none' }}>
          <Pill tone="pink">{label} ↗</Pill>
        </a>
      ))}
    </div>
  );
};

const BaseBadge = ({ style, dark = false }) => (
  <span style={{
    display: 'inline-flex', alignItems: 'center', gap: 6, fontFamily: 'JetBrains Mono', fontSize: 10.5,
    letterSpacing: 0.8, fontWeight: 600, textTransform: 'uppercase',
    color: dark ? 'rgba(255,255,255,.75)' : '#0F3F66',
    background: dark ? 'rgba(255,255,255,.1)' : '#E1EEF8',
    padding: '4px 10px 4px 8px', borderRadius: 999, ...style,
  }}>
    <span style={{ width: 6, height: 6, borderRadius: 999, background: '#1F6FB2', boxShadow: '0 0 0 3px rgba(31,111,178,.18)' }} />
    Base
  </span>
);

// Deterministic soft tint per ticker — keeps the grid colorful but calm
const TOKEN_TINTS = [
  { bg: '#E7F4EC', fg: '#0F4E27' },
  { bg: '#FBE7F1', fg: '#9A2367' },
  { bg: '#E1EEF8', fg: '#0F3F66' },
  { bg: '#FCEBD0', fg: '#7A4A0B' },
];
const tokenTint = (ticker) => {
  let h = 0;
  for (let i = 0; i < ticker.length; i++) h = (h * 31 + ticker.charCodeAt(i)) >>> 0;
  return TOKEN_TINTS[h % TOKEN_TINTS.length];
};

const TokenGlyph = ({ ticker, size = 40, graduated, image }) => {
  const [imgOk, setImgOk] = React.useState(true);
  const tint = graduated ? { bg: '#16201A', fg: '#F6F8F4' } : tokenTint(ticker);
  if (image && imgOk) {
    return (
      <img src={image} alt={ticker} onError={() => setImgOk(false)} style={{
        width: size, height: size, borderRadius: '30%', objectFit: 'cover', flex: `0 0 ${size}px`,
        border: '1px solid #E6EAE6', background: tint.bg,
      }} />
    );
  }
  return (
    <div style={{
      width: size, height: size, borderRadius: '30%', background: tint.bg, color: tint.fg,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flex: `0 0 ${size}px`,
    }}>
      <span className="sf-serif" style={{ fontStyle: 'italic', fontSize: size * 0.5, lineHeight: 1 }}>{ticker[0]}</span>
    </div>
  );
};

// Status — quiet mono label with a colored dot, no pill chrome
const StatusDot = ({ t }) => {
  const s = t.graduated
    ? { c: '#1F6FB2', label: 'GRADUATED' }
    : PEDBY_CURVE.progress(t.curve) >= 0.7
      ? { c: '#C77A0B', label: 'ALMOST' }
      : { c: '#1E8C45', label: 'LIVE' };
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: 0.8, color: '#6B7570', flexShrink: 0 }}>
      <span style={{ width: 6, height: 6, borderRadius: 999, background: s.c }} />
      {s.label}
    </span>
  );
};

// Curve progress — thin, quiet
const CurveProgress = ({ curve, graduated, compact = false }) => {
  const pct = graduated ? 100 : Math.round(PEDBY_CURVE.progress(curve) * 100);
  return (
    <div>
      <div style={{ height: 4, borderRadius: 999, background: '#EFF2EE', overflow: 'hidden' }}>
        <div style={{
          height: '100%', width: `${pct}%`, borderRadius: 999, transition: 'width .6s ease',
          background: graduated ? 'linear-gradient(90deg,#1F6FB2,#1E8C45)' : '#1E8C45',
        }} />
      </div>
      {!compact && (
        <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 6, fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: 0.6, color: '#9AA39E' }}>
          <span>{graduated ? 'LIQUIDITY LOCKED' : 'CURVE'}</span>
          <span>{graduated ? 'ON DEX' : pct + '% → DEX'}</span>
        </div>
      )}
    </div>
  );
};

// Sparkline v2 — area chart with soft fill
const Sparkline = ({ data, w = 120, h = 36 }) => {
  const max = Math.max(...data), min = Math.min(...data);
  const range = max - min || 1;
  const pts = data.map((v, i) => [
    (i / (data.length - 1)) * w,
    (h - 4) - ((v - min) / range) * (h - 8) + 2,
  ]);
  const line = 'M ' + pts.map(p => p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' L ');
  const area = line + ` L ${w},${h} L 0,${h} Z`;
  const up = data[data.length - 1] >= data[0];
  const c = up ? '#1E8C45' : '#C0392B';
  const gid = 'sg' + (up ? 'u' : 'd');
  return (
    <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: 'block' }}>
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={c} stopOpacity="0.16" />
          <stop offset="100%" stopColor={c} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill={`url(#${gid})`} />
      <path d={line} fill="none" stroke={c} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
    </svg>
  );
};

// ── Candlestick chart — TradingView-style terminal built from on-chain trades ──
// Trades are bucketed into time intervals; each bucket becomes an OHLC candle
// (open carried forward through empty buckets) with an ETH-volume bar below.
const pedbyCandleUsd = (ethPrice) => {
  const v = ethPrice * PEDBY_CURVE.ETH_USD;
  if (v === 0) return '$0';
  if (v < 0.01) return '$' + v.toPrecision(3).replace(/e-?\d+$/, m => m); // keeps tiny prices readable
  return '$' + v.toFixed(v < 1 ? 4 : 2);
};

function CandleChart({ trades, height = 240 }) {
  const [hover, setHover] = React.useState(null);
  const W = 620, H = height;
  const padR = 74, padT = 12, padB = 34, padL = 8;
  const volH = 34;
  const plotW = W - padL - padR;
  const plotH = H - padT - padB - volH - 8;

  const candles = React.useMemo(() => {
    if (!trades || trades.length < 2) return null;
    const now = Date.now() / 1000;
    const pts = trades.map(t => ({ t: now - t.agoSec, p: t.price, v: t.ethAmt })).sort((a, b) => a.t - b.t);
    const t0 = pts[0].t, span = Math.max(120, pts[pts.length - 1].t - pts[0].t);
    const nB = Math.max(10, Math.min(40, Math.ceil(pts.length * 2)));
    const bs = span / nB;
    const out = [];
    let last = pts[0].p;
    for (let i = 0; i < nB; i++) {
      const s = t0 + i * bs, e = s + bs;
      const inB = pts.filter(p => p.t >= s && (i === nB - 1 ? p.t <= e + 1 : p.t < e));
      if (!inB.length) { out.push({ o: last, h: last, l: last, c: last, v: 0, ts: s, empty: true }); continue; }
      const o = last, c = inB[inB.length - 1].p;
      out.push({
        o, c,
        h: Math.max(o, ...inB.map(p => p.p)),
        l: Math.min(o, ...inB.map(p => p.p)),
        v: inB.reduce((s2, p) => s2 + p.v, 0),
        ts: s,
      });
      last = c;
    }
    return out;
  }, [trades]);

  if (!candles) return null;

  const hi = Math.max(...candles.map(c => c.h));
  const lo = Math.min(...candles.map(c => c.l));
  const range = (hi - lo) || hi * 0.1 || 1;
  const yy = (p) => padT + (1 - (p - (lo - range * 0.06)) / (range * 1.12)) * plotH;
  const maxV = Math.max(...candles.map(c => c.v), 1e-9);
  const cw = plotW / candles.length;
  const bw = Math.max(2, cw * 0.55);
  const lastC = candles[candles.length - 1];
  const upLast = lastC.c >= lastC.o;

  const fmtT = (ts) => {
    const d = new Date(ts * 1000);
    const ago = Date.now() / 1000 - ts;
    if (ago > 86400) return (d.getMonth() + 1) + '/' + d.getDate();
    return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
  };

  const onMove = (evt) => {
    const rect = evt.currentTarget.getBoundingClientRect();
    const x = (evt.clientX - rect.left) / rect.width * W;
    const idx = Math.max(0, Math.min(candles.length - 1, Math.floor((x - padL) / cw)));
    setHover(idx);
  };

  const gridLines = 4;
  const hc = hover != null ? candles[hover] : null;

  return (
    <div style={{ position: 'relative' }}>
      {/* OHLC readout */}
      <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', fontFamily: 'JetBrains Mono', fontSize: 10.5, color: '#6B7570', marginBottom: 8, minHeight: 15 }}>
        {(hc || lastC) && ['O', 'H', 'L', 'C'].map((k, i) => {
          const c = hc || lastC;
          const v = [c.o, c.h, c.l, c.c][i];
          return <span key={k}>{k} <span style={{ color: c.c >= c.o ? '#0F4E27' : '#7A1E15', fontWeight: 600 }}>{pedbyCandleUsd(v)}</span></span>;
        })}
        {(hc || lastC) && <span>VOL <span style={{ color: '#16201A', fontWeight: 600 }}>{(hc || lastC).v.toFixed(4)} ETH</span></span>}
      </div>

      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block', cursor: 'crosshair' }}
        onMouseMove={onMove} onMouseLeave={() => setHover(null)}>
        {/* grid + price axis */}
        {Array.from({ length: gridLines + 1 }, (_, i) => {
          const p = lo - range * 0.06 + (range * 1.12) * (i / gridLines);
          const gy = yy(p);
          return (
            <g key={i}>
              <line x1={padL} x2={W - padR} y1={gy} y2={gy} stroke="#EFF2EE" strokeWidth="1" />
              <text x={W - padR + 8} y={gy + 3.5} fontFamily="JetBrains Mono" fontSize="9.5" fill="#9AA39E">{pedbyCandleUsd(p)}</text>
            </g>
          );
        })}

        {/* last-price dashed line */}
        <line x1={padL} x2={W - padR} y1={yy(lastC.c)} y2={yy(lastC.c)}
          stroke={upLast ? '#1E8C45' : '#C0392B'} strokeWidth="1" strokeDasharray="3 3" opacity="0.7" />
        <rect x={W - padR + 2} y={yy(lastC.c) - 8} width={padR - 6} height={16} rx={4} fill={upLast ? '#1E8C45' : '#C0392B'} />
        <text x={W - padR + 6} y={yy(lastC.c) + 3.5} fontFamily="JetBrains Mono" fontSize="9" fill="#fff">{pedbyCandleUsd(lastC.c)}</text>

        {/* candles */}
        {candles.map((c, i) => {
          const cx = padL + i * cw + cw / 2;
          const up = c.c >= c.o;
          const col = c.empty ? '#D8DED8' : up ? '#1E8C45' : '#C0392B';
          const top = yy(Math.max(c.o, c.c));
          const bot = yy(Math.min(c.o, c.c));
          return (
            <g key={i} opacity={hover != null && hover !== i ? 0.55 : 1}>
              <line x1={cx} x2={cx} y1={yy(c.h)} y2={yy(c.l)} stroke={col} strokeWidth="1.2" />
              <rect x={cx - bw / 2} y={top} width={bw} height={Math.max(1.4, bot - top)} rx={1} fill={col} />
            </g>
          );
        })}

        {/* volume bars */}
        {candles.map((c, i) => {
          if (!c.v) return null;
          const cx = padL + i * cw + cw / 2;
          const vh = Math.max(1.5, (c.v / maxV) * volH);
          const up = c.c >= c.o;
          return <rect key={'v' + i} x={cx - bw / 2} y={H - padB - vh} width={bw} height={vh} rx={1}
            fill={up ? '#1E8C45' : '#C0392B'} opacity={hover === i ? 0.85 : 0.35} />;
        })}

        {/* time axis */}
        {[0, Math.floor(candles.length / 2), candles.length - 1].map(i => (
          <text key={'t' + i} x={padL + i * cw + cw / 2} y={H - padB + 16} textAnchor="middle"
            fontFamily="JetBrains Mono" fontSize="9.5" fill="#9AA39E">{fmtT(candles[i].ts)}</text>
        ))}

        {/* crosshair */}
        {hover != null && (
          <line x1={padL + hover * cw + cw / 2} x2={padL + hover * cw + cw / 2} y1={padT} y2={H - padB}
            stroke="#16201A" strokeWidth="0.8" strokeDasharray="3 3" opacity="0.5" />
        )}
      </svg>
    </div>
  );
}

// ── Token card v2 ────────────────────────────────────────────────
const TokenCard = ({ t, onClick, onTrade }) => {
  const [open, setOpen] = React.useState(false);
  const [side, setSide] = React.useState('buy');
  const [amt, setAmt] = React.useState(0.05);
  const [flash, setFlash] = React.useState(null);
  const held = t.held || 0;
  const mcap = PEDBY_CURVE.marketCapUsd(t.curve);
  const up = t.spark[t.spark.length - 1] >= t.spark[0];

  const doTrade = (e) => {
    e.stopPropagation();
    if (!onTrade) return;
    const amount = side === 'sell' ? Math.min(amt, held) : amt;
    if (amount <= 0) return;
    onTrade(t.id, side, amount);
    setFlash(side);
    setTimeout(() => setFlash(null), 700);
  };

  return (
    <div onClick={onClick} className="pedby-card" style={{
      background: '#fff', border: '1px solid #E6EAE6', borderRadius: 18, padding: '18px 18px 16px',
      cursor: 'pointer', display: 'flex', flexDirection: 'column', position: 'relative',
      animation: flash ? `pedby-flash-${flash} .7s ease-out` : 'none',
    }}>
      {/* header */}
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
        <TokenGlyph ticker={t.ticker} graduated={t.graduated} image={t.image} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontWeight: 700, fontSize: 15, letterSpacing: -0.2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.name}</div>
          <div style={{ fontFamily: 'JetBrains Mono', fontSize: 11, color: '#9AA39E', marginTop: 2 }}>${t.ticker} · {t.createdAgo}</div>
        </div>
        <StatusDot t={t} />
      </div>
      {t.featured && (
        <span style={{
          position: 'absolute', top: -9, right: 14, fontFamily: 'JetBrains Mono', fontSize: 9.5, letterSpacing: 1,
          background: '#D6398A', color: '#fff', padding: '3px 9px', borderRadius: 999, fontWeight: 600,
        }}>FEATURED</span>
      )}

      {/* market cap */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 16 }}>
        <div>
          <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: 0.8, color: '#9AA39E' }}>MARKET CAP</div>
          <div className="sf-num" style={{ fontSize: 24, fontWeight: 700, letterSpacing: -0.6, marginTop: 2 }}>{PEDBY_CURVE.fmtUsd(mcap)}</div>
        </div>
        <span style={{ fontFamily: 'JetBrains Mono', fontSize: 11.5, fontWeight: 600, color: up ? '#0F4E27' : '#7A1E15' }}>
          {up ? '▲' : '▼'} {(Math.abs(t.spark[t.spark.length - 1] - t.spark[0]) / (t.spark[0] || 1) * 100).toFixed(1)}%
        </span>
      </div>

      {/* chart */}
      <div style={{ margin: '10px -4px 12px' }}>
        <Sparkline data={t.spark} w={260} h={44} />
      </div>

      <CurveProgress curve={t.curve} graduated={t.graduated} />

      {/* trade */}
      {onTrade && (
        <div onClick={e => e.stopPropagation()} style={{ marginTop: 14, paddingTop: 12, borderTop: '1px solid #EFF2EE' }}>
          {!open ? (
            <div style={{ display: 'flex', gap: 8 }}>
              <button onClick={() => { setSide('buy'); setOpen(true); }} style={cardBuyBtn}>Buy</button>
              <button onClick={() => { setSide('sell'); setOpen(true); }} style={cardSellBtn}>Sell</button>
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <div style={{ display: 'flex', gap: 6 }}>
                <button onClick={() => setSide('buy')} style={cardTabStyle(side === 'buy', '#E7F4EC', '#0F4E27')}>Buy</button>
                <button onClick={() => setSide('sell')} style={cardTabStyle(side === 'sell', '#FBE3E0', '#7A1E15')}>Sell</button>
              </div>
              {side === 'buy' ? (
                <div style={{ display: 'flex', gap: 6 }}>
                  {[0.02, 0.05, 0.1].map(v => (
                    <button key={v} onClick={() => setAmt(v)} style={{ ...quickAmtBtn, background: amt === v ? '#EFF2EE' : '#fff', borderColor: amt === v ? '#16201A' : '#E6EAE6' }}>{v} ETH</button>
                  ))}
                </div>
              ) : (
                <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10.5, color: '#6B7570' }}>HOLDING {Math.round(held).toLocaleString()} ${t.ticker}</div>
              )}
              <input type={side === 'buy' ? 'number' : 'range'} min={0} max={side === 'sell' ? held : undefined} step={side === 'buy' ? 0.01 : 1}
                value={side === 'buy' ? amt : Math.min(amt, held)} onChange={e => setAmt(+e.target.value)}
                style={side === 'buy' ? cardInputStyle : { width: '100%', accentColor: '#1E8C45' }} />
              <div style={{ display: 'flex', gap: 6 }}>
                <Btn kind={side === 'buy' ? 'primary' : 'dark'} size="sm" style={{ flex: 1, justifyContent: 'center' }} onClick={doTrade}>
                  {side === 'buy' ? `Buy $${t.ticker}` : `Sell $${t.ticker}`}
                </Btn>
                <button onClick={() => setOpen(false)} style={{ ...quickAmtBtn, flex: '0 0 34px', height: 30 }}>✕</button>
              </div>
            </div>
          )}
        </div>
      )}
      {t.graduated && !onTrade && (
        <div style={{ marginTop: 14, paddingTop: 12, borderTop: '1px solid #EFF2EE', fontFamily: 'JetBrains Mono', fontSize: 10.5, color: '#9AA39E', letterSpacing: 0.4 }}>
          TRADES ON DEX · LP LOCKED
        </div>
      )}
    </div>
  );
};

const cardBuyBtn = {
  flex: 1, height: 32, borderRadius: 9, border: 'none', background: '#1E8C45', color: '#fff',
  fontFamily: 'inherit', fontSize: 12.5, fontWeight: 700, cursor: 'pointer', letterSpacing: -0.1,
};
const cardSellBtn = {
  flex: 1, height: 32, borderRadius: 9, border: '1px solid #E6EAE6', background: '#fff', color: '#3B453F',
  fontFamily: 'inherit', fontSize: 12.5, fontWeight: 700, cursor: 'pointer', letterSpacing: -0.1,
};
const quickAmtBtn = {
  flex: 1, height: 28, borderRadius: 8, border: '1px solid #E6EAE6', background: '#fff',
  fontFamily: 'JetBrains Mono', fontSize: 10.5, cursor: 'pointer', color: '#3B453F',
};
const cardTabStyle = (active, bg, fg) => ({
  flex: 1, height: 28, borderRadius: 8, border: 'none', cursor: 'pointer', fontFamily: 'inherit',
  fontSize: 11.5, fontWeight: 700, background: active ? bg : '#EFF2EE', color: active ? fg : '#6B7570',
});
const cardInputStyle = {
  height: 32, borderRadius: 8, border: '1px solid #E6EAE6', padding: '0 10px', fontSize: 12.5,
  fontFamily: 'JetBrains Mono', outline: 'none', background: '#fff', width: '100%',
};

// Filter chip
const Chip = ({ active, children, ...rest }) => (
  <button {...rest} style={{
    height: 32, padding: '0 14px', borderRadius: 999, fontSize: 12.5, fontWeight: 600,
    fontFamily: 'inherit', cursor: 'pointer', border: `1px solid ${active ? '#16201A' : '#E6EAE6'}`,
    background: active ? '#16201A' : '#fff', color: active ? '#F6F8F4' : '#3B453F',
    transition: 'all .15s',
  }}>
    {children}
  </button>
);

// ── Nav ──────────────────────────────────────────────────────────
const PedbyNav = ({ page, onNav, wallet }) => (
  <div className="pedby-nav" style={{
    position: 'sticky', top: 0, zIndex: 20, background: 'rgba(246,248,244,.9)', backdropFilter: 'blur(12px)',
    borderBottom: '1px solid #E6EAE6', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
    padding: '0 32px', height: 66,
  }}>
    <div style={{ display: 'flex', alignItems: 'center', gap: 32 }}>
      <div style={{ cursor: 'pointer' }} onClick={() => onNav('landing')}><PedbyLogo size={21} /></div>
      <div className="pedby-nav-tabs" style={{ display: 'flex', gap: 2 }}>
        {[['marketplace', 'Marketplace'], ['create', 'Create']].map(([id, label]) => (
          <button key={id} onClick={() => onNav(id)} style={{
            height: 34, padding: '0 14px', borderRadius: 999, border: 'none', cursor: 'pointer',
            fontFamily: 'inherit', fontSize: 13.5, fontWeight: 600, transition: 'all .15s',
            background: page === id ? '#16201A' : 'transparent', color: page === id ? '#F6F8F4' : '#6B7570',
          }}>{label}</button>
        ))}
      </div>
    </div>
    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
      <span className="pedby-nav-base"><BaseBadge /></span>
      {wallet.connecting ? (
        <div style={{
          display: 'flex', alignItems: 'center', gap: 8, height: 38, padding: '0 14px',
          borderRadius: 999, border: '1px solid #E6EAE6', background: '#fff',
          fontFamily: 'JetBrains Mono', fontSize: 11.5, fontWeight: 600, color: '#6B7570', letterSpacing: 0.3,
        }}>
          <span style={{ width: 7, height: 7, borderRadius: 999, background: '#1E8C45', animation: 'pedby-pulse-dot 1.1s ease-in-out infinite' }} />
          Connecting…
        </div>
      ) : wallet.connected ? (
        <button onClick={() => onNav('profile')} style={{
          display: 'flex', alignItems: 'center', gap: 9, height: 38, padding: '0 5px 0 14px',
          borderRadius: 999, border: '1px solid #E6EAE6', background: '#fff', cursor: 'pointer', fontFamily: 'inherit',
        }}>
          <span className="sf-num" style={{ fontSize: 12.5, fontWeight: 600 }}>{wallet.eth.toFixed(3)} ETH</span>
          <Avatar name={(wallet.addressFull || wallet.address || 'P').replace(/^0x/i, '')} tone="soft" size={28} />
        </button>
      ) : (
        <Btn kind="dark" size="sm" onClick={wallet.connect}>Connect wallet</Btn>
      )}
    </div>
  </div>
);

// ── Trading tape — ink band, marquee ─────────────────────────────
const TradeTape = ({ tokens, style }) => {
  const items = [...tokens, ...tokens];
  return (
    <div style={{ background: '#16201A', overflow: 'hidden', ...style }}>
      <div style={{ display: 'flex', width: 'max-content', animation: 'pedby-marquee 36s linear infinite' }}>
        {items.map((t, i) => {
          const up = t.spark[t.spark.length - 1] >= t.spark[0];
          return (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 9, padding: '11px 26px',
              fontFamily: 'JetBrains Mono', fontSize: 11.5, whiteSpace: 'nowrap',
              borderRight: '1px solid rgba(255,255,255,.08)',
            }}>
              <span style={{ color: '#F6F8F4', fontWeight: 600 }}>${t.ticker}</span>
              <span style={{ color: 'rgba(255,255,255,.55)' }}>{PEDBY_CURVE.fmtUsd(PEDBY_CURVE.marketCapUsd(t.curve))}</span>
              <span style={{ color: t.graduated ? '#7FB2E0' : up ? '#5FC98A' : '#E08A7F' }}>
                {t.graduated ? '◆ DEX' : up ? '▲' : '▼'}
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
};

// Legacy alias (live trades feed rendered inside DexPanel now)
const LiveTicker = () => null;

Object.assign(window, { PedbyLogo, BaseBadge, CurveProgress, Sparkline, CandleChart, TokenCard, TokenGlyph, StatusDot, Chip, PedbyNav, LiveTicker, TradeTape, tokenTint, SocialLinks, pedbySocialUrl });
