/* $CLOWNZ — sections built from the mascot library + tweaks. */

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

// Cycle through random jeeter rows
const SHAME_REASONS = [
  "Paper hands at -3%",
  "VC unlock dump 🤡",
  "Typical shill exit",
  "Telegram pump caller",
  "Insider seed allocation",
  "Market maker drain",
  "CEX listing dump",
  "Anon dev rug suspect",
  "Bought top, sold bottom",
  "'Trust me bro' KOL",
  "Snitched on Twitter Spaces",
  "Flipped while shilling",
];
const NAMES = [
  "0xRugLord","ChadVC.eth","FloorJanitor","GloriousJeeter","Pumperino",
  "ShillTzu","BagholderXL","WenLambo69","ClownDeck","ExitLiquidity",
  "MMSnake","RetroAirdrop","SizeMatters","DegenDave","RektMaximus"
];
const TIMES = ["12s ago","1m ago","3m ago","6m ago","11m ago","18m ago","27m ago","42m ago","1h ago","2h ago"];

function shorten(addr){return addr.slice(0,6)+"…"+addr.slice(-4)}
function rndAddr(seed){
  const hex="0123456789abcdef";let s="0x";let x=seed*9301+49297;
  for(let i=0;i<40;i++){x=(x*1664525+1013904223)>>>0;s+=hex[x%16]}return s;
}
function rndAmt(seed){
  const x=((seed*2654435761)>>>0)%900000+15000;
  return "$"+x.toLocaleString();
}

/* ---------- LIVE STATS ---------- */
const CONTRACT_ADDRESS = "33KvdXWCQvBAoESDS6wSuxZRh2cAbJsPuyDEoB8RwY6y";
const RPC_URL = "/api/rpc";

async function rpc(method, params=[]) {
  const r = await fetch(RPC_URL, {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({jsonrpc:'2.0', id:1, method, params}),
  });
  if (!r.ok) throw new Error('rpc http '+r.status);
  const j = await r.json();
  if (j.error) throw new Error(j.error.message);
  return j.result;
}

function timeAgo(ms) {
  const s = Math.max(0, Math.floor(ms/1000));
  if (s < 60) return s+'s ago';
  const m = Math.floor(s/60);
  if (m < 60) return m+'m ago';
  const h = Math.floor(m/60);
  if (h < 24) return h+'h ago';
  return Math.floor(h/24)+'d ago';
}

function hashStr(s) {
  let h = 2166136261 >>> 0;
  for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619) >>> 0; }
  return h;
}

