// sections.jsx — Nav, Hero, LivePulse, Games, Hubs, Clans, Trainers, Directory, Sanciones, Stats, CTA, Footer
const { useState, useEffect, useRef, useMemo } = React;

/* ─────────────── INVITES REALES (2026-05-15) ─────────────── */
const INVITE_SERVER  = 'https://discord.gg/zaPG9adRmq';
const INVITE_ZEROHEX = 'https://discord.gg/K5UP8Pa3jq';
const INVITE_BOT     = 'https://discord.com/api/oauth2/authorize?client_id=1469356955108901129&permissions=8&scope=bot%20applications.commands';
// Exponer en window por si otros JSXs los usan
if (typeof window !== 'undefined') {
  window.OW_INVITE_SERVER  = INVITE_SERVER;
  window.OW_INVITE_ZEROHEX = INVITE_ZEROHEX;
  window.OW_INVITE_BOT     = INVITE_BOT;
}

/* ─────────────── Reveal-on-scroll hook ─────────────── */
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { e.target.classList.add('in'); io.disconnect(); }
    }, { threshold: 0.12 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return ref;
}

/* ─────────────── Logo mark ─────────────── */
const LogoMark = ({ size = 28 }) => (
  <svg width={size} height={size} viewBox="0 0 32 32" fill="none">
    <defs>
      <linearGradient id="lg" x1="0" y1="0" x2="1" y2="1">
        <stop offset="0%" stopColor="var(--accent)"/>
        <stop offset="100%" stopColor="var(--accent-2)"/>
      </linearGradient>
    </defs>
    <path d="M16 2L29 9.5v13L16 30 3 22.5v-13L16 2z" stroke="url(#lg)" strokeWidth="1.6" fill="none"/>
    <path d="M16 9L23 13v6l-7 4-7-4v-6l7-4z" fill="url(#lg)" opacity="0.9"/>
    <path d="M11 13l5 3 5-3M16 16v6" stroke="var(--bg)" strokeWidth="1.2" strokeLinecap="round"/>
  </svg>
);

/* ─────────────── i18n REAL (2026-05-15) ─────────────── */
const LANGS = [
  { code: 'es', label: 'ES', name: 'Español' },
  { code: 'en', label: 'EN', name: 'English' },
  { code: 'pt', label: 'PT', name: 'Português' },
];

const STRINGS = {
  // Nav
  'nav.inicio':       { es: 'Inicio',       en: 'Home',         pt: 'Início' },
  'nav.hubs':         { es: 'Hubs',         en: 'Hubs',         pt: 'Hubs' },
  'nav.clanes':       { es: 'Clanes',       en: 'Clans',        pt: 'Clãs' },
  'nav.comunidades':  { es: 'Comunidades',  en: 'Communities',  pt: 'Comunidades' },
  'nav.trainers':     { es: 'Trainers',     en: 'Trainers',     pt: 'Treinadores' },
  'nav.start':        { es: 'Cómo empezar', en: 'Get started',  pt: 'Como começar' },
  'nav.usage':        { es: 'Comandos',     en: 'Commands',     pt: 'Comandos' },
  'nav.directorio':   { es: 'Directorio',   en: 'Directory',    pt: 'Diretório' },
  'nav.sanciones':    { es: 'Sanciones',    en: 'Sanctions',    pt: 'Sanções' },
  'nav.usage':        { es: 'Comandos',     en: 'Commands',     pt: 'Comandos' },
  'nav.discord':      { es: 'Discord',      en: 'Discord',      pt: 'Discord' },

  // Hero
  'hero.eyebrow':     { es: 'v3.0 · Plataforma competitiva', en: 'v3.0 · Competitive platform', pt: 'v3.0 · Plataforma competitiva' },
  // Título completo por idioma — usa <em></em> para la palabra acentuada
  // (en el render se reemplaza por <span class="emph">). Evita armar el título
  // por partes que se rompía en inglés (ej. "Where the los best compete").
  'hero.title.html':  { es: 'Donde compiten<br/><em>los mejores.</em>',
                        en: 'Where <em>the best</em><br/>compete.',
                        pt: 'Onde competem<br/><em>os melhores.</em>' },
  'hero.desc':        { es: 'Rankings en vivo, clanes verificados, torneos coordinados por Discord y un directorio público para toda la escena hispanohablante. Un solo lugar, múltiples juegos.',
                        en: 'Live rankings, verified clans, Discord-coordinated tournaments and a public directory for the whole community. One place, multiple games.',
                        pt: 'Rankings ao vivo, clãs verificados, torneios coordenados pelo Discord e um diretório público para toda a cena. Um só lugar, múltiplos jogos.' },
  'hero.cta.discord': { es: 'Unirse a Discord', en: 'Join Discord', pt: 'Entrar no Discord' },
  'hero.cta.bot':     { es: 'Invitar bot',      en: 'Invite bot',   pt: 'Convidar bot' },
  'hero.stat.cmds':   { es: 'Comandos · 14 módulos', en: 'Commands · 14 modules', pt: 'Comandos · 14 módulos' },
  'hero.stat.games':  { es: 'Juegos soportados',     en: 'Games supported',       pt: 'Jogos suportados' },
  'hero.stat.discord':{ es: 'Multi-guild',           en: 'Multi-guild',           pt: 'Multi-guild' },
  'hero.stat.uptime': { es: 'Bot online',            en: 'Bot online',            pt: 'Bot online' },

  // Sections
  'sec.juegos.eyebrow':   { es: '01 · Juegos',        en: '01 · Games',         pt: '01 · Jogos' },
  'sec.juegos.title.a':   { es: 'Una plataforma,',    en: 'One platform,',      pt: 'Uma plataforma,' },
  'sec.juegos.title.b':   { es: 'múltiples títulos.', en: 'multiple titles.',   pt: 'múltiplos títulos.' },
  'sec.juegos.sub':       { es: 'OneWay soporta The Strongest Battlegrounds y Jujutsu Shenanigans. La infraestructura — rankings, clanes, torneos, sanciones — es agnóstica al juego.',
                            en: 'OneWay supports The Strongest Battlegrounds and Jujutsu Shenanigans. The infrastructure — rankings, clans, tournaments, sanctions — is game-agnostic.',
                            pt: 'OneWay suporta The Strongest Battlegrounds e Jujutsu Shenanigans. A infraestrutura — rankings, clãs, torneios, sanções — é agnóstica ao jogo.' },
  'sec.juegos.cta':       { es: 'Ver clanes y comunidades', en: 'View clans and communities', pt: 'Ver clãs e comunidades' },

  'sec.hubs.eyebrow':     { es: '03 · Sedes oficiales', en: '03 · Official hubs', pt: '03 · Sedes oficiais' },
  'sec.hubs.title.a':     { es: 'Hubs oficiales',      en: 'Official hubs',      pt: 'Hubs oficiais' },
  'sec.hubs.title.b':     { es: 'por región.',         en: 'by region.',         pt: 'por região.' },
  'sec.hubs.sub':         { es: 'Cada hub es una comunidad activa del juego en su región. OneWay conecta y verifica la red, no las posee. Nomenclatura: TSB + código de región.',
                            en: 'Each hub is an active community of the game in its region. OneWay connects and verifies the network, it does not own the hubs. Naming: TSB + region code.',
                            pt: 'Cada hub é uma comunidade ativa do jogo em sua região. OneWay conecta e verifica a rede, não é dona dos hubs. Nomenclatura: TSB + código de região.' },
  'sec.hubs.apply':       { es: 'Aplicar a un hub',    en: 'Apply to a hub',     pt: 'Candidatar-se a um hub' },
  'sec.hubs.unirse':      { es: 'Unirse',              en: 'Join',               pt: 'Entrar' },
  'sec.hubs.lead.empty':  { es: 'Lead sin asignar',    en: 'Lead unassigned',    pt: 'Líder não atribuído' },
  'sec.hubs.miembros':    { es: 'Miembros',            en: 'Members',            pt: 'Membros' },
  'sec.hubs.clanes':      { es: 'Clanes',              en: 'Clans',              pt: 'Clãs' },

  // Zero Hex (clan oficial One Way)
  'sec.zerohex.eyebrow':  { es: 'Clan oficial · propiedad de One Way', en: 'Official clan · owned by One Way', pt: 'Clã oficial · propriedade da One Way' },
  'sec.zerohex.title.a':  { es: 'Zero Hex',           en: 'Zero Hex',           pt: 'Zero Hex' },
  'sec.zerohex.title.b':  { es: 'el clan insignia.',  en: 'the flagship clan.', pt: 'o clã carro-chefe.' },
  'sec.zerohex.sub':      { es: 'Nuestro clan principal. Los miembros más activos de la plataforma, élite competitiva y staff fundador. Actualmente top 3 LATAM en el hub TSBL. Verificación manual y proceso de entrada exigente.',
                            en: 'Our flagship clan. Most active platform members, competitive elite and founding staff. Currently top 3 LATAM in the TSBL hub. Manual verification and strict entry process.',
                            pt: 'Nosso clã principal. Membros mais ativos da plataforma, elite competitiva e staff fundador. Atualmente top 3 LATAM no hub TSBL. Verificação manual e processo de entrada exigente.' },
  'sec.zerohex.join':     { es: 'Unirme al clan',     en: 'Join the clan',      pt: 'Entrar no clã' },
  'sec.zerohex.tag':      { es: 'ONW',                en: 'ONW',                pt: 'ONW' },

  'sec.clanes.eyebrow':   { es: '04 · Clanes',         en: '04 · Clans',         pt: '04 · Clãs' },
  'sec.clanes.title.a':   { es: 'Élite del',           en: 'Top of',             pt: 'Elite do' },
  'sec.clanes.title.b':   { es: 'ranking.',            en: 'the ranking.',       pt: 'ranking.' },
  'sec.clanes.sub':       { es: 'Verificación manual, ranking público por puntos y miembros activos.',
                            en: 'Manual verification, public ranking by points and active members.',
                            pt: 'Verificação manual, ranking público por pontos e membros ativos.' },
  'sec.clanes.register':  { es: 'Registrar mi clan',   en: 'Register my clan',   pt: 'Registrar meu clã' },

  'sec.start.eyebrow':    { es: '05 · Cómo empezar',   en: '05 · Get started',   pt: '05 · Como começar' },
  'sec.start.title.a':    { es: 'Sumate en',           en: 'Join in',            pt: 'Entre em' },
  'sec.start.title.b':    { es: 'cuatro pasos.',       en: 'four steps.',        pt: 'quatro passos.' },
  'sec.start.sub':        { es: 'Sin formularios, sin esperas. Todo lo importante se gestiona desde Discord.',
                            en: 'No forms, no waiting. Everything important happens from Discord.',
                            pt: 'Sem formulários, sem esperas. Tudo o que importa acontece pelo Discord.' },
  'sec.start.s1.t':       { es: 'Unite al Discord',    en: 'Join the Discord',   pt: 'Entrar no Discord' },
  'sec.start.s1.d':       { es: 'Aceptá la invitación al servidor principal de OneWay. Es público, gratuito y vale como puerta de entrada a toda la red.',
                            en: 'Accept the invite to the main OneWay server. Public, free and the entry point to the whole network.',
                            pt: 'Aceite o convite para o servidor principal da OneWay. É público, gratuito e a porta de entrada para toda a rede.' },
  'sec.start.s1.cta':     { es: 'Abrir Discord',       en: 'Open Discord',       pt: 'Abrir Discord' },
  'sec.start.s2.t':       { es: 'Verificá tu cuenta',  en: 'Verify your account', pt: 'Verifique sua conta' },
  'sec.start.s2.d':       { es: 'Corré .verify en cualquier canal. La verificación queda asociada a tu Discord y vale en toda la red de servidores conectados.',
                            en: 'Run .verify in any channel. Verification ties to your Discord and is valid across the connected network.',
                            pt: 'Execute .verify em qualquer canal. A verificação fica ligada ao seu Discord e vale em toda a rede conectada.' },
  'sec.start.s2.cta':     { es: 'Cómo verificar',      en: 'How to verify',      pt: 'Como verificar' },
  'sec.start.s3.t':       { es: 'Encontrá tu hub',     en: 'Find your hub',      pt: 'Encontre seu hub' },
  'sec.start.s3.d':       { es: 'Cada juego y región tiene un hub público (TSBL, TSBC, TSBV…). Aplicá al que coincida con tu zona y juego.',
                            en: 'Each game and region has a public hub (TSBL, TSBC, TSBV…). Apply to the one matching your area and game.',
                            pt: 'Cada jogo e região tem um hub público (TSBL, TSBC, TSBV…). Candidate-se ao que combinar com sua área e jogo.' },
  'sec.start.s3.cta':     { es: 'Ver hubs',            en: 'See hubs',           pt: 'Ver hubs' },
  'sec.start.s4.t':       { es: 'Registrá tu clan',    en: 'Register your clan', pt: 'Registre seu clã' },
  'sec.start.s4.d':       { es: 'Si liderás un clan, usá .clan register para sumarlo a la red. Pasa por verificación manual antes de aparecer en el directorio público.',
                            en: 'If you lead a clan, use .clan register to add it to the network. Goes through manual verification before appearing in the public directory.',
                            pt: 'Se você lidera um clã, use .clan register para adicioná-lo. Passa por verificação manual antes de aparecer no diretório.' },
  'sec.start.s4.cta':     { es: 'Registrar clan',      en: 'Register clan',      pt: 'Registrar clã' },

  'sec.cta.eyebrow':      { es: 'Empezar',             en: 'Get started',        pt: 'Começar' },
  'sec.cta.title.a':      { es: 'Una plataforma,',     en: 'One platform,',      pt: 'Uma plataforma,' },
  'sec.cta.title.b':      { es: 'todo en un solo lugar.', en: 'all in one place.', pt: 'tudo num só lugar.' },
  'sec.cta.sub':          { es: 'Rankings, torneos, clanes, verificación cross-server y coaching. Operados por OneWay — el bot es la herramienta, la plataforma es nuestra.',
                            en: 'Rankings, tournaments, clans, cross-server verification and coaching. Operated by OneWay — the bot is the tool, the platform is ours.',
                            pt: 'Rankings, torneios, clãs, verificação cross-server e coaching. Operados pela OneWay — o bot é a ferramenta, a plataforma é nossa.' },
  'sec.cta.join':         { es: 'Unirse al servidor',  en: 'Join the server',    pt: 'Entrar no servidor' },
  'sec.cta.invite':       { es: 'Invitar bot a tu server', en: 'Invite bot to your server', pt: 'Convidar bot ao seu server' },
  'footer.terms':         { es: 'Términos',            en: 'Terms',              pt: 'Termos' },
  'footer.rights':        { es: 'Todos los derechos reservados.', en: 'All rights reserved.', pt: 'Todos os direitos reservados.' },

  'lang.progress':        { es: 'Traducción en progreso', en: 'Translation in progress', pt: 'Tradução em andamento' },
};

