/* ============================================================
   $LAND — UI components
   ============================================================ */
const D = window.LandData;
const { Tile } = window.Land;
const F = D.fmt;
const rarByKey = (k) => D.rarities.find(r => r.key === k);

/* ---------- small helpers ---------- */
function RarityBadge({ rk, size }) {
  const r = rarByKey(rk);
  const sm = size === 'sm';
  return (
    <span className="rar-badge" style={{
      background: rk === 'common' ? 'var(--surface3)' : hexA(varForRarity(rk), .14),
      color: rk === 'legendary' ? '#7a5e16' : 'var(--ink)',
      border: '1px solid ' + (rk === 'common' ? 'var(--line2)' : hexA(varForRarity(rk), .5)),
      height: sm ? 22 : 26, fontSize: sm ? 10.5 : 11.5,
    }}>
      <span className="rar-dot" style={{ background: varForRarity(rk), boxShadow: r.glow ? '0 0 8px ' + varForRarity(rk) : 'none' }}></span>
      {r.label}
    </span>
  );
}
function varForRarity(rk) { return 'var(--r-' + rk + ')'; }
function hexA(v, a) { return v; } // var() can't take alpha; fall back to solid (used sparingly)

function Addr({ a, n = 4, copy = true }) {
  const [c, setC] = React.useState(false);
  return (
    <span className="mono" style={{ fontSize: 12.5, cursor: copy ? 'pointer' : 'default', whiteSpace: 'nowrap' }}
      onClick={(e) => { if (!copy) return; e.stopPropagation(); navigator.clipboard && navigator.clipboard.writeText(a); setC(true); setTimeout(() => setC(false), 900); }}
      title={a}>
      {c ? 'copied ✓' : F.addr(a, n)}
    </span>
  );
}

function ExtLink({ label, href }) {
  return <span className="link-mono" style={{ whiteSpace: 'nowrap' }} onClick={(e) => e.stopPropagation()} title={href}>{label} ↗</span>;
}

function Spark({ up }) {
  return <span style={{ color: up ? 'var(--grass-d)' : '#c25b3f', fontWeight: 700, fontSize: 13 }}>{up ? '▲' : '▼'}</span>;
}

/* ---------- honest empty state (no data yet / before launch) ---------- */
function EmptyState({ text }) {
  return (
    <div className="faint" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', textAlign: 'center', height: '100%', minHeight: 80, padding: 20, fontSize: 12.5, lineHeight: 1.6 }}>
      {text}
    </div>
  );
}

/* ---------- countdown ---------- */
function useCountdown(ts) {
  const [now, setNow] = React.useState(Date.now());
  React.useEffect(() => { const id = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(id); }, []);
  let d = Math.max(0, ts - now);
  const h = Math.floor(d / 3600000); d -= h * 3600000;
  const m = Math.floor(d / 60000); d -= m * 60000;
  const s = Math.floor(d / 1000);
  return { h, m, s, pad: (n) => String(n).padStart(2, '0') };
}

/* ---------- LOGO ---------- */
function Logo({ size = 38 }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 11 }}>
      <div style={{ width: size, height: size }}>
        <Tile kind="parcel" rarity="legendary" size={size} seed={7} />
      </div>
      <div style={{ lineHeight: 1 }}>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 19, letterSpacing: '-.03em' }}>$LAND</div>
        <div className="mono faint" style={{ fontSize: 9.5, letterSpacing: '.08em', marginTop: 3 }}>NON-RENEWABLE RESOURCE</div>
      </div>
    </div>
  );
}