function fmtMoney(n) {
  if (!n || !isFinite(n)) return "—";
  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)}`;
}
function fmtPrice(n) {
  if (!n || !isFinite(n)) return "—";
  if (n >= 1) return `$${n.toFixed(4)}`;
  if (n >= 0.001) return `$${n.toFixed(5)}`;
  if (n >= 0.00001) return `$${n.toFixed(7)}`;
  return `$${n.toExponential(3)}`;
}

const TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
let _solUsd = { v: 0, t: 0 };
async function getSolUsd() {
  if (Date.now() - _solUsd.t < 120000 && _solUsd.v > 0) return _solUsd.v;
  try {
    const r = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd');
    const j = await r.json();
    const v = j?.solana?.usd || 0;
    if (v > 0) _solUsd = { v, t: Date.now() };
    return v || _solUsd.v;
  } catch { return _solUsd.v || 0; }
}

// Cache shared across all hook consumers so we don't double-poll.
let _statsCache = null;
let _statsSubs = new Set();
function _notify() { _statsSubs.forEach(fn => fn(_statsCache)); }

let _statsLoopStarted = false;
async function _loadChainStats() {
  try {
    const solUsd = await getSolUsd();
    const supplyRes = await rpc('getTokenSupply', [CONTRACT_ADDRESS]);
    const supply = supplyRes?.value?.uiAmount || 0;

    const accounts = await rpc('getProgramAccounts', [TOKEN_PROGRAM, {
      encoding: 'jsonParsed',
      filters: [
        { dataSize: 165 },
        { memcmp: { offset: 0, bytes: CONTRACT_ADDRESS } }
      ]
    }]);
    // Group balances by owner; pool/curve PDAs holding >30% of supply get filtered
    // so the count tracks actual human holders rather than program vaults.
    const byOwner = new Map();
    for (const a of accounts || []) {
      const info = a?.account?.data?.parsed?.info;
      if (!info) continue;
      const amt = parseFloat(info.tokenAmount?.uiAmountString || '0');
      if (amt <= 0) continue;
      byOwner.set(info.owner, (byOwner.get(info.owner) || 0) + amt);
    }
    let holders = 0;
    for (const [, amt] of byOwner) {
      if (supply > 0 && amt / supply > 0.3) continue;
      holders++;
    }

    const sigs = await rpc('getSignaturesForAddress', [CONTRACT_ADDRESS, { limit: 1000 }]);
    const txCount = sigs?.length || 0;

    const dayAgo = Math.floor(Date.now()/1000) - 86400;
    const sample = (sigs || []).slice(0, 25);
    const txs = await Promise.all(sample.map(s =>
      rpc('getTransaction', [s.signature, {encoding:'jsonParsed', maxSupportedTransactionVersion: 0, commitment:'confirmed'}]).catch(() => null)
    ));

    let buys24h = 0, sells24h = 0, volumeSol24h = 0;
    // Collect meaningful swaps for volume-weighted price (filter dust).
    const recentSwaps = [];   // newest first
    const olderSwaps = [];    // pre-24h for change comparison
    const MIN_SOL = 0.02;     // ignore swaps smaller than this — Vybes platform fees + priority tips dominate dust trades
    const MIN_TOKENS = 1000;
    for (let i = 0; i < txs.length; i++) {
      const tx = txs[i];
      const sig = sample[i];
      if (!tx || !tx.meta) continue;
      const keys = tx.transaction?.message?.accountKeys || [];
      const feePayer = keys[0]?.pubkey || keys[0];
      const pre = (tx.meta.preTokenBalances||[]).find(b => b.mint === CONTRACT_ADDRESS && b.owner === feePayer);
      const post = (tx.meta.postTokenBalances||[]).find(b => b.mint === CONTRACT_ADDRESS && b.owner === feePayer);
      const preAmt = pre?.uiTokenAmount?.uiAmount || 0;
      const postAmt = post?.uiTokenAmount?.uiAmount || 0;
      const tokenDelta = postAmt - preAmt;
      if (Math.abs(tokenDelta) < 1) continue;
      const preSol = tx.meta.preBalances?.[0] || 0;
      const postSol = tx.meta.postBalances?.[0] || 0;
      const fee = tx.meta.fee || 0;
      const solSpent = (preSol - postSol - fee) / 1e9;
      const within24h = (sig.blockTime || 0) >= dayAgo;
      if (within24h) {
        if (tokenDelta > 0) buys24h++; else sells24h++;
        volumeSol24h += Math.abs(solSpent);
      }
      // Only use meaningful trades for price discovery — dust trades have
      // platform fees that make per-token price look 3–5x too high.
      if (Math.abs(solSpent) >= MIN_SOL && Math.abs(tokenDelta) >= MIN_TOKENS) {
        const swap = { sol: Math.abs(solSpent), tokens: Math.abs(tokenDelta), blockTime: sig.blockTime || 0 };
        if (within24h) recentSwaps.push(swap); else olderSwaps.push(swap);
      }
    }

    // VWAP across the last up-to-6 meaningful swaps (more samples = less noise).
    function vwap(arr, n=6) {
      const slice = arr.slice(0, n);
      let s = 0, t = 0;
      for (const x of slice) { s += x.sol; t += x.tokens; }
      return t > 0 ? s / t : 0;
    }
    const latestPriceSol = vwap(recentSwaps);
    const latestPriceSol24hAgo = vwap(olderSwaps);

    const priceUsd = latestPriceSol * solUsd;
    const priceUsd24hAgo = latestPriceSol24hAgo * solUsd;
    const marketCap = priceUsd * supply;
    const volume24h = volumeSol24h * solUsd;
    const change24h = priceUsd24hAgo > 0 ? ((priceUsd - priceUsd24hAgo) / priceUsd24hAgo) * 100 : 0;

    _statsCache = {
      loaded: true,
      available: priceUsd > 0 || holders > 0,
      price: priceUsd,
      priceSol: latestPriceSol,
      solUsd,
      marketCap,
      fdv: marketCap,
      holders,
      supply,
      txCount,
      buys24h,
      sells24h,
      volume24h,
      change24h,
    };
    _notify();
  } catch (e) {
    _statsCache = {
      loaded: true, available: false,
      price: 0, marketCap: 0, fdv: 0, holders: 0, supply: 0,
      txCount: 0, buys24h: 0, sells24h: 0, volume24h: 0, change24h: 0,
      ...(_statsCache || {}),
      loaded: true,
    };
    _notify();
  }
}

function useLiveStats() {
  const [data, setData] = useState(_statsCache || {
    loaded: false, available: false,
    price: 0, marketCap: 0, fdv: 0, holders: 0, supply: 0,
    txCount: 0, buys24h: 0, sells24h: 0, volume24h: 0, change24h: 0,
  });
  useEffect(() => {
    const fn = d => setData(d);
    _statsSubs.add(fn);
    if (!_statsLoopStarted) {
      _statsLoopStarted = true;
      _loadChainStats();
      setInterval(_loadChainStats, 45000);
    } else if (_statsCache) {
      setData(_statsCache);
    }
    return () => _statsSubs.delete(fn);
  }, []);
  return data;
}

function useLiveJeeters() {
  const [rows, setRows] = useState([]);
  const [status, setStatus] = useState('loading');
  useEffect(() => {
    let cancelled = false;
    async function load() {
      try {
        const sigs = await rpc('getSignaturesForAddress', [CONTRACT_ADDRESS, {limit: 25}]);
        if (cancelled) return;
        if (!sigs || sigs.length === 0) { setRows([]); setStatus('empty'); return; }
        const txs = await Promise.all(sigs.slice(0, 12).map(s =>
          rpc('getTransaction', [s.signature, {encoding:'jsonParsed', maxSupportedTransactionVersion: 0, commitment:'confirmed'}]).catch(() => null)
        ));
        if (cancelled) return;
        const jeets = [];
        txs.forEach((tx, i) => {
          if (!tx || !tx.meta) return;
          const keys = tx.transaction?.message?.accountKeys || [];
          const feePayer = keys[0]?.pubkey || keys[0];
          if (!feePayer) return;
          const pre = (tx.meta.preTokenBalances||[]).find(b => b.mint === CONTRACT_ADDRESS && b.owner === feePayer);
          const post = (tx.meta.postTokenBalances||[]).find(b => b.mint === CONTRACT_ADDRESS && b.owner === feePayer);
          const preAmt = pre?.uiTokenAmount?.uiAmount || 0;
          const postAmt = post?.uiTokenAmount?.uiAmount || 0;
          const delta = preAmt - postAmt;
          if (delta > 0) {
            const t = sigs[i].blockTime;
            const ageMs = t ? Date.now() - t*1000 : 0;
            const preSol = tx.meta.preBalances?.[0] || 0;
            const postSol = tx.meta.postBalances?.[0] || 0;
            const fee = tx.meta.fee || 0;
            const solReceived = Math.max(0, (postSol - preSol + fee) / 1e9);
            const h = hashStr(feePayer);
            const h2 = hashStr(sigs[i].signature);
            jeets.push({
              addr: feePayer,
              amount: delta,
              solReceived,
              ageMs,
              sig: sigs[i].signature,
              name: NAMES[h % NAMES.length],
              reason: SHAME_REASONS[h2 % SHAME_REASONS.length],
            });
          }
        });
        setRows(jeets.slice(0, 9));
        setStatus(jeets.length ? 'live' : 'no-jeets');
      } catch (e) {
        if (!cancelled) setStatus('error');
      }
    }
    load();
    const id = setInterval(load, 30000);
    return () => { cancelled = true; clearInterval(id); };
  }, []);
  return { rows, status };
}

/* ---------- HEADER NAV ---------- */
function Nav() {
  const [open, setOpen] = useState(false);
  useEffect(() => {
    document.body.style.overflow = open ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [open]);
  const close = () => setOpen(false);
  return (
    <nav className={"nav" + (open ? " nav-open" : "")}>
      <div className="brand">
        <span className="nose"></span>
        <span className="name">$CLOWNZ</span>
      </div>
      <ul>
        <li><a href="#dashboard" onClick={close}>SCOREBOARD</a></li>
        <li><a href="#jeeters" onClick={close}>JEETERS</a></li>
        <li><a href="#circus" onClick={close}>CIRCUS</a></li>
        <li><a href="#bigtop" onClick={close}>FAIR LAUNCH</a></li>
        <li><a href="#buy" onClick={close}>HOW TO BUY</a></li>
        <li className="nav-cta-mobile"><span className="cta cta-casino cta-soon" aria-disabled="true" title="Coming soon">🎰 CASINO<span className="soon-tag">COMING SOON</span></span></li>
        <li className="nav-cta-mobile"><a className="cta" href="https://vybes.fun/launch/b5df2907-8f63-4cda-93b3-27f602923d9d" target="_blank" rel="noopener noreferrer" onClick={close}>BUY $CLOWNZ →</a></li>
      </ul>
      <span className="cta cta-casino cta-soon nav-cta-desktop" aria-disabled="true" title="Coming soon">🎰 CASINO<span className="soon-tag">COMING SOON</span></span>
      <a className="cta nav-cta-desktop" href="https://vybes.fun/launch/b5df2907-8f63-4cda-93b3-27f602923d9d" target="_blank" rel="noopener noreferrer">BUY $CLOWNZ →</a>
      <button
        type="button"
        className="nav-toggle"
        aria-label={open ? "Close menu" : "Open menu"}
        aria-expanded={open}
        onClick={() => setOpen(o => !o)}
      >
        <span></span><span></span><span></span>
      </button>
    </nav>
  );
}

/* ---------- MARQUEE ---------- */
function Marquee() {
  const items = [
    "JEETED BY YOUR FAVOURITE INFLUENCER", "VCs IN CLOWN MAKEUP",
    "PAPER HANDS DETECTED", "ANOTHER MM JUST DRAINED A POOL",
    "TG GROUP LARPING AS COMMUNITY", "PUMP. DUMP. REPEAT.",
    "WE ARE ALL CLOWNS HERE", "THIS IS NOT FINANCIAL ADVICE",
    "🤡 NAME & SHAME 🤡"
  ];
  const row = (
    <div className="marquee-track">
      {[...items, ...items, ...items].map((t, i) => (
        <span key={i}><span className="dot"></span>{t}</span>
      ))}
    </div>
  );
  return <div className="marquee">{row}</div>;
}

/* ---------- HERO ---------- */
function Hero({ tweaks }) {
  const live = useLiveStats();
  const [copied, setCopied] = useState(false);
  const contract = CONTRACT_ADDRESS;
  const up = live.change24h >= 0;
  return (
    <header className="hero" id="top">
      <div className="hero-bg"><div className="spotlight"></div></div>
      <div className="hero-inner">
        <span className="eyebrow"><span className="blink"></span>LIVE • THE GREATEST SHOW IN WEB3</span>
        <h1>
          <span className="dollar">$</span>CLOWN<br/>ZZZ!
        </h1>
        <div className="tagline">The only honest memecoin in Web3 — <em>we're all clowns here.</em></div>
        <p className="sub">Exposing the shills, jeeters, VCs, and market makers one dump at a time. Powered entirely by paper hands and second-hand humiliation.</p>
        <div className="hero-ctas">
          <a className="btn-primary" href="https://vybes.fun/launch/b5df2907-8f63-4cda-93b3-27f602923d9d" target="_blank" rel="noopener noreferrer">BUY $CLOWNZ <span className="arrow">→</span></a>
          <a className="btn-ghost" href="#jeeters">SEE WHO JEETED</a>
          <div className="socials">
            <a href="https://x.com/clownzdotfun" target="_blank" rel="noopener noreferrer" title="X">𝕏</a>
            <a href="https://t.me/clownzdotfun" target="_blank" rel="noopener noreferrer" title="Telegram">TG</a>
          </div>
        </div>
        <div className="contract-row">
          <span className="label">CA</span>
          <code>{contract}</code>
          <button className="copy-btn" onClick={()=>{navigator.clipboard?.writeText(contract);setCopied(true);setTimeout(()=>setCopied(false),1200)}}>{copied?"COPIED":"COPY"}</button>
        </div>
      </div>

      <div className="hero-side">
        <div className="ticker-card">
          <div className="head">
            <h4>$CLOWNZ / USD</h4>
            <span className="live"><span className="pulse"></span>LIVE</span>
          </div>
          <div className="ticker-price">{live.available ? fmtPrice(live.price) : (live.loaded ? "PRE-GRAD" : "…")}</div>
          <div className="ticker-change">
            {live.available
              ? `${up ? '▲ +' : '▼ '}${Math.abs(live.change24h).toFixed(2)}% (24h) — pure clownery`
              : "ON VYBES.FUN BONDING CURVE"}
          </div>
          <div className="ticker-grid">
            <div><div className="k">Mkt Cap</div><div className="v">{live.available ? fmtMoney(live.marketCap) : "—"}</div></div>
            <div><div className="k">24h Vol</div><div className="v">{live.available ? fmtMoney(live.volume24h) : "—"}</div></div>
            <div><div className="k">Holders</div><div className="v">{live.holders ? Number(live.holders).toLocaleString() : "—"}</div></div>
            <div><div className="k">Total Txns</div><div className="v">{live.txCount ? Number(live.txCount).toLocaleString() : "—"}</div></div>
          </div>
        </div>

        <div className="mascot-wrap">
          <div className="ring"></div>
          <div className="juggle">
            <div className="ball b1">$</div>
            <div className="ball b2">RUG</div>
            <div className="ball b3">$</div>
            <div className="ball b4">VC</div>
          </div>
          <video
            className="hero-video"
            src="closeup-vid.mp4"
            autoPlay
            loop
            muted
            playsInline
            preload="auto"
            onMouseEnter={e => { const v = e.currentTarget; v.muted = false; v.play().catch(()=>{}); }}
            onMouseLeave={e => { e.currentTarget.muted = true; }}
          />
          <div className="nameplate" aria-hidden="true">
            <span className="nameplate-kicker">★ STARRING ★</span>
            <span className="nameplate-name">BOZO <em>BELFORT</em></span>
            <span className="nameplate-sub">CEO of Clowning, Inc.</span>
          </div>
        </div>
      </div>
    </header>
  );
}

/* ---------- DASHBOARD ---------- */
function Dashboard() {
  const live = useLiveStats();
  const [gauges, setGauges] = useState({rug:73, paper:88, vc:91});
  const [flashKey, setFlashKey] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setGauges({
        rug: 60 + Math.floor(Math.random()*30),
        paper: 70 + Math.floor(Math.random()*25),
        vc: 80 + Math.floor(Math.random()*15),
      });
      setFlashKey(k=>k+1);
    }, 2200);
    return () => clearInterval(id);
  }, []);
  const up = live.change24h >= 0;
  return (
    <section id="dashboard" className="confetti-bg">
      <div className="section-head">
        <span className="kicker">★ THE CARNIVAL SCOREBOARD ★</span>
        <h2>STEP RIGHT UP, <em>JEETER!</em></h2>
        <p>Live numbers, gauges, and spinning wheels. Watch the value of your bags evaporate in real-time, in the most entertaining format possible.</p>
      </div>
      <div className="dashboard">
        <span className="scoreboard-tag">★ LIVE SCOREBOARD ★</span>
        <div className="scoreboard-grid">
          <div className="score-cell" key={"p"+flashKey}>
            <div className="label">Price</div>
            <div className="value">{live.available ? fmtPrice(live.price) : (live.loaded ? "PRE-GRAD" : "…")}</div>
            <div className={"delta "+(up?"up":"down")}>{live.available ? `${up?'▲ +':'▼ '}${Math.abs(live.change24h).toFixed(2)}%` : "BONDING CURVE"}</div>
          </div>
          <div className="score-cell">
            <div className="label">24h Volume</div>
            <div className="value">{live.available ? fmtMoney(live.volume24h) : "—"}</div>
            <div className="delta up">▲ live</div>
          </div>
          <div className="score-cell">
            <div className="label">Holders</div>
            <div className="value">{live.holders ? Number(live.holders).toLocaleString() : "—"}</div>
            <div className="delta up">▲ clowns onboard</div>
          </div>
          <div className="score-cell">
            <div className="label">Mkt Cap</div>
            <div className="value">{live.available ? fmtMoney(live.marketCap) : "—"}</div>
            <div className="delta up">▲ to the BIG TOP</div>
          </div>
          <div className="score-cell">
            <div className="label">Total Txns</div>
            <div className="value">{live.txCount ? Number(live.txCount).toLocaleString() : "—"}</div>
            <div className="delta">📜 all-time clownery</div>
          </div>
          <div className="score-cell">
            <div className="label">Jeets 24h</div>
            <div className="value" style={{color:"var(--red)"}}>{Number(live.sells24h || 0).toLocaleString()}</div>
            <div className="delta down">▼ clowns</div>
          </div>
        </div>
        <div className="gauges">
          <div className="gauge">
            <h5>RUG-O-METER</h5>
            <div className="gauge-bar"><div className="fill" style={{width:gauges.rug+"%"}}></div></div>
            <div className="num">{gauges.rug}%</div>
            <div className="meta">probability the next big launch rugs you</div>
            <div className="spin-wheel"></div>
          </div>
          <div className="gauge">
            <h5>PAPER HANDS INDEX</h5>
            <div className="gauge-bar"><div className="fill" style={{width:gauges.paper+"%"}}></div></div>
            <div className="num">{gauges.paper}%</div>
            <div className="meta">of holders sell within 24h of a 2x</div>
            <div className="spin-wheel"></div>
          </div>
          <div className="gauge">
            <h5>VC UNLOCK ANXIETY</h5>
            <div className="gauge-bar"><div className="fill" style={{width:gauges.vc+"%"}}></div></div>
            <div className="num">{gauges.vc}%</div>
            <div className="meta">of supply waiting to dump on retail</div>
            <div className="spin-wheel"></div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ---------- JEETERS LEADERBOARD ---------- */
function JeetersBoard() {
  const { rows: liveRows, status } = useLiveJeeters();
  const live = useLiveStats();

  function honk(){
    try{
      const ctx = new (window.AudioContext||window.webkitAudioContext)();
      const o = ctx.createOscillator();
      const g = ctx.createGain();
      o.type="square";o.frequency.value=180;
      g.gain.value=.08;
      o.connect(g);g.connect(ctx.destination);
      o.start();
      o.frequency.exponentialRampToValueAtTime(80, ctx.currentTime+.25);
      g.gain.exponentialRampToValueAtTime(.0001, ctx.currentTime+.4);
      o.stop(ctx.currentTime+.4);
    }catch(e){}
  }

  return (
    <section id="jeeters">
      <div className="section-head">
        <span className="kicker">⚠ NAME & SHAME ⚠</span>
        <h2>JEETERS <em>LEADERBOARD</em></h2>
        <p>Updated live. Every significant sell is logged, ranked, and immortalized. Top jeeter receives the coveted Biggest Clown crown — and our eternal pity.</p>
      </div>
      <div className="jeeters">
        <div className="hdr">
          <h3>🤡 BIGGEST CLOWNS THIS HOUR</h3>
          <div className="legend">
            <span className="crown">👑</span> = Crowned Top Jeeter
            <span style={{marginLeft:14, color:"#fff"}}>HOVER A 📯 TO HONK</span>
          </div>
        </div>
        <div style={{
          display:"grid", gridTemplateColumns:"60px 1fr 160px 130px 1fr 80px",
          padding:"10px 22px", background:"#1a0b2e",
          fontFamily:"Bungee, system-ui", fontSize:"11px",
          color:"var(--yellow)", letterSpacing:".1em"
        }}>
          <div>RANK</div><div>CLOWN WALLET</div><div>JEETED</div><div>TIME</div><div>REASON</div><div style={{textAlign:"right"}}>HONK</div>
        </div>
        {liveRows.length > 0 ? liveRows.map((r,i)=>{
          const usd = r.solReceived * (live.solUsd || 0);
          return (
          <div className={"jeet-row "+(i===0?"top":"")} key={r.sig}>
            <div className="rank">#{i+1}</div>
            <div className="who">
              <div className="avatar"><JeeterAvatar seed={hashStr(r.addr) % 1000} /></div>
              <div>
                <div className="name">
                  {r.name}
                  {i===0 && <span className="crown-badge" style={{marginLeft:8}}>👑 BIGGEST CLOWN</span>}
                </div>
                <a href={`https://solscan.io/account/${r.addr}`} target="_blank" rel="noopener noreferrer"
                   className="wallet" style={{color:"var(--bone-2)", textDecoration:"none"}} title={r.addr}>
                  {shorten(r.addr)} ↗
                </a>
              </div>
            </div>
            <div className="amt" style={{lineHeight:1.15}}>
              {Math.round(r.amount).toLocaleString()}
              <div style={{fontFamily:"'JetBrains Mono'", fontSize:11, color:"var(--bone-2)", marginTop:2}}>
                {r.solReceived > 0 ? `${r.solReceived.toFixed(4)} SOL` : ""}
                {usd > 0 ? `  ·  ${fmtMoney(usd)}` : ""}
              </div>
            </div>
            <div className="time">
              <a href={`https://solscan.io/tx/${r.sig}`} target="_blank" rel="noopener noreferrer" style={{color:"inherit", textDecoration:"none"}}>
                {timeAgo(r.ageMs)} ↗
              </a>
            </div>
            <div className="reason">"{r.reason}"</div>
            <button className="horn" onMouseEnter={honk} onClick={honk} title="HONK">📯</button>
          </div>
          );
        }) : (
          <div style={{padding:"50px 22px", textAlign:"center", color:"var(--bone-2)", fontFamily:"'Bungee', system-ui", fontSize:14, letterSpacing:".05em"}}>
            {status === 'loading' && "🎪 FETCHING ONCHAIN HUMILIATION…"}
            {status === 'empty' && "🤡 NO TXNS YET. CONTRACT IS COLD."}
            {status === 'no-jeets' && "🎩 NO JEETERS YET. EVERYONE'S DIAMOND-HANDED. SUSPICIOUS."}
            {status === 'error' && "📯 RPC HONKED. RETRYING IN 30s."}
          </div>
        )}
        <div style={{padding:"16px 22px", textAlign:"center", color:"var(--bone-2)", fontSize:13, fontFamily:"JetBrains Mono"}}>
          Live from Solana RPC. <a href={`https://solscan.io/token/${CONTRACT_ADDRESS}`} target="_blank" rel="noopener noreferrer" style={{color:"var(--yellow)"}}>VIEW THE FULL HALL OF SHAME →</a>
        </div>
      </div>
    </section>
  );
}