function readLang() {
  try { return localStorage.getItem('ow_lang') || 'es'; } catch (_) { return 'es'; }
}
function writeLang(code) {
  try { localStorage.setItem('ow_lang', code); } catch (_) {}
  document.documentElement.setAttribute('lang', code);
  // Disparar evento global para re-render
  try { window.dispatchEvent(new CustomEvent('ow:lang', { detail: code })); } catch (_) {}
}

// Hook que re-renderiza al cambiar idioma
function useLang() {
  const [lang, setLang] = useState(readLang());
  useEffect(() => {
    const onLang = (e) => setLang(e.detail || readLang());
    window.addEventListener('ow:lang', onLang);
    return () => window.removeEventListener('ow:lang', onLang);
  }, []);
  return lang;
}

// Helper t(key) — retorna string en el idioma activo, fallback ES
function t(key, lang) {
  const e = STRINGS[key];
  if (!e) return key; // fallback al key como hint si no existe
  return e[lang] || e.es || key;
}
// useT() hook combinado
function useT() {
  const lang = useLang();
  return (key) => t(key, lang);
}

// Exponer en window para JSXs hermanos
if (typeof window !== 'undefined') {
  window.OW_useT = useT;
  window.OW_useLang = useLang;
  window.OW_STRINGS = STRINGS;
  window.OW_t = t;
}

function LangSelector() {
  const [lang, setLang] = useState(readLang());
  const [open, setOpen] = useState(false);
  useEffect(() => { writeLang(lang); }, [lang]);
  const current = LANGS.find(l => l.code === lang) || LANGS[0];

  return (
    <div style={{ position: 'relative' }}>
      <button
        onClick={() => setOpen(o => !o)}
        aria-label="Idioma"
        aria-expanded={open}
        style={{
          display: 'flex', alignItems: 'center', gap: 6,
          height: 36, padding: '0 12px', borderRadius: 10,
          background: 'var(--surface-2)', border: '1px solid var(--border)',
          color: 'var(--text-dim)', cursor: 'pointer',
          fontFamily: 'Geist Mono, ui-monospace, monospace',
          fontSize: 12, fontWeight: 600, letterSpacing: '0.04em',
          transition: 'all .2s',
        }}
        onMouseEnter={e => { e.currentTarget.style.color = 'var(--text)'; e.currentTarget.style.borderColor = 'var(--border-strong)'; }}
        onMouseLeave={e => { e.currentTarget.style.color = 'var(--text-dim)'; e.currentTarget.style.borderColor = 'var(--border)'; }}
      >
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
          <circle cx="12" cy="12" r="10"/>
          <line x1="2" y1="12" x2="22" y2="12"/>
          <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
        </svg>
        {current.label}
      </button>
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 49 }}/>
          <div style={{
            position: 'absolute', top: 'calc(100% + 6px)', right: 0, zIndex: 50,
            minWidth: 140, padding: 4,
            background: 'var(--surface)', border: '1px solid var(--border-strong)',
            borderRadius: 10, boxShadow: 'var(--shadow-md)',
            display: 'flex', flexDirection: 'column', gap: 1,
          }}>
            {LANGS.map(l => (
              <button key={l.code}
                onClick={() => { setLang(l.code); setOpen(false); }}
                style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  padding: '8px 12px', borderRadius: 7,
                  background: lang === l.code ? 'var(--accent-soft)' : 'transparent',
                  border: 'none', cursor: 'pointer',
                  color: lang === l.code ? 'var(--accent)' : 'var(--text)',
                  fontSize: 13, fontWeight: 500, fontFamily: 'inherit',
                  textAlign: 'left',
                }}
                onMouseEnter={e => { if (lang !== l.code) e.currentTarget.style.background = 'var(--surface-2)'; }}
                onMouseLeave={e => { if (lang !== l.code) e.currentTarget.style.background = 'transparent'; }}>
                <span>{l.name}</span>
                <span className="mono" style={{ fontSize: 10, opacity: 0.7 }}>{l.label}</span>
              </button>
            ))}
            <div style={{ padding: '8px 12px', fontSize: 10, color: 'var(--text-muted)', fontFamily: 'Geist Mono, monospace', borderTop: '1px solid var(--border)', marginTop: 2 }}>
              {t('lang.progress', lang)}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

