/* ============================================================
   basket.fun — shared components, icons, charts
   Exposes everything onto window at the bottom.
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------------- responsive hook ---------------- */
function useIsMobile(bp = 760) {
  const [m, setM] = useState(typeof window !== 'undefined' ? window.innerWidth <= bp : false);
  useEffect(() => {
    const on = () => setM(window.innerWidth <= bp);
    window.addEventListener('resize', on);
    on();
    return () => window.removeEventListener('resize', on);
  }, [bp]);
  return m;
}

/* ---------------- in-view detection (robust: IO + rect check + fallback) ---------------- */
function useInView({ threshold = 0.15, rootMargin = '0px 0px -6% 0px', once = true } = {}) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    let done = false;
    const trigger = () => { if (done && once) return; done = true; setInView(true); };
    const check = () => {
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.top < vh * 0.94 && r.bottom > 0) trigger();
    };
    let io;
    try {
      io = new IntersectionObserver((es) => es.forEach(e => { if (e.isIntersecting) trigger(); }), { threshold, rootMargin });
      io.observe(el);
    } catch (e) {}
    // immediate check across two frames (handles iframe-initially-hidden viewport)
    const raf = requestAnimationFrame(() => requestAnimationFrame(check));
    const onScroll = () => check();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    // safety: never leave content hidden
    const fb = setTimeout(trigger, 1500);
    return () => { io && io.disconnect(); cancelAnimationFrame(raf); clearTimeout(fb); window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); };
  }, []);
  return [ref, inView];
}

/* ---------------- scroll reveal (WAAPI-driven; visible by default) ---------------- */
function Reveal({ children, delay = 0, y = 20, style, as = 'div', className = '' }) {
  const [ioRef, seen] = useInView();
  const elRef = useRef(null);
  const played = useRef(false);
  const setRef = (n) => { elRef.current = n; ioRef.current = n; };
  useEffect(() => {
    if (seen && elRef.current && elRef.current.animate && !played.current) {
      played.current = true;
      const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      if (reduce) return;
      elRef.current.animate(
        [{ opacity: 0, transform: `translateY(${y}px)` }, { opacity: 1, transform: 'none' }],
        { duration: 680, easing: 'cubic-bezier(.22,.61,.36,1)', delay: 70 + delay, fill: 'forwards' }
      );
    }
  }, [seen]);
  const El = as;
  return <El ref={setRef} className={`${className}`} style={style}>{children}</El>;
}

/* ---------------- count-up number ---------------- */
function useCountUp(target, { dur = 1100, start = true } = {}) {
  const [v, setV] = useState(0);
  const raf = useRef(0);
  useEffect(() => {
    if (!start) return;
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce) { setV(target); return; }
    let t0;
    const tick = (t) => {
      if (!t0) t0 = t;
      const p = Math.min(1, (t - t0) / dur);
      const e = 1 - Math.pow(1 - p, 3); // easeOutCubic
      setV(target * e);
      if (p < 1) raf.current = requestAnimationFrame(tick);
    };
    raf.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf.current);
  }, [target, start]);
  return v;
}
// reveal-then-count: starts when scrolled into view
function CountUp({ to, dur = 1100, format = (n) => Math.round(n).toLocaleString(), className, style }) {
  const [ref, go] = useInView({ threshold: 0.4 });
  const v = useCountUp(to, { dur, start: go });
  return <span ref={ref} className={className} style={style}>{format(v)}</span>;
}

/* ---------------- theme ---------------- */
function applyTheme(t) {
  document.documentElement.dataset.theme = t;
  try { localStorage.setItem('basket.theme', t); } catch (e) {}
}
function useTheme() {
  const [theme, setTheme] = useState(() => {
    try { return localStorage.getItem('basket.theme') || document.documentElement.dataset.theme || 'light'; } catch (e) { return 'light'; }
  });
  useEffect(() => { applyTheme(theme); }, [theme]);
  return [theme, () => setTheme(t => (t === 'dark' ? 'light' : 'dark'))];
}
function ThemeToggle({ size = 40 }) {
  const [theme, toggle] = useTheme();
  const dark = theme === 'dark';
  return (
    <button onClick={toggle} aria-label="Toggle theme" title={dark ? 'Switch to light' : 'Switch to dark'}
      style={{
        width: size, height: size, borderRadius: 11, border: '1px solid var(--line)', background: 'var(--surface)',
        cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', overflow: 'hidden',
      }}>
      <span style={{ display: 'inline-flex', transition: 'transform .4s cubic-bezier(.6,0,.2,1)', transform: dark ? 'rotate(0deg)' : 'rotate(360deg)' }}>
        {dark
          ? <svg width={size * 0.46} height={size * 0.46} viewBox="0 0 24 24" fill="none"><path d="M21 12.8A8.5 8.5 0 1111.2 3a6.5 6.5 0 009.8 9.8z" stroke="var(--text-2)" strokeWidth="1.8" strokeLinejoin="round" /></svg>
          : <svg width={size * 0.5} height={size * 0.5} viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="4.2" stroke="var(--accent-ink)" strokeWidth="1.8" /><g stroke="var(--accent-ink)" strokeWidth="1.8" strokeLinecap="round">{[0,45,90,135,180,225,270,315].map(a=>{const r=a*Math.PI/180,x1=12+Math.cos(r)*7.2,y1=12+Math.sin(r)*7.2,x2=12+Math.cos(r)*9.2,y2=12+Math.sin(r)*9.2;return <line key={a} x1={x1} y1={y1} x2={x2} y2={y2} />;})}</g></svg>}
      </span>
    </button>
  );
}