/* ---------- THE CIRCUS ---------- */
function Circus() {
  const clowns = [
    { kind:"vc", title:"THE VC CLOWN", desc:"Allocations at $0.001. Talks 'long-term alignment' on podcasts. Wallet unlocked at 6:00 AM, dumped by 6:04.", stats:{ supply:"22%", honesty:"3/100" }},
    { kind:"shill", title:"THE PAID SHILL", desc:"\"Not financial advice\" in tiny font. \"100x gem\" in giant font. The disclosed disclosure is undisclosed.", stats:{ rate:"$2k/post", honesty:"7/100" }},
    { kind:"mm", title:"THE MARKET MAKER", desc:"Two-sided liquidity, one-sided ethics. Spreads so wide you could park a circus tent in them.", stats:{ takerage:"5%", honesty:"12/100" }},
    { kind:"cex", title:"THE CEX EXTRACTOR", desc:"Listing fee: your soul. Withdrawals: paused. Customer support: a parking cone in a clown suit.", stats:{ fee:"BIG", honesty:"4/100" }},
    { kind:"pump", title:"THE PUMP & DUMP CREW", desc:"Telegram of 30,000 'whales' all named Sergio. Coordinated entries, accidental exits, mysterious early bags.", stats:{ groups:"47", honesty:"1/100" }},
  ];
  return (
    <section id="circus">
      <div className="section-head">
        <span className="kicker">🎪 THE CIRCUS 🎪</span>
        <h2>WELCOME TO THE <em>BIG SHOW</em></h2>
        <p>Five archetypes. One ruined market. We're not naming names — but you know who they are. (We are also naming names.)</p>
      </div>
      <div className="circus-grid">
        {clowns.map(c => (
          <div key={c.kind} className={"clown-card c-"+c.kind}>
            <div className="portrait">
              <span className="badge">{c.kind.toUpperCase()}</span>
              <ClownPortrait kind={c.kind} />
            </div>
            <h4>{c.title}</h4>
            <p>{c.desc}</p>
            <div className="stats">
              {Object.entries(c.stats).map(([k,v])=>(
                <span key={k}>{k.toUpperCase()}: <span className="v">{v}</span></span>
              ))}
            </div>
          </div>
        ))}
      </div>

      <div className="manifesto">
        <h3>THE $CLOWNZ MANIFESTO</h3>
        <p>Web3 was supposed to be open, fair, and built by the people. Instead, we got <strong>insider seed rounds</strong>, <strong>paid 'organic' shills</strong>, <strong>infinite-mint stablecoins</strong>, and <strong>'community-owned' tokens</strong> where the community owns 4%.</p>
        <p>$CLOWNZ doesn't pretend. We're a memecoin. We will probably go to zero. The difference is — we'll do it <strong>honestly</strong>, in front of you, with a <strong>red nose on</strong>, while pointing at every single bad actor on the way down.</p>
        <p>This is satire-as-infrastructure. Every jeeter logged. Every wallet exposed. Every VC unlock turned into a public spectacle. The only way out is through — laughing.</p>
        <p style={{fontFamily:"'Bungee', system-ui", fontSize:18, color:"var(--yellow)", marginTop:18}}>WE ARE ALL CLOWNS. AT LEAST WE'RE HONEST ABOUT IT. 🤡</p>
      </div>
    </section>
  );
}