/* ---------- HEADER ---------- */
function Header({ wallet, onConnect, onDisconnect, onAcquire, onNav, route }) {
  return (
    <header style={{ position: 'sticky', top: 0, zIndex: 60, background: 'rgba(241,226,188,.82)', backdropFilter: 'blur(14px)', borderBottom: '2.5px solid var(--black)' }}>
      <div className="wrap" style={{ height: 66, display: 'flex', alignItems: 'center', gap: 24 }}>
        <a href="#/" onClick={(e) => { e.preventDefault(); onNav(''); }}><Logo /></a>
        <nav style={{ display: 'flex', gap: 4, marginLeft: 10 }}>
          {[['Map', ''], ['Buildings', 'buildings'], ['Holders', 'holders'], ['Rounds', 'rounds']].map(([l, id]) => {
            const active = (route || '') === id;
            return (
              <button key={id} style={{ padding: '7px 12px', fontSize: 13, fontWeight: 700, borderRadius: 0, textTransform: 'uppercase', letterSpacing: '.03em', background: active ? 'var(--black)' : 'transparent', color: active ? 'var(--surface3)' : 'var(--ink2)' }}
                onClick={() => onNav(id)}>{l}</button>
            );
          })}
        </nav>
        <div style={{ flex: 1 }}></div>
        <div style={{ display: 'flex', gap: 6, marginRight: 4 }}>
          {[['𝕏', 'https://x.com/norenewableland'], ['pump', 'https://pump.fun/coin/' + D.LAND_MINT], ['Program ↗', 'https://solscan.io/account/' + D.PROGRAM_ID]].map(([l, h]) => (
            <a key={l} className="btn btn-ghost btn-sm" href={h} target="_blank" rel="noreferrer" style={{ padding: '0 10px', fontSize: 11.5 }}>{l}</a>
          ))}
        </div>
        {wallet
          ? <button className="btn btn-ghost btn-sm" title={wallet + ' — click to disconnect'} onClick={onDisconnect}><span className="rar-dot" style={{ background: 'var(--grass)' }}></span><span className="mono">{F.addr(wallet)}</span></button>
          : <button className="btn btn-ghost btn-sm" onClick={onConnect}>Connect Wallet</button>}
        <button className="btn btn-primary btn-sm" onClick={onAcquire}>Claim Land</button>
      </div>
    </header>
  );
}
function McBadge() {
  const s = D.stats;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '5px 13px', borderRadius: 0, border: '2.5px solid var(--black)', background: 'var(--surface3)', boxShadow: '3px 3px 0 var(--black)' }}>
      <div>
        <div className="mono faint" style={{ fontSize: 9, letterSpacing: '.1em' }}>MARKET CAP</div>
        <div style={{ fontWeight: 700, fontSize: 14.5, fontFamily: 'var(--font-display)' }} className="tnum">{F.usd(s.marketcap)}</div>
      </div>
      <div style={{ fontSize: 12, fontWeight: 700 }}><Spark up={s.mcChange > 0} /> <span className="tnum" style={{ color: 'var(--ink2)' }}>{Math.abs(s.mcChange)}%</span></div>
    </div>
  );
}
function PhantomIcon() {
  return <svg width="15" height="15" viewBox="0 0 24 24" fill="none"><path d="M12 3C7 3 3 7 3 12c0 1 .8 1.6 1.6 1.2.6-.3 1-.9 1.4-1.4.5-.7 1.2-1.2 2-1.2.9 0 1.4.7 1.4 1.6 0 .6.5 1 1 1s1-.5 1.2-1c.3-.8 1-1.6 2-1.6.9 0 1.4.7 1.4 1.6 0 .6.5 1 1 1s1-.4 1-1V12c0-5-4-9-9-9Z" fill="#6f5b44" /></svg>;
}

/* ============================================================
   LAND MAP — top-down grid
   ============================================================ */