/* ─────────────── Nav ─────────────── */
function Nav({ dark, onToggleTheme }) {
  const [scrolled, setScrolled] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);
  const tr = useT();
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  useEffect(() => {
    document.body.style.overflow = mobileOpen ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [mobileOpen]);

  // NAV-LINKS-REAL (2026-05-15): apuntar a las paginas reales del ecosistema
  // OneWay (verificadas en Traefik dynamic configs). Comunidades aun no tiene
  // PathPrefix propio en Traefik, asi que cae en /clans (donde se listan).
  const items = [
    { id: 'inicio',      key: 'nav.inicio',      icon: Icon.Home,    href: '/inicio' },
    { id: 'hubs',        key: 'nav.hubs',        icon: Icon.Hubs,    href: '/hubs' },
    { id: 'clanes',      key: 'nav.clanes',      icon: Icon.Users,   href: '/clans' },
    { id: 'comunidades', key: 'nav.comunidades', icon: Icon.Globe,   href: '/clans' },
    { id: 'trainers',    key: 'nav.trainers',    icon: Icon.Trainer, href: '/trainers' },
    { id: 'directorio',  key: 'nav.directorio',  icon: Icon.Search,  href: '/directory' },
    { id: 'sanciones',   key: 'nav.sanciones',   icon: Icon.Warn,    href: '/warnings' },
    { id: 'usage',       key: 'nav.usage',       icon: Icon.Hash,    href: '/usage' },
  ];

  return (
    <header style={{
      position: 'sticky', top: 0, zIndex: 50,
      backdropFilter: scrolled ? 'blur(20px) saturate(160%)' : 'none',
      WebkitBackdropFilter: scrolled ? 'blur(20px) saturate(160%)' : 'none',
      background: scrolled ? 'color-mix(in srgb, var(--bg) 70%, transparent)' : 'transparent',
      borderBottom: scrolled ? '1px solid var(--border)' : '1px solid transparent',
      transition: 'all .25s ease',
    }}>
      <div className="container" style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        height: 76, gap: 18,
      }}>
        <a href="/inicio" style={{ display: 'flex', alignItems: 'center', gap: 12, textDecoration: 'none', color: 'var(--text)', flexShrink: 0 }}>
          <img src="https://cdn.discordapp.com/icons/1417173139778965692/5cf54bab9936a51f79915c777915865f.webp?size=128"
            alt="OneWay" width="40" height="40"
            style={{ width: 40, height: 40, borderRadius: 11, border: '1px solid var(--border-strong)', boxShadow: '0 4px 14px -4px rgba(0,0,0,0.4)', objectFit: 'cover' }}
            onError={(e) => { e.currentTarget.style.display = 'none'; }}/>
          <span style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.015em' }}>OneWay</span>
        </a>

        <nav style={{ display: 'flex', alignItems: 'center', gap: 2, flex: 1, justifyContent: 'center' }} className="nav-items">
          {items.map((it, i) => (
            <a key={it.id} href={it.href}
               style={{
                 display: 'flex', alignItems: 'center', gap: 7,
                 padding: '8px 12px', borderRadius: 8,
                 color: i === 0 ? 'var(--text)' : 'var(--text-dim)',
                 textDecoration: 'none',
                 fontSize: 13, fontWeight: 500,
                 transition: 'color .2s, background .2s',
               }}
               onMouseEnter={e => { e.currentTarget.style.color = 'var(--text)'; e.currentTarget.style.background = 'var(--surface-2)'; }}
               onMouseLeave={e => { e.currentTarget.style.color = i === 0 ? 'var(--text)' : 'var(--text-dim)'; e.currentTarget.style.background = 'transparent'; }}>
              <it.icon size={15}/>
              {tr(it.key)}
            </a>
          ))}
        </nav>

        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <LangSelector/>
          <button onClick={onToggleTheme}
            aria-label="Cambiar tema"
            style={{
              width: 36, height: 36, borderRadius: 10,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              background: 'var(--surface-2)', border: '1px solid var(--border)',
              color: 'var(--text-dim)', cursor: 'pointer',
              transition: 'all .2s',
            }}>
            {dark ? <Icon.Sun size={16}/> : <Icon.Moon size={16}/>}
          </button>
          <a className="btn btn-discord nav-discord-btn" href={INVITE_SERVER} target="_blank" rel="noopener noreferrer">
            <Icon.Discord size={16}/>
            <span className="nav-discord-label">Discord</span>
          </a>
          <button onClick={() => setMobileOpen(v => !v)}
            aria-label="Menú"
            className="nav-burger"
            style={{
              display: 'none',
              width: 40, height: 40, borderRadius: 10,
              alignItems: 'center', justifyContent: 'center',
              background: 'var(--surface-2)', border: '1px solid var(--border)',
              color: 'var(--text)', cursor: 'pointer',
            }}>
            {mobileOpen ? <Icon.X size={18}/> : <Icon.Menu size={18}/>}
          </button>
        </div>
      </div>

      {/* Mobile drawer (fade backdrop + slide-down items with stagger) */}
      <div className={`nav-drawer ${mobileOpen ? 'nav-drawer-open' : ''}`}
        onClick={() => setMobileOpen(false)}
        aria-hidden={!mobileOpen}
        style={{
          position: 'fixed', inset: '76px 0 0 0', zIndex: 49,
          background: 'color-mix(in srgb, var(--bg) 96%, transparent)',
          backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)',
          overflowY: 'auto', padding: '20px 0 40px',
          opacity: mobileOpen ? 1 : 0,
          pointerEvents: mobileOpen ? 'auto' : 'none',
          transition: 'opacity .28s cubic-bezier(.2,.7,.2,1)',
        }}>
        <div className="container" onClick={e => e.stopPropagation()} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {items.map((it, idx) => (
            <a key={it.id} href={it.href} onClick={() => setMobileOpen(false)}
              className="nav-drawer-item"
              style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '14px 16px', borderRadius: 12,
                background: 'var(--surface-2)', border: '1px solid var(--border)',
                color: 'var(--text)', textDecoration: 'none',
                fontSize: 15, fontWeight: 500,
                transform: mobileOpen ? 'translateY(0)' : 'translateY(-8px)',
                opacity: mobileOpen ? 1 : 0,
                transition: `transform .35s cubic-bezier(.2,.7,.2,1) ${idx * 35}ms, opacity .3s ease ${idx * 35}ms, background .18s, border-color .18s`,
              }}
              onMouseEnter={e => { e.currentTarget.style.background = 'var(--surface-3)'; e.currentTarget.style.borderColor = 'var(--border-strong)'; }}
              onMouseLeave={e => { e.currentTarget.style.background = 'var(--surface-2)'; e.currentTarget.style.borderColor = 'var(--border)'; }}>
              <span style={{
                width: 32, height: 32, borderRadius: 10,
                background: 'color-mix(in srgb, var(--accent) 14%, transparent)',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                flexShrink: 0,
              }}>
                <it.icon size={16} style={{ color: 'var(--accent)' }}/>
              </span>
              {tr(it.key)}
              <Icon.Arrow size={14} style={{ marginLeft: 'auto', color: 'var(--text-muted)' }}/>
            </a>
          ))}
        </div>
      </div>

      <style>{`
        @media (max-width: 1100px) {
          .nav-items { display: none !important; }
          .nav-burger { display: flex !important; }
        }
        @media (max-width: 540px) {
          .nav-discord-label { display: none; }
        }
        .nav-burger { transition: transform .25s cubic-bezier(.2,.7,.2,1), background .2s, border-color .2s, color .2s; }
        .nav-burger:active { transform: scale(0.92); }
        .nav-burger:hover { color: var(--text); border-color: var(--border-strong); }
      `}</style>
    </header>
  );
}

/* ─────────────── Hero ─────────────── */
function Hero() {
  const ref = useReveal();
  const tr = useT();
  return (
    <section id="inicio" style={{ paddingTop: 64, paddingBottom: 72, overflow: 'hidden' }}>
      <div className="grid-bg" style={{ position: 'absolute', inset: 0, opacity: 0.5 }}/>
      <div className="container" style={{ position: 'relative' }}>
        <div ref={ref} className="reveal" style={{
          display: 'grid',
          gridTemplateColumns: 'minmax(0, 1.3fr) minmax(0, 1fr)',
          gap: 64, alignItems: 'center',
        }} id="hero-grid">
          {/* Left: typography */}
          <div>
            <div className="pill" style={{ marginBottom: 28 }}>
              <span className="dot"></span>
              <span>{tr('hero.eyebrow')}</span>
            </div>

            <h1 className="display" style={{
              fontSize: 'clamp(48px, 7vw, 92px)',
              margin: 0,
            }}
              dangerouslySetInnerHTML={{
                __html: tr('hero.title.html').replace(/<em>(.+?)<\/em>/g, '<span class="emph">$1</span>')
              }}
            />
            {/* (chip One Way removido — la identidad ya está en el navbar agrandado) */}

            <p style={{
              fontSize: 18, color: 'var(--text-dim)',
              maxWidth: 520, marginTop: 28, lineHeight: 1.55,
            }}>
              {tr('hero.desc')}
            </p>

            <div style={{ display: 'flex', gap: 12, marginTop: 36, flexWrap: 'wrap' }}>
              <a className="btn btn-discord" href={INVITE_SERVER} target="_blank" rel="noopener noreferrer">
                <Icon.Discord size={16}/>
                {tr('hero.cta.discord')}
              </a>
              <a className="btn btn-ghost" href={INVITE_BOT} target="_blank" rel="noopener noreferrer">
                {tr('hero.cta.bot')}
                <Icon.Arrow size={15}/>
              </a>
            </div>

            <div style={{ display: 'flex', gap: 28, marginTop: 44, flexWrap: 'wrap' }}>
              {[
                { v: '220+',    l: tr('hero.stat.cmds') },
                { v: '2',       l: tr('hero.stat.games') },
                { v: 'Discord', l: tr('hero.stat.discord') },
                { v: '24/7',    l: tr('hero.stat.uptime') },
              ].map(s => (
                <div key={s.l}>
                  <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: 'var(--text)', letterSpacing: '-0.02em' }}>{s.v}</div>
                  <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>{s.l}</div>
                </div>
              ))}
            </div>
          </div>

          {/* Right: live telemetry panel */}
          <HeroTelemetry/>
        </div>
      </div>
      <style>{`
        @media (max-width: 980px) { #hero-grid { grid-template-columns: 1fr !important; gap: 40px !important; } }
      `}</style>
    </section>
  );
}