/* ---------- BIG TOP / TOKENOMICS ---------- */
function BigTop() {
  const tokens = [
    { name:"Meteora Auto-Liquidity Pool", pct:100, color:"#ff2d55", desc:"100% of bonded supply migrates into a Meteora pool at graduation. No team wallet. No treasury wallet. No 'strategic reserve' wallet. The pool IS the supply." },
  ];
  // Donut math (single slice for the dramatic 100%)
  const r = 80, c = 2*Math.PI*r;
  return (
    <section id="bigtop">
      <div className="section-head">
        <span className="kicker">🎟 STEP RIGHT UP 🎟</span>
        <h2>THE <em>BIG TOP</em></h2>
        <p>A Vybes.fun fair launch on Solana. No presale. No allocations. No clown VCs. Bonding curve from t=0, graduates to a Meteora pool at $35K market cap, then routed by Jupiter.</p>
      </div>
      <div className="bigtop">
        <span className="poster-tag">ONE NIGHT ONLY • FAIR LAUNCH</span>
        <h2>$CLOWNZ FAIR LAUNCH</h2>
        <div className="sub">★ VYBES.FUN ★ SOLANA ★ GRADUATION $35K ★ AUTO LIQ ON METEORA ★ ROUTED BY JUPITER ★</div>
        <div className="row">
          <div className="donut">
            <svg viewBox="0 0 200 200">
              <circle cx="100" cy="100" r={r} fill="none" stroke="#eee" strokeWidth="36"/>
              <circle cx="100" cy="100" r={r} fill="none" stroke="#ff2d55" strokeWidth="36" strokeDasharray={`${c} 0`}/>
            </svg>
            <div className="center">
              <div>
                <div className="total">100%</div>
                <div className="label">IN THE HOOK</div>
              </div>
            </div>
          </div>
          <div className="tokenomics-list">
            <div className="token-item">
              <div className="swatch" style={{background:"#ffd60a"}}></div>
              <div>
                <div className="name">FAIR LAUNCH ON VYBES.FUN</div>
                <div className="desc">No presale. No private round. No "advisors". Anyone with a Solana wallet can clown in on the bonding curve at the same price.</div>
              </div>
              <div className="pct" style={{fontSize:14}}>FAIR</div>
            </div>
            <div className="token-item">
              <div className="swatch" style={{background:"#39ff14"}}></div>
              <div>
                <div className="name">GRADUATION MCAP: $35,000</div>
                <div className="desc">Thirty-five thousand dollars. Hit it and the bonding curve graduates — liquidity auto-deposits into a Meteora pool. If you're getting in at $50M FDV, you're already the clown.</div>
              </div>
              <div className="pct" style={{fontSize:14}}>$35K</div>
            </div>
            <div className="token-item">
              <div className="swatch" style={{background:"#ff2bd6"}}></div>
              <div>
                <div className="name">METEORA AUTO LIQUIDITY</div>
                <div className="desc">On graduation, all bonded SOL + supply migrates straight into a Meteora pool. No team multisig touches it. If we rug, we'd have to rug ourselves.</div>
              </div>
              <div className="pct" style={{fontSize:14}}>POOL</div>
            </div>
            <div className="token-item">
              <div className="swatch" style={{background:"#00e5ff"}}></div>
              <div>
                <div className="name">SOLANA + JUPITER</div>
                <div className="desc">Sub-cent fees. Sub-second blocks. Every post-grad swap routes through Jupiter for best execution. Maximum velocity for jeeters to humiliate themselves in real time.</div>
              </div>
              <div className="pct" style={{fontSize:14}}>SOL</div>
            </div>
          </div>
        </div>
        <div className="guarantees">
          <div className="guarantee"><div className="icon">🎪</div><div className="t">VYBES.FUN FAIR LAUNCH</div><div className="d">Bonding-curve fair launch. No insider allocs. No locked team supply. Pure circus.</div></div>
          <div className="guarantee"><div className="icon">🪝</div><div className="t">100% IN METEORA</div><div className="d">On graduation the entire bonded supply migrates into a Meteora pool. No team wallet to dump from.</div></div>
          <div className="guarantee"><div className="icon">🟣</div><div className="t">SOLANA NATIVE</div><div className="d">Deployed on Solana. No bridge. No wrapper. No "coming to Base soon".</div></div>
          <div className="guarantee"><div className="icon">📜</div><div className="t">NO TEAM TOKENS</div><div className="d">We don't have an unlock schedule. We can't dump on you. The math doesn't allow it.</div></div>
        </div>
      </div>
    </section>
  );
}