/* ---------------- Logo ---------------- */
// The real brand mark (creature sitting in a basket).
const MARK_PATH = "M1769 2561 c-25 -141 2 -309 65 -409 l25 -41 -18 -43 c-10 -24 -30 -78 -45 -120 -64 -179 -155 -234 -298 -177 -349 138 -752 51 -1009 -217 -193 -203 -298 -513 -260 -768 14 -98 48 -125 116 -94 30 14 28 0 29 178 2 172 17 243 82 375 122 251 341 401 641 440 77 10 223 -12 348 -52 169 -55 269 -39 365 57 52 52 83 116 136 277 53 161 53 149 5 223 -47 72 -86 230 -61 245 29 18 143 -167 156 -251 8 -53 15 -60 79 -69 94 -13 149 -42 244 -126 93 -83 105 -104 73 -132 -15 -14 -41 -17 -138 -17 -199 0 -253 -41 -316 -245 -97 -312 -209 -454 -417 -529 -52 -19 -85 -20 -496 -23 -431 -4 -440 -4 -458 -24 -23 -28 -22 -105 2 -201 94 -368 412 -603 816 -602 396 0 727 251 815 617 29 118 27 170 -6 196 -64 50 -120 10 -135 -94 -63 -459 -573 -703 -1014 -486 -99 49 -179 120 -243 214 -47 70 -100 206 -88 225 3 5 137 7 328 5 439 -6 531 8 680 106 171 111 265 264 357 578 34 114 57 128 228 137 181 9 266 92 223 220 -34 103 -235 259 -375 290 -49 11 -49 12 -70 73 -48 137 -127 230 -253 296 -96 51 -98 50 -113 -32z";
function Mark({ size = 30, stroke, glow = true }) {
  const col = stroke || 'var(--logo-ink)';
  return (
    <svg width={size} height={size} viewBox="0 0 293 285" fill="none"
      style={{ filter: glow ? 'var(--logo-glow)' : 'none', flexShrink: 0, display: 'block' }}>
      <g transform="translate(0,285) scale(0.1,-0.1)" fill={col} stroke="none">
        <path d={MARK_PATH} />
      </g>
    </svg>
  );
}

function Logo({ size = 30, showWord = true, word = 'basket' }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
      <Mark size={size} />
      {showWord && (
        <span style={{ fontWeight: 300, fontSize: size * 0.74, letterSpacing: '-0.01em', color: 'var(--text)' }}>
          {word}
        </span>
      )}
    </div>
  );
}

