/* fungames — Data + SVG art library */

const GAMES = [
  { id: 'diffuser', code: '#01', title: 'Diffuser', kind: 'Crash · Risk', volatility: 'Variable', rtp: '96.8%', maxWin: '10,000×',
    tag: 'Flagship', accent: 'rust',
    desc: 'A multi-tiered risk algorithm where players actively manage their exposure grid. Verifiably fair RNG, intensely engaging progression curve.',
    treatment: 'art-diffuser', demoUrl: 'https://simple-games-production.up.railway.app/' },
  { id: 'vault-drop', code: '#02', title: 'Vault Drop', kind: 'Drop · Physics', volatility: 'High', rtp: '96.4%', maxWin: '50,000×',
    tag: 'Playable', accent: 'ochre',
    desc: 'Precision drop mechanics with physics-driven outcomes. Time your release, watch the vault chamber resolve — every drop is verifiably fair.',
    treatment: 'art-vault', demoUrl: '/demos/vault-drop/' },
  { id: 'lock-pick', code: '#03', title: 'Lock Pick', kind: 'Grid · Strategy', volatility: 'Medium-High', rtp: '96.5%', maxWin: '5,000×',
    tag: 'Playable', accent: 'indigo',
    desc: 'A tactical board game where players probe lock mechanisms to find the right combination. Pattern recognition meets calculated risk.',
    treatment: 'abbrev', bg: 'indigo', demoUrl: '/demos/lock-pick/' },
  { id: 'marbles', code: '#04', title: 'Marbles', kind: 'Physics · 3D', volatility: 'Variable', rtp: '97.0%', maxWin: '25,000×',
    tag: 'Playable', accent: 'moss',
    desc: 'Full 3D physics simulation. Launch marbles into a pocketed bowl — real-time collision dynamics determine the payout bucket.',
    treatment: 'abbrev', bg: 'moss', demoUrl: '/demos/marbles/' },
  { id: 'rugpull', code: '#05', title: 'Rugpull Beast Mode', kind: 'Crash · Multiplier', volatility: 'High', rtp: '97.0%', maxWin: '100,000×',
    tag: 'Playable', accent: 'rust',
    desc: 'High-stakes multiplier game with adaptive difficulty curves. Beast Mode unlocks volatile payout tiers for experienced players.',
    treatment: 'art-diffuser', demoUrl: '/demos/rugpull-beast-mode/' },
  { id: 'vault-run', code: '#17', title: 'Vault Run', kind: 'Multiplayer · Heist', volatility: 'High', rtp: '96.8%', maxWin: '14×',
    tag: 'Playable', accent: 'plum',
    desc: 'Multiplayer heist game — breach the vault, survive phases, bank or ride through escalating offers. Final Gambit doubles or nothing.',
    treatment: 'abbrev', bg: 'plum', demoUrl: '/demos/vault-run/' },
  { id: 'velocity', code: '#06', title: 'Velocity', kind: 'Crash · Multiplier', volatility: 'High', rtp: '97.0%', maxWin: '25,000×',
    tag: '', accent: 'indigo',
    desc: 'Pure curve mechanics. Cash out before the multiplier collapses; held positions earn structural compounding.',
    treatment: 'art-velocity', demoUrl: null },
  { id: 'mines',    code: '#07', title: 'Mines',    kind: 'Grid · Logic', volatility: 'Medium-High', rtp: '96.5%', maxWin: '5,000×',
    tag: '', accent: 'rust',
    desc: 'A 5×5 hazard grid. Reveal cells, accumulate multipliers, banking against probabilistic collapse.',
    treatment: 'abbrev', bg: 'rust', demoUrl: null },
  { id: 'goal',     code: '#08', title: 'Goal',     kind: 'Path · Risk', volatility: 'Medium', rtp: '96.7%', maxWin: '12,500×',
    tag: '', accent: 'indigo',
    desc: 'Step-based progression. Clear lanes, dodge defenders, raise stake at every tier.',
    treatment: 'abbrev', bg: 'indigo', demoUrl: null },
  { id: 'hilo',     code: '#09', title: 'HiLo',     kind: 'Card · Streak', volatility: 'Low-Medium', rtp: '98.2%', maxWin: '1,000×',
    tag: '', accent: 'plum',
    desc: 'High or low than the next card. The longest live streak running our analytics across 14 markets.',
    treatment: 'abbrev', bg: 'plum', demoUrl: null },
  { id: 'plinko',   code: '#10', title: 'Plinko',   kind: 'Drop · Physics', volatility: 'Variable', rtp: '97.5%', maxWin: '1,500×',
    tag: '', accent: 'moss',
    desc: 'Deterministic physics, configurable rows, variable risk profile.',
    treatment: 'abbrev', bg: 'moss', demoUrl: null },
  { id: 'baccarat', code: '#11', title: 'Baccarat', kind: 'Card · Classic', volatility: 'Low', rtp: '98.9%', maxWin: '8×',
    tag: '', accent: 'rust',
    desc: 'Standard baccarat with optional side-bet structures.',
    treatment: 'abbrev', bg: 'rust', demoUrl: null },
  { id: 'dice',     code: '#12', title: 'Dice',     kind: 'Roll · Skill', volatility: 'Variable', rtp: '99.0%', maxWin: '9,900×',
    tag: '', accent: 'indigo',
    desc: 'Configure your own probability ceiling. Highest declared RTP in catalogue.',
    treatment: 'abbrev', bg: 'ink', demoUrl: null },
  { id: 'aviator',  code: '#13', title: 'Aviator',  kind: 'Crash · Multiplier', volatility: 'High', rtp: '97.0%', maxWin: '20,000×',
    tag: '', accent: 'moss',
    desc: 'Continuous-flight multiplier with synchronized multi-table rounds.',
    treatment: 'art-aviator', demoUrl: null },
  { id: 'keno',     code: '#14', title: 'Keno',     kind: 'Pick · Lottery', volatility: 'Variable', rtp: '96.0%', maxWin: '10,000×',
    tag: '', accent: 'ochre',
    desc: '40-cell pick game with operator-tunable hit thresholds.',
    treatment: 'abbrev', bg: 'ochre', demoUrl: null },
  { id: 'tower',    code: '#15', title: 'Tower',    kind: 'Climb · Risk', volatility: 'Medium', rtp: '96.6%', maxWin: '3,500×',
    tag: '', accent: 'plum',
    desc: 'Tier-by-tier ascent. Each floor multiplies; one wrong door collapses the run.',
    treatment: 'abbrev', bg: 'plum', demoUrl: null },
  { id: 'wheel',    code: '#16', title: 'Wheel',    kind: 'Spin · Live', volatility: 'Variable', rtp: '96.0%', maxWin: '50×',
    tag: '', accent: 'indigo',
    desc: 'Configurable segment wheel — risk modes from cautious to volatile.',
    treatment: 'abbrev', bg: 'indigo', demoUrl: null },
];