// useCountUp — cubic-ease animation from 0 -> target. Refresh anim when target changes.
function useCountUp(target, durationMs = 1800) {
  const [value, setValue] = useState(0);
  useEffect(() => {
    let raf;
    const start = performance.now();
    const tick = (now) => {
      const t = Math.min(1, (now - start) / durationMs);
      const eased = 1 - Math.pow(1 - t, 3);
      setValue(Math.floor(target * eased));
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, durationMs]);
  return value;
}

function HeroTelemetry() {
  // Live activity panel. Pulls /api/public/stats (visits/members/voice/24h-events)
  // AND /api/clans (count of official + community clans). Mantiene un set base
  // de "siempre visibles" para que la card nunca se sienta vacía aunque algunos
  // endpoints estén apagados o sin tracking aún.
  const [stats, setStats] = useState(null);
  const [clans, setClans] = useState(null);
  const [lastSync, setLastSync] = useState(null);

  useEffect(() => {
    let cancelled = false;

    const hydrate = async () => {
      try {
        let r = await fetch('api/public/stats', { cache: 'no-store' });
        if (!r.ok) r = await fetch('/inicio/api/public/stats', { cache: 'no-store' });
        if (r.ok) {
          const j = await r.json();
          if (cancelled) return;
          const visits = Number.isFinite(j.total)        ? j.total
                       : Number.isFinite(j.visits)       ? j.visits
                       : Number.isFinite(j.totalVisits)  ? j.totalVisits
                       : Number.isFinite(j.visitCount)   ? j.visitCount
                       : null;
          const members = Number.isFinite(j.totalMembers) ? j.totalMembers
                        : Number.isFinite(j.members)      ? j.members
                        : Number.isFinite(j.memberCount)  ? j.memberCount
                        : Number.isFinite(j.users)        ? j.users
                        : null;
          const voice = Number.isFinite(j.voice)      ? j.voice
                      : Number.isFinite(j.voiceCount) ? j.voiceCount
                      : Number.isFinite(j.onlineVoice) ? j.onlineVoice
                      : 0;
          const events24h = Number.isFinite(j.events24h)
                          ? j.events24h
                          : Number.isFinite(j.eventsLast24h) ? j.eventsLast24h
                          : 0;
          setStats({ visits, members, voice, events24h });
          setLastSync(new Date());
        }
      } catch (_) { /* endpoint offline */ }

      try {
        // /clans/api/clans is the wepage-local route that always reaches the
        // local handler (proven by our earlier routing-probe — /clans/* IS
        // in WEB_HANDLED_PREFIXES so it doesn't get proxied to the bot).
        let rc = await fetch('/clans/api/clans', { cache: 'no-store' });
        if (!rc.ok) rc = await fetch('/inicio/api/public/clans', { cache: 'no-store' });
        if (rc.ok) {
          let arr = await rc.json();
          if (cancelled) return;
          if (!Array.isArray(arr) && arr && Array.isArray(arr.clans)) arr = arr.clans;
          if (Array.isArray(arr)) {
            const official  = arr.filter(c => c && c.status === 'official').length;
            const community = arr.filter(c => c && c.status === 'community').length;
            const top = arr.slice().sort((a, b) => (b.points || 0) - (a.points || 0))[0] || null;
            setClans({ total: arr.length, official, community, top });
          }
        }
      } catch (_) { /* clans endpoint offline */ }
    };

    hydrate();
    const id = setInterval(hydrate, 30_000);
    return () => { cancelled = true; clearInterval(id); };
  }, []);

  // Animated counters — fall back to "best-known" values so the card never
  // shows zeros while the endpoints are warming up.
  const members  = useCountUp(stats?.members  ?? 5685);
  const events24 = useCountUp(stats?.events24h ?? 0);
  const voice    = useCountUp(stats?.voice    ?? 0);
  const clanCount = useCountUp(clans?.official ?? 0);
  const topPts   = useCountUp(clans?.top?.points ?? 0);

  const fmt = (n) => Number(n || 0).toLocaleString('es');

  // ── Always-visible rows (the section never feels empty) ───────────────────
  const rows = [
    {
      label: 'Miembros Discord',
      value: fmt(members),
      sub: 'red OneWay verificada · cross-server',
      icon: Icon.Users,
      color: 'var(--accent-2)',
    },
    {
      label: 'Clanes oficiales',
      value: clans ? String(clans.official) : '—',
      sub: clans
        ? `+ ${clans.community} en comunidad · ${clans.total} registrados`
        : 'verificación manual del staff',
      icon: Icon.Shield,
      color: 'var(--accent)',
    },
    {
      label: 'Hubs regionales',
      value: '6',
      sub: 'LATAM · BR · NA · EU · SAO · GLOBAL',
      icon: Icon.Globe,
      color: 'var(--live)',
    },
    {
      label: 'Comandos disponibles',
      value: '220+',
      sub: '14 módulos · 7 idiomas · multi-guild',
      icon: Icon.Sparkle,
      color: 'var(--warn)',
    },
  ];

  // ── Conditional extra rows (only when endpoint really returns >0) ─────────
  if (Number.isFinite(stats?.voice) && stats.voice > 0) {
    rows.push({ label: 'En canales de voz', value: fmt(voice), sub: 'ahora mismo', icon: Icon.Discord, color: 'var(--discord)' });
  }
  if (Number.isFinite(stats?.events24h) && stats.events24h > 0) {
    rows.push({ label: 'Eventos · últimas 24h', value: fmt(events24), sub: 'verifs · sanciones · torneos · joins', icon: Icon.Bolt, color: '#F472B6' });
  }

  // ── Recent activity ticker (rotates every 3.4s) ───────────────────────────
  // Cycles through a hand-picked list of meta-events. Cuando el feed real
  // exista (WS pulse_update), reemplazá este array por el state hidratado.
  const pulses = useMemo(() => {
    const out = [
      { ico: Icon.Shield, txt: 'Verificación manual de clanes', tag: 'continuo' },
      { ico: Icon.Trophy, txt: 'Sistema de phases · ELO público', tag: 'live' },
      { ico: Icon.Discord, txt: 'Bot multi-guild en 30+ servers', tag: 'estable' },
      { ico: Icon.Bolt,   txt: 'Rankings sincronizados con Discord', tag: 'real-time' },
      { ico: Icon.Sparkle, txt: 'Trainers verificados por staff', tag: 'curado' },
      { ico: Icon.Calendar, txt: 'Brackets de torneo oficial', tag: 'recurrente' },
    ];
    if (clans?.top?.name) {
      out.unshift({ ico: Icon.Trophy, txt: `Top del ranking · ${clans.top.name}`, tag: `${fmt(clans.top.points)} pts` });
    }
    return out;
  }, [clans?.top?.name, clans?.top?.points]);
  const [pulseIdx, setPulseIdx] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setPulseIdx((i) => (i + 1) % pulses.length), 3400);
    return () => clearInterval(id);
  }, [pulses.length]);
  const pulse = pulses[pulseIdx] || pulses[0];

  const syncedAgo = (() => {
    if (!lastSync) return 'sincronizando…';
    const s = Math.max(0, Math.floor((Date.now() - lastSync.getTime()) / 1000));
    if (s < 60)   return `actualizado hace ${s}s`;
    if (s < 3600) return `actualizado hace ${Math.floor(s/60)}m`;
    return `actualizado hace ${Math.floor(s/3600)}h`;
  })();

  return (
    <div className="card" style={{
      padding: 22, borderRadius: 20,
      background: 'linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%)',
      boxShadow: 'var(--shadow-lg)',
      position: 'relative', overflow: 'hidden',
    }}>
      {/* Subtle radial wash so the card feels alive */}
      <div style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
        background: 'radial-gradient(600px 200px at 80% -10%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 70%)',
      }}/>

      <div style={{ position: 'relative', zIndex: 1 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18, gap: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
            <div style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--live)', boxShadow: '0 0 10px var(--live)', animation: 'pulse 1.6s ease-in-out infinite' }}/>
            <span className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--text-dim)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              Actividad · en vivo
            </span>
          </div>
          <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>{syncedAgo}</span>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {rows.map((r) => (
            <div key={r.label} style={{
              display: 'grid', gridTemplateColumns: '38px 1fr auto', gap: 14, alignItems: 'center',
              padding: '12px 14px',
              background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 12,
              transition: 'border-color .2s ease, transform .2s ease',
            }}
            onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--border-strong)'; e.currentTarget.style.transform = 'translateY(-1px)'; }}
            onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.transform = 'translateY(0)'; }}>
              <div style={{ width: 38, height: 38, borderRadius: 10,
                background: `color-mix(in srgb, ${r.color} 14%, transparent)`,
                display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <r.icon size={18} style={{ color: r.color }}/>
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 2, minWidth: 0 }}>
                <span style={{ fontSize: 12, color: 'var(--text-dim)' }}>{r.label}</span>
                <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.sub}</span>
              </div>
              <div className="mono" style={{ fontSize: 20, fontWeight: 700, color: 'var(--text)', letterSpacing: '-0.02em', minWidth: 0, textAlign: 'right' }}>
                {r.value}
              </div>
            </div>
          ))}
        </div>

        {/* Featured top clan ribbon — shows live #1 from /clans */}
        {clans?.top && (
          <a href={`/clans/${encodeURIComponent(clans.top.id || '')}`} style={{ textDecoration: 'none', color: 'inherit' }}>
            <div style={{
              marginTop: 14, padding: '12px 14px',
              display: 'grid', gridTemplateColumns: 'auto 1fr auto', gap: 12, alignItems: 'center',
              background: 'linear-gradient(135deg, color-mix(in srgb, var(--warn) 14%, transparent), color-mix(in srgb, var(--accent) 10%, transparent))',
              border: '1px solid color-mix(in srgb, var(--warn) 35%, var(--border))',
              borderRadius: 12, position: 'relative', overflow: 'hidden',
              transition: 'transform .2s ease',
            }}
            onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-1px)'; }}
            onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; }}>
              {clans.top.logo ? (
                <img src={clans.top.logo} alt={clans.top.name} width="38" height="38"
                  style={{ width: 38, height: 38, borderRadius: 10, objectFit: 'cover', border: '1px solid rgba(255,255,255,0.18)' }}
                  onError={(e) => { e.currentTarget.style.display = 'none'; }}/>
              ) : (
                <div style={{ width: 38, height: 38, borderRadius: 10, background: 'color-mix(in srgb, var(--warn) 22%, transparent)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon.Trophy size={16} style={{ color: 'var(--warn)' }}/>
                </div>
              )}
              <div style={{ minWidth: 0 }}>
                <div className="mono" style={{ fontSize: 9, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--warn)', fontWeight: 700, marginBottom: 2 }}>
                  Top del ranking
                </div>
                <div style={{ fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                  {clans.top.name}
                </div>
              </div>
              <div className="mono" style={{ fontSize: 16, fontWeight: 700, color: 'var(--text)', letterSpacing: '-0.02em', textAlign: 'right' }}>
                {fmt(topPts)} <span style={{ fontSize: 10, color: 'var(--text-muted)', letterSpacing: '0.06em' }}>PTS</span>
              </div>
            </div>
          </a>
        )}

        {/* Rotating pulse ticker — qualitative meta-events */}
        <div style={{
          marginTop: 14, padding: '10px 14px',
          background: 'var(--surface-3)', border: '1px solid var(--border)', borderRadius: 12,
          display: 'grid', gridTemplateColumns: '20px 1fr auto', gap: 12, alignItems: 'center',
          minHeight: 42,
        }}>
          <pulse.ico size={14} style={{ color: 'var(--accent-2)' }}/>
          <div key={pulseIdx} style={{
            fontSize: 12, color: 'var(--text-dim)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
            animation: 'pulseFade .5s ease',
          }}>
            {pulse.txt}
          </div>
          <span className="mono" style={{
            fontSize: 9, letterSpacing: '0.12em', textTransform: 'uppercase',
            padding: '3px 8px', borderRadius: 999,
            background: 'color-mix(in srgb, var(--accent-2) 12%, transparent)',
            color: 'var(--accent-2)', fontWeight: 600,
          }}>
            {pulse.tag}
          </span>
        </div>

        {/* CTA footer */}
        <div style={{
          marginTop: 14, paddingTop: 14, borderTop: '1px dashed var(--border)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
        }}>
          <a href="/dashboard" style={{ textDecoration: 'none', color: 'var(--text-dim)', fontSize: 12, display: 'inline-flex', alignItems: 'center', gap: 6 }}>
            Ver dashboard completo <Icon.Arrow size={12}/>
          </a>
          <a href="/clans" style={{ textDecoration: 'none', color: 'var(--accent)', fontSize: 12, fontWeight: 500 }}>
            Directorio →
          </a>
        </div>
      </div>

      <style>{`
        @keyframes slideIn {
          from { opacity: 0; transform: translateY(-6px); }
          to { opacity: 1; transform: translateY(0); }
        }
        @keyframes pulseFade {
          from { opacity: 0; transform: translateX(6px); }
          to { opacity: 1; transform: translateX(0); }
        }
      `}</style>
    </div>
  );
}

/* ─────────────── Live Pulse marquee ─────────────── */
function LivePulse() {
  // FAKE-DATA-REMOVED (2026-05-14): pulse real desde /api/public/news + WebSocket pulse_update
  // Por ahora ticker genérico sin inventar nombres ni números.
  const events = [
    'OneWay · Plataforma competitiva',
    'Bot · Multi-guild · 14 módulos',
    'Verificación cross-server',
    'Rankings por phase · ELO público',
    'Trainers verificados',
    'Sanciones públicas y auditables',
    'Sumate desde Discord',
  ];
  const items = [...events, ...events];
  return (
    <div style={{
      borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)',
      background: 'var(--surface)',
      overflow: 'hidden',
      padding: '14px 0',
    }}>
      <div style={{
        display: 'flex', gap: 48,
        animation: 'marquee 60s linear infinite',
        whiteSpace: 'nowrap',
        width: 'max-content',
      }}>
        {items.map((e, i) => (
          <span key={i} className="mono" style={{ fontSize: 12, color: 'var(--text-muted)', letterSpacing: '0.04em', display: 'flex', alignItems: 'center', gap: 12 }}>
            <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--accent)', display: 'inline-block' }}/>
            {e}
          </span>
        ))}
      </div>
      <style>{`
        @keyframes marquee {
          from { transform: translateX(0); }
          to { transform: translateX(-50%); }
        }
      `}</style>
    </div>
  );
}