function cellHash(i) { return ((i * 2654435761) >>> 0) % 100 / 100; }
function LandMap({ onSelect, density }) {
  const [tip, setTip] = React.useState(null);
  const wrapRef = React.useRef(null);
  const [box, setBox] = React.useState({ w: 900, h: 560 });
  React.useEffect(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver((ents) => { for (const e of ents) setBox({ w: e.contentRect.width, h: e.contentRect.height }); });
    ro.observe(wrapRef.current);
    setBox({ w: wrapRef.current.clientWidth, h: wrapRef.current.clientHeight });
    return () => ro.disconnect();
  }, []);

  // Keep cells SQUARE; derive how many cols/rows fit the container at `density`
  // px per cell. The grid count adapts to the screen — bigger screen → more
  // cells — and fills the panel with no empty margins. This is purely a
  // VISUAL remap; the underlying ownership data (D.cells over MAP_W×MAP_H) is
  // unchanged and just sampled onto the responsive grid.
  const gap = 2;
  const cell = Math.max(12, density || 20);
  const cols = Math.max(8, Math.floor((box.w + gap) / (cell + gap)));
  const rows = Math.max(6, Math.floor((box.h + gap) / (cell + gap)));
  const srcW = D.MAP_W, srcH = D.MAP_H;
  const sample = (x, y) => {
    const sx = Math.min(srcW - 1, Math.floor(x / cols * srcW));
    const sy = Math.min(srcH - 1, Math.floor(y / rows * srcH));
    return D.cells[sy * srcW + sx];
  };

  const out = [];
  for (let y = 0; y < rows; y++) {
    for (let x = 0; x < cols; x++) {
      const i = y * cols + x;
      const owner = sample(x, y);
      if (owner === null || owner === undefined) { out.push(<div key={i} className="mcell empty"></div>); continue; }
      const h = D.holderById[owner];
      const r = rarByKey(h.rarity);
      out.push(
        <div key={i} className="mcell owned" style={cellStyle(h, r, i)}
          onMouseEnter={(e) => { const b = e.currentTarget.getBoundingClientRect(); setTip({ x: b.left + b.width / 2, y: b.top, h }); }}
          onClick={() => onSelect(h)}>
          {r.glow >= 3 && <span style={{ position: 'absolute', top: 1, right: 1, width: 3, height: 3, borderRadius: 0, background: '#fff' }}></span>}
        </div>
      );
    }
  }

  const gridW = cols * cell + (cols - 1) * gap;
  const gridH = rows * cell + (rows - 1) * gap;
  return (
    <div ref={wrapRef} style={{ position: 'relative', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
      <div className="mapgrid" style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, ${cell}px)`, gridTemplateRows: `repeat(${rows}, ${cell}px)`, gap, width: gridW, height: gridH }}
        onMouseLeave={() => setTip(null)}>
        {out}
      </div>
      {D.holders.length === 0 && (
        <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}>
          <div style={{ background: 'var(--surface3)', border: '2.5px solid var(--black)', boxShadow: 'var(--shadow)', padding: '16px 22px', textAlign: 'center', maxWidth: 360 }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 16, textTransform: 'uppercase' }}>Land not claimed yet</div>
            <div className="faint" style={{ fontSize: 12.5, marginTop: 6 }}>{D.loaded ? 'The map fills in as wallets acquire $LAND. Hold ≥100k $LAND to claim territory.' : 'Loading the map…'}</div>
          </div>
        </div>
      )}
      {tip && <MapTip tip={tip} />}
    </div>
  );
}
function cellStyle(h, r, i) {
  // two-tone faceted green, varied by rarity tint.
  // PERF: cheap flat inset ring only — NO blur glow (0 0 Npx) shadows. Those
  // recompute per-cell on every paint and tanked the 880-cell map. Rarity is
  // signalled by a solid ring colour instead of a glow.
  const greens = {
    common: ['#7eb04f', '#5f9550'], uncommon: ['#73ad58', '#4f9272'],
    rare: ['#6aa863', '#3f86b8'], epic: ['#7a9e6a', '#6a5fb0'], legendary: ['#9bb84e', '#be9a3a'],
  }[h.rarity];
  const g1 = greens[0], g2 = greens[1];
  const ringW = { common: 0, uncommon: 1, rare: 1.4, epic: 1.6, legendary: 2 }[h.rarity];
  const ac = varForRarity(h.rarity);
  let shadow = `inset 1px 1px 0 rgba(255,255,255,.18), inset -1px -1px 0 rgba(0,0,0,.14)`;
  if (ringW) shadow += `, inset 0 0 0 ${ringW}px ${ac}`;
  return {
    background: `linear-gradient(135deg, ${g1} 0 49%, ${g2} 49% 100%)`,
    boxShadow: shadow,
  };
}
function MapTip({ tip }) {
  const h = tip.h, r = rarByKey(h.rarity);
  return (
    <div className="tip" style={{ left: tip.x, top: tip.y }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 14, marginBottom: 6 }}>
        <span className="mono" style={{ fontWeight: 700, fontSize: 12.5 }}>{F.addr(h.address, 4)}</span>
        <RarityBadge rk={h.rarity} size="sm" />
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
        <span className="faint">Holds</span><span className="mono" style={{ fontWeight: 600 }}>{F.land(h.vol)} $LAND</span>
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginTop: 3 }}>
        <span className="faint">Territory</span><span className="mono" style={{ fontWeight: 600 }}>{h.cells.length} cells</span>
      </div>
      <div className="mono faint" style={{ fontSize: 10, marginTop: 8, opacity: .8 }}>click for details →</div>
    </div>
  );
}

/* ---------- rarity legend ---------- */
function RarityLegend() {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
      {D.rarities.map(r => {
        const count = D.holders.filter(h => h.rarity === r.key).length;
        return (
          <div key={r.key} style={{ display: 'flex', alignItems: 'center', gap: 7, padding: '4px 9px 4px 7px', borderRadius: 0, border: '2px solid var(--black)', background: 'var(--surface3)', fontSize: 11.5 }}>
            <span style={{ width: 11, height: 11, borderRadius: 0, background: varForRarity(r.key) }}></span>
            <span style={{ fontWeight: 700 }}>{r.label}</span>
            <span className="mono faint" style={{ fontSize: 10.5 }}>{count}</span>
          </div>
        );
      })}
    </div>
  );
}

/* ============================================================
   PAYOUT / LEGITIMACY PANEL
   ============================================================ */
function PayoutPanel({ onAcquire, onReturn }) {
  const s = D.stats;
  const c = useCountdown(s.nextRoundTs);
  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <div style={{ padding: '18px 20px 16px', background: 'linear-gradient(180deg, rgba(105,162,85,.12), transparent)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
          <span className="live"></span><span className="eyebrow">Pool to distribute now</span>
        </div>
        <div className="tnum mono" style={{ fontWeight: 800, fontSize: 38, letterSpacing: '-.02em', marginTop: 6, lineHeight: 1 }}>
          {s.currentPool.toFixed(2)} <span style={{ fontSize: 18, color: 'var(--ink2)' }}>SOL</span>
        </div>
        <div className="faint" style={{ fontSize: 12.5, marginTop: 6 }}>split by territory share across all holders of ownership rights</div>
      </div>
      <div className="hr"></div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 1, background: 'var(--line)' }}>
        <div style={{ background: 'var(--surface2)', padding: '14px 16px' }}>
          <div className="eyebrow">Total paid out</div>
          <div className="tnum mono" style={{ fontWeight: 800, fontSize: 21, marginTop: 4 }}>{Math.round(s.totalPaid).toLocaleString('en-US')}<span style={{ fontSize: 12, color: 'var(--ink2)' }}> SOL</span></div>
        </div>
        <div style={{ background: 'var(--surface2)', padding: '14px 16px' }}>
          <div className="eyebrow">Next round</div>
          <div className="tnum mono" style={{ fontWeight: 700, fontSize: 22, marginTop: 4 }}>{c.pad(c.h)}:{c.pad(c.m)}:{c.pad(c.s)}</div>
        </div>
      </div>
      <div className="hr"></div>
      <div style={{ padding: 16, display: 'grid', gap: 9 }}>
        <button className="btn btn-primary btn-block" onClick={onAcquire}>Claim ownership</button>
        <button className="btn btn-ghost btn-block" onClick={onReturn}>Return tokens</button>
        <div className="faint" style={{ fontSize: 11, textAlign: 'center', marginTop: 2 }}>map is finite · {s.claimedPct}% taken · {s.totalCells - s.ownedCells} cells free</div>
      </div>
    </div>
  );
}

/* ---------- claim progress bar ---------- */
function ClaimBar() {
  const s = D.stats;
  return (
    <div className="card card-pad">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <span className="eyebrow">Territory claimed</span>
        <span className="mono tnum" style={{ fontWeight: 700, fontSize: 14 }}>{s.claimedPct}%</span>
      </div>
      <div style={{ height: 12, borderRadius: 999, background: 'var(--cream-d)', marginTop: 10, overflow: 'hidden', boxShadow: 'inset 0 1px 2px rgba(70,57,44,.12)' }}>
        <div style={{ width: s.claimedPct + '%', height: '100%', background: 'linear-gradient(90deg,var(--grass-l),var(--grass-d))', borderRadius: 999 }}></div>
      </div>
      <div className="faint" style={{ fontSize: 11.5, marginTop: 9 }}>{s.ownedCells} of {s.totalCells} cells claimed by {s.holders} holders. Unclaimed land is gone forever.</div>
    </div>
  );
}

/* ============================================================
   ROUNDS LEDGER
   ============================================================ */
function RoundsLedger() {
  const fmtTime = (ts) => { const d = new Date(ts); const p = (n) => String(n).padStart(2, '0'); return p(d.getDate()) + '.' + p(d.getMonth() + 1) + ' ' + p(d.getHours()) + ':' + p(d.getMinutes()); };
  return (
    <div className="card" style={{ overflow: 'hidden', display: 'flex', flexDirection: 'column', minHeight: 0, width: '100%' }}>
      <div style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
        <h3 style={{ fontSize: 15 }}>Rounds ledger</h3>
        <span className="chip"><span className="live"></span>transparency</span>
      </div>
      <div className="hr"></div>
      <div style={{ display: 'grid', gridTemplateColumns: '40px 1fr 96px 70px', gap: 8, padding: '8px 16px', fontSize: 10, letterSpacing: '.1em', textTransform: 'uppercase', color: 'var(--muted)', fontFamily: 'var(--font-mono)', flexShrink: 0 }}>
        <span>Rd</span><span>Time</span><span>Amount</span><span style={{ textAlign: 'right' }}>Tx</span>
      </div>
      <div className="hr"></div>
      <div className="scroll" style={{ flex: 1, minHeight: 0, overflowY: 'auto' }}>
        {D.rounds.length === 0
          ? <EmptyState text={D.loaded ? 'No payout rounds yet. The first round runs once dev-fees accrue.' : 'Loading rounds…'} />
          : D.rounds.map((r, i) => (
          <div key={r.round} className="hrow" style={{ gridTemplateColumns: '40px 1fr 96px 70px', alignItems: 'center', padding: '9px 16px', borderTop: i ? '1px solid var(--line)' : 'none', fontSize: 12.5, cursor: r.tx ? 'pointer' : 'default', borderRadius: 0 }}
            onClick={() => r.tx && window.open('https://solscan.io/tx/' + r.tx, '_blank')} title={r.tx ? 'Open round #' + r.round + ' on Solscan' : ''}>
            <span className="mono" style={{ fontWeight: 700 }}>#{r.round}</span>
            <span className="faint mono" style={{ fontSize: 11.5, whiteSpace: 'nowrap' }}>{r.ts ? fmtTime(r.ts) : '—'}</span>
            <span className="mono tnum" style={{ fontWeight: 700, color: 'var(--grass-d)', whiteSpace: 'nowrap' }}>+{r.amount.toFixed(2)} SOL</span>
            <span className="mono faint" style={{ fontSize: 11, textAlign: 'right' }}>{r.tx ? r.tx.slice(0, 4) + '…' : '—'}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ============================================================
   HOLDERS PANEL — rights of ownership
   ============================================================ */
function HoldersPanel({ onSelect }) {
  return (
    <div className="card" style={{ overflow: 'hidden', display: 'flex', flexDirection: 'column', minHeight: 0, width: '100%' }}>
      <div style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
        <h3 style={{ fontSize: 15 }}>Ownership rights</h3>
        <span className="mono faint" style={{ fontSize: 11 }}>{D.holders.length} holders</span>
      </div>
      <div className="hr"></div>
      <div className="scroll" style={{ padding: 6, flex: 1, minHeight: 0, overflowY: 'auto' }}>
        {D.holders.length === 0
          ? <EmptyState text={D.loaded ? 'No holders yet — land is claimed by holding $LAND.' : 'Loading holders…'} />
          : D.holders.map((h) => (
          <div key={h.id} className="hrow" style={{ gridTemplateColumns: '22px 32px 1fr auto', cursor: 'pointer', padding: '6px 8px', gap: 9 }} onClick={() => onSelect(h)}>
            <span className="mono faint tnum" style={{ fontSize: 11, textAlign: 'center' }}>{h.rank}</span>
            <div style={{ width: 30, height: 30 }}>
              <Tile kind="parcel" rarity={h.rarity} size={30} seed={h.id * 13 + 5} />
            </div>
            <div style={{ minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span className="mono" style={{ fontWeight: 700, fontSize: 12 }}>{F.addr(h.address, 4)}</span>
                <span style={{ width: 7, height: 7, borderRadius: 0, background: varForRarity(h.rarity) }}></span>
              </div>
              <div className="mono faint" style={{ fontSize: 10, marginTop: 1 }}>{h.cells.length}c · {h.buildingsCount}b · #{h.rank}</div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div className="mono tnum" style={{ fontWeight: 700, fontSize: 12.5 }}>{F.land(h.vol)}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ============================================================
   BUILDINGS SECTION — 8 iso cards
   ============================================================ */
function BuildingsSection({ onSelect, compact }) {
  if (compact) {
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div style={{ padding: '11px 14px' }}>
          <div className="eyebrow">8 building tokens</div>
          <h3 style={{ fontSize: 15, marginTop: 3 }}>Buildings</h3>
        </div>
        <div className="hr"></div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 1, background: 'var(--black)' }}>
          {D.buildingDefs.map((b, i) => (
            <button key={b.ticker} onClick={() => onSelect(b)} style={{ background: 'var(--surface2)', textAlign: 'left', padding: '8px 10px', display: 'flex', alignItems: 'center', gap: 9 }}>
              <div style={{ width: 34, height: 34, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Tile kind="building" building={b.key} size={34} /></div>
              <div style={{ minWidth: 0 }}>
                <div className="mono" style={{ fontSize: 11, fontWeight: 800 }}>${b.ticker}</div>
                <div className="mono faint" style={{ fontSize: 9.5 }}>{b.holderCount} holders</div>
              </div>
            </button>
          ))}
        </div>
      </div>
    );
  }
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 22 }}>
        <div>
          <div className="eyebrow">8 building tokens · medieval city</div>
          <h2 style={{ fontSize: 32, marginTop: 8 }}>Buildings</h2>
        </div>
        <p className="faint" style={{ maxWidth: 360, fontSize: 13.5, textAlign: 'right' }}>Each building is its own pump.fun token. Build on your parcel to boost your share and rarity.</p>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
        {D.buildingDefs.map((b, i) => <BuildingCard key={b.ticker} b={b} i={i} onSelect={onSelect} />)}
      </div>
    </div>
  );
}
function BuildingCard({ b, i, onSelect }) {
  const holders = b.holderCount;
  const mc = b.marketCapUsd;
  return (
    <button className="card" style={{ overflow: 'hidden', textAlign: 'left', padding: 0, display: 'block' }} onClick={() => onSelect(b)}
      onMouseEnter={(e) => { e.currentTarget.style.boxShadow = 'var(--shadow)'; e.currentTarget.style.transform = 'translateY(-3px)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.boxShadow = 'var(--shadow-sm)'; e.currentTarget.style.transform = ''; }}>
      <div style={{ background: `linear-gradient(180deg, ${b.accent}14, transparent 70%)`, padding: '6px 10px 0', position: 'relative' }}>
        <div style={{ height: 150, display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Tile kind="building" building={b.key} size={150} /></div>
        <span className="mono" style={{ position: 'absolute', top: 12, left: 12, fontSize: 11, fontWeight: 700, padding: '3px 8px', borderRadius: 7, background: 'var(--surface3)', border: '1px solid var(--line)', color: b.accent }}>${b.ticker}</span>
      </div>
      <div style={{ padding: '12px 15px 15px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <h3 style={{ fontSize: 16 }}>{b.name}</h3>
          <span style={{ width: 10, height: 10, borderRadius: 3, background: b.accent }}></span>
        </div>
        <div className="faint" style={{ fontSize: 12, marginTop: 4 }}>{b.hook}</div>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 12, paddingTop: 11, borderTop: '1px solid var(--line)' }}>
          <span className="mono faint" style={{ fontSize: 11 }}>MC {F.usd(mc)}</span>
          <span className="mono faint" style={{ fontSize: 11 }}>{holders} holders</span>
        </div>
      </div>
    </button>
  );
}

/* ============================================================
   FULL PAGES (hash routes) — scrollable, full info
   ============================================================ */
function PageShell({ title, sub, children }) {
  return (
    <div className="wrap scroll" style={{ flex: 1, minHeight: 0, overflowY: 'auto', paddingTop: 18, paddingBottom: 40 }}>
      <div style={{ marginBottom: 18 }}>
        <h1 style={{ fontSize: 30 }}>{title}</h1>
        {sub && <p className="faint" style={{ fontSize: 13.5, marginTop: 6 }}>{sub}</p>}
      </div>
      {children}
    </div>
  );
}

/* --- BUILDINGS PAGE: all 8 tokens, full cards --- */
function BuildingsPage({ onSelect }) {
  return (
    <PageShell title="Buildings" sub="8 building tokens · medieval city. Hold ≥150,000 of any building token to add +1 building to your parcel (max 3), boosting your share of the daily payouts.">
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
        {D.buildingDefs.map((b, i) => <BuildingCard key={b.ticker} b={b} i={i} onSelect={onSelect} />)}
      </div>
    </PageShell>
  );
}

/* --- HOLDERS PAGE: full table of every holder --- */
function HoldersPage({ onSelect }) {
  return (
    <PageShell title="Land holders" sub={D.holders.length + ' holders · ranked by $LAND balance. Territory is claimed by holding the token — hold ≥100,000 $LAND to appear on the map.'}>
      {D.holders.length === 0 ? <div className="card card-pad"><EmptyState text={D.loaded ? 'No holders yet. The board fills in once wallets hold $LAND.' : 'Loading holders…'} /></div> : (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '44px 52px minmax(120px,1fr) 110px 80px 80px 90px', gap: 10, padding: '10px 16px', fontSize: 10, letterSpacing: '.1em', textTransform: 'uppercase', color: 'var(--muted)', fontFamily: 'var(--font-mono)' }}>
          <span>Rank</span><span>Tile</span><span>Holder</span><span>Wallet</span><span style={{ textAlign: 'right' }}>Cells</span><span style={{ textAlign: 'right' }}>Builds</span><span style={{ textAlign: 'right' }}>$LAND</span>
        </div>
        <div className="hr"></div>
        {D.holders.map((h, idx) => {
          const r = rarByKey(h.rarity);
          return (
            <div key={h.id} className="hrow" style={{ gridTemplateColumns: '44px 52px minmax(120px,1fr) 110px 80px 80px 90px', alignItems: 'center', padding: '8px 16px', borderTop: idx ? '1px solid var(--line)' : 'none', cursor: 'pointer', borderRadius: 0 }} onClick={() => onSelect(h)}>
              <span className="mono faint tnum" style={{ fontSize: 12 }}>#{h.rank}</span>
              <div style={{ width: 38, height: 38 }}><Tile kind="parcel" rarity={h.rarity} size={38} seed={h.id * 13 + 5} /></div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
                <span className="mono" style={{ fontWeight: 700, fontSize: 13 }}>{F.addr(h.address, 6)}</span>
                <RarityBadge rk={h.rarity} size="sm" />
              </div>
              <span className="mono faint" style={{ fontSize: 11.5 }}>{F.addr(h.address, 4)}</span>
              <span className="mono tnum" style={{ textAlign: 'right', fontSize: 13 }}>{h.cells.length}</span>
              <span className="mono tnum" style={{ textAlign: 'right', fontSize: 13 }}>{h.buildingsCount}</span>
              <span className="mono tnum" style={{ textAlign: 'right', fontWeight: 700, fontSize: 13 }}>{F.land(h.vol)}</span>
            </div>
          );
        })}
      </div>
      )}
    </PageShell>
  );
}

/* --- ROUNDS PAGE: full payout history --- */
function RoundsPage() {
  const fmtTime = (ts) => { const d = new Date(ts); const p = (n) => String(n).padStart(2, '0'); return p(d.getDate()) + '.' + p(d.getMonth() + 1) + ' ' + p(d.getHours()) + ':' + p(d.getMinutes()); };
  const s = D.stats;
  return (
    <PageShell title="Rounds ledger" sub="Every payout is onchain. Click a round to open its transaction on Solscan.">
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, marginBottom: 18 }}>
        {[['Total paid out', Math.round(s.totalPaid).toLocaleString('en-US') + ' SOL'],
          ['Current pool', s.currentPool.toFixed(2) + ' SOL'],
          ['Rounds', D.rounds.length]].map(([l, v]) => (
          <div key={l} className="card card-pad">
            <div className="eyebrow">{l}</div>
            <div className="tnum mono" style={{ fontWeight: 800, fontSize: 22, marginTop: 4 }}>{v}</div>
          </div>
        ))}
      </div>
      <div className="card" style={{ overflow: 'hidden' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 140px 90px 90px', gap: 10, padding: '10px 16px', fontSize: 10, letterSpacing: '.1em', textTransform: 'uppercase', color: 'var(--muted)', fontFamily: 'var(--font-mono)' }}>
          <span>Round</span><span>Time</span><span>Amount</span><span style={{ textAlign: 'right' }}>Wallets</span><span style={{ textAlign: 'right' }}>Tx</span>
        </div>
        <div className="hr"></div>
        {D.rounds.map((r, i) => (
          <div key={r.round} className="hrow" style={{ gridTemplateColumns: '60px 1fr 140px 90px 90px', alignItems: 'center', padding: '11px 16px', borderTop: i ? '1px solid var(--line)' : 'none', cursor: 'pointer', borderRadius: 0 }}
            onClick={() => window.open('https://solscan.io/tx/' + r.tx, '_blank')}>
            <span className="mono" style={{ fontWeight: 700 }}>#{r.round}</span>
            <span className="faint mono" style={{ fontSize: 12 }}>{fmtTime(r.ts)}</span>
            <span className="mono tnum" style={{ fontWeight: 700, color: 'var(--grass-d)' }}>+{r.amount.toFixed(2)} SOL</span>
            <span className="mono tnum faint" style={{ textAlign: 'right' }}>{r.wallets}</span>
            <span className="mono faint" style={{ textAlign: 'right', fontSize: 11.5 }}>{r.tx.slice(0, 4)}…</span>
          </div>
        ))}
      </div>
    </PageShell>
  );
}

Object.assign(window, {
  Logo, Header, RarityBadge, Addr, ExtLink, Spark, useCountdown, varForRarity,
  LandMap, RarityLegend, PayoutPanel, ClaimBar, RoundsLedger, HoldersPanel,
  BuildingsSection, BuildingCard, rarByKey, cellStyle,
  BuildingsPage, HoldersPage, RoundsPage,
});