const NEWS = [
  { id: 'mga-license',  date: '2026-04-22', cat: 'Compliance', title: 'simplegames.fun secures MGA B2B license extension through 2031',
    excerpt: 'A renewed Class 4 supplier license consolidates our regulatory footing across the Maltese gaming jurisdiction.',
    body: null },
  { id: 'tier-1-aggregator', date: '2026-04-09', cat: 'Distribution', title: 'Integration with Tier-1 aggregator expands reach to 480+ operators',
    excerpt: 'Our full title catalogue is now distributed by a Tier-1 aggregator partnering with industry-leading operators.',
    body: null },
  { id: 'diffuser-launch', date: '2026-03-18', cat: 'Release', title: 'Diffuser ships: a new architecture for player-managed risk',
    excerpt: 'Eighteen months of engineering produced a proprietary risk-tier algorithm with verifiable RNG provenance.',
    body: null },
  { id: 'rng-cert', date: '2026-02-27', cat: 'Compliance', title: 'GLI-19 recertification across full 2026 catalogue',
    excerpt: 'All thirteen titles now hold current GLI-19 standard certifications.',
    body: null },
  { id: 'sigma-talk', date: '2026-02-04', cat: 'Studio', title: 'Lead engineer Maya Halberg keynotes SiGMA Eurasia on game determinism',
    excerpt: 'How a hashed seed-pair model produces verifiability without sacrificing player experience.',
    body: null },
  { id: 'engagement', date: '2026-01-15', cat: 'Product', title: 'Engagement Tools 2.0: tournaments, missions, and bonus engine',
    excerpt: 'Our engagement layer is now a standalone module that operators can wire into any title.',
    body: null },
];