/* ─────────────── Games ─────────────── */
// GAMES-REAL (2026-05-15): solo los juegos que OneWay soporta hoy.
// TSB · The Strongest Battlegrounds (Roblox) — juego principal
// JJS · Jujutsu Shenanigans (Roblox) — secundario
// Las imágenes son thumbnails oficiales de Roblox CDN. Cuando agreguemos un
// juego nuevo, sumar entrada aquí.
function Games() {
  const ref = useReveal();
  const tr = useT();
  const games = [
    {
      id: 'tsbg',
      name: 'The Strongest Battlegrounds',
      shortName: 'TSB',
      platform: 'Roblox',
      tag: 'Fighting · 1v1',
      accent: '#5B8CFF',
      gradFallback: 'linear-gradient(135deg, #5B8CFF 0%, #1A1E5C 100%)',
      thumbs: [
        'https://tr.rbxcdn.com/180DAY-ccd8bcf9f66dc37834534d2cb3cfd1b9/768/432/Image/Webp/noFilter',
        'https://tr.rbxcdn.com/180DAY-c947df5b221c30672c3591247a8c6495/768/432/Image/Webp/noFilter',
        'https://tr.rbxcdn.com/180DAY-a65199acc4245274be48753c7872dd2b/768/432/Image/Webp/noFilter',
      ],
      desc: 'El juego insignia de la plataforma. Sistema de phases, brackets oficiales y rankings sincronizados con el bot.',
      href: '/clans',
    },
    {
      id: 'jjs',
      name: 'Jujutsu Shenanigans',
      shortName: 'JJS',
      platform: 'Roblox',
      tag: 'Fighting · Anime',
      accent: '#A66CFF',
      gradFallback: 'linear-gradient(135deg, #A66CFF 0%, #4A1180 100%)',
      thumbs: [
        'https://tr.rbxcdn.com/180DAY-decec85a41b80e4ac0beca8bc2952a7f/768/432/Image/Webp/noFilter',
        'https://tr.rbxcdn.com/180DAY-1c91deb8bfc839e3665bf517ef6161b9/768/432/Image/Webp/noFilter',
        'https://tr.rbxcdn.com/180DAY-7af6989082332e54ac6554ab761356ae/768/432/Image/Webp/noFilter',
      ],
      desc: 'Segundo juego oficial. Hubs, clanes y rankings comparten el mismo bot e infraestructura competitiva.',
      href: '/clans',
    },
  ];

  return (
    <section id="juegos">
      <div className="container">
        <SectionHeader
          eyebrow={tr('sec.juegos.eyebrow')}
          title={<>{tr('sec.juegos.title.a')} <span className="emph">{tr('sec.juegos.title.b')}</span></>}
          subtitle={tr('sec.juegos.sub')}
        />

        <div ref={ref} className="reveal" style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(2, 1fr)',
          gap: 18,
          marginTop: 48,
        }} id="games-grid">
          {games.map(g => <GameCard key={g.id} game={g}/>)}
        </div>
      </div>
      <style>{`
        @media (max-width: 900px) { #games-grid { grid-template-columns: 1fr !important; } }
        @keyframes thumbCycle { 0%, 28% { opacity: 1; } 33%, 95% { opacity: 0; } 100% { opacity: 1; } }
      `}</style>
    </section>
  );
}

function GameCard({ game }) {
  const tr = useT();
  const [thumbIdx, setThumbIdx] = useState(0);
  useEffect(() => {
    if (!game.thumbs || game.thumbs.length <= 1) return;
    const t = setInterval(() => setThumbIdx(i => (i + 1) % game.thumbs.length), 4500);
    return () => clearInterval(t);
  }, [game.thumbs]);

  const hasThumb = game.thumbs && game.thumbs.length > 0;
  const currentThumb = hasThumb ? game.thumbs[thumbIdx] : null;

  return (
    <a href={game.href} className="card card-hover" style={{
      textDecoration: 'none', color: 'inherit',
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
      minHeight: 360,
      position: 'relative',
    }}>
      {/* Background image / fallback gradient + heavy blur */}
      <div style={{
        position: 'absolute', inset: 0,
        background: game.gradFallback,
        zIndex: 0,
      }}/>
      {hasThumb && (
        <div style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
          {game.thumbs.map((src, i) => (
            <img key={src} src={src} alt={game.name + ' thumbnail ' + (i+1)}
              loading="lazy"
              style={{
                position: 'absolute', inset: 0,
                width: '100%', height: '100%',
                objectFit: 'cover',
                filter: 'blur(2px) brightness(0.55) saturate(1.1)',
                opacity: i === thumbIdx ? 1 : 0,
                transition: 'opacity 1.2s ease-in-out',
              }}/>
          ))}
          {/* Dark vignette overlay for legibility */}
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(180deg, rgba(8,9,12,0.35) 0%, rgba(8,9,12,0.85) 70%, rgba(8,9,12,0.95) 100%)',
          }}/>
        </div>
      )}
      {!hasThumb && (
        <div style={{
          position: 'absolute', inset: 0, zIndex: 1,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: 'Geist', fontWeight: 800,
          fontSize: 140, letterSpacing: '-0.06em',
          color: 'rgba(255,255,255,0.08)',
          userSelect: 'none',
        }}>{game.shortName}</div>
      )}

      {/* Content overlay */}
      <div style={{ position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', height: '100%', padding: '24px 26px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
          <span className="mono" style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '5px 10px',
            background: 'rgba(0,0,0,0.45)',
            backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
            border: '1px solid rgba(255,255,255,0.15)',
            borderRadius: 999,
            fontSize: 11, color: 'white',
          }}>
            <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--live)', boxShadow: '0 0 6px var(--live)' }}/>
            Activo
          </span>
          <span className="mono" style={{
            fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase',
            color: 'rgba(255,255,255,0.7)',
            padding: '4px 10px',
            background: 'rgba(0,0,0,0.4)', borderRadius: 6,
            border: '1px solid rgba(255,255,255,0.1)',
          }}>{game.platform}</span>
        </div>

        <div style={{ marginTop: 'auto' }}>
          <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', color: 'rgba(255,255,255,0.7)', textTransform: 'uppercase', marginBottom: 10 }}>{game.tag}</div>
          <div style={{ fontSize: 28, fontWeight: 700, color: 'white', letterSpacing: '-0.02em', lineHeight: 1.05 }}>{game.name}</div>
          <p style={{ fontSize: 14, color: 'rgba(255,255,255,0.75)', marginTop: 12, lineHeight: 1.5, maxWidth: 480 }}>{game.desc}</p>

          <div style={{
            display: 'inline-flex', alignItems: 'center', gap: 8,
            marginTop: 18, padding: '9px 16px',
            background: 'rgba(255,255,255,0.1)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
            border: '1px solid rgba(255,255,255,0.18)',
            borderRadius: 10,
            fontSize: 13, fontWeight: 500, color: 'white',
            transition: 'background .2s',
          }} className="game-cta">
            {tr('sec.juegos.cta')}
            <Icon.ArrowUpRight size={14}/>
          </div>
        </div>
      </div>
    </a>
  );
}

/* ─────────────── Section header ─────────────── */
function SectionHeader({ eyebrow, title, subtitle, action }) {
  const ref = useReveal();
  return (
    <div ref={ref} className="reveal" style={{
      display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between',
      gap: 32, flexWrap: 'wrap',
    }}>
      <div style={{ maxWidth: 720 }}>
        <div className="eyebrow" style={{ marginBottom: 16 }}>{eyebrow}</div>
        <h2 className="display" style={{ fontSize: 'clamp(32px, 4.5vw, 56px)', lineHeight: 1.05 }}>{title}</h2>
        {subtitle && (
          <p style={{ fontSize: 17, color: 'var(--text-dim)', marginTop: 18, maxWidth: 620, lineHeight: 1.55 }}>{subtitle}</p>
        )}
      </div>
      {action}
    </div>
  );
}