/* ---------------- Icons (outline set) ---------------- */
const ICONS = {
  explore:  'M11 4a7 7 0 105.2 11.7l3.6 3.6 1.4-1.4-3.6-3.6A7 7 0 0011 4zm0 2a5 5 0 110 10 5 5 0 010-10z',
  basket:   'M5 9h14l-1.3 9.3a2 2 0 01-2 1.7H8.3a2 2 0 01-2-1.7L5 9zm3-3a4 4 0 018 0M9 12v4M15 12v4',
  wallet:   'M4 7a2 2 0 012-2h12a2 2 0 012 2v10a2 2 0 01-2 2H6a2 2 0 01-2-2V7zm13 4a1.5 1.5 0 100 3 1.5 1.5 0 000-3z',
  chart:    'M4 19V5M4 19h16M8 16l3-4 3 2 4-6',
  user:     'M12 12a4 4 0 100-8 4 4 0 000 8zm-7 8a7 7 0 0114 0',
  bell:     'M6 16V10a6 6 0 1112 0v6l2 2H4l2-2zM10 20a2 2 0 004 0',
  plus:     'M12 5v14M5 12h14',
  shield:   'M12 3l7 3v6c0 4.4-3 7.6-7 9-4-1.4-7-4.6-7-9V6l7-3z',
  swap:     'M7 7h11l-3-3M17 17H6l3 3',
  arrowUp:  'M12 19V5M6 11l6-6 6 6',
  arrowDn:  'M12 5v14M6 13l6 6 6-6',
  arrowR:   'M5 12h14M13 6l6 6-6 6',
  check:    'M5 12l5 5L20 6',
  clock:    'M12 3a9 9 0 100 18 9 9 0 000-18zm0 4v5l3 2',
  bolt:     'M13 2L4 14h6l-1 8 9-12h-6l1-8z',
  copy:     'M9 9h10v10H9zM5 15V5h10',
  spark:    'M12 2l2.4 6.4L21 9l-5 4 1.6 7L12 16.4 6.4 20 8 13 3 9l6.6-.6L12 2z',
  layers:   'M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5M3 17l9 5 9-5',
  info:     'M12 3a9 9 0 100 18 9 9 0 000-18zm0 5v.5M12 11v6',
  x:        'M6 6l12 12M18 6L6 18',
  ext:      'M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 01-1 1H5a1 1 0 01-1-1V7a1 1 0 011-1h5',
  swap2:    'M16 4l4 4-4 4M20 8H9M8 20l-4-4 4-4M4 16h11',
  gauge:    'M12 13l4-4M5 18a9 9 0 1114 0',
  flame:    'M12 3c3 4 6 6 6 10a6 6 0 01-12 0c0-2 1-3 2-4 0 1 .5 2 1.5 2.2C9 11 9 8 12 3z',
  lock:     'M6 10V8a6 6 0 1112 0v2M5 10h14v10H5z',
  grid:     'M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z',
  list:     'M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01',
};
function Icon({ name, size = 18, color = 'currentColor', sw = 1.8, fill = 'none', style }) {
  const d = ICONS[name];
  const filled = ['spark'].includes(name);
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={filled ? color : 'none'} style={style}>
      <path d={d} stroke={filled ? 'none' : color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function walletBrandKind(id, label, chain) {
  const s = `${id || ''} ${label || ''}`.toLowerCase();
  if (s.includes('phantom')) return 'phantom';
  if (s.includes('okx')) return 'okx';
  if (s.includes('backpack')) return 'backpack';
  if (s.includes('solflare')) return 'solflare';
  if (s.includes('metamask')) return 'metamask';
  if (s.includes('coinbase')) return 'coinbase';
  if (s.includes('rabby')) return 'rabby';
  if (s.includes('base')) return 'base';
  if (s.includes('browser solana') || chain === 'solana' || chain === 'sol') return 'solana';
  if (chain === 'base' || chain === 'evm') return 'evm';
  return 'wallet';
}

function WalletBrandIcon({ id, label, chain, size = 28, style }) {
  const kind = walletBrandKind(id, label, chain);
  const common = {
    width: size,
    height: size,
    borderRadius: Math.round(size * 0.32),
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    flexShrink: 0,
    overflow: 'hidden',
    boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.18)',
    ...style,
  };
  const svgProps = { width: size, height: size, viewBox: '0 0 32 32', style: { display: 'block' } };
  const mono = { fontFamily: 'Inter, var(--font)', fontSize: 13, fontWeight: 850, fill: '#fff', dominantBaseline: 'middle', textAnchor: 'middle' };

  if (kind === 'phantom') {
    return (
      <span title="Phantom" style={{ ...common, background: '#AB9FF2' }}>
        <svg {...svgProps}>
          <path d="M7 20.6c0-7 4.2-11.6 10-11.6 5.2 0 8 3.7 8 8.8 0 3.5-1.4 5.8-3.7 5.8-1.4 0-2.3-.8-2.5-2.1-.9 1.4-2.2 2.1-3.8 2.1h-2.6c-3.1 0-5.4-.8-5.4-3z" fill="#fff" opacity="0.96" />
          <circle cx="14" cy="15.8" r="1.35" fill="#AB9FF2" />
          <circle cx="20.2" cy="15.8" r="1.35" fill="#AB9FF2" />
          <path d="M23.6 19.4h2.2M22.8 22h1.7" stroke="#fff" strokeWidth="1.6" strokeLinecap="round" opacity="0.92" />
        </svg>
      </span>
    );
  }
  if (kind === 'okx') {
    return (
      <span title="OKX Wallet" style={{ ...common, background: '#050505' }}>
        <svg {...svgProps}>
          {[[7,7],[17,7],[7,17],[17,17]].map(([x,y], i) => <rect key={i} x={x} y={y} width="8" height="8" rx="1.4" fill="#fff" />)}
        </svg>
      </span>
    );
  }
  if (kind === 'backpack') {
    return (
      <span title="Backpack" style={{ ...common, background: 'linear-gradient(135deg,#14151f,#272a3a)' }}>
        <svg {...svgProps}>
          <rect x="8.2" y="9.8" width="15.6" height="13.2" rx="4" fill="#fff" />
          <path d="M12 10c.6-2.2 2-3.4 4-3.4s3.4 1.2 4 3.4" stroke="#fff" strokeWidth="2" strokeLinecap="round" fill="none" />
          <circle cx="13.4" cy="16.5" r="1.35" fill="#171923" />
          <circle cx="18.6" cy="16.5" r="1.35" fill="#171923" />
        </svg>
      </span>
    );
  }
  if (kind === 'solflare') {
    return (
      <span title="Solflare" style={{ ...common, background: 'linear-gradient(135deg,#FFB000,#FF5A1F)' }}>
        <svg {...svgProps}>
          <circle cx="16" cy="16" r="5.2" fill="#fff" />
          {[0,45,90,135,180,225,270,315].map(a => {
            const r = a * Math.PI / 180;
            const x1 = 16 + Math.cos(r) * 8.1, y1 = 16 + Math.sin(r) * 8.1;
            const x2 = 16 + Math.cos(r) * 11, y2 = 16 + Math.sin(r) * 11;
            return <line key={a} x1={x1} y1={y1} x2={x2} y2={y2} stroke="#fff" strokeWidth="2" strokeLinecap="round" />;
          })}
        </svg>
      </span>
    );
  }
  if (kind === 'metamask') {
    return (
      <span title="MetaMask" style={{ ...common, background: '#FFF3E6' }}>
        <svg {...svgProps}>
          <path d="M7 7l7 5-2.7 3.4L7 13z" fill="#E2761B" />
          <path d="M25 7l-7 5 2.7 3.4L25 13z" fill="#F6851B" />
          <path d="M11.3 15.4l4.7-2.8 4.7 2.8-1.3 7.1H12.6z" fill="#F6851B" />
          <path d="M12.6 22.5l3.4 2 3.4-2-3.4-2z" fill="#C65A1E" />
        </svg>
      </span>
    );
  }
  if (kind === 'coinbase') {
    return (
      <span title="Coinbase Wallet" style={{ ...common, background: '#0052FF' }}>
        <svg {...svgProps}>
          <circle cx="16" cy="16" r="8.8" fill="none" stroke="#fff" strokeWidth="4.2" />
          <rect x="16" y="11.4" width="7.8" height="9.2" fill="#0052FF" />
        </svg>
      </span>
    );
  }
  if (kind === 'rabby') {
    return (
      <span title="Rabby" style={{ ...common, background: 'linear-gradient(135deg,#6B7FFF,#43D6FF)' }}>
        <svg {...svgProps}>
          <path d="M10 14c-.8-3.6.2-6 2.2-6 1.5 0 2.6 1.6 3 4h1.6c.4-2.4 1.5-4 3-4 2 0 3 2.4 2.2 6 1.5 1.1 2.4 2.8 2.4 4.7 0 3.7-3.3 6.3-8.4 6.3s-8.4-2.6-8.4-6.3c0-1.9.9-3.6 2.4-4.7z" fill="#fff" />
          <circle cx="13.2" cy="18" r="1.15" fill="#5267FF" />
          <circle cx="18.8" cy="18" r="1.15" fill="#5267FF" />
        </svg>
      </span>
    );
  }
  if (kind === 'base') {
    return (
      <span title="Base" style={{ ...common, background: '#0052FF', borderRadius: 999 }}>
        <svg {...svgProps}>
          <circle cx="16" cy="16" r="8" fill="none" stroke="#fff" strokeWidth="5" />
          <rect x="16" y="10" width="9" height="12" fill="#0052FF" />
        </svg>
      </span>
    );
  }
  if (kind === 'solana') {
    return (
      <span title={label || 'Solana wallet'} style={{ ...common, background: 'linear-gradient(135deg,#14F195,#9945FF)' }}>
        <svg {...svgProps}>
          <path d="M10 10h13l-2 3H8zM9 15h13l-2 3H7zM8 20h13l-2 3H6z" fill="#fff" opacity="0.95" />
        </svg>
      </span>
    );
  }
  if (kind === 'evm') {
    return (
      <span title={label || 'EVM wallet'} style={{ ...common, background: 'linear-gradient(135deg,#4C6FFF,#1DC9A0)' }}>
        <svg {...svgProps}><text x="16" y="16.6" {...mono}>E</text></svg>
      </span>
    );
  }
  return (
    <span title={label || 'Wallet'} style={{ ...common, background: 'var(--surface-2)', color: 'var(--text-2)', border: '1px solid var(--line)' }}>
      <Icon name="wallet" size={Math.round(size * 0.58)} color="currentColor" />
    </span>
  );
}

/* ---------------- formatting helpers ---------------- */
const fmtUsd = (n, dp = 2) => '$' + n.toLocaleString('en-US', { minimumFractionDigits: dp, maximumFractionDigits: dp });
const fmtCompact = (n) => {
  if (n >= 1e9) return '$' + (n / 1e9).toFixed(2) + 'B';
  if (n >= 1e6) return '$' + (n / 1e6).toFixed(2) + 'M';
  if (n >= 1e3) return '$' + (n / 1e3).toFixed(1) + 'K';
  return '$' + n.toFixed(0);
};
const sign = (n) => (n > 0 ? '+' : n < 0 ? '−' : '');
const fmtPct = (n) => sign(n) + Math.abs(n).toFixed(1) + '%';

/* ---------------- Pct chip ---------------- */
function Pct({ v, size = 14, weight = 600, arrow = false, bg = false }) {
  const pos = v >= 0;
  const col = pos ? 'var(--accent-ink)' : 'var(--danger)';
  const inner = (
    <span className="tnum" style={{ color: col, fontWeight: weight, fontSize: size, display: 'inline-flex', alignItems: 'center', gap: 3 }}>
      {arrow && <Icon name={pos ? 'arrowUp' : 'arrowDn'} size={size - 1} color={col} sw={2.4} />}
      {fmtPct(v)}
    </span>
  );
  if (!bg) return inner;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', padding: '3px 9px', borderRadius: 999,
      background: pos ? 'var(--accent-wash)' : 'rgba(255,107,107,0.12)',
    }}>{inner}</span>
  );
}