/* SVG art kit — abstract architectural compositions per game */
function ArtDiffuser() {
  return (
    <svg viewBox="0 0 600 380" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <radialGradient id="dfg" cx="50%" cy="50%" r="60%">
          <stop offset="0%" stopColor="#3a2018" />
          <stop offset="100%" stopColor="#1a1009" />
        </radialGradient>
        <radialGradient id="dfg2" cx="50%" cy="50%" r="40%">
          <stop offset="0%" stopColor="#ff7a3c" stopOpacity="0.9" />
          <stop offset="60%" stopColor="#b8450c" stopOpacity="0.7" />
          <stop offset="100%" stopColor="#7f2a00" stopOpacity="0" />
        </radialGradient>
      </defs>
      <rect width="600" height="380" fill="url(#dfg)" />
      {/* concentric rings */}
      <g transform="translate(300 190)">
        {[...Array(9)].map((_, i) => (
          <circle key={i} r={20 + i * 16} fill="none" stroke="#ff7a3c" strokeOpacity={0.08 + i * 0.04} strokeWidth="1" />
        ))}
        <circle r="60" fill="url(#dfg2)" />
        <circle r="20" fill="none" stroke="#ffb38a" strokeWidth="1.5" />
        <circle r="6" fill="#ffd9b8" />
        {/* radial ticks */}
        {[...Array(24)].map((_, i) => {
          const a = (i * 15) * Math.PI / 180;
          const r1 = 100, r2 = 120;
          return <line key={i} x1={Math.cos(a)*r1} y1={Math.sin(a)*r1} x2={Math.cos(a)*r2} y2={Math.sin(a)*r2} stroke="#ff7a3c" strokeOpacity="0.4" strokeWidth="1" />;
        })}
      </g>
      {/* hairline grid overlay */}
      <g opacity="0.12">
        {[...Array(8)].map((_, i) => <line key={i} x1={i*75} y1="0" x2={i*75} y2="380" stroke="#ffe7df" strokeWidth="0.5" />)}
        {[...Array(6)].map((_, i) => <line key={i} x1="0" y1={i*60} x2="600" y2={i*60} stroke="#ffe7df" strokeWidth="0.5" />)}
      </g>
    </svg>
  );
}

function ArtVelocity() {
  return (
    <svg viewBox="0 0 600 380" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <linearGradient id="vbg" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stopColor="#1E3A5F" />
          <stop offset="100%" stopColor="#0a1828" />
        </linearGradient>
        <linearGradient id="vline" x1="0" y1="1" x2="1" y2="0">
          <stop offset="0%" stopColor="#ffb599" stopOpacity="0" />
          <stop offset="50%" stopColor="#ff7a3c" />
          <stop offset="100%" stopColor="#fff1e5" />
        </linearGradient>
      </defs>
      <rect width="600" height="380" fill="url(#vbg)" />
      {/* grid */}
      <g opacity="0.18" stroke="#a7c8ff" strokeWidth="0.5">
        {[...Array(13)].map((_, i) => <line key={i} x1={i*50} y1="0" x2={i*50} y2="380" />)}
        {[...Array(9)].map((_, i) => <line key={i} x1="0" y1={i*45} x2="600" y2={i*45} />)}
      </g>
      {/* curve */}
      <path d="M 20 340 Q 200 335 300 280 T 480 80 L 580 30" fill="none" stroke="url(#vline)" strokeWidth="3" />
      <path d="M 20 340 Q 200 335 300 280 T 480 80 L 580 30 L 580 340 L 20 340 Z" fill="url(#vline)" opacity="0.18" />
      {/* Dots along curve */}
      <circle cx="300" cy="280" r="4" fill="#ff7a3c" />
      <circle cx="480" cy="80" r="5" fill="#fff1e5" />
      <circle cx="480" cy="80" r="11" fill="none" stroke="#ff7a3c" strokeWidth="1" />
      <text x="490" y="68" fill="#fff1e5" fontFamily="Space Grotesk" fontSize="12" letterSpacing="0.1em">8.42×</text>
      <text x="32" y="354" fill="#a7c8ff" fontFamily="Space Grotesk" fontSize="10" letterSpacing="0.16em">T+0.0s</text>
      <text x="540" y="354" fill="#a7c8ff" fontFamily="Space Grotesk" fontSize="10" letterSpacing="0.16em">T+8.4s</text>
    </svg>
  );
}