/* ─────────────── Hubs / Sedes oficiales por región ─────────────── */
function Hubs() {
  const ref = useReveal();
  const tr = useT();
  // FAKE-DATA-REMOVED (2026-05-14): nombres de leads + counts + estado live
  // se obtienen del bot — no inventar. Placeholders neutros hasta wiring.
  // HUBS-REAL (2026-05-15): solo hubs con servidor Discord activo.
  // Icons = imagenes reales del server Discord (CDN icons). No alucinar
  // regiones sin server real.
  const hubs = [
    { code: 'TSBL', name: 'TSB Latinoamérica', region: 'LATAM · ES', members: null, clans: null, lead: null, live: null,
      icon: 'https://cdn.discordapp.com/icons/1241243439283048470/e55215206d55792f01124d84f6d01c60.webp?size=512',
      grad: 'linear-gradient(135deg, #5B8CFF 0%, #1A1E5C 100%)', accent: '#5B8CFF' },
    { code: 'TSBC', name: 'TSB Colombia',     region: 'CO · ES',    members: null, clans: null, lead: null, live: null,
      icon: 'https://cdn.discordapp.com/icons/1361443323969142824/2551c9fcaace6b9ca36285bd5eba8740.webp?size=512',
      grad: 'linear-gradient(135deg, #FFD400 0%, #9A0F18 100%)', accent: '#FFD400' },
    { code: 'TSBV', name: 'TSB Venezuela',    region: 'VE · ES',    members: null, clans: null, lead: null, live: null,
      icon: 'https://cdn.discordapp.com/icons/1479848996355965080/dea9841b6c5c18b84f7aa5fe38e0f4aa.webp?size=512',
      grad: 'linear-gradient(135deg, #FFCC00 0%, #00247D 100%)', accent: '#FFCC00' },
  ];
  const [active, setActive] = useState(0);

  return (
    <section id="hubs" style={{ background: 'var(--surface)', borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)' }}>
      <div className="container">
        <SectionHeader
          eyebrow={tr('sec.hubs.eyebrow')}
          title={<>{tr('sec.hubs.title.a')} <span className="emph">{tr('sec.hubs.title.b')}</span></>}
          subtitle={tr('sec.hubs.sub')}
          action={
            <a href={INVITE_SERVER} target="_blank" rel="noopener noreferrer" className="btn btn-ghost" style={{ fontSize: 13 }}>
              {tr('sec.hubs.apply')}
              <Icon.Arrow size={14}/>
            </a>
          }
        />

        <div ref={ref} className="reveal" style={{
          display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginTop: 48,
        }} id="hubs-grid">
          {hubs.map((h, i) => (
            <article key={h.code}
              onMouseEnter={() => setActive(i)}
              style={{
                background: 'var(--surface-2)',
                border: `1px solid ${active === i ? 'var(--border-strong)' : 'var(--border)'}`,
                borderRadius: 16, overflow: 'hidden', cursor: 'pointer',
                transition: 'transform .25s, border-color .25s, box-shadow .25s',
                transform: active === i ? 'translateY(-2px)' : 'translateY(0)',
                boxShadow: active === i ? 'var(--shadow-md)' : 'none',
                display: 'flex', flexDirection: 'column',
              }}>
              <div style={{
                height: 120, background: h.grad, position: 'relative',
                display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden',
              }}>
                {h.icon ? (
                  <img src={h.icon} alt={h.name}
                    style={{ width: 80, height: 80, borderRadius: 16, objectFit: 'cover',
                             border: '2px solid rgba(255,255,255,0.18)',
                             boxShadow: '0 6px 18px -6px rgba(0,0,0,0.55)' }}
                    onError={(e) => { e.currentTarget.style.display = 'none'; }}/>
                ) : (
                  <div style={{
                    fontFamily: 'Geist', fontWeight: 800, fontSize: 56, letterSpacing: '-0.03em',
                    color: 'rgba(255,255,255,0.95)', textShadow: '0 4px 24px rgba(0,0,0,0.3)',
                    mixBlendMode: 'overlay', opacity: 0.85,
                  }}>{h.code}</div>
                )}
                <div style={{
                  position: 'absolute', bottom: 10, left: 12,
                  fontFamily: 'Geist Mono', fontWeight: 600, fontSize: 11,
                  color: 'rgba(255,255,255,0.85)', letterSpacing: '0.08em',
                  padding: '3px 8px', borderRadius: 6,
                  background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(6px)',
                }}>{h.code}</div>
                <div className="mono" style={{
                  position: 'absolute', top: 12, right: 12,
                  display: 'flex', alignItems: 'center', gap: 6,
                  padding: '4px 9px', borderRadius: 999,
                  background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
                  border: '1px solid rgba(255,255,255,0.12)',
                  fontSize: 10, color: 'white', letterSpacing: '0.04em',
                }}>
                  {h.live ? (
                    <>
                      <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--live)', boxShadow: '0 0 6px var(--live)', animation: 'pulse 1.6s ease-in-out infinite' }}/>
                      {h.live}
                    </>
                  ) : (
                    <span style={{ color: 'rgba(255,255,255,0.6)' }}>—</span>
                  )}
                </div>
              </div>

              <div style={{ padding: 18, display: 'flex', flexDirection: 'column', gap: 14, flex: 1 }}>
                <div>
                  <span style={{ fontSize: 16, fontWeight: 600, letterSpacing: '-0.01em' }}>{h.name}</span>
                  <div className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 4, letterSpacing: '0.06em' }}>{h.region}</div>
                </div>

                <div style={{
                  display: 'grid', gridTemplateColumns: '1fr 1px 1fr', alignItems: 'center',
                  background: 'var(--surface-3)', border: '1px solid var(--border)',
                  borderRadius: 9, padding: '10px 0',
                }}>
                  <div style={{ textAlign: 'center' }}>
                    <div className="mono" style={{ fontSize: 15, fontWeight: 700, color: h.accent }}>{h.members != null ? h.members.toLocaleString() : '—'}</div>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--text-muted)', marginTop: 1 }}>{tr('sec.hubs.miembros')}</div>
                  </div>
                  <div style={{ width: 1, height: 28, background: 'var(--border)' }}/>
                  <div style={{ textAlign: 'center' }}>
                    <div className="mono" style={{ fontSize: 15, fontWeight: 700 }}>{h.clans != null ? h.clans : '—'}</div>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--text-muted)', marginTop: 1 }}>{tr('sec.hubs.clanes')}</div>
                  </div>
                </div>

                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, marginTop: 'auto' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 11, color: 'var(--text-dim)' }}>
                    {h.lead ? (
                      <>
                        <div style={{ width: 22, height: 22, borderRadius: '50%', background: h.grad, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white', fontWeight: 600, fontSize: 10 }}>{h.lead[0].toUpperCase()}</div>
                        <span>@{h.lead}</span>
                      </>
                    ) : (
                      <span className="mono" style={{ fontSize: 10, color: 'var(--text-muted)', letterSpacing: '0.04em' }}>{tr('sec.hubs.lead.empty')}</span>
                    )}
                  </div>
                  <a href={INVITE_SERVER} target="_blank" rel="noopener noreferrer" className="btn btn-discord" style={{ padding: '6px 11px', fontSize: 11 }}>
                    <Icon.Discord size={12}/> {tr('sec.hubs.unirse')}
                  </a>
                </div>
              </div>
            </article>
          ))}
        </div>

        <div style={{
          marginTop: 28, padding: '14px 18px',
          background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 12,
          display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap',
        }}>
          <Icon.Sparkle size={16} style={{ color: 'var(--accent)' }}/>
          <span style={{ fontSize: 13, color: 'var(--text-dim)', flex: 1, minWidth: 240 }}>
            <strong style={{ color: 'var(--text)' }}>Nomenclatura:</strong> &lt;código de juego de 3 letras&gt; + &lt;código de región&gt;. Hoy: TSB · JJS. Cuando sumemos un juego nuevo, su hub sigue el mismo patrón.
          </span>
        </div>
      </div>
      <style>{`
        @media (max-width: 1000px) { #hubs-grid { grid-template-columns: repeat(2, 1fr) !important; } }
        @media (max-width: 600px) { #hubs-grid { grid-template-columns: 1fr !important; } }
      `}</style>
    </section>
  );
}

/* ─────────────── Clanes (LEGACY — reemplazada por <ClanesElite/> de rankings-clans.jsx) ─────────────── */
// FAKE-DATA-REMOVED (2026-05-15): Novus Ordo / Kamigami / Zero Protocol eran clanes inventados.
// Esta funcion ya no se usa en la composicion (app.jsx), pero queda como empty-state por si se referencia.
function Clanes() {
  const ref = useReveal();
  const clans = [];

  return (
    <section id="clanes">
      <div className="container">
        <SectionHeader
          eyebrow="04 · Clanes"
          title={<>Élite del <span className="emph">ranking.</span></>}
          subtitle="Cada clan pasa por verificación manual antes de aparecer en el directorio público. Sin verificación, no hay representación en torneos oficiales."
          action={
            <a href="#" className="btn btn-ghost" style={{ fontSize: 13 }}>
              Registrar mi clan
              <Icon.Arrow size={14}/>
            </a>
          }
        />

        <div ref={ref} className="reveal" style={{
          display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 18, marginTop: 48,
        }} id="clanes-grid">
          {clans.map((c, i) => (
            <article key={c.tag} className="card card-hover" style={{ padding: 24, position: 'relative', overflow: 'hidden' }}>
              <div style={{
                position: 'absolute', top: -40, right: -40, width: 180, height: 180,
                background: c.vibe, opacity: 0.15, borderRadius: '50%', filter: 'blur(40px)',
              }}/>
              <div style={{ position: 'relative' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 18 }}>
                  <div style={{
                    width: 56, height: 56, borderRadius: 12,
                    background: c.vibe,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    color: 'white', fontWeight: 700, fontSize: 16, letterSpacing: '0.04em',
                    fontFamily: 'Geist Mono',
                  }}>{c.tag}</div>
                  <div>
                    <div style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.01em' }}>{c.name}</div>
                    <div className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 2 }}>{c.region}</div>
                  </div>
                </div>

                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10, marginBottom: 18 }}>
                  <Stat label="ELO" value={c.elo}/>
                  <Stat label="Win rate" value={`${c.wr}%`}/>
                  <Stat label="Miembros" value={c.members}/>
                </div>

                <div style={{ height: 1, background: 'var(--border)', margin: '4px 0 16px' }}/>

                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: c.trend === 'up' ? 'var(--live)' : 'var(--text-muted)' }}>
                    <Icon.Bolt size={13}/>
                    {c.trend === 'up' ? 'En ascenso · #' + (i+1) : 'Estable · #' + (i+1)}
                  </div>
                  <a href="#" style={{ display: 'flex', alignItems: 'center', gap: 4, color: 'var(--text-dim)', textDecoration: 'none', fontSize: 13, fontWeight: 500 }}>
                    Perfil <Icon.ArrowUpRight size={13}/>
                  </a>
                </div>
              </div>
            </article>
          ))}
        </div>
      </div>
      <style>{`
        @media (max-width: 900px) { #clanes-grid { grid-template-columns: 1fr !important; } }
      `}</style>
    </section>
  );
}

function Stat({ label, value }) {
  return (
    <div>
      <div className="mono" style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--text)' }}>{value}</div>
      <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 2 }}>{label}</div>
    </div>
  );
}