/* ---------------- Sparkline ---------------- */
function Sparkline({ data, w = 96, h = 32, color = 'var(--accent-ink)', sw = 2, fill = false }) {
  const max = Math.max(...data), min = Math.min(...data);
  const rng = max - min || 1;
  const pts = data.map((v, i) => [
    (i / (data.length - 1)) * w,
    h - 4 - ((v - min) / rng) * (h - 8),
  ]);
  const d = pts.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
  const area = d + ` L${w} ${h} L0 ${h} Z`;
  const id = useMemo(() => 'sg' + Math.random().toString(36).slice(2, 8), []);
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block', overflow: 'visible' }}>
      {fill && (
        <>
          <defs>
            <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={color} stopOpacity="0.28" />
              <stop offset="100%" stopColor={color} stopOpacity="0" />
            </linearGradient>
          </defs>
          <path d={area} fill={`url(#${id})`} />
        </>
      )}
      <path d={d} fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

/* ---------------- Area performance chart (animated draw) ---------------- */
function AreaChart({ data, w = 720, h = 240, color = 'var(--accent-ink)', animate = true, showAxis = true }) {
  const max = Math.max(...data), min = Math.min(...data);
  const rng = max - min || 1;
  const padT = 16, padB = 8;
  const pts = data.map((v, i) => [
    (i / (data.length - 1)) * w,
    padT + (h - padT - padB) - ((v - min) / rng) * (h - padT - padB),
  ]);
  // smooth path (catmull-rom-ish)
  let d = `M${pts[0][0]} ${pts[0][1]}`;
  for (let i = 0; i < pts.length - 1; i++) {
    const [x0, y0] = pts[i], [x1, y1] = pts[i + 1];
    const cx = (x0 + x1) / 2;
    d += ` C${cx} ${y0}, ${cx} ${y1}, ${x1} ${y1}`;
  }
  const area = d + ` L${w} ${h} L0 ${h} Z`;
  const id = useMemo(() => 'ag' + Math.random().toString(36).slice(2, 8), []);
  const ref = useRef(null);
  const [len, setLen] = useState(2000);
  useEffect(() => { if (ref.current) setLen(ref.current.getTotalLength()); }, [data]);
  const last = pts[pts.length - 1];
  return (
    <svg width="100%" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: 'block', overflow: 'visible' }}>
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.26" />
          <stop offset="80%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {showAxis && [0.25, 0.5, 0.75].map((g, i) => (
        <line key={i} x1="0" x2={w} y1={padT + (h - padT - padB) * g} y2={padT + (h - padT - padB) * g}
          stroke="var(--line)" strokeWidth="1" strokeDasharray="2 6" />
      ))}
      <path d={area} fill={`url(#${id})`} style={animate ? { animation: 'fadeIn .8s ease .3s forwards' } : {}} />
      <path ref={ref} d={d} fill="none" stroke={color} strokeWidth="2.5" strokeLinecap="round"
        style={animate ? { '--len': len, strokeDasharray: len, animation: 'drawLine 1.1s cubic-bezier(.6,0,.2,1) .05s forwards' } : {}} />
      <circle cx={last[0]} cy={last[1]} r="4.5" fill={color}
        style={animate ? { animation: 'fadeIn .3s ease 1s forwards' } : {}}>
        <animate attributeName="r" values="4.5;7;4.5" dur="2.4s" repeatCount="indefinite" />
      </circle>
    </svg>
  );
}