/* ---------- HOW TO BUY ---------- */
function HowToBuy() {
  const steps = [
    { n:1, t:"GET ON SOLANA", d:"Any Solana wallet — Phantom, Solflare, Backpack. Fund it with some SOL from your favorite CEX or bridge it in." },
    { n:2, t:"OPEN VYBES.FUN", d:"Find the $CLOWNZ token page on Vybes.fun. Bonding-curve fair launch — graduation hits at a $35K market cap. Yes, that low. No, you didn't miss it (yet)." },
    { n:3, t:"APE ON VYBES OR JUPITER", d:"Ape on the Vybes.fun bonding curve pre-graduation, or after graduation swap on Jupiter against the Meteora pool. Contract address goes here once it's live." },
    { n:4, t:"DON'T JEET", d:"Sell within 24h and you'll be immortalized on the leaderboard with a horn-honk for every visitor. The crown is real. The shame is real." },
  ];
  return (
    <section id="buy" style={{paddingBottom:60}}>
      <div className="section-head">
        <span className="kicker">🎟 HOW TO BUY ON SOLANA 🎟</span>
        <h2>JOIN THE <em>CIRCUS</em></h2>
        <p>Fair launch on Vybes.fun. Buy on the bonding curve, or after graduation through the Meteora pool via Jupiter. Four steps, zero excuses.</p>
      </div>
      <div className="steps">
        {steps.map(s=>(
          <div className="step" key={s.n}>
            <div className="num">{s.n}</div>
            <h4>{s.t}</h4>
            <p>{s.d}</p>
          </div>
        ))}
      </div>

      <div style={{
        maxWidth:1200, margin:"40px auto 0",
        display:"grid", gridTemplateColumns:"1fr 1fr", gap:18
      }} className="buy-cards">
        <a href="https://www.vybes.fun" target="_blank" rel="noopener noreferrer" className="step" style={{borderColor:"var(--green)", borderStyle:"solid", display:"block", textDecoration:"none"}}>
          <div className="num" style={{background:"var(--green)"}}>🎪</div>
          <h4 style={{color:"var(--green)"}}>APE ON VYBES.FUN</h4>
          <p>Head to <strong style={{color:"var(--bone)"}}>vybes.fun</strong> and buy directly on the bonding curve. Follow <strong style={{color:"var(--bone)"}}>@vybesfun</strong> on X for graduation alerts. The lazy-degen express lane to the leaderboard.</p>
          <div style={{marginTop:14, fontFamily:"'Bungee', system-ui", fontSize:13, color:"var(--green)"}}>OPEN VYBES.FUN →</div>
        </a>
        <a href="https://jup.ag" target="_blank" rel="noopener noreferrer" className="step" style={{borderColor:"var(--magenta)", borderStyle:"solid", display:"block", textDecoration:"none"}}>
          <div className="num" style={{background:"var(--magenta)", color:"#fff"}}>🪐</div>
          <h4 style={{color:"var(--magenta)"}}>SWAP ON JUPITER</h4>
          <p>Post-graduation swap routed by <strong style={{color:"var(--bone)"}}>Jupiter</strong> against the <strong style={{color:"var(--bone)"}}>Meteora pool</strong> on Solana. Paste the contract, set slippage, click swap, post chart in TG.</p>
          <div style={{marginTop:14, fontFamily:"'Bungee', system-ui", fontSize:13, color:"var(--magenta)"}}>OPEN JUPITER →</div>
        </a>
      </div>
      <div style={{
        maxWidth:1200, margin:"22px auto 0", padding:"14px 18px",
        background:"#000", border:"2px dashed var(--yellow)", borderRadius:12,
        fontFamily:"'JetBrains Mono'", fontSize:12, color:"var(--bone-2)", textAlign:"center"
      }}>
        CONTRACT ADDRESS: <span style={{color:"var(--yellow)"}}>33KvdXWCQvBAoESDS6wSuxZRh2cAbJsPuyDEoB8RwY6y</span> — only trust the address posted on this site & official $CLOWNZ X. Imposters incoming.
      </div>
    </section>
  );
}