/* ─────────────── Cómo empezar (reemplaza Trainers que aun no esta listo) ─────────────── */
function Trainers() {
  const ref = useReveal();
  const tr = useT();

  const steps = [
    { num: '01', icon: Icon.Discord, color: 'var(--accent)',   t: tr('sec.start.s1.t'), d: tr('sec.start.s1.d'), cta: tr('sec.start.s1.cta'), href: INVITE_SERVER, ext: true },
    { num: '02', icon: Icon.Shield,  color: 'var(--accent-2)', t: tr('sec.start.s2.t'), d: tr('sec.start.s2.d'), cta: tr('sec.start.s2.cta'), href: '/usage#verify', ext: false },
    { num: '03', icon: Icon.Hubs,    color: 'var(--warn)',     t: tr('sec.start.s3.t'), d: tr('sec.start.s3.d'), cta: tr('sec.start.s3.cta'), href: '/hubs', ext: false },
    { num: '04', icon: Icon.Users,   color: 'var(--live)',     t: tr('sec.start.s4.t'), d: tr('sec.start.s4.d'), cta: tr('sec.start.s4.cta'), href: '/clans', ext: false },
  ];

  return (
    <section id="comoempezar" style={{ background: 'var(--surface)', borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)' }}>
      <div className="container">
        <SectionHeader
          eyebrow={tr('sec.start.eyebrow')}
          title={<>{tr('sec.start.title.a')} <span className="emph">{tr('sec.start.title.b')}</span></>}
          subtitle={tr('sec.start.sub')}
        />

        <div ref={ref} className="reveal" style={{
          display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14, marginTop: 48,
        }} id="start-grid">
          {steps.map(s => (
            <article key={s.num} className="card card-hover" style={{ padding: 24, display: 'flex', flexDirection: 'column', minHeight: 260 }}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18 }}>
                <div style={{
                  width: 44, height: 44, borderRadius: 12,
                  background: `color-mix(in srgb, ${s.color} 14%, transparent)`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  <s.icon size={20} style={{ color: s.color }}/>
                </div>
                <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', letterSpacing: '0.12em' }}>{s.num}</span>
              </div>
              <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em', marginBottom: 8 }}>{s.t}</div>
              <div style={{ fontSize: 12.5, color: 'var(--text-dim)', lineHeight: 1.5, marginBottom: 18, flex: 1 }}>{s.d}</div>
              <a href={s.href}
                 {...(s.ext ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
                 style={{
                   display: 'inline-flex', alignItems: 'center', gap: 6,
                   fontSize: 12, color: s.color, textDecoration: 'none', fontWeight: 500,
                 }}>
                {s.cta}
                <Icon.Arrow size={13}/>
              </a>
            </article>
          ))}
        </div>
      </div>
      <style>{`
        @media (max-width: 1000px) { #start-grid { grid-template-columns: repeat(2, 1fr) !important; } }
        @media (max-width: 540px) { #start-grid { grid-template-columns: 1fr !important; } }
      `}</style>
    </section>
  );
}

/* Reusable avatar with graceful onError fallback. Discord CDN sometimes
   404s stale avatar hashes; the old inline `<img onError display:none>`
   left the parent grid column empty, which made the member's name visually
   slide into the avatar slot ("rows destruidas"). This component swaps to
   an initial-circle of the same dimensions, preserving the grid layout. */
function MemberAvatar({ src, name, size = 36 }) {
  const [failed, setFailed] = useState(false);
  const initial = (name || '?').trim().charAt(0).toUpperCase() || '?';
  const dim = { width: size, height: size, borderRadius: Math.round(size / 4) };
  if (failed || !src || typeof src !== 'string' || !/^https?:\/\//.test(src)) {
    return (
      <div style={{
        ...dim,
        background: 'var(--surface-3)', border: '1px solid var(--border)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontSize: Math.round(size * 0.36), fontWeight: 600, color: 'var(--text-dim)',
        flexShrink: 0,
      }}>{initial}</div>
    );
  }
  return (
    <img src={src} alt={name || ''} width={size} height={size}
      referrerPolicy="no-referrer"
      style={{ ...dim, objectFit: 'cover', border: '1px solid var(--border)', flexShrink: 0 }}
      onError={() => setFailed(true)}/>
  );
}

/* ─────────────── Directory + Sanctions ─────────────── */
function Directorio() {
  const ref = useReveal();
  const [q, setQ] = useState('');
  const [tab, setTab] = useState('directorio');
  const [expanded, setExpanded] = useState(false);

  // DIRECTORY-WIRED: consume members del bot. Probamos varios endpoints en orden
  // hasta encontrar uno que devuelva una lista no-vacia. Si todos vuelven empty,
  // dejamos la lista en [] y el empty state se muestra honestamente.
  const [members, setMembers] = useState([]);
  const [fetched, setFetched] = useState(false); // distingue "loading" de "really empty"
  useEffect(() => {
    let cancelled = false;
    const fetchMembers = async () => {
      // Fallback chain: relative -> absolute -> /api/public/data (which contains
      // `.members` array as a side-channel). The first endpoint to give us a
      // non-empty array wins.
      const urls = [
        '/inicio/api/public/members',
        'api/public/members',
        '/inicio/api/public/data',
      ];
      let list = [];
      for (const u of urls) {
        try {
          const r = await fetch(u, { cache: 'no-store' });
          if (!r.ok) continue;
          const j = await r.json();
          const got = Array.isArray(j) ? j
                    : Array.isArray(j.members) ? j.members
                    : Array.isArray(j.users)   ? j.users
                    : [];
          if (got.length > 0) { list = got; break; }
        } catch (_) { /* try next */ }
      }
      if (cancelled) return;

      // DIRECTORY-LENIENT: antes pedíamos un `link` HTTPS explícito y filtrábamos
      // todo el resto, lo que dejaba la lista vacía cuando los miembros aún no
      // habían corrido .edit. Ahora basta con que la entrada tenga algún display
      // identificable (displayName / username / nickname / tag) y que no esté
      // marcada como `pending`. Los con link real se priorizan en el ordenamiento.
      const isShowable = (m) => {
        if (!m || m.pending) return false;
        return Boolean(m.displayName || m.username || m.nickname || m.tag || m.id);
      };
      const linkOf = (m) => {
        const v = m && (m.link || m.profileUrl || m.profileLink || m.profile);
        return (typeof v === 'string' && /^https?:\/\//.test(v)) ? v : null;
      };
      const clean = list
        .filter(isShowable)
        .map(m => ({ ...m, _hasLink: Boolean(linkOf(m)) }))
        .sort((a, b) => {
          // 1) Members with a verified profile link float to the top
          if (a._hasLink !== b._hasLink) return a._hasLink ? -1 : 1;
          // 2) Then real numeric rank
          const ra = Number(a.rank), rb = Number(b.rank);
          if (Number.isFinite(ra) && Number.isFinite(rb)) return ra - rb;
          if (Number.isFinite(ra)) return -1;
          if (Number.isFinite(rb)) return 1;
          // 3) Then kills desc
          const ka = Number(a.kills) || 0, kb = Number(b.kills) || 0;
          if (ka !== kb) return kb - ka;
          // 4) Finally joined-asc (oldest first)
          return new Date(a.joinedAt || 0) - new Date(b.joinedAt || 0);
        });
      setMembers(clean);
      setFetched(true);
    };
    fetchMembers();
    const id = setInterval(fetchMembers, 60_000);
    return () => { cancelled = true; clearInterval(id); };
  }, []);

  const ql = q.trim().toLowerCase();
  const filtered = ql === ''
    ? members
    : members.filter(m => {
        const name = (m.displayName || m.nickname || m.username || '').toLowerCase();
        const tag  = (m.tag || m.discordTag || '').toLowerCase();
        const role = (m.staffRole || '').toLowerCase();
        const roleNames = Array.isArray(m.roles) ? m.roles.map(r => (r.name || '').toLowerCase()).join(' ') : '';
        return name.includes(ql) || tag.includes(ql) || role.includes(ql) || roleNames.includes(ql);
      });
  const visible = expanded ? filtered.slice(0, 100) : filtered.slice(0, 12);

  return (
    <section id="directorio">
      <div className="container">
        <SectionHeader
          eyebrow="06 · Transparencia"
          title={<>Todo público, <span className="emph">todo auditable.</span></>}
          subtitle="Directorio abierto de miembros registrados y registro público de sanciones. La reputación se gana — y se pierde — a la vista de todos."
        />

        <div ref={ref} className="reveal card" style={{
          marginTop: 48, padding: 0, overflow: 'hidden',
        }}>
          {/* Tabs */}
          <div style={{ display: 'flex', borderBottom: '1px solid var(--border)' }}>
            {[
              { id: 'directorio', label: 'Directorio público', icon: Icon.Search, count: members.length > 0 ? members.length.toLocaleString('es') : null },
              { id: 'sanciones', label: 'Sanciones', icon: Icon.Warn, count: null },
            ].map(t => (
              <button key={t.id} onClick={() => setTab(t.id)}
                style={{
                  flex: 1, padding: '18px 20px',
                  background: tab === t.id ? 'var(--surface-2)' : 'transparent',
                  border: 'none',
                  borderBottom: tab === t.id ? '2px solid var(--accent)' : '2px solid transparent',
                  color: tab === t.id ? 'var(--text)' : 'var(--text-dim)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
                  cursor: 'pointer', fontSize: 14, fontWeight: 500,
                  fontFamily: 'inherit',
                  transition: 'all .2s',
                }}>
                <t.icon size={16}/>
                {t.label}
                <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', padding: '2px 7px', background: 'var(--surface-3)', borderRadius: 6 }}>{t.count}</span>
              </button>
            ))}
          </div>

          {/* Search */}
          <div style={{ padding: '20px 24px', borderBottom: '1px solid var(--border)' }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '12px 16px',
              background: 'var(--surface-2)',
              border: '1px solid var(--border)',
              borderRadius: 10,
            }}>
              <Icon.Search size={16} style={{ color: 'var(--text-muted)' }}/>
              <input
                value={q}
                onChange={e => setQ(e.target.value)}
                placeholder={tab === 'directorio' ? 'Buscar miembro, tag, rol…' : 'Buscar por usuario o ID de caso…'}
                style={{
                  flex: 1, background: 'transparent', border: 'none',
                  color: 'var(--text)', fontSize: 14, outline: 'none',
                  fontFamily: 'inherit',
                }}
              />
              <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', padding: '3px 7px', background: 'var(--surface-3)', borderRadius: 5, border: '1px solid var(--border)' }}>⌘ K</span>
            </div>
          </div>

          {/* List */}
          <div style={{ maxHeight: 380, overflowY: 'auto' }}>
            {tab === 'directorio' && members.length === 0 && (
              <div style={{ padding: '36px 24px', textAlign: 'center', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
                <div style={{
                  width: 48, height: 48, borderRadius: 14,
                  background: 'var(--accent-soft)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  <Icon.Users size={22} style={{ color: 'var(--accent)' }}/>
                </div>
                <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Cargando directorio…</div>
                <div className="mono" style={{ fontSize: 12, color: 'var(--text-muted)', maxWidth: 380, lineHeight: 1.55 }}>
                  Leyendo /api/public/members. Si esto persiste, verificá tu cuenta con <span style={{ color: 'var(--accent)' }}>.verify</span> en Discord.
                </div>
              </div>
            )}
            {tab === 'directorio' && members.length > 0 && filtered.length === 0 && (
              <div style={{ padding: '36px 24px', textAlign: 'center' }}>
                <div className="mono" style={{ fontSize: 12, color: 'var(--text-muted)' }}>Sin resultados para "{q}"</div>
              </div>
            )}
            {tab === 'sanciones' && (
              <div style={{ padding: '36px 24px', textAlign: 'center', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
                <div style={{
                  width: 48, height: 48, borderRadius: 14,
                  background: 'color-mix(in srgb, var(--warn) 14%, transparent)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  <Icon.Warn size={22} style={{ color: 'var(--warn)' }}/>
                </div>
                <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Registro público de sanciones</div>
                <div className="mono" style={{ fontSize: 12, color: 'var(--text-muted)', maxWidth: 380, lineHeight: 1.55 }}>
                  El feed completo de strikes, warns y blacklist vive en <span style={{ color: 'var(--accent)' }}>oneway.lat/warnings</span>. Pasá por ahí para auditar el historial.
                </div>
                <a href="/warnings" className="btn btn-ghost" style={{ marginTop: 4, fontSize: 13 }}>
                  Ver sanciones
                  <Icon.Arrow size={14}/>
                </a>
              </div>
            )}
            {tab === 'directorio' && visible.length > 0 && visible.map((m, i) => {
              const name = m.displayName || m.nickname || m.username || 'Miembro';
              const tagText = m.tag || m.discordTag || '';
              const isStaff = (m.staffLevel || 0) > 0 || m.staffRole;
              const link = m.link || m.profileUrl || m.profileLink || m.profile;
              const meta = [m.country, m.phase ? `Phase ${m.phase}` : null, m.platform].filter(Boolean).join(' · ');
              const linkLabel = link ? (link.includes('roblox') ? 'Roblox' : link.includes('twitch') ? 'Twitch' : link.includes('twitter') || link.includes('x.com') ? 'X' : 'Perfil') : null;
              return (
                <a key={m.userId || m.id || `${name}-${i}`}
                  href={link} target="_blank" rel="noopener noreferrer"
                  style={{
                    display: 'grid',
                    gridTemplateColumns: '44px 1fr auto auto',
                    gap: 14, alignItems: 'center',
                    padding: '12px 24px',
                    borderTop: i === 0 ? 'none' : '1px solid var(--border)',
                    transition: 'background .15s',
                    textDecoration: 'none', color: 'inherit',
                  }} onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
                     onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                  <MemberAvatar src={m.avatar} name={name} size={36}/>
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontSize: 14, fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                      {name}
                      {isStaff && (
                        <span className="mono" style={{
                          fontSize: 9, marginLeft: 8, padding: '2px 6px', borderRadius: 999,
                          background: 'color-mix(in srgb, var(--accent) 14%, transparent)',
                          color: 'var(--accent)', letterSpacing: '0.08em',
                        }}>STAFF</span>
                      )}
                    </div>
                    <div className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                      {tagText && tagText !== name ? `@${tagText}` : ''}
                      {tagText && meta ? ' · ' : ''}{meta}
                    </div>
                  </div>
                  {Number.isFinite(Number(m.rank)) && Number(m.rank) > 0 && (
                    <span className="mono" style={{
                      fontSize: 11, padding: '3px 8px', borderRadius: 6,
                      background: 'var(--accent-soft)', color: 'var(--accent)',
                      flexShrink: 0, letterSpacing: '0.04em',
                    }}>#{m.rank}</span>
                  )}
                  <span style={{
                    fontSize: 10, padding: '4px 9px', borderRadius: 999,
                    background: 'color-mix(in srgb, var(--accent) 12%, transparent)',
                    color: 'var(--accent)', letterSpacing: '0.06em', flexShrink: 0,
                    display: 'inline-flex', alignItems: 'center', gap: 4,
                  }} className="mono">
                    {linkLabel}
                    <Icon.ArrowUpRight size={11}/>
                  </span>
                </a>
              );
            })}
            {tab === 'directorio' && filtered.length > visible.length && (
              <div style={{ padding: '14px 24px', textAlign: 'center', borderTop: '1px solid var(--border)', background: 'var(--surface-2)' }}>
                <button onClick={() => setExpanded(true)} style={{
                  background: 'transparent', border: 'none', cursor: 'pointer',
                  color: 'var(--text-dim)', fontSize: 13, fontFamily: 'inherit',
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                }}>
                  {expanded ? `Mostrando ${visible.length} de ${filtered.length}` : `Ver más (${filtered.length - visible.length} miembros)`}
                  <Icon.Arrow size={13}/>
                </button>
              </div>
            )}
            {/* Tab 'blacklist' removida; sanciones empty state arriba apunta a /warnings */}
          </div>
        </div>
      </div>
    </section>
  );
}

/* ─────────────── CTA + Footer ─────────────── */
function FinalCTA() {
  const ref = useReveal();
  const tr = useT();
  return (
    <section style={{ padding: '120px 0' }}>
      <div className="container">
        <div ref={ref} className="reveal" style={{
          position: 'relative',
          padding: '72px 56px',
          background: 'linear-gradient(135deg, var(--surface) 0%, var(--surface-2) 100%)',
          border: '1px solid var(--border)',
          borderRadius: 24, overflow: 'hidden',
          textAlign: 'center',
        }}>
          <div style={{
            position: 'absolute', top: '-50%', left: '50%', transform: 'translateX(-50%)',
            width: 800, height: 800, borderRadius: '50%',
            background: 'radial-gradient(circle, var(--accent-soft) 0%, transparent 60%)',
            pointerEvents: 'none',
          }}/>
          <div style={{ position: 'relative' }}>
            <div className="eyebrow" style={{ marginBottom: 20 }}>{tr('sec.cta.eyebrow')}</div>
            <h2 className="display" style={{ fontSize: 'clamp(36px, 5vw, 64px)', maxWidth: 820, margin: '0 auto' }}>
              {tr('sec.cta.title.a')} <span className="emph">{tr('sec.cta.title.b')}</span>
            </h2>
            <p style={{ fontSize: 17, color: 'var(--text-dim)', maxWidth: 580, margin: '20px auto 0', lineHeight: 1.5 }}>
              {tr('sec.cta.sub')}
            </p>
            <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 36, flexWrap: 'wrap' }}>
              <a className="btn btn-discord" href={INVITE_SERVER} target="_blank" rel="noopener noreferrer" style={{ padding: '14px 22px', fontSize: 15 }}>
                <Icon.Discord size={18}/>
                {tr('sec.cta.join')}
              </a>
              <a className="btn btn-ghost" href={INVITE_BOT} target="_blank" rel="noopener noreferrer" style={{ padding: '14px 22px', fontSize: 15 }}>
                {tr('sec.cta.invite')}
                <Icon.Arrow size={15}/>
              </a>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ─────────────── Zero Hex (clan oficial One Way) ─────────────── */
function ZeroHexHighlight() {
  const ref = useReveal();
  const tr = useT();
  return (
    <section id="zero-hex" style={{ padding: '64px 0', background: 'var(--surface)', borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)' }}>
      <div className="container">
        <div ref={ref} className="reveal card" style={{
          padding: 0, overflow: 'hidden',
          display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1.2fr)',
          gap: 0, alignItems: 'stretch',
          /* No more flat-blue gradient — use the actual clan logo as backdrop
             (same pattern /clans uses when a guild has no Discord banner). */
          background: '#0B1330',
          border: '1px solid var(--border-strong)',
          boxShadow: 'var(--shadow-lg)',
          borderRadius: 24,
        }} id="zerohex-grid">
          {/* Left: gif logo + tag — animated icon doubles as the backdrop */}
          <div style={{
            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
            padding: 48, gap: 18,
            position: 'relative', overflow: 'hidden',
          }}>
            {/* Backdrop: same animated icon, scaled up + blurred + dimmed. Gives
                the card a real "guild presence" instead of the flat blue. */}
            <img
              src="https://cdn.discordapp.com/icons/1449200817973231781/a_c5edde50d47b474fbcedff6ed6872d08.gif?animated=true&size=1024"
              alt=""
              aria-hidden="true"
              style={{
                position: 'absolute', inset: 0,
                width: '100%', height: '100%', objectFit: 'cover',
                transform: 'scale(1.12)',
                filter: 'blur(28px) brightness(0.45) saturate(1.15)',
                opacity: 0.95, zIndex: 0,
                pointerEvents: 'none',
              }}
            />
            {/* Top sheen + bottom fade so the foreground logo + chip pop */}
            <div style={{
              position: 'absolute', inset: 0,
              background:
                'radial-gradient(600px 400px at 30% 30%, rgba(255,255,255,0.10), transparent 60%),' +
                'linear-gradient(180deg, rgba(8,9,12,0) 0%, rgba(8,9,12,0.55) 100%)',
              pointerEvents: 'none',
              zIndex: 0,
            }}/>
            <img src="https://cdn.discordapp.com/icons/1449200817973231781/a_c5edde50d47b474fbcedff6ed6872d08.gif?animated=true&size=512"
              alt="Zero Hex" width="200" height="200"
              style={{
                width: 200, height: 200, borderRadius: 28,
                border: '2px solid rgba(255,255,255,0.22)',
                boxShadow: '0 28px 64px -18px rgba(0,0,0,0.7), 0 0 70px rgba(91,140,255,0.35)',
                position: 'relative', zIndex: 1,
              }}/>
            <div className="mono" style={{
              padding: '6px 14px', borderRadius: 999,
              background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
              border: '1px solid rgba(255,255,255,0.18)',
              fontSize: 11, color: 'white', letterSpacing: '0.18em', fontWeight: 600,
              position: 'relative', zIndex: 1,
            }}>{tr('sec.zerohex.tag')}</div>
          </div>

          {/* Right: copy + CTAs */}
          <div style={{
            padding: '48px 44px',
            background: 'rgba(8,9,12,0.6)',
            backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
            display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 22,
          }}>
            <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.7)', display: 'flex', alignItems: 'center', gap: 10 }}>
              <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--live)', boxShadow: '0 0 8px var(--live)' }}/>
              {tr('sec.zerohex.eyebrow')}
            </div>
            <h2 className="display" style={{ fontSize: 'clamp(34px, 4.5vw, 52px)', lineHeight: 1.05, color: 'white' }}>
              {tr('sec.zerohex.title.a')} <span className="emph">{tr('sec.zerohex.title.b')}</span>
            </h2>
            <p style={{ fontSize: 16, color: 'rgba(255,255,255,0.75)', lineHeight: 1.55, maxWidth: 560 }}>
              {tr('sec.zerohex.sub')}
            </p>
            <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginTop: 6 }}>
              <a className="btn btn-discord" href={INVITE_ZEROHEX} target="_blank" rel="noopener noreferrer" style={{ padding: '11px 18px', fontSize: 14 }}>
                <Icon.Discord size={16}/>
                {tr('sec.zerohex.join')}
              </a>
              <a className="btn btn-ghost" href="#clanes" style={{ padding: '11px 18px', fontSize: 14, color: 'white', borderColor: 'rgba(255,255,255,0.25)' }}>
                {tr('sec.clanes.register')}
                <Icon.Arrow size={14}/>
              </a>
            </div>
          </div>
        </div>
      </div>
      <style>{`
        @media (max-width: 880px) { #zerohex-grid { grid-template-columns: 1fr !important; } }
      `}</style>
    </section>
  );
}

function Footer() {
  const tr = useT();
  const year = new Date().getFullYear();
  return (
    <footer style={{ borderTop: '1px solid var(--border)', padding: '40px 0 32px' }}>
      <div className="container" style={{
        display: 'flex', gap: 18, alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <img src="https://cdn.discordapp.com/icons/1417173139778965692/5cf54bab9936a51f79915c777915865f.webp?size=64"
            alt="OneWay" width="22" height="22"
            style={{ width: 22, height: 22, borderRadius: 6, objectFit: 'cover' }}/>
          <span className="mono" style={{ fontSize: 12, color: 'var(--text-muted)', letterSpacing: '0.1em' }}>
            © {year} OneWay · {tr('footer.rights')}
          </span>
        </div>
        <div style={{ display: 'flex', gap: 18, fontSize: 12, color: 'var(--text-muted)' }} className="mono">
          <a href="/terms" style={{ color: 'inherit', textDecoration: 'none' }}>{tr('footer.terms')}</a>
        </div>
      </div>
    </footer>
  );
}

Object.assign(window, { Nav, Hero, LivePulse, Games, Hubs, Clanes, Trainers, Directorio, FinalCTA, Footer, ZeroHexHighlight, useReveal, SectionHeader });