/* ---------------- Weight ring (donut) ---------------- */
function WeightRing({ tokens, size = 180, thickness = 18, gap = 2, centerLabel, centerSub }) {
  const r = (size - thickness) / 2;
  const c = 2 * Math.PI * r;
  let acc = 0;
  const [ref, go] = useInView({ threshold: 0.3 });
  return (
    <div ref={ref} style={{ position: 'relative', width: size, height: size }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ transform: 'rotate(-90deg)', overflow: 'visible' }}>
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--line)" strokeWidth={thickness} />
        {tokens.map((t, i) => {
          const frac = t.weight / 100;
          const seg = c * frac;
          const visible = Math.max(0, seg - gap);
          const dash = `${visible} ${c - visible}`;
          const off = -acc * c;
          acc += frac;
          return (
            <circle key={i} cx={size / 2} cy={size / 2} r={r} fill="none"
              stroke={t.color} strokeWidth={thickness}
              strokeDasharray={go ? dash : `0 ${c}`} strokeDashoffset={off}
              strokeLinecap="butt"
              style={{ transition: `stroke-dasharray .9s cubic-bezier(.22,.61,.36,1) ${0.08 + i * 0.11}s`, filter: 'drop-shadow(0 0 5px ' + t.color + '55)' }} />
          );
        })}
      </svg>
      {centerLabel !== undefined && (
        <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 2, animation: 'fadeIn .5s ease .5s forwards' }}>
          <div style={{ fontSize: size * 0.16, fontWeight: 700, letterSpacing: '-0.02em' }} className="tnum">{centerLabel}</div>
          {centerSub && <div style={{ fontSize: 11, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{centerSub}</div>}
        </div>
      )}
    </div>
  );
}

/* ---------------- Token dot / avatar ---------------- */
function TokenDot({ color, sym, logoUrl, logoSources, size = 28, ring = false }) {
  const sources = useMemo(() => {
    const list = [];
    const add = (url) => { if (url && !list.includes(url)) list.push(url); };
    add(logoUrl);
    (logoSources || []).forEach(add);
    return list;
  }, [logoUrl, JSON.stringify(logoSources || [])]);
  const [srcIndex, setSrcIndex] = useState(0);
  useEffect(() => { setSrcIndex(0); }, [sources.join('|')]);
  const src = sources[srcIndex] || '';
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flexShrink: 0,
      background: `linear-gradient(135deg, ${color}, ${color}cc)`,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: size * 0.34, fontWeight: 700, color: '#08110a',
      boxShadow: ring ? `0 0 0 3px var(--bg)` : 'none',
      border: '1px solid rgba(255,255,255,0.16)',
      overflow: 'hidden',
    }}>
      {src ? (
        <img src={src} alt={sym || 'token'} loading="lazy" referrerPolicy="no-referrer" onError={() => setSrcIndex(i => i + 1)}
          style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
      ) : (sym ? sym.slice(0, 2) : '')}
    </div>
  );
}

function TokenStack({ tokens, size = 26, max = 5 }) {
  const shown = tokens.slice(0, max);
  const extra = tokens.length - shown.length;
  return (
    <div style={{ display: 'flex', alignItems: 'center' }}>
      {shown.map((t, i) => (
        <div key={i} style={{ marginLeft: i ? -size * 0.34 : 0, zIndex: shown.length - i }}>
          <TokenDot color={t.color} sym={t.sym} logoUrl={t.logo_url || t.logoUrl} logoSources={t.logo_sources || t.logoSources} size={size} ring />
        </div>
      ))}
      {extra > 0 && (
        <div style={{
          marginLeft: -size * 0.34, width: size, height: size, borderRadius: '50%',
          background: 'var(--surface-2)', border: '1px solid var(--line-2)', boxShadow: '0 0 0 3px var(--bg)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: size * 0.34, fontWeight: 600, color: 'var(--text-2)',
        }}>+{extra}</div>
      )}
    </div>
  );
}

/* ---------------- KOL avatar ---------------- */
function KolAvatar({ kol, size = 40, ring = false }) {
  const name = (kol && kol.name) || (kol && kol.handle) || 'KOL';
  const avatarColor = (kol && kol.avatar) || 'var(--blue)';
  const initials = name.split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
  const [imgFailed, setImgFailed] = useState(false);
  const img = kol && kol.avatarUrl && !imgFailed ? kol.avatarUrl : '';
  return (
    <div style={{ position: 'relative', flexShrink: 0 }}>
      <div style={{
        width: size, height: size, borderRadius: '50%',
        background: `radial-gradient(120% 120% at 30% 20%, ${avatarColor}, ${avatarColor}88 70%, ${avatarColor}44)`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontWeight: 700, fontSize: size * 0.36, color: '#0b1402',
        border: ring ? '2px solid var(--bg)' : '1px solid rgba(255,255,255,0.18)',
        overflow: 'hidden',
      }}>
        {img ? (
          <img src={img} alt={name} loading="lazy" referrerPolicy="no-referrer" onError={() => setImgFailed(true)}
            style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
        ) : initials}
      </div>
      {kol && kol.verified && (
        <div style={{
          position: 'absolute', right: -2, bottom: -2, width: size * 0.42, height: size * 0.42,
          background: 'var(--blue)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
          border: '2px solid var(--bg)',
        }}>
          <Icon name="check" size={size * 0.24} color="#fff" sw={3} />
        </div>
      )}
    </div>
  );
}