/* ---------- FAQ ---------- */
function FAQ() {
  const qa = [
    ["Is $CLOWNZ a scam?", "Every memecoin is, in some sense, a scam. The difference is we say so on the homepage. Read the manifesto."],
    ["Will it go to zero?", "Statistically, yes. So will most of your portfolio. At least with $CLOWNZ you'll get a leaderboard ranking out of it."],
    ["Why am I on the Jeeters board?", "Because you sold. The contract sees all. The clown remembers."],
    ["Do you have a CEX listing?", "We're talking to several extractors. Negotiations are going about as well as you'd expect."],
    ["This is offensive to professional clowns.", "We agree. Real clowns deserve better than being compared to VCs. We apologize to the International Clown Hall of Fame."],
  ];
  return (
    <section id="faq">
      <div className="section-head">
        <span className="kicker">❓ FREQUENTLY ASKED ❓</span>
        <h2>THINGS YOU <em>SHOULDN'T</em> ASK</h2>
      </div>
      <div className="faq">
        {qa.map(([q,a],i)=>(
          <details key={i} open={i===0}>
            <summary>{q}</summary>
            <p>{a}</p>
          </details>
        ))}
      </div>
    </section>
  );
}

/* ---------- FOOTER ---------- */
function Footer() {
  return (
    <footer>
      <div className="curtain-strip"></div>
      <div className="foot-warn">
        ⚠ THIS IS NOT FINANCIAL ADVICE. THIS IS <em>FINANCIAL COMEDY.</em> ⚠
      </div>
      <div className="foot-grid">
        <div className="foot-brand">
          <div className="name">$CLOWNZ</div>
          <p>The greatest show in Web3. Brought to you by paper hands, public humiliation, and the eternal optimism of degenerate gamblers everywhere.</p>
        </div>
        <div>
          <h5>THE CIRCUS</h5>
          <ul>
            <li><a href="#dashboard">Scoreboard</a></li>
            <li><a href="#jeeters">Hall of Shame</a></li>
            <li><a href="#circus">Manifesto</a></li>
            <li><a href="#bigtop">Tokenomics</a></li>
          </ul>
        </div>
        <div>
          <h5>JOIN</h5>
          <ul>
            <li><a href="#">𝕏 / Twitter</a></li>
            <li><a href="#">Telegram</a></li>
            <li><a href="#">Discord</a></li>
            <li><a href="#">Dexscreener</a></li>
          </ul>
        </div>
        <div>
          <h5>TOOLS</h5>
          <ul>
            <li><a href="#">Jeeter Lookup</a></li>
            <li><a href="#">VC Unlock Calendar</a></li>
            <li><a href="#">Shill Detector (beta)</a></li>
            <li><a href="#">Honk Sound Pack</a></li>
          </ul>
        </div>
      </div>
      <div style={{
        maxWidth:1200, margin:"30px auto 0",
        padding:"22px",
        textAlign:"center",
        fontFamily:"'Bagel Fat One', 'Sigmar One', system-ui",
        fontSize:"clamp(22px, 3.4vw, 44px)",
        color:"var(--red)",
        textShadow:"-2px 0 0 #000, 2px 0 0 #000, 0 -2px 0 #000, 0 2px 0 #000, 5px 5px 0 var(--yellow), 10px 10px 0 #000",
        transform:"rotate(-1.5deg) skewX(-3deg)",
        letterSpacing:".01em"
      }}>
        YOU WILL GET RUGGED. THE QUESTION IS BY WHOM.
      </div>
      <div className="foot-bottom">
        <span>© 2026 $CLOWNZ — A Comedy Production. Not a security. Not a promise. Definitely a clown.</span>
        <span>BUILT WITH 🤡 AND CONTEMPT FOR VCs</span>
      </div>
    </footer>
  );
}