function ArtVault() {
  return (
    <svg viewBox="0 0 600 380" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <linearGradient id="vlg" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#3a2818" />
          <stop offset="100%" stopColor="#1a120a" />
        </linearGradient>
      </defs>
      <rect width="600" height="380" fill="url(#vlg)" />
      <g transform="translate(180 80)">
        {[0,1,2].map(r => [0,1,2].map(c => {
          const x = c * 80, y = r * 80, idx = r*3+c;
          const isCenter = idx === 4;
          return (
            <g key={idx} transform={`translate(${x} ${y})`}>
              <rect x="0" y="0" width="72" height="72" fill={isCenter ? '#B8853C' : '#241810'} stroke="#5c4030" strokeWidth="1" />
              {!isCenter && <circle cx="36" cy="36" r="14" fill="none" stroke="#5c4030" strokeWidth="1.5" />}
              {isCenter && <circle cx="36" cy="36" r="14" fill="none" stroke="#fff1e5" strokeWidth="1.5" />}
              {isCenter && <text x="36" y="42" textAnchor="middle" fill="#fff1e5" fontFamily="Space Grotesk" fontSize="14" fontWeight="600">★</text>}
            </g>
          );
        }))}
      </g>
      <g opacity="0.25" stroke="#B8853C" strokeWidth="0.5">
        {[...Array(13)].map((_, i) => <line key={i} x1={i*50} y1="0" x2={i*50} y2="380" />)}
      </g>
    </svg>
  );
}

function ArtAviator() {
  return (
    <svg viewBox="0 0 600 380" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <linearGradient id="avg" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#2a3d2a" />
          <stop offset="100%" stopColor="#0e1a0e" />
        </linearGradient>
      </defs>
      <rect width="600" height="380" fill="url(#avg)" />
      <g opacity="0.25" stroke="#a7c8a7" strokeWidth="0.5">
        {[...Array(8)].map((_, i) => (
          <line key={i} x1="0" y1={i*55} x2="600" y2={i*55-100} />
        ))}
      </g>
      <path d="M 30 350 Q 300 350 420 200 T 580 40" fill="none" stroke="#ff7a3c" strokeWidth="2" strokeDasharray="6 4" />
      <g transform="translate(420 200) rotate(-30)">
        <path d="M 0 -8 L 28 0 L 0 8 L 6 0 Z" fill="#fff1e5" stroke="#1a1a1a" strokeWidth="0.8" />
      </g>
      <text x="32" y="40" fill="#fff1e5" fontFamily="Space Grotesk" fontSize="11" letterSpacing="0.18em">FLIGHT 0428</text>
    </svg>
  );
}