/* ---------------- Button ---------------- */
function Btn({ children, variant = 'primary', size = 'md', full, icon, iconR, onClick, disabled, style }) {
  const sizes = {
    sm: { padding: '8px 14px', fontSize: 13, radius: 10 },
    md: { padding: '12px 20px', fontSize: 15, radius: 12 },
    lg: { padding: '15px 26px', fontSize: 16, radius: 14 },
  }[size];
  const variants = {
    primary: { background: 'var(--accent)', color: 'var(--on-accent)', border: '1px solid var(--accent)', boxShadow: '0 6px 20px -6px var(--accent-glow)' },
    dark:    { background: 'var(--primary)', color: 'var(--text)', border: '1px solid var(--line-2)' },
    ghost:   { background: 'transparent', color: 'var(--text)', border: '1px solid var(--line-2)' },
    soft:    { background: 'var(--surface-2)', color: 'var(--text)', border: '1px solid var(--line)' },
    danger:  { background: 'rgba(255,107,107,0.12)', color: 'var(--danger)', border: '1px solid rgba(255,107,107,0.3)' },
    blue:    { background: 'var(--blue)', color: '#fff', border: '1px solid var(--blue)', boxShadow: '0 6px 20px -6px rgba(107,127,255,0.5)' },
  }[variant];
  const [hov, setHov] = useState(false);
  return (
    <button onClick={onClick} disabled={disabled}
      onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      className={variant === 'primary' || variant === 'blue' ? 'sheen' : ''}
      style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        fontFamily: 'var(--font)', fontWeight: 700, cursor: disabled ? 'not-allowed' : 'pointer',
        padding: sizes.padding, fontSize: sizes.fontSize, borderRadius: sizes.radius,
        width: full ? '100%' : 'auto', letterSpacing: '-0.01em',
        opacity: disabled ? 0.45 : 1,
        transform: hov && !disabled ? 'translateY(-1.5px)' : 'none',
        transition: 'transform .18s cubic-bezier(.22,.61,.36,1), filter .18s ease, box-shadow .18s, background .15s',
        filter: hov && !disabled ? 'brightness(1.05)' : 'none',
        ...variants, ...style,
      }}>
      {icon && <Icon name={icon} size={sizes.fontSize + 2} color="currentColor" sw={2} />}
      {children}
      {iconR && <Icon name={iconR} size={sizes.fontSize + 2} color="currentColor" sw={2} />}
    </button>
  );
}

/* ---------------- Chip ---------------- */
function Chip({ children, active, dot, onClick, color }) {
  return (
    <button onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 7, padding: '7px 14px', borderRadius: 999,
      fontFamily: 'var(--font)', fontSize: 13, fontWeight: 600, cursor: onClick ? 'pointer' : 'default',
      background: active ? 'var(--text)' : 'var(--surface)',
      color: active ? 'var(--bg)' : 'var(--text-2)',
      border: '1px solid ' + (active ? 'var(--text)' : 'var(--line)'),
      transition: 'all .15s', whiteSpace: 'nowrap',
    }}>
      {dot && <span style={{ width: 7, height: 7, borderRadius: 99, background: color || 'var(--accent)' }} />}
      {children}
    </button>
  );
}

/* ---------------- Card ---------------- */
function Card({ children, style, hover, onClick, pad = 22, glow = true, className = '', spot = false }) {
  const [h, setH] = useState(false);
  return (
    <div onClick={onClick}
      onMouseEnter={() => hover && setH(true)} onMouseLeave={() => hover && setH(false)}
      onMouseMove={spot ? spotlight : undefined}
      className={`surface-grad ${spot ? 'spot' : ''} ${className}`}
      style={{
        border: '1px solid var(--line)', borderRadius: 'var(--r-lg)',
        padding: pad, position: 'relative',
        transition: 'transform .26s cubic-bezier(.22,.61,.36,1), border-color .26s, box-shadow .26s',
        cursor: onClick ? 'pointer' : 'default',
        transform: h ? 'translateY(-4px)' : 'none',
        borderColor: h ? 'var(--line-strong)' : 'var(--line)',
        boxShadow: h ? (glow ? 'var(--shadow), var(--glow)' : 'var(--shadow)') : 'var(--shadow-sm)',
        ...style,
      }}>
      {children}
    </div>
  );
}

/* ---------------- Stat ---------------- */
function Stat({ label, value, sub, accent }) {
  return (
    <div>
      <div className="eyebrow" style={{ marginBottom: 6 }}>{label}</div>
      <div className="data" style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.03em', color: accent ? 'var(--accent-ink)' : 'var(--text)' }}>{value}</div>
      {sub && <div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 3 }}>{sub}</div>}
    </div>
  );
}

/* ---------------- Estimate badge (core product idea) ---------------- */
function EstimateBadge({ children = 'Estimate', tip }) {
  return (
    <span title={tip} style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, padding: '2px 8px', borderRadius: 999,
      fontSize: 10.5, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase',
      background: 'var(--blue-wash)', color: 'var(--blue)', border: '1px solid rgba(107,127,255,0.28)',
      cursor: tip ? 'help' : 'default',
    }}>
      <Icon name="info" size={11} color="var(--blue)" sw={2} />{children}
    </span>
  );
}