/* ---------- TWEAKS ---------- */
function ClownzTweaks({ tweaks, setTweak }) {
  return (
    <TweaksPanel title="CLOWNZ TWEAKS">
      <TweakSection title="Mascot">
        <TweakRadio
          label="Mood"
          value={tweaks.mood}
          options={[
            { value:"smug", label:"Smug" },
            { value:"scheming", label:"Scheming" },
            { value:"rugged", label:"Rugged" },
          ]}
          onChange={v => setTweak("mood", v)}
        />
        <TweakToggle label="Bigger mascot" value={tweaks.bigMascot} onChange={v => setTweak("bigMascot", v)} />
      </TweakSection>
      <TweakSection title="Vibe">
        <TweakToggle label="Confetti rain" value={tweaks.confetti} onChange={v => setTweak("confetti", v)} />
        <TweakToggle label="Spotlights" value={tweaks.spotlights} onChange={v => setTweak("spotlights", v)} />
      </TweakSection>
    </TweaksPanel>
  );
}

/* ---------- CONFETTI RAIN ---------- */
function ConfettiRain() {
  const pieces = useMemo(() => Array.from({length: 40}, (_, i) => ({
    id: i,
    left: Math.random()*100,
    delay: Math.random()*8,
    dur: 6+Math.random()*8,
    color: ["#ff2d55","#ffd60a","#39ff14","#00e5ff","#ff2bd6","#fff7e8"][i%6],
    rot: Math.random()*360,
    shape: i%3,
  })), []);
  return (
    <div style={{position:"fixed",inset:0,pointerEvents:"none",zIndex:5,overflow:"hidden"}}>
      <style>{`
        @keyframes cfall {
          to { transform: translateY(110vh) rotate(720deg); }
        }
      `}</style>
      {pieces.map(p => (
        <div key={p.id} style={{
          position:"absolute",
          left:p.left+"%", top:"-20px",
          width:p.shape===2?14:10, height:p.shape===2?6:14,
          background:p.color,
          borderRadius:p.shape===0?"50%":p.shape===1?"2px":"1px",
          transform:`rotate(${p.rot}deg)`,
          animation:`cfall ${p.dur}s linear ${p.delay}s infinite`,
          opacity:.85,
        }}/>
      ))}
    </div>
  );
}

/* ---------- APP ---------- */
function App() {
  const [tweaks, setTweak] = useTweaks(/*EDITMODE-BEGIN*/{
    "mood": "smug",
    "bigMascot": false,
    "confetti": true,
    "spotlights": true
  }/*EDITMODE-END*/);
  return (
    <div>
      <Nav />
      <Marquee />
      <Hero tweaks={tweaks}/>
      <Marquee />
      <Dashboard />
      <JeetersBoard />
      <Circus />
      <BigTop />
      <HowToBuy />
      <FAQ />
      <Footer />
      {tweaks.confetti && <ConfettiRain />}
      <ClownzTweaks tweaks={tweaks} setTweak={setTweak} />
    </div>
  );
}

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