/* Per-game icon glyphs (drawn with thin strokes, white) */
function GameIcon({ id }) {
  const s = { fill: 'none', stroke: 'rgba(255,255,255,0.9)', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' };
  switch (id) {
    case 'vault-drop': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><rect x="8" y="8" width="24" height="24" rx="3" {...s}/><circle cx="20" cy="18" r="5" {...s}/><path d="M 14 30 L 20 24 L 26 30" {...s}/><line x1="20" y1="4" x2="20" y2="8" {...s}/></svg>);
    case 'lock-pick': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="20" cy="16" r="8" {...s}/><rect x="16" y="22" width="8" height="12" rx="1" {...s}/><circle cx="20" cy="16" r="2" fill="rgba(255,255,255,0.9)"/><line x1="20" y1="18" x2="20" y2="28" {...s}/></svg>);
    case 'marbles': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="14" cy="14" r="5" {...s}/><circle cx="28" cy="18" r="4" {...s}/><circle cx="18" cy="28" r="6" {...s}/><path d="M 6 34 Q 20 38 34 34" {...s}/></svg>);
    case 'rugpull': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><path d="M 10 34 L 20 6 L 30 34" {...s}/><line x1="14" y1="24" x2="26" y2="24" {...s}/><circle cx="20" cy="14" r="3" {...s}/><path d="M 6 36 L 34 36" {...s}/></svg>);
    case 'vault-run': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><rect x="8" y="6" width="24" height="28" rx="3" {...s}/><circle cx="20" cy="18" r="6" {...s}/><line x1="20" y1="14" x2="20" y2="22" {...s}/><line x1="16" y1="18" x2="24" y2="18" {...s}/><path d="M 14 28 L 26 28" {...s}/><path d="M 12 32 L 28 32" {...s}/></svg>);
    case 'mines': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><polygon points="20,4 36,14 30,34 10,34 4,14" {...s}/><circle cx="20" cy="20" r="4" {...s}/><line x1="20" y1="11" x2="20" y2="16" {...s}/></svg>);
    case 'goal': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><rect x="6" y="10" width="28" height="22" {...s}/><line x1="6" y1="32" x2="34" y2="32" {...s}/><path d="M 12 10 L 12 32 M 28 10 L 28 32 M 20 10 L 20 32" {...s}/><path d="M 6 18 L 34 18" {...s}/></svg>);
    case 'hilo': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><rect x="8" y="6" width="18" height="26" rx="2" {...s}/><rect x="14" y="12" width="18" height="22" rx="2" {...s}/><path d="M 19 20 L 23 24 L 27 20" {...s}/></svg>);
    case 'plinko': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="14" cy="12" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="26" cy="12" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="10" cy="20" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="20" cy="20" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="30" cy="20" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="14" cy="28" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="26" cy="28" r="1.5" fill="rgba(255,255,255,0.9)"/><polygon points="20,4 32,32 8,32" {...s}/></svg>);
    case 'baccarat': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="14" cy="14" r="6" {...s}/><circle cx="26" cy="14" r="6" {...s}/><circle cx="20" cy="26" r="6" {...s}/><path d="M 20 22 L 20 32" {...s}/></svg>);
    case 'dice': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><rect x="8" y="8" width="24" height="24" rx="3" {...s}/><circle cx="14" cy="14" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="26" cy="14" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="20" cy="20" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="14" cy="26" r="1.5" fill="rgba(255,255,255,0.9)"/><circle cx="26" cy="26" r="1.5" fill="rgba(255,255,255,0.9)"/></svg>);
    case 'keno': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="14" cy="14" r="6" {...s}/><circle cx="26" cy="14" r="6" {...s}/><circle cx="14" cy="26" r="6" {...s}/><circle cx="26" cy="26" r="6" {...s}/><text x="14" y="17" textAnchor="middle" fill="rgba(255,255,255,0.9)" fontFamily="Space Grotesk" fontSize="7">7</text><text x="26" y="17" textAnchor="middle" fill="rgba(255,255,255,0.9)" fontFamily="Space Grotesk" fontSize="7">3</text><text x="14" y="29" textAnchor="middle" fill="rgba(255,255,255,0.9)" fontFamily="Space Grotesk" fontSize="7">5</text></svg>);
    case 'tower': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><path d="M 12 32 L 28 32 L 28 8 L 12 8 Z" {...s}/><line x1="12" y1="14" x2="28" y2="14" {...s}/><line x1="12" y1="20" x2="28" y2="20" {...s}/><line x1="12" y1="26" x2="28" y2="26" {...s}/><circle cx="20" cy="5" r="1.5" fill="rgba(255,255,255,0.9)"/></svg>);
    case 'wheel': return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="20" cy="20" r="14" {...s}/><line x1="20" y1="6" x2="20" y2="34" {...s}/><line x1="6" y1="20" x2="34" y2="20" {...s}/><line x1="10" y1="10" x2="30" y2="30" {...s}/><line x1="30" y1="10" x2="10" y2="30" {...s}/><circle cx="20" cy="20" r="2" fill="rgba(255,255,255,0.9)"/></svg>);
    default: return (<svg viewBox="0 0 40 40" width="100%" height="100%"><circle cx="20" cy="20" r="14" {...s}/><circle cx="20" cy="20" r="6" {...s}/></svg>);
  }
}