function ChainBadge({ chain, size = 'md' }) {
  const map = { sol: { label: 'Solana', color: '#9945FF' }, base: { label: 'Base', color: '#4C6FFF' } };
  const c = map[chain] || map.sol;
  const sm = size === 'sm';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, padding: sm ? '2px 7px' : '4px 10px', borderRadius: 999,
      fontSize: sm ? 10.5 : 12, fontWeight: 600, background: c.color + '1f', color: c.color, border: '1px solid ' + c.color + '40',
    }}>
      <span style={{ width: 6, height: 6, borderRadius: 99, background: c.color }} />{c.label}
    </span>
  );
}

/* ---------------- Bottom sheet (mobile) ---------------- */
function BottomSheet({ open, onClose, title, children, maxH = '82vh' }) {
  const [drag, setDrag] = useState(0);
  const [mounted, setMounted] = useState(open);
  const start = useRef(null);
  useEffect(() => {
    if (open) { setMounted(true); setDrag(0); }
    else { const t = setTimeout(() => setMounted(false), 280); return () => clearTimeout(t); }
  }, [open]);
  useEffect(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = prev; };
  }, [open]);
  if (!mounted) return null;

  const onTS = (e) => { start.current = e.touches[0].clientY; };
  const onTM = (e) => {
    if (start.current == null) return;
    const dy = e.touches[0].clientY - start.current;
    if (dy > 0) setDrag(dy);
  };
  const onTE = () => {
    if (drag > 90) onClose();
    setDrag(0);
    start.current = null;
  };

  return (
    <Portal>
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 85, display: 'flex', alignItems: 'flex-end',
      background: 'rgba(5,6,11,0.6)', backdropFilter: 'blur(3px)',
      opacity: open ? 1 : 0, transition: 'opacity .28s ease',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '100%', background: 'var(--surface)', borderTopLeftRadius: 24, borderTopRightRadius: 24,
        borderTop: '1px solid var(--line-2)', boxShadow: '0 -20px 50px -20px rgba(0,0,0,0.6)',
        maxHeight: maxH, display: 'flex', flexDirection: 'column',
        transform: open ? `translateY(${drag}px)` : 'translateY(100%)',
        transition: drag ? 'none' : 'transform .3s cubic-bezier(.22,.61,.36,1)',
        paddingBottom: 'env(safe-area-inset-bottom, 12px)',
      }}>
        <div onTouchStart={onTS} onTouchMove={onTM} onTouchEnd={onTE}
          style={{ padding: '12px 0 6px', display: 'flex', flexDirection: 'column', alignItems: 'center', cursor: 'grab', flexShrink: 0 }}>
          <div style={{ width: 40, height: 5, borderRadius: 99, background: 'var(--line-strong)' }} />
        </div>
        {title && (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 20px 14px', borderBottom: '1px solid var(--line)', flexShrink: 0 }}>
            <span style={{ fontWeight: 700, fontSize: 17 }}>{title}</span>
            <button onClick={onClose} style={{ width: 32, height: 32, borderRadius: 9, border: '1px solid var(--line)', background: 'var(--surface-2)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon name="x" size={16} color="var(--text-2)" />
            </button>
          </div>
        )}
        <div style={{ overflowY: 'auto', padding: '18px 20px 8px', WebkitOverflowScrolling: 'touch' }}>{children}</div>
      </div>
    </div>
    </Portal>
  );
}

/* ---------------- swipe hook ---------------- */
function useSwipe({ onLeft, onRight, threshold = 56 }) {
  const s = useRef(null);
  return {
    onTouchStart: (e) => { s.current = { x: e.touches[0].clientX, y: e.touches[0].clientY }; },
    onTouchEnd: (e) => {
      if (!s.current) return;
      const dx = e.changedTouches[0].clientX - s.current.x;
      const dy = e.changedTouches[0].clientY - s.current.y;
      if (Math.abs(dx) > threshold && Math.abs(dx) > Math.abs(dy) * 1.4) {
        if (dx < 0) onLeft && onLeft(); else onRight && onRight();
      }
      s.current = null;
    },
  };
}

/* ---------------- ticker tape ---------------- */
function fmtPrice(p) {
  if (p >= 1000) return '$' + p.toLocaleString('en-US', { maximumFractionDigits: 0 });
  if (p >= 1) return '$' + p.toFixed(2);
  if (p >= 0.001) return '$' + p.toFixed(3);
  return '$' + p.toExponential(1).replace('e-', 'e‑');
}
function TickerTape() {
  const { ticker } = window.DB;
  const [rows, setRows] = useState(() => ticker.map(t => ({ ...t, prev: t.price })));
  useEffect(() => {
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce) return;
    const id = setInterval(() => {
      setRows(rs => rs.map(t => {
        if (Math.random() > 0.5) return t; // only some tick each cycle
        const drift = (Math.random() - 0.48) * 0.012;
        const np = Math.max(t.price * 0.5, t.price * (1 + drift));
        return { ...t, prev: t.price, price: np, chg: +(t.chg + drift * 100).toFixed(1) };
      }));
    }, 1800);
    return () => clearInterval(id);
  }, []);
  const row = (key) => (
    <div className="marquee-track" aria-hidden={key !== 0}>
      {rows.concat(rows).map((t, i) => {
        const pos = t.chg >= 0;
        const up = t.price >= t.prev;
        return (
          <div key={key + '-' + i} style={{ display: 'inline-flex', alignItems: 'center', gap: 9, padding: '0 22px', borderRight: '1px solid var(--line)', whiteSpace: 'nowrap' }}>
            <span style={{ width: 7, height: 7, borderRadius: 99, background: t.color, boxShadow: `0 0 6px ${t.color}` }} />
            <span style={{ fontSize: 12, fontWeight: 700, letterSpacing: '-0.01em' }}>{t.sym}</span>
            <span key={t.price} className="data" style={{ fontSize: 12, color: 'var(--text-2)', animation: t.price !== t.prev ? `${up ? 'flashUp' : 'flashDn'} 1s ease` : 'none' }}>{fmtPrice(t.price)}</span>
            <span className="data" style={{ fontSize: 11.5, fontWeight: 600, color: pos ? 'var(--accent-ink)' : 'var(--danger)' }}>
              {pos ? '▲' : '▼'} {Math.abs(t.chg).toFixed(1)}%
            </span>
          </div>
        );
      })}
    </div>
  );
  return (
    <div className="marquee" style={{ borderBottom: '1px solid var(--line)', background: 'var(--bg-2)', height: 38, alignItems: 'center' }}>
      {row(0)}{row(1)}
    </div>
  );
}