function GameTile({ game, size = 'md', mini = false }) {
  if (game.treatment === 'art-diffuser') return <ArtDiffuser />;
  if (game.treatment === 'art-velocity') return <ArtVelocity />;
  if (game.treatment === 'art-vault')    return <ArtVault />;
  if (game.treatment === 'art-aviator')  return <ArtAviator />;
  // abbrev — Spribe-inspired vibrant gradients with iconography
  const bgMap = {
    rust:   'linear-gradient(135deg, #FF8A4D 0%, #C8501C 50%, #7F2A00 100%)',
    indigo: 'linear-gradient(135deg, #5B7FD9 0%, #2A4D9E 50%, #16265C 100%)',
    moss:   'linear-gradient(135deg, #6FB463 0%, #2F8A3E 55%, #1A4F26 100%)',
    plum:   'linear-gradient(135deg, #D14D8E 0%, #8E2A66 55%, #481236 100%)',
    ochre:  'linear-gradient(135deg, #FFB347 0%, #D17A1F 55%, #6E4A1C 100%)',
    ink:    'linear-gradient(135deg, #4B5BA8 0%, #1F2769 55%, #0B1235 100%)',
  };
  const abbr = game.title.replace(/^The\s+/,'').slice(0,2);
  return (
    <div style={{ width: '100%', height: '100%', background: bgMap[game.bg] || bgMap.rust, position: 'relative', overflow: 'hidden' }}>
      {/* glossy arc highlight */}
      <div style={{ position: 'absolute', inset: 0,
        background: 'radial-gradient(ellipse 90% 60% at 30% -10%, rgba(255,255,255,0.28), transparent 60%)' }} />
      {/* big bottom-right concentric arcs */}
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMaxYMax slice" style={{ position: 'absolute', right: -40, bottom: -50, width: '70%', height: '90%', opacity: 0.18, pointerEvents: 'none' }}>
        <circle cx="200" cy="200" r="80"  fill="none" stroke="white" strokeWidth="1.4" />
        <circle cx="200" cy="200" r="130" fill="none" stroke="white" strokeWidth="1.2" />
        <circle cx="200" cy="200" r="180" fill="none" stroke="white" strokeWidth="1" />
      </svg>
      {/* icon top-right */}
      <div style={{ position: 'absolute', top: mini ? 12 : 18, right: mini ? 12 : 18, width: mini ? 28 : 44, height: mini ? 28 : 44 }}>
        <GameIcon id={game.id} />
      </div>
      {/* abbreviation + title */}
      <div style={{ position: 'absolute', left: mini ? 14 : 24, top: mini ? '40%' : '34%', transform: 'translateY(-50%)', color: 'white' }}>
        <div className="tile-abbrev" style={{ fontSize: mini ? 44 : 78, lineHeight: 0.9, letterSpacing: '-0.04em' }}>{abbr}</div>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: mini ? 16 : 26, marginTop: 6, letterSpacing: '-0.01em' }}>{game.title}</div>
      </div>
      {/* code chip bottom-left */}
      <span style={{ position: 'absolute', bottom: mini ? 12 : 18, left: mini ? 14 : 24, fontFamily: 'var(--font-mono)', fontSize: 10, color: 'rgba(255,255,255,0.7)', letterSpacing: '0.18em', textTransform: 'uppercase' }}>{game.code} · {game.kind.split('·')[0]?.trim()}</span>
    </div>
  );
}

Object.assign(window, { GAMES, NEWS, GameTile, GameIcon, ArtDiffuser, ArtVelocity, ArtVault, ArtAviator });