/* ---------------- kinetic headline (WAAPI clip reveal per line) ---------------- */
function Kinetic({ lines, style, lineStyle }) {
  const [ioRef, seen] = useInView({ threshold: 0.2 });
  const h1Ref = useRef(null);
  const played = useRef(false);
  const setRef = (n) => { h1Ref.current = n; ioRef.current = n; };
  useEffect(() => {
    if (seen && h1Ref.current && !played.current) {
      played.current = true;
      const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      if (reduce) return;
      const spans = h1Ref.current.querySelectorAll('.kin-line > span');
      spans.forEach((s, i) => {
        if (!s.animate) return;
        s.animate(
          [{ opacity: 0, transform: 'translateY(115%) rotate(3deg)' }, { opacity: 1, transform: 'translateY(0) rotate(0)' }],
          { duration: 880, easing: 'cubic-bezier(.16,1,.3,1)', delay: 90 + i * 110, fill: 'forwards' }
        );
      });
    }
  }, [seen]);
  return (
    <h1 ref={setRef} className="display kin" style={{ margin: 0, ...style }}>
      {lines.map((ln, i) => (
        <span key={i} className="kin-line" style={lineStyle}>
          <span>{ln}</span>
        </span>
      ))}
    </h1>
  );
}

/* ---------------- section label (technical numbering) ---------------- */
function SectionLabel({ n, children, right }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18 }}>
      <span className="eyebrow" style={{ color: 'var(--accent-ink)' }}>{n}</span>
      <span style={{ height: 1, width: 26, background: 'var(--line-2)' }} />
      <span className="eyebrow" style={{ color: 'var(--text-2)' }}>{children}</span>
      <span style={{ flex: 1, height: 1, background: 'var(--line)' }} />
      {right}
    </div>
  );
}

/* ---------------- page header (eyebrow + display title) ---------------- */
function PageHead({ n, title, sub, right }) {
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16, marginBottom: 26, flexWrap: 'wrap' }}>
      <div>
        {n && <div className="eyebrow" style={{ color: 'var(--accent-ink)', marginBottom: 12 }}>{n}</div>}
        <h1 className="display" style={{ margin: 0, fontSize: 'clamp(30px, 3.4vw, 44px)', fontWeight: 900, lineHeight: 1.0 }}>{title}</h1>
        {sub && <p style={{ margin: '10px 0 0', color: 'var(--text-2)', fontSize: 15.5, maxWidth: 540, lineHeight: 1.5 }}>{sub}</p>}
      </div>
      {right}
    </div>
  );
}

/* ---------------- magnetic wrapper ---------------- */
function Magnetic({ children, strength = 0.32, style }) {
  const ref = useRef(null);
  const onMove = (e) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = (e.clientX - r.left - r.width / 2) * strength;
    const y = (e.clientY - r.top - r.height / 2) * strength;
    el.style.transform = `translate(${x}px, ${y}px)`;
  };
  const reset = () => { if (ref.current) ref.current.style.transform = 'translate(0,0)'; };
  return (
    <div ref={ref} onMouseMove={onMove} onMouseLeave={reset}
      style={{ display: 'inline-flex', transition: 'transform .35s cubic-bezier(.22,1,.3,1)', ...style }}>
      {children}
    </div>
  );
}

/* ---------------- spotlight handlers (attach to a .spot element) ---------------- */
function spotlight(e) {
  const el = e.currentTarget;
  const r = el.getBoundingClientRect();
  el.style.setProperty('--mx', ((e.clientX - r.left) / r.width) * 100 + '%');
  el.style.setProperty('--my', ((e.clientY - r.top) / r.height) * 100 + '%');
}

/* ---------------- portal (escape stacking contexts) ---------------- */
function Portal({ children }) {
  const el = useRef(null);
  if (!el.current) { el.current = document.createElement('div'); el.current.setAttribute('data-portal', ''); }
  useEffect(() => {
    document.body.appendChild(el.current);
    return () => { try { document.body.removeChild(el.current); } catch (e) {} };
  }, []);
  return ReactDOM.createPortal(children, el.current);
}

/* ---------------- skeleton + fake-load ---------------- */
function Skel({ w = '100%', h = 14, r = 8, style }) {
  return <span className="skel" style={{ display: 'inline-block', width: w, height: h, borderRadius: r, ...style }} />;
}
function useFakeLoad(ms = 600) {
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce) { setLoading(false); return; }
    const t = setTimeout(() => setLoading(false), ms);
    return () => clearTimeout(t);
  }, []);
  return loading;
}

Object.assign(window, {
  Mark, Logo, Icon, ICONS, WalletBrandIcon, Sparkline, AreaChart, WeightRing, TokenDot, TokenStack,
  KolAvatar, Btn, Chip, Card, Stat, Pct, EstimateBadge, ChainBadge,
  fmtUsd, fmtCompact, fmtPct, sign, useIsMobile, useTheme, ThemeToggle, applyTheme,
  Reveal, CountUp, useCountUp, BottomSheet, useSwipe,
  TickerTape, Kinetic, SectionLabel, PageHead, Magnetic, spotlight, fmtPrice, useInView,
  Skel, useFakeLoad, Portal,
});
