/* Public landing page sections for TECOS ELITE */

/** Ancla de sección siempre en el DOM; el contenido se monta al acercarse o al pulsar el menú. */
function LandingSectionAnchor ({ id, children, minHeight = 200, rootMargin = '400px 0px', eager = false }) {
  const [show, setShow] = React.useState(!!eager);

  React.useEffect(() => {
    if (show || !id) return undefined;
    const onForceMount = (e) => {
      if (e.detail?.sectionId === id) setShow(true);
    };
    window.addEventListener('tecos:lazy-mount-section', onForceMount);
    return () => window.removeEventListener('tecos:lazy-mount-section', onForceMount);
  }, [id, show]);

  React.useEffect(() => {
    if (show || !id) return undefined;
    const node = document.getElementById(id);
    if (!node) return undefined;
    if (typeof IntersectionObserver === 'undefined') {
      setShow(true);
      return undefined;
    }
    const io = new IntersectionObserver(
      (entries) => {
        if (entries.some((e) => e.isIntersecting)) {
          setShow(true);
          io.disconnect();
        }
      },
      { rootMargin, threshold: 0.01 }
    );
    io.observe(node);
    return () => io.disconnect();
  }, [id, show, rootMargin]);

  return (
    <div id={id} className="tecos-landing-section-anchor" style={show ? undefined : { minHeight }}>
      {show ? children : null}
    </div>
  );
}

/** @deprecated Usar LandingSectionAnchor */
function LazyWhenVisible ({ sectionId, id, children, minHeight, rootMargin }) {
  const anchorId = id || sectionId;
  return (
    <LandingSectionAnchor id={anchorId} minHeight={minHeight} rootMargin={rootMargin}>
      {children}
    </LandingSectionAnchor>
  );
}

const HERO_COURT_LOGO = 'assets/logo-tecos.png?v=no-bg-2';
const HERO_COURT_LOGO_W = 150;
const HERO_COURT_LOGO_H = Math.round(HERO_COURT_LOGO_W / (918 / 887));

/** Jugador estilizado para la cancha del hero (usa `color` = color de jersey). */
const HeroCourtPlayerDefs = () => (
  <>
    <symbol id="heroPlayerReady" viewBox="0 0 40 56">
      <ellipse cx="20" cy="53" rx="11" ry="2.5" fill="#0f172a" opacity="0.16" />
      <path d="M13 36 L10 52 M27 36 L30 52" stroke="#334155" strokeWidth="3.5" strokeLinecap="round" />
      <rect x="12" y="28" width="16" height="9" rx="2" fill="#334155" />
      <rect x="11" y="16" width="18" height="14" rx="3" fill="currentColor" />
      <circle cx="20" cy="9" r="7" fill="#fcd34d" />
      <path d="M12 20 L5 30" stroke="#fcd34d" strokeWidth="3" strokeLinecap="round" />
      <path d="M28 20 L35 30" stroke="#fcd34d" strokeWidth="3" strokeLinecap="round" />
    </symbol>
    <symbol id="heroPlayerBlock" viewBox="0 0 40 56">
      <ellipse cx="20" cy="53" rx="11" ry="2.5" fill="#0f172a" opacity="0.16" />
      <path d="M14 36 L12 52 M26 36 L28 52" stroke="#334155" strokeWidth="3.5" strokeLinecap="round" />
      <rect x="12" y="28" width="16" height="9" rx="2" fill="#334155" />
      <rect x="11" y="18" width="18" height="12" rx="3" fill="currentColor" />
      <circle cx="20" cy="9" r="7" fill="#fcd34d" />
      <path d="M10 20 L8 6 M30 20 L32 6" stroke="#fcd34d" strokeWidth="3" strokeLinecap="round" />
    </symbol>
    <symbol id="heroPlayerSpike" viewBox="0 0 40 56">
      <ellipse cx="20" cy="53" rx="11" ry="2.5" fill="#0f172a" opacity="0.16" />
      <path d="M15 34 L12 50 M25 34 L28 48" stroke="#334155" strokeWidth="3.5" strokeLinecap="round" />
      <rect x="12" y="26" width="16" height="9" rx="2" fill="#334155" />
      <rect x="11" y="16" width="18" height="13" rx="3" fill="currentColor" />
      <circle cx="20" cy="9" r="7" fill="#fcd34d" />
      <path d="M28 18 L34 2" stroke="#fcd34d" strokeWidth="3.2" strokeLinecap="round" />
      <path d="M12 20 L8 32" stroke="#fcd34d" strokeWidth="3" strokeLinecap="round" />
    </symbol>
    <symbol id="heroPlayerDig" viewBox="0 0 40 56">
      <ellipse cx="20" cy="53" rx="11" ry="2.5" fill="#0f172a" opacity="0.16" />
      <path d="M14 36 L18 52 M26 36 L22 52" stroke="#334155" strokeWidth="3.5" strokeLinecap="round" />
      <rect x="12" y="30" width="16" height="8" rx="2" fill="#334155" />
      <rect x="11" y="20" width="18" height="12" rx="3" fill="currentColor" />
      <circle cx="20" cy="11" r="7" fill="#fcd34d" />
      <path d="M12 24 L4 38" stroke="#fcd34d" strokeWidth="3" strokeLinecap="round" />
      <path d="M28 22 L32 34" stroke="#fcd34d" strokeWidth="3" strokeLinecap="round" />
    </symbol>
  </>
);

/** Fondo del hero — estilo juvenil: cancha colorida, rebotes y magia voleibol. */
const HeroAnimation = ({ theme, variant }) => {
  const isStatic = variant === 'static';
  const sceneStyle = {
    '--hero-bg': theme.bg,
    '--hero-elev': theme.bgElev,
    '--hero-primary': theme.primary,
    '--hero-accent': theme.accent,
    '--hero-warning': theme.warning,
    '--hero-success': theme.success,
    '--hero-text': theme.text,
  };

  const sparkles = [...Array(18)].map((_, i) => i);
  const confetti = [...Array(14)].map((_, i) => i);
  const courtLogoX = (1200 - HERO_COURT_LOGO_W) / 2;
  const courtLogoY = 280 - HERO_COURT_LOGO_H - 8;
  return (
    <div className={`tecos-hero-play${isStatic ? ' tecos-hero-play--static' : ''}`} style={sceneStyle} aria-hidden>
      <div className="tecos-hero-play__sky" />
      <div className="tecos-hero-play__sun" />
      <div className="tecos-hero-play__rainbow" />

      <div className="tecos-hero-play__cloud tecos-hero-play__cloud--1" />
      <div className="tecos-hero-play__cloud tecos-hero-play__cloud--2" />
      <div className="tecos-hero-play__cloud tecos-hero-play__cloud--3" />

      <div className="tecos-hero-play__court">
        <svg viewBox="0 0 1200 280" preserveAspectRatio="none" className="tecos-hero-play__court-svg">
          <defs>
            <linearGradient id="heroPlayCourt" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={theme.success} stopOpacity="0.35" />
              <stop offset="100%" stopColor={theme.primary} stopOpacity="0.12" />
            </linearGradient>
            <pattern id="heroCourtGrid" width="36" height="36" patternUnits="userSpaceOnUse">
              <path d="M36 0 L0 0 0 36" fill="none" stroke="#fff" strokeWidth="0.8" strokeOpacity="0.14" />
            </pattern>
            <filter id="heroCourtLogoShadow" x="-20%" y="-20%" width="140%" height="140%">
              <feDropShadow dx="0" dy="2" stdDeviation="3" floodColor="#0f172a" floodOpacity="0.25" />
            </filter>
            <HeroCourtPlayerDefs />
          </defs>
          <rect x="0" y="0" width="1200" height="280" fill="url(#heroPlayCourt)" rx="0" />
          <rect x="0" y="0" width="1200" height="280" fill="url(#heroCourtGrid)" />
          <line x1="0" y1="40" x2="1200" y2="40" stroke={theme.primary} strokeWidth="4" strokeOpacity="0.5" />
          <line x1="600" y1="40" x2="600" y2="280" stroke="#fff" strokeWidth="3" strokeOpacity="0.65" />
          <line x1="0" y1="160" x2="1200" y2="160" stroke={theme.accent} strokeWidth="2" strokeOpacity="0.4" strokeDasharray="12 8" />

          {/* Logo oficial completo en el piso */}
          <image
            href={HERO_COURT_LOGO}
            x={courtLogoX}
            y={courtLogoY}
            width={HERO_COURT_LOGO_W}
            height={HERO_COURT_LOGO_H}
            preserveAspectRatio="xMidYMid meet"
            opacity="0.95"
            filter="url(#heroCourtLogoShadow)"
          />

          {/* Equipo izquierdo (4) — color primario */}
          <g className="tecos-hero-play__court-team tecos-hero-play__court-team--a" color={theme.primary}>
            <g className="tecos-hero-play__court-player">
              <use href="#heroPlayerDig" x="95" y="208" width="40" height="56" />
            </g>
            <g className="tecos-hero-play__court-player tecos-hero-play__court-player--delay">
              <use href="#heroPlayerReady" x="235" y="210" width="40" height="56" />
            </g>
            <g className="tecos-hero-play__court-player tecos-hero-play__court-player--block">
              <use href="#heroPlayerBlock" x="355" y="178" width="40" height="56" />
            </g>
            <g className="tecos-hero-play__court-player tecos-hero-play__court-player--spike">
              <use href="#heroPlayerSpike" x="455" y="158" width="40" height="56" />
            </g>
          </g>

          {/* Equipo derecho (4) — color acento, espejo */}
          <g className="tecos-hero-play__court-team tecos-hero-play__court-team--b" color={theme.accent}>
            <g transform="translate(1065,208) scale(-1,1)">
              <g className="tecos-hero-play__court-player">
                <use href="#heroPlayerDig" width="40" height="56" />
              </g>
            </g>
            <g transform="translate(925,210) scale(-1,1)">
              <g className="tecos-hero-play__court-player tecos-hero-play__court-player--delay">
                <use href="#heroPlayerReady" width="40" height="56" />
              </g>
            </g>
            <g transform="translate(805,178) scale(-1,1)">
              <g className="tecos-hero-play__court-player tecos-hero-play__court-player--block">
                <use href="#heroPlayerBlock" width="40" height="56" />
              </g>
            </g>
            <g transform="translate(705,158) scale(-1,1)">
              <g className="tecos-hero-play__court-player tecos-hero-play__court-player--spike">
                <use href="#heroPlayerSpike" width="40" height="56" />
              </g>
            </g>
          </g>

        </svg>
        <div className="tecos-hero-play__court-ball" aria-hidden>
          <Volleyball size={isStatic ? 44 : 56} detailed theme={theme} />
        </div>
      </div>

      <svg className="tecos-hero-play__arc" viewBox="0 0 400 200" aria-hidden>
        <path
          d="M 20 180 Q 120 20 280 80 T 380 40"
          fill="none"
          stroke={theme.warning}
          strokeWidth="3"
          strokeOpacity="0.55"
          strokeLinecap="round"
          strokeDasharray="8 10"
          className="tecos-hero-play__arc-line"
        />
      </svg>

      <div className="tecos-hero-play__sparkles">
        {sparkles.map((i) => (
          <span key={i} className="tecos-hero-play__spark" style={{ '--si': i }} />
        ))}
      </div>

      {!isStatic && (
        <div className="tecos-hero-play__confetti">
          {confetti.map((i) => (
            <span key={i} className="tecos-hero-play__confetti-piece" style={{ '--ci': i }} />
          ))}
        </div>
      )}

      <div className={`tecos-hero-play__ball tecos-hero-play__ball--star${isStatic ? ' tecos-hero-play__ball--solo' : ''}`}>
        <Volleyball size={isStatic ? 240 : 168} detailed theme={theme} />
      </div>

      {!isStatic && (
        <>
          <div className="tecos-hero-play__ball tecos-hero-play__ball--a">
            <Volleyball size={76} detailed theme={theme} />
          </div>
          <div className="tecos-hero-play__ball tecos-hero-play__ball--b">
            <Volleyball size={58} detailed theme={theme} />
          </div>
          <div className="tecos-hero-play__ball tecos-hero-play__ball--c">
            <Volleyball size={48} detailed theme={theme} />
          </div>
        </>
      )}

      <div className="tecos-hero-play__net">
        <Net color={theme.text} height={88} />
      </div>
      <div className="tecos-hero-play__wave" />
      <div className="tecos-hero-play__readability" />
    </div>
  );
};

const TopNav = ({ theme, onNav, current, themeKey, onThemeToggle }) => {
  const [menuOpen, setMenuOpen] = React.useState(false);
  const isMobile = useIsMobile();
  React.useEffect(() => {
    if (!isMobile) setMenuOpen(false);
  }, [isMobile]);
  React.useEffect(() => {
    if (!isMobile || !menuOpen) return undefined;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = prev; };
  }, [isMobile, menuOpen]);
  const items = [
    { id: 'home', label: 'Inicio' },
    { id: 'avisos', label: 'Avisos' },
    { id: 'calendario', label: 'Eventos' },
    { id: 'entrenadores', label: 'Entrenadores' },
    { id: 'galeria', label: 'Galería' },
    { id: 'tienda', label: 'Tienda' },
    { id: 'ubicacion', label: 'Ubicación' },
    { id: 'contacto', label: 'Contacto' },
  ];
  const pickNav = (id) => {
    setMenuOpen(false);
    document.body.style.overflow = '';
    onNav(id);
  };
  return (
    <>
      <header className="tecos-public-topnav" style={{
        position: 'sticky', top: 0, zIndex: 120,
        background: `${theme.bg}cc`, backdropFilter: 'blur(14px)',
        borderBottom: `1px solid ${theme.border}`,
        padding: '14px 40px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 10,
      }}>
        <button
          type="button"
          className="tecos-landing-logo-btn"
          onClick={() => pickNav('home')}
          aria-label="Ir al inicio"
          style={{
            border: 'none', background: 'transparent', padding: 0, cursor: 'pointer',
            display: 'flex', alignItems: 'center', flexShrink: 0,
          }}
        >
          <Logo size={isMobile ? 44 : 52} theme={theme} />
        </button>
        {!isMobile && (
          <nav className="tecos-public-topnav__nav" style={{ display: 'flex', gap: 6, flexWrap: 'wrap', flex: 1, justifyContent: 'center' }}>
            {items.map(it => (
              <button
                key={it.id}
                type="button"
                className="tecos-public-topnav__link"
                onClick={() => pickNav(it.id)}
                style={{
                  padding: '10px 16px', borderRadius: 8, cursor: 'pointer',
                  border: 'none',
                  fontSize: 13, fontWeight: 600, letterSpacing: 0.5,
                  color: current === it.id ? theme.primary : theme.textDim,
                  background: current === it.id ? `${theme.primary}15` : 'transparent',
                  textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
                  transition: 'all .2s',
                  textDecoration: 'none',
                }}
                onMouseEnter={e => { e.currentTarget.style.color = theme.text; }}
                onMouseLeave={e => { e.currentTarget.style.color = current === it.id ? theme.primary : theme.textDim; }}
              >{it.label}</button>
            ))}
          </nav>
        )}
        <div className="tecos-public-topnav__actions" style={{ display: 'flex', gap: 10, alignItems: 'center', flexShrink: 0 }}>
          {isMobile && (
            <MobileMenuButton theme={theme} open={menuOpen} onClick={() => setMenuOpen(o => !o)} />
          )}
          <ThemeToggleButton theme={theme} themeKey={themeKey} onToggle={onThemeToggle} />
          <span className="tecos-hide-mobile">
            <Btn theme={theme} kind="ghost" size="sm" icon="user" onClick={() => pickNav('login')}>Iniciar sesión</Btn>
          </span>
          <span className="tecos-hide-mobile">
            <Btn theme={theme} kind="primary" size="sm" icon="plus" onClick={() => pickNav('inscripcion')}>Inscribirme</Btn>
          </span>
        </div>
      </header>
      {isMobile && menuOpen && (
        <>
          <DrawerBackdrop open onClose={() => setMenuOpen(false)} />
          <div
            className="tecos-public-drawer"
            role="dialog"
            aria-modal="true"
            aria-label="Menú de navegación"
            style={{ background: theme.bg, ['--tecos-drawer-bg']: theme.bg }}
          >
            <div className="tecos-public-drawer__head" style={{ borderBottom: `1px solid ${theme.border}` }}>
              <Logo size={40} theme={theme} />
              <MobileMenuButton theme={theme} open onClick={() => setMenuOpen(false)} />
            </div>
            <nav className="tecos-public-drawer__nav">
              {items.map(it => (
                <button
                  key={it.id}
                  type="button"
                  className={current === it.id ? 'tecos-public-drawer__link is-active' : 'tecos-public-drawer__link'}
                  onClick={() => pickNav(it.id)}
                  style={{
                    background: current === it.id ? `${theme.primary}22` : 'transparent',
                    color: current === it.id ? theme.primary : theme.text,
                  }}
                >
                  {it.label}
                </button>
              ))}
            </nav>
            <div className="tecos-public-drawer__foot" style={{ borderTop: `1px solid ${theme.border}` }}>
              <Btn theme={theme} kind="primary" icon="plus" onClick={() => pickNav('inscripcion')} style={{ width: '100%' }}>
                Inscribirme
              </Btn>
              <Btn theme={theme} kind="ghost" icon="user" onClick={() => pickNav('login')} style={{ width: '100%' }}>
                Iniciar sesión
              </Btn>
            </div>
          </div>
        </>
      )}
    </>
  );
};

/* HERO */
const Hero = ({ theme, onNav, heroVariant }) => {
  const { settings: s, reload } = typeof useAcademySettings === 'function'
    ? useAcademySettings()
    : { settings: normalizeAcademySettings(null), reload: () => {} };
  const stats = typeof getHeroStats === 'function' ? getHeroStats(s) : [];

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:settings-changed', onChange);
    return () => window.removeEventListener('tecos:settings-changed', onChange);
  }, [reload]);

  return (
    <section className="tecos-hero tecos-hero--play" style={{
      position: 'relative', minHeight: 800, overflow: 'hidden',
      background: theme.bg,
    }}>
      <HeroAnimation theme={theme} variant={heroVariant} />

      <div className="tecos-hero-inner" style={{
        position: 'relative', maxWidth: 1280, margin: '0 auto',
        padding: '92px 40px 112px', zIndex: 2,
      }}>
        <div className="tecos-hero-content" style={{
          display: 'grid', gap: 28, maxWidth: '100%',
        }}>
        <div className="tecos-hero-badge" style={{
          display: 'inline-flex', alignSelf: 'flex-start',
          alignItems: 'center', gap: 10, padding: '10px 18px', borderRadius: 999,
          background: `linear-gradient(135deg, ${theme.primary}33, ${theme.accent}22)`,
          border: `2px solid ${theme.primary}66`,
          fontSize: 12, fontWeight: 800, letterSpacing: 2, color: theme.primary,
          textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
          boxShadow: `0 8px 24px ${theme.primary}22`,
        }}>
          <span style={{
            width: 8, height: 8, borderRadius: '50%', background: theme.warning,
            boxShadow: `0 0 14px ${theme.warning}`,
            animation: 'pulseGlow 2s ease-in-out infinite',
            '--g': theme.warning,
          }} />
          {s.hero_badge}
        </div>

        <div className="tecos-hero-headline-row">
          <h1 className="tecos-hero-title" style={{
            fontFamily: 'Bebas Neue, sans-serif', fontSize: 120, lineHeight: 0.9,
            letterSpacing: 2, color: theme.text, margin: 0,
            fontWeight: 400, textWrap: 'balance',
          }}>
            {s.hero_title_line1}<br />
            <span style={gradientTextStyle(theme)}>{s.hero_title_line2}</span><br />
            {s.hero_title_line3}
          </h1>
          <Logo size={200} prominent theme={theme} className="tecos-hero-logo-side" />
        </div>

        <div>
          <p style={{
            fontSize: 19, color: theme.textDim, maxWidth: 580, lineHeight: 1.5,
            fontFamily: 'Inter, sans-serif', margin: 0,
          }}>
            {s.hero_subtitle}
          </p>
          <SocialLinksRow theme={theme} settings={s} />
        </div>

        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, marginTop: 10 }}>
          <Btn theme={theme} size="lg" icon="plus" onClick={() => onNav('inscripcion')}>Inscribirme ahora</Btn>
          <Btn theme={theme} size="lg" kind="ghost" icon="cal" onClick={() => onNav('calendario')}>Ver eventos y anuncios</Btn>
          <Btn theme={theme} size="lg" kind="ghost" icon="cart" onClick={() => onNav('tienda')}>Ir a la tienda</Btn>
        </div>

        <div className="tecos-hero-stats" style={{
          marginTop: 60, display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 20,
          paddingTop: 30, borderTop: `1px solid ${theme.border}`,
        }}>
          {stats.map(m => (
            <div key={m.l}>
              <div style={{
                fontFamily: 'Bebas Neue, sans-serif', fontSize: 56, color: theme.text,
                letterSpacing: 1, lineHeight: 1,
              }}>{m.v}</div>
              <div style={{
                fontSize: 11, color: theme.textDim, marginTop: 6, letterSpacing: 1.5,
                textTransform: 'uppercase', fontFamily: 'Inter, sans-serif', fontWeight: 600,
              }}>{m.l}</div>
            </div>
          ))}
        </div>
        </div>
      </div>
    </section>
  );
};

/* AVISOS — registro alumno / no alumno */
const digitsFromTecCode = (code) => {
  const m = String(code || '').match(/(\d{1,6})/);
  return typeof formatTecCodeDigitsInput === 'function'
    ? formatTecCodeDigitsInput(m ? m[1] : '')
    : String(m ? m[1] : '').replace(/\D/g, '').slice(0, 3);
};

const AnnouncementMemberModal = ({ open, onClose, theme, announcementId, avisoColor, avisoTitle, onRegistered }) => {
  const [digits, setDigits] = React.useState('');
  const [pendingStudent, setPendingStudent] = React.useState(null);
  const [studentVerified, setStudentVerified] = React.useState(null);
  const [lookupBusy, setLookupBusy] = React.useState(false);
  const [checkBusy, setCheckBusy] = React.useState(false);
  const [alreadyRegistered, setAlreadyRegistered] = React.useState(false);
  const [registrationDone, setRegistrationDone] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [ok, setOk] = React.useState('');
  const lookupTimerRef = React.useRef(null);
  const glow = avisoColor || theme.primary;

  const fullCode = typeof buildTecStudentCode === 'function'
    ? buildTecStudentCode(digits)
    : (digits ? `TEC${digits.padStart(3, '0')}` : '');

  const locked = alreadyRegistered || registrationDone;

  React.useEffect(() => {
    if (!open) return;
    setDigits('');
    setErr('');
    setOk('');
    setPendingStudent(null);
    setStudentVerified(null);
    setLookupBusy(false);
    setCheckBusy(false);
    setAlreadyRegistered(false);
    setRegistrationDone(false);
    if (lookupTimerRef.current) clearTimeout(lookupTimerRef.current);
  }, [open]);

  const verifyAlreadyRegistered = async (codeStr) => {
    if (!announcementId || !codeStr || !isSupabaseReady()) return false;
    if (typeof checkAnnouncementRegistration !== 'function') return false;
    setCheckBusy(true);
    try {
      const exists = await checkAnnouncementRegistration(announcementId, codeStr);
      setAlreadyRegistered(!!exists);
      return !!exists;
    } catch {
      return false;
    } finally {
      setCheckBusy(false);
    }
  };

  const lookupStudentByCode = async (rawCode) => {
    const normalized = normalizeTecStudentCode(rawCode);
    if (!normalized) {
      setPendingStudent(null);
      setAlreadyRegistered(false);
      return;
    }
    if (!isSupabaseReady()) return;
    setLookupBusy(true);
    setErr('');
    try {
      const exists = await verifyAlreadyRegistered(normalized);
      if (exists) {
        setPendingStudent(null);
        setStudentVerified(null);
        setLookupBusy(false);
        return;
      }
      const found = await lookupStudentCodePublic(rawCode);
      if (!found) {
        setPendingStudent(null);
        setStudentVerified(null);
        return;
      }
      if (found.status && found.status !== 'activo') {
        setPendingStudent(null);
        setStudentVerified(null);
        setErr('Ese alumno no está activo. Contacta a la academia.');
        return;
      }
      setPendingStudent(found);
      setStudentVerified(null);
    } catch (e) {
      setPendingStudent(null);
      setErr(e.message || 'No se pudo buscar el alumno.');
    }
    setLookupBusy(false);
  };

  const scheduleLookup = (d) => {
    if (lookupTimerRef.current) clearTimeout(lookupTimerRef.current);
    const code = typeof buildTecStudentCode === 'function' ? buildTecStudentCode(d) : '';
    if (!code) {
      setPendingStudent(null);
      setAlreadyRegistered(false);
      return;
    }
    lookupTimerRef.current = setTimeout(() => lookupStudentByCode(code), 450);
  };

  const onDigitsChange = (d) => {
    setDigits(d);
    setStudentVerified(null);
    setPendingStudent(null);
    setOk('');
    setErr('');
    setAlreadyRegistered(false);
    scheduleLookup(d);
  };

  const submit = async () => {
    if (!fullCode) {
      setErr('Escribe el número de tu ID (ej. 001 o 067).');
      return;
    }
    if (locked) return;
    if (pendingStudent && !studentVerified) {
      setErr('Confirma con «Sí» o «No» si el ID te corresponde.');
      return;
    }
    const confirmCode = studentVerified?.code || fullCode;
    if (await verifyAlreadyRegistered(confirmCode)) {
      setErr('Este ID ya está registrado en este aviso.');
      return;
    }
    setSaving(true);
    setErr('');
    setOk('');
    try {
      const data = await confirmAnnouncementRegistration(announcementId, confirmCode);
      setRegistrationDone(true);
      setAlreadyRegistered(true);
      setOk(`Registro confirmado: ${data?.student_name || data?.student_code || confirmCode}`);
      window.dispatchNotificationsChanged?.();
      onRegistered?.();
    } catch (e) { setErr(e.message || 'No se pudo registrar'); }
    setSaving(false);
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} title="Soy alumno Tecos" width={440}>
      <div style={{ padding: '8px 24px 24px', display: 'grid', gap: 14, fontFamily: 'Inter, sans-serif' }}>
        {avisoTitle && (
          <p style={{ margin: 0, fontSize: 12, color: theme.textDim }}>
            Aviso: <strong style={{ color: theme.text }}>{avisoTitle}</strong>
          </p>
        )}
        <p style={{ margin: 0, fontSize: 13, color: theme.textDim }}>
          Escribe solo el número de tu ID (el prefijo TEC ya está incluido).
        </p>
        <TecStudentIdField
          theme={theme}
          label="Número de ID"
          icon="medal"
          digits={digits}
          onDigitsChange={onDigitsChange}
          onBlur={() => fullCode && lookupStudentByCode(fullCode)}
          disabled={locked}
        />
        {(lookupBusy || checkBusy) && (
          <p style={{ margin: 0, fontSize: 12, color: theme.textDim }}>
            {checkBusy ? 'Verificando registro…' : 'Buscando alumno…'}
          </p>
        )}
        {locked && !ok && (
          <div style={{
            padding: 12, borderRadius: 10, background: `${theme.warning}18`,
            border: `1px solid ${theme.warning}44`, fontSize: 13, color: theme.text,
          }}>
            Este ID ya registró su interés en este aviso. No se puede volver a enviar con el mismo número.
          </div>
        )}
        {pendingStudent && !studentVerified && !locked && (
          <StudentConfirmNeonBanner
            theme={theme}
            student={pendingStudent}
            glowColor={glow}
            onYes={() => {
              setStudentVerified(pendingStudent);
              setDigits(digitsFromTecCode(pendingStudent.code));
              setPendingStudent(null);
            }}
            onNo={() => { setPendingStudent(null); setStudentVerified(null); }}
            subtitle="Si confirmas, quedarás registrado en este aviso."
          />
        )}
        {studentVerified && !locked && (
          <div style={{
            padding: 12, borderRadius: 10,
            background: `${glow}18`, border: `1px solid ${glow}44`,
            fontSize: 12, color: theme.text, boxShadow: `0 0 16px ${glow}33`,
          }}>
            Identidad confirmada: <strong>{studentVerified.code}</strong> — {studentVerified.full_name}
          </div>
        )}
        {err && <div style={{ color: theme.danger, fontSize: 13 }}>{err}</div>}
        {ok && <div style={{ color: theme.success, fontSize: 13 }}>{ok}</div>}
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
          <Btn theme={theme} kind="ghost" onClick={onClose}>Cerrar</Btn>
          <Btn theme={theme} icon="check" onClick={submit}
            disabled={saving || !announcementId || locked || !fullCode}
            style={studentVerified && !locked ? { boxShadow: `0 0 18px ${glow}66` } : undefined}>
            {saving ? 'Registrando…' : locked ? 'Ya registrado' : 'Registrar mi interés'}
          </Btn>
        </div>
      </div>
    </Modal>
  );
};

const AnnouncementGuestModal = ({ open, onClose, theme, announcementId, avisoTitle, avisoColor }) => {
  const [name, setName] = React.useState('');
  const [phoneDial, setPhoneDial] = React.useState(DEFAULT_PHONE_DIAL);
  const [phoneNational, setPhoneNational] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [sent, setSent] = React.useState(false);
  const glow = avisoColor || theme.primary;

  React.useEffect(() => {
    if (!open) return;
    setName('');
    setPhoneDial(DEFAULT_PHONE_DIAL);
    setPhoneNational('');
    setErr('');
    setSent(false);
  }, [open]);

  const submit = async () => {
    if (!name.trim()) { setErr('Escribe tu nombre'); return; }
    const phone = combinePhoneWithDial(phoneDial, phoneNational);
    if (!phone.trim()) { setErr('Escribe tu teléfono'); return; }
    setBusy(true);
    setErr('');
    try {
      if (typeof window.submitInterestedLead !== 'function') {
        throw new Error('El envío no está disponible. Recarga la página (F5).');
      }
      await window.submitInterestedLead({
        name: name.trim(),
        phone: phone.trim(),
        source: 'anuncio',
        announcement_id: announcementId,
      });
      window.notifyLeadsChanged?.();
      window.dispatchNotificationsChanged?.();
      setSent(true);
    } catch (e) { setErr(e.message || 'No se pudo enviar'); }
    setBusy(false);
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} title="No soy alumno" width={420}>
      <div style={{ padding: '8px 24px 24px', display: 'grid', gap: 14, fontFamily: 'Inter, sans-serif' }}>
        {avisoTitle && (
          <p style={{ margin: 0, fontSize: 12, color: theme.textDim }}>
            Interés en: <strong style={{ color: theme.text }}>{avisoTitle}</strong>
          </p>
        )}
        {sent ? (
          <div style={{
            textAlign: 'center', padding: 20, borderRadius: 12,
            background: `${glow}14`, border: `1px solid ${glow}44`,
            animation: 'avisoGlow 2s ease-in-out infinite',
          }}>
            <Icon name="check" size={36} style={{ color: theme.success }} />
            <p style={{ margin: '12px 0 0', fontSize: 14, color: theme.text }}>
              ¡Listo! Te contactaremos pronto.
            </p>
          </div>
        ) : (
          <>
            <p style={{ margin: 0, fontSize: 13, color: theme.textDim }}>
              Déjanos tu nombre y teléfono. Aparecerás en el módulo de interesados de la academia.
            </p>
            <Input theme={theme} label="Tu nombre" placeholder="Nombre completo" icon="user" value={name} onChange={e => setName(e.target.value)} />
            <PhoneInput theme={theme} label="Teléfono" icon="wa"
              dial={phoneDial} national={phoneNational}
              onDialChange={setPhoneDial} onNationalChange={setPhoneNational} />
            {err && <div style={{ color: theme.danger, fontSize: 13 }}>{err}</div>}
            <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
              <Btn theme={theme} kind="ghost" onClick={onClose}>Cancelar</Btn>
              <Btn theme={theme} icon="send" onClick={submit} disabled={busy || !announcementId}>
                {busy ? 'Enviando…' : 'Enviar interés'}
              </Btn>
            </div>
          </>
        )}
        {sent && <Btn theme={theme} kind="ghost" onClick={onClose} style={{ justifySelf: 'end' }}>Cerrar</Btn>}
      </div>
    </Modal>
  );
};

/* AVISOS — estilo franjas + brillo suave (distinto al neón de eventos) */
const AvisoModal = ({ open, onClose, aviso, theme }) => {
  const closeModal = React.useCallback(bindModalClose(onClose), [onClose]);
  const [memberOpen, setMemberOpen] = React.useState(false);
  const [guestOpen, setGuestOpen] = React.useState(false);
  if (!aviso) return null;
  const a = aviso._raw ? mapAnnouncementCard(aviso._raw, theme) : (aviso.title ? mapAnnouncementCard(aviso, theme) : aviso);
  const announcementId = a.id || aviso.id || aviso._raw?.id;

  return (
    <>
    <Modal open={open} onClose={onClose} theme={theme} width={600}>
      <div>
        <div style={{
          height: typeof LANDING_AVISO_MODAL_IMAGE_HEIGHT === 'number' ? LANDING_AVISO_MODAL_IMAGE_HEIGHT : 248,
          position: 'relative', overflow: 'hidden',
          background: a.imageUrl ? '#111' : `linear-gradient(120deg, ${a.color}44, ${theme.bgElev})`,
        }}>
          <div style={{
            position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.12,
            background: `repeating-linear-gradient(-45deg, ${a.color}, ${a.color} 2px, transparent 2px, transparent 14px)`,
            animation: 'avisoStripe 8s linear infinite',
          }} />
          {a.imageUrl ? (
            <img src={a.imageUrl} alt="" style={{
              position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover',
              objectPosition: `${a.imageFocusX}% ${a.imageFocusY}%`,
            }} />
          ) : (
            <div style={{ position: 'absolute', inset: 0, display: 'grid', placeItems: 'center', opacity: 0.35 }}>
              <Icon name="megaphone" size={80} style={{ color: a.color }} />
            </div>
          )}
          <div style={{
            position: 'absolute', top: 14, left: 14, padding: '4px 12px', borderRadius: 6,
            background: a.color, color: '#fff', fontSize: 10, fontWeight: 800, letterSpacing: 1.2,
            fontFamily: 'Inter, sans-serif', textTransform: 'uppercase',
            boxShadow: `0 0 16px ${a.color}88`,
          }}>Aviso oficial</div>
          <button type="button" tabIndex={-1} onMouseDown={(e) => e.preventDefault()} onClick={closeModal} style={{
            position: 'absolute', top: 14, right: 14, width: 36, height: 36, borderRadius: 8,
            background: 'rgba(0,0,0,0.45)', border: 'none', color: '#fff', cursor: 'pointer',
            display: 'grid', placeItems: 'center', outline: 'none', boxShadow: 'none',
          }}><Icon name="x" size={18} /></button>
          <div style={{ position: 'absolute', bottom: 16, left: 24, right: 24 }}>
            <Badge theme={theme} color={a.tag === 'urgente' ? 'urgent' : a.tag === 'alta' ? 'warning' : 'info'}>{a.tag}</Badge>
            <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 32, letterSpacing: 1, color: '#fff', margin: '6px 0 0', fontWeight: 400, textShadow: '0 2px 12px rgba(0,0,0,0.5)' }}>{a.title}</h3>
          </div>
        </div>
        <div style={{ padding: 24 }}>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: 16, marginBottom: 20 }}>
            {[
              { l: 'Publicado', v: a.date, i: 'cal' },
              a.dateLabel && a.dateLabel !== '—' ? { l: 'Vigencia', v: a.dateLabel, i: 'clock' } : null,
              { l: 'Lugar', v: a.location || '—', i: 'pin' },
            ].filter(Boolean).map(it => (
              <div key={it.l}>
                <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: 6, fontFamily: 'Inter, sans-serif' }}>
                  <Icon name={it.i} size={12} />{it.l}
                </div>
                <div style={{ color: theme.text, fontSize: 14, marginTop: 4, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>{it.v}</div>
              </div>
            ))}
          </div>
          {a.mapsUrl && (
            <p style={{ margin: '0 0 16px' }}>
              <a href={a.mapsUrl} target="_blank" rel="noopener noreferrer" style={{ color: a.color, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>
                Ver en Google Maps →
              </a>
            </p>
          )}
          <p style={{ color: theme.textDim, fontSize: 14, lineHeight: 1.55, margin: 0, fontFamily: 'Inter, sans-serif', whiteSpace: 'pre-wrap' }}>{a.body}</p>
          <div style={{ marginTop: 24, display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            <Btn theme={theme} icon="medal" onClick={() => setMemberOpen(true)} disabled={!announcementId}
              style={{ boxShadow: `0 0 12px ${a.color}44` }}>
              Soy alumno — registrar con ID
            </Btn>
            <Btn theme={theme} kind="soft" icon="user" onClick={() => setGuestOpen(true)} disabled={!announcementId}>
              No soy alumno
            </Btn>
            <Btn theme={theme} kind="ghost" onClick={closeModal}>Cerrar</Btn>
          </div>
        </div>
      </div>
    </Modal>
    <AnnouncementMemberModal
      open={memberOpen}
      onClose={() => setMemberOpen(false)}
      theme={theme}
      announcementId={announcementId}
      avisoColor={a.color}
      avisoTitle={a.title}
    />
    <AnnouncementGuestModal
      open={guestOpen}
      onClose={() => setGuestOpen(false)}
      theme={theme}
      announcementId={announcementId}
      avisoTitle={a.title}
      avisoColor={a.color}
    />
    </>
  );
};

const useAnnouncementsFeed = () => {
  const { data: rows, loading, error, reload } = useLiveData(() => fetchPublishedAnnouncements(), []);
  React.useEffect(() => {
    const on = () => reload();
    window.addEventListener('tecos:announcements-changed', on);
    return () => window.removeEventListener('tecos:announcements-changed', on);
  }, [reload]);
  return { rows, loading, error, reload };
};

const AvisosSection = ({ theme }) => {
  const { rows, loading, error } = useAnnouncementsFeed();
  const avisos = (rows || []).map(a => mapAnnouncementCard(a, theme));
  const [selected, setSelected] = React.useState(null);

  return (
    <section className="tecos-landing-section" style={{ padding: '100px 40px', background: theme.bgElev, position: 'relative' }}>
      <div style={{ maxWidth: 1280, margin: '0 auto' }}>
        <SectionTitle theme={theme} kicker="Avisos importantes"
          title="MANTENTE INFORMADO"
          sub="Comunicados oficiales de la academia, recordatorios y noticias de la temporada." />
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 20 }}>
          {loading ? (
            <LoadingBlock theme={theme} label="Cargando avisos…" />
          ) : error ? (
            <EmptyState theme={theme} title="No se pudieron cargar los avisos" message={error} icon="warning" />
          ) : avisos.length === 0 ? (
            <EmptyState theme={theme} title="Sin avisos publicados" message="En Admin → Anuncios usa «Publicar ahora» o asigna fecha de publicación en el pasado." icon="megaphone" />
          ) : avisos.map((a) => (
            <Card
              key={a.id || a.title}
              theme={theme}
              hover
              onClick={() => setSelected(a._raw || a)}
              style={{
                position: 'relative', overflow: 'hidden', cursor: 'pointer',
                border: `1px solid ${a.color}44`,
                boxShadow: `0 4px 20px ${a.color}22`,
                animation: 'avisoGlow 3s ease-in-out infinite',
                ['--aviso-g']: `${a.color}55`,
              }}
            >
              <div style={{
                position: 'absolute', inset: 0, pointerEvents: 'none', opacity: 0.07,
                background: `repeating-linear-gradient(-45deg, ${a.color}, ${a.color} 2px, transparent 2px, transparent 12px)`,
                animation: 'avisoStripe 10s linear infinite',
              }} />
              <div style={{
                position: 'absolute', top: 12, right: -32, transform: 'rotate(45deg)',
                background: a.color, color: '#fff', fontSize: 9, fontWeight: 800, letterSpacing: 1,
                padding: '4px 36px', fontFamily: 'Inter, sans-serif', textTransform: 'uppercase',
                boxShadow: `0 0 12px ${a.color}66`,
              }}>Aviso</div>
              {a.imageUrl && (
                <div style={{
                  height: typeof LANDING_AVISO_CARD_IMAGE_HEIGHT === 'number' ? LANDING_AVISO_CARD_IMAGE_HEIGHT : 156,
                  margin: '-1px -1px 0', overflow: 'hidden',
                }}>
                  <img src={a.imageUrl} alt="" style={{
                    width: '100%', height: '100%', objectFit: 'cover',
                    objectPosition: `${a.imageFocusX}% ${a.imageFocusY}%`,
                  }} />
                </div>
              )}
              <div style={{ padding: a.imageUrl ? '14px 16px 16px' : '16px', position: 'relative' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', marginBottom: 12 }}>
                  <div style={{
                    width: 42, height: 42, borderRadius: 10, background: `${a.color}22`, color: a.color,
                    display: 'grid', placeItems: 'center', border: `1px solid ${a.color}44`,
                  }}><Icon name={a.icon} size={20} /></div>
                  <Badge theme={theme} color={a.tag === 'urgente' ? 'urgent' : a.tag === 'alta' ? 'warning' : 'info'}>{a.tag}</Badge>
                </div>
                <h3 style={{
                  fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, letterSpacing: 1,
                  color: theme.text, margin: '0 0 8px', lineHeight: 1.1, fontWeight: 400,
                }}>{a.title}</h3>
                <p style={{
                  color: theme.textDim, fontSize: 13, lineHeight: 1.5, margin: 0, fontFamily: 'Inter, sans-serif',
                  display: '-webkit-box', WebkitLineClamp: 3, WebkitBoxOrient: 'vertical', overflow: 'hidden',
                }}>{a.body}</p>
                <div style={{
                  marginTop: 14, paddingTop: 12, borderTop: `1px solid ${theme.border}`,
                  display: 'flex', justifyContent: 'space-between', fontSize: 11,
                  color: theme.textMute, fontFamily: 'Inter, sans-serif', fontWeight: 600,
                  letterSpacing: 0.5, textTransform: 'uppercase',
                }}>
                  <span>Pub. {a.date}</span>
                  <span>{a.dur !== '—' ? a.dur : 'Ver más'}</span>
                </div>
              </div>
            </Card>
          ))}
        </div>
      </div>
      <AvisoModal open={!!selected} onClose={() => setSelected(null)} aviso={selected} theme={theme} />
    </section>
  );
};

/* CALENDARIO */
const monthLabel = (year, month) => {
  const d = new Date(year, month, 1);
  return d.toLocaleDateString('es-MX', { month: 'long', year: 'numeric' }).toUpperCase();
};

const buildMonthCells = (year, month) => {
  const firstDay = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < firstDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) {
    cells.push({ day: d, dayKey: formatEventDayKey(new Date(year, month, d)) });
  }
  return cells;
};

/** 1 → completo; 2 → mitades; 3+ → tercios en la celda. */
function calendarDayGridLayout (count) {
  if (count <= 1) return { cols: 1, visible: 1 };
  if (count === 2) return { cols: 2, visible: 2 };
  return { cols: 3, visible: Math.min(count, 3) };
}

const CalendarDayMedia = ({ item, theme }) => {
  const focus = `${item?.imageFocusX ?? 50}% ${item?.imageFocusY ?? 50}%`;
  if (item?.imageUrl) {
    return (
      <img
        src={item.imageUrl}
        alt=""
        loading="lazy"
        draggable={false}
        style={{
          width: '100%', height: '100%', objectFit: 'cover', objectPosition: focus,
          display: 'block', pointerEvents: 'none',
        }}
      />
    );
  }
  const kind = item?.itemKind;
  return (
    <div style={{
      width: '100%', height: '100%', display: 'grid', placeItems: 'center',
      background: `${item?.color || theme.primary}33`,
    }}>
      {kind === 'anuncio' ? (
        <Icon name="megaphone" size={16} style={{ color: item?.color }} />
      ) : kind === 'birthday' ? (
        <Icon name="cake" size={18} style={{ color: item?.color }} />
      ) : (
        <Icon name="cal" size={16} style={{ color: item?.color }} />
      )}
    </div>
  );
};

const calendarItemKindLabel = (item) => {
  if (item?.itemKind === 'anuncio') return 'Aviso';
  if (item?.itemKind === 'birthday') return 'Cumpleaños';
  return item?.type || 'Evento';
};

const calendarDayTabLabel = (item, index) => {
  if (item?.itemKind === 'anuncio') return `Aviso ${index + 1}`;
  if (item?.itemKind === 'birthday') return index > 0 ? `Cumpleaños ${index + 1}` : 'Cumpleaños';
  return `Evento ${index + 1}`;
};

function birthdayDisplayFromItem (item) {
  const raw = item?._raw || {};
  const name = String(raw.full_name || item?.name || 'Alumno').replace(/^🎂\s*/, '').trim();
  const age = raw.birth_date && typeof ageFromBirthDate === 'function'
    ? ageFromBirthDate(raw.birth_date)
    : null;
  const ageNum = age && age !== '—' ? age : null;
  return {
    name,
    ageLabel: ageNum ? `${ageNum} años` : null,
    imageUrl: item?.imageUrl || null,
    imageFocusX: item?.imageFocusX ?? 50,
    imageFocusY: item?.imageFocusY ?? 50,
    dateLabel: item?.dateLabel || '',
    studentId: raw.student_id || null,
  };
}

/** Captura un nodo DOM como PNG (foreignObject → canvas, sin dependencias extra). */
async function captureElementAsPngBlob (node, { scale = 2 } = {}) {
  if (!node?.offsetWidth || !node?.offsetHeight) {
    throw new Error('No se pudo medir la tarjeta de cumpleaños.');
  }

  const hidden = [];
  node.querySelectorAll('[data-capture-hide]').forEach((el) => {
    hidden.push([el, el.style.visibility]);
    el.style.visibility = 'hidden';
  });

  try {
    try { await document.fonts.ready; } catch {}

    const toDataURL = (url) => {
      if (!url || String(url).indexOf('data:') === 0) return Promise.resolve(url);
      return fetch(url, { mode: 'cors' })
        .then((r) => (r.ok ? r.blob() : Promise.reject()))
        .then((b) => new Promise((res, rej) => {
          const fr = new FileReader();
          fr.onload = () => res(fr.result);
          fr.onerror = () => rej(new Error('img'));
          fr.readAsDataURL(b);
        }))
        .catch(() => url);
    };

    const fontRules = [];
    const pending = [];
    const seen = new Set();
    const scrapeCss = (href) => {
      if (seen.has(href)) return;
      seen.add(href);
      pending.push(fetch(href).then((r) => r.text()).then((css) => {
        for (const m of css.match(/@font-face\s*{[^}]*}/g) || []) fontRules.push({ css: m, base: href });
      }).catch(() => {}));
    };
    const walk = (rules, base) => {
      for (const r of rules) {
        if (r.type === CSSRule.FONT_FACE_RULE) fontRules.push({ css: r.cssText, base });
        else if (r.type === CSSRule.IMPORT_RULE && r.styleSheet) {
          const ibase = r.styleSheet.href || base;
          try { walk(r.styleSheet.cssRules, ibase); } catch { scrapeCss(ibase); }
        } else if (r.cssRules) walk(r.cssRules, base);
      }
    };
    for (const ss of document.styleSheets) {
      const base = ss.href || location.href;
      try { walk(ss.cssRules, base); } catch { if (ss.href) scrapeCss(ss.href); }
    }
    while (pending.length) await pending.shift();
    const fontCss = (await Promise.all(fontRules.map(async (rule) => {
      let out = rule.css;
      let m;
      const re = /url\((['"]?)([^'")]+)\1\)/g;
      while ((m = re.exec(rule.css))) {
        if (m[2].indexOf('data:') === 0) continue;
        let abs;
        try { abs = new URL(m[2], rule.base).href; } catch { continue; }
        out = out.split(m[0]).join(`url("${await toDataURL(abs)}")`);
      }
      return out;
    }))).join('\n');

    const cloneStyled = (src) => {
      if (src.nodeType === 8 || (src.nodeType === 1 && src.tagName === 'SCRIPT')) return document.createTextNode('');
      const dst = src.cloneNode(false);
      if (src.nodeType === 1) {
        const cs = getComputedStyle(src);
        let txt = '';
        for (let i = 0; i < cs.length; i++) txt += `${cs[i]}:${cs.getPropertyValue(cs[i])};`;
        dst.setAttribute('style', `${txt}animation:none;transition:none;`);
      }
      for (let c = src.firstChild; c; c = c.nextSibling) dst.appendChild(cloneStyled(c));
      return dst;
    };

    const clone = cloneStyled(node);
    clone.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
    clone.style.boxShadow = 'none';
    clone.style.margin = '0';

    const jobs = [];
    clone.querySelectorAll('img').forEach((el) => {
      const s = el.getAttribute('src');
      if (s && s.indexOf('data:') !== 0) {
        jobs.push(toDataURL(el.src).then((d) => { if (d) el.setAttribute('src', d); }));
      }
    });
    [clone, ...clone.querySelectorAll('*')].forEach((el) => {
      const bg = el.style?.backgroundImage;
      if (!bg) return;
      let m;
      const re = /url\(["']?([^"')]+)["']?\)/g;
      while ((m = re.exec(bg))) {
        const tok = m[0];
        const url = m[1];
        if (url.indexOf('data:') === 0) continue;
        jobs.push(toDataURL(url).then((d) => {
          el.style.backgroundImage = el.style.backgroundImage.split(tok).join(`url("${d}")`);
        }));
      }
    });
    await Promise.all(jobs);

    const w = node.offsetWidth;
    const h = node.offsetHeight;
    const xml = new XMLSerializer().serializeToString(clone);
    const px = scale;
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w * px}" height="${h * px}" viewBox="0 0 ${w} ${h}"><foreignObject width="${w}" height="${h}">${fontCss ? `<style><![CDATA[${fontCss}]]></style>` : ''}${xml}</foreignObject></svg>`;
    const img = new Image();
    await new Promise((res, rej) => {
      img.onload = res;
      img.onerror = () => rej(new Error('No se pudo renderizar la imagen.'));
      img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
    });
    const cv = document.createElement('canvas');
    cv.width = w * px;
    cv.height = h * px;
    cv.getContext('2d').drawImage(img, 0, 0);
    const blob = await new Promise((res, rej) => {
      cv.toBlob((b) => (b ? res(b) : rej(new Error('No se pudo generar el PNG.'))), 'image/png');
    });
    return blob;
  } finally {
    hidden.forEach(([el, prev]) => { el.style.visibility = prev; });
  }
}

function downloadImageBlob (blob, filename) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}

/** Comparte la tarjeta de cumpleaños: Web Share con PNG o descarga + wa.me. */
async function shareBirthdayCongratsViaWhatsApp ({ blob, displayName, schoolName }) {
  const slug = String(displayName || 'cumpleanos').toLowerCase().replace(/[^a-z0-9áéíóúñ]+/gi, '-').replace(/^-|-$/g, '') || 'cumpleanos';
  const filename = `felicitacion-${slug}.png`;
  const file = new File([blob], filename, { type: 'image/png' });
  const msg = displayName
    ? `¡Feliz cumpleaños, ${displayName}! 🎂 De parte de ${schoolName}.`
    : `¡Feliz cumpleaños! 🎂 De parte de ${schoolName}.`;

  if (typeof navigator !== 'undefined' && navigator.share) {
    try {
      const canFiles = navigator.canShare?.({ files: [file] });
      if (canFiles) {
        await navigator.share({ files: [file], title: 'Felicitación de cumpleaños', text: msg });
        return { mode: 'share' };
      }
    } catch (e) {
      if (e?.name === 'AbortError') throw e;
    }
  }

  downloadImageBlob(blob, filename);
  const shareText = `${msg}\n\nSe descargó la imagen de la felicitación; adjúntala en este chat.`;
  if (typeof openWhatsAppChat === 'function') {
    openWhatsAppChat('', shareText);
  } else {
    window.open(`https://wa.me/?text=${encodeURIComponent(shareText)}`, '_blank', 'noopener,noreferrer');
  }
  return { mode: 'download_and_wa' };
}

/** Modal de felicitación al cumpleañero (calendario y lista). */
const BirthdayCongratsModal = ({ open, onClose, item, theme, viewerStudent }) => {
  const closeModal = React.useCallback(bindModalClose(onClose), [onClose]);
  const display = React.useMemo(() => (item ? birthdayDisplayFromItem(item) : null), [item]);
  const [schoolName, setSchoolName] = React.useState('TECOS ELITE VOLLEYBALL');
  const [sharing, setSharing] = React.useState(false);
  const captureRef = React.useRef(null);

  React.useEffect(() => {
    if (!open || typeof fetchAcademySettings !== 'function') return;
    fetchAcademySettings().then((s) => {
      if (s?.venue_name) setSchoolName(s.venue_name);
    }).catch(() => {});
  }, [open]);

  if (!open || !display) return null;

  const isSelf = !!(
    viewerStudent?._uuid
    && display.studentId
    && String(viewerStudent._uuid) === String(display.studentId)
  );

  const message = isSelf
    ? '¡Feliz cumpleaños! Todo el club celebra contigo hoy. Sigue entrenando con el corazón de campeón que nos caracteriza en Tecos Elite.'
    : `¡Feliz cumpleaños, ${display.name}! De parte de toda la familia ${schoolName} te deseamos un día increíble y un año lleno de éxitos dentro y fuera de la cancha.`;

  const onShareWhatsApp = async () => {
    if (sharing || !captureRef.current) return;
    setSharing(true);
    try {
      const blob = await captureElementAsPngBlob(captureRef.current);
      await shareBirthdayCongratsViaWhatsApp({
        blob,
        displayName: display.name,
        schoolName,
      });
    } catch (e) {
      if (e?.name !== 'AbortError') {
        window.alert(e?.message || 'No se pudo generar la imagen para compartir. Intenta de nuevo.');
      }
    } finally {
      setSharing(false);
    }
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} width={500} borderless>
      <div ref={captureRef} style={{
        position: 'relative', overflow: 'hidden', background: theme.bgElev,
        border: 'none', boxShadow: 'none', outline: 'none',
      }}>
        <div style={{
          padding: '28px 24px 22px',
          background: `linear-gradient(145deg, ${theme.urgent} 0%, ${theme.primary} 55%, ${theme.primaryDark || theme.primary} 100%)`,
          color: '#fff',
          textAlign: 'center',
          position: 'relative',
        }}>
          <div data-capture-hide style={{ position: 'absolute', top: 12, right: 12 }}>
            <button type="button" tabIndex={-1} onMouseDown={(e) => e.preventDefault()} onClick={closeModal} aria-label="Cerrar" style={{
              width: 34, height: 34, borderRadius: 8, border: 'none',
              background: 'rgba(0,0,0,0.25)', color: '#fff', cursor: 'pointer',
              display: 'grid', placeItems: 'center', outline: 'none', boxShadow: 'none',
            }}>
              <Icon name="x" size={16} />
            </button>
          </div>
          <div style={{ fontSize: 40, lineHeight: 1, marginBottom: 8 }}>🎂</div>
          <div style={{ fontSize: 11, letterSpacing: 2, fontWeight: 700, textTransform: 'uppercase', opacity: 0.9, fontFamily: 'Inter, sans-serif' }}>
            {isSelf ? '¡Es tu día!' : 'Felicitaciones'}
          </div>
          <h3 style={{
            fontFamily: 'Bebas Neue, sans-serif', fontSize: 36, margin: '10px 0 6px',
            letterSpacing: 1.5, fontWeight: 400, lineHeight: 1.05,
          }}>
            {(display.name || '').toUpperCase()}
          </h3>
          {display.ageLabel && (
            <div style={{ fontSize: 14, opacity: 0.92, fontFamily: 'Inter, sans-serif', fontWeight: 600 }}>
              Cumple {display.ageLabel}
            </div>
          )}
          {display.dateLabel && (
            <div style={{ fontSize: 12, opacity: 0.85, marginTop: 6, fontFamily: 'Inter, sans-serif' }}>
              {display.dateLabel}
            </div>
          )}
        </div>

        <div style={{
          padding: 24, display: 'grid', gap: 18, justifyItems: 'center',
          marginTop: -1, position: 'relative', zIndex: 1, background: theme.bgElev,
        }}>
          {display.imageUrl ? (
            <img
              src={display.imageUrl}
              alt=""
              style={{
                width: 120, height: 120, borderRadius: '50%', objectFit: 'cover',
                border: `4px solid ${theme.urgent}`,
                boxShadow: `0 8px 32px ${theme.urgent}55`,
                objectPosition: `${display.imageFocusX}% ${display.imageFocusY}%`,
              }}
            />
          ) : (
            <Avatar name={display.name} size={120} color={theme.urgent} />
          )}

          <p style={{
            margin: 0, fontSize: 15, lineHeight: 1.55, color: theme.text,
            fontFamily: 'Inter, sans-serif', textAlign: 'center', maxWidth: 400,
          }}>
            {message}
          </p>

          <p style={{
            margin: 0, fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif',
            textAlign: 'center', fontStyle: 'italic',
          }}>
            — {schoolName} —
          </p>
        </div>
      </div>

      <div style={{ padding: '0 24px 24px', display: 'grid', gap: 10, justifyItems: 'center' }}>
        <Btn
          theme={theme}
          kind="wa"
          icon="wa"
          onClick={onShareWhatsApp}
          disabled={sharing}
          style={{ width: '100%', maxWidth: 280 }}
        >
          {sharing ? 'Generando imagen…' : 'Compartir por WhatsApp'}
        </Btn>
        <Btn theme={theme} kind="soft" icon="cake" onClick={closeModal} style={{ width: '100%', maxWidth: 280 }}>
          ¡Gracias!
        </Btn>
      </div>
    </Modal>
  );
};

const CalendarDayModal = ({ open, onClose, items, tab, onTabChange, theme, onOpenItem, onBirthdayClick }) => {
  const closeModal = React.useCallback(bindModalClose(onClose), [onClose]);
  const current = items?.[tab];
  if (!current) return null;
  const extra = (items?.length || 0) > 3 ? items.length - 3 : 0;

  return (
    <Modal open={open} onClose={onClose} theme={theme} width={520}>
      <div style={{ padding: '20px 20px 0' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 24, color: theme.text, margin: 0, fontWeight: 400, letterSpacing: 1 }}>
            EVENTOS DEL DÍA
          </h3>
          <button type="button" tabIndex={-1} onMouseDown={(e) => e.preventDefault()} onClick={closeModal} aria-label="Cerrar" style={{
            width: 32, height: 32, borderRadius: 8, background: theme.bgInput,
            border: `1px solid ${theme.border}`, color: theme.text, cursor: 'pointer',
            display: 'grid', placeItems: 'center', outline: 'none', boxShadow: 'none',
          }}>
            <Icon name="x" size={14} />
          </button>
        </div>
        {items.length > 1 && (
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 14 }}>
            {items.map((it, i) => (
              <button
                key={`${it.itemKind}-${it.id}-${i}`}
                type="button"
                onClick={() => onTabChange(i)}
                style={{
                  padding: '8px 14px', borderRadius: 999, cursor: 'pointer',
                  fontFamily: 'Inter, sans-serif', fontSize: 11, fontWeight: 700,
                  letterSpacing: 0.5, textTransform: 'uppercase',
                  border: `1px solid ${tab === i ? it.color : theme.border}`,
                  background: tab === i ? `${it.color}33` : theme.bgInput,
                  color: tab === i ? theme.text : theme.textDim,
                }}
              >
                {calendarDayTabLabel(it, i)}
              </button>
            ))}
          </div>
        )}
      </div>
      <div style={{ padding: '0 20px 20px' }}>
        <button
          type="button"
          aria-label={current.itemKind === 'birthday' ? 'Ver felicitación' : 'Ver detalle'}
          onClick={() => {
            if (current.itemKind === 'birthday') {
              closeModal();
              onBirthdayClick?.(current);
            } else {
              closeModal();
              onOpenItem?.(current);
            }
          }}
          style={{
            width: '100%', height: 200, padding: 0, borderRadius: 12, overflow: 'hidden',
            position: 'relative', cursor: 'pointer', display: 'block',
            background: current.imageUrl ? '#111' : `linear-gradient(135deg, ${current.color}, ${theme.bgElev})`,
            border: `1px solid ${current.color}55`,
          }}
        >
          <CalendarDayMedia item={current} theme={theme} />
          <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, transparent 35%, rgba(0,0,0,0.8))', pointerEvents: 'none' }} />
          <div style={{ position: 'absolute', bottom: 12, left: 14, right: 14, pointerEvents: 'none' }}>
            <span style={{
              fontSize: 10, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase',
              color: '#fff', fontFamily: 'Inter, sans-serif', opacity: 0.9,
            }}>
              {calendarItemKindLabel(current)}
            </span>
            <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 26, color: '#fff', letterSpacing: 0.5, lineHeight: 1.1, marginTop: 4 }}>
              {current.name}
            </div>
          </div>
        </button>
        <p style={{ fontSize: 13, color: theme.textDim, margin: '12px 0 0', fontFamily: 'Inter, sans-serif' }}>
          {current.dateLabel || current.time || ''}
          {extra > 0 && tab >= 2 ? ` · +${extra} más en este día (solo se muestran 3 en el calendario)` : ''}
        </p>
        {current.description && (
          <p style={{ fontSize: 13, color: theme.textDim, marginTop: 8, lineHeight: 1.45, fontFamily: 'Inter, sans-serif' }}>
            {current.description}
          </p>
        )}
        <div style={{ display: 'flex', gap: 10, marginTop: 18, flexWrap: 'wrap' }}>
          {current.itemKind === 'birthday' && (
            <Btn theme={theme} icon="cake" onClick={() => { closeModal(); onBirthdayClick?.(current); }}>
              Ver felicitación
            </Btn>
          )}
          {(current.itemKind === 'event' || current.itemKind === 'anuncio') && (
            <Btn theme={theme} icon="chev" onClick={() => onOpenItem?.(current)}>
              {current.itemKind === 'anuncio' ? 'Ver aviso completo' : 'Ver evento'}
            </Btn>
          )}
          <Btn theme={theme} kind="ghost" onClick={closeModal}>Cerrar</Btn>
        </div>
      </div>
    </Modal>
  );
};

const CalendarioSection = ({
  theme,
  onEventClick,
  onAnnouncementClick,
  onCalendarFocus,
  onBirthdayClick,
  highlightDayKey,
  highlightColor,
  variant = 'landing',
}) => {
  const isMobile = useIsMobile();
  const { data: eventRows, loading: loadingEvents, reload: reloadEvents } = useLiveData(() => fetchPublicEvents(), []);
  const { rows: annRows, loading: loadingAnn, error: annError, reload: reloadAnn } = useAnnouncementsFeed();
  const todayKey = formatEventDayKey(new Date());
  const [viewYear, setViewYear] = React.useState(() => new Date().getFullYear());
  const [viewMonth, setViewMonth] = React.useState(() => new Date().getMonth());
  const [bdayRows, setBdayRows] = React.useState([]);
  const [loadingBdays, setLoadingBdays] = React.useState(false);
  const [filter, setFilter] = React.useState('todos');
  const [dayModal, setDayModal] = React.useState(null);

  React.useEffect(() => {
    const on = () => { reloadEvents(); reloadAnn(); };
    window.addEventListener('tecos:announcements-changed', on);
    return () => window.removeEventListener('tecos:announcements-changed', on);
  }, [reloadEvents, reloadAnn]);

  React.useEffect(() => {
    let cancelled = false;
    if (typeof fetchCalendarBirthdays !== 'function') {
      setBdayRows([]);
      return undefined;
    }
    setLoadingBdays(true);
    fetchCalendarBirthdays(viewYear, viewMonth)
      .then((rows) => { if (!cancelled) setBdayRows(rows || []); })
      .catch(() => { if (!cancelled) setBdayRows([]); })
      .finally(() => { if (!cancelled) setLoadingBdays(false); });
    return () => { cancelled = true; };
  }, [viewYear, viewMonth]);

  const loading = loadingEvents || loadingAnn || loadingBdays;
  const eventos = (eventRows || []).map(e => mapEventCalendar(e, theme));
  const avisos = (annRows || []).map(a => mapAnnouncementCalendar(a, theme)).filter(Boolean);
  const cumpleanos = (bdayRows || []).map(b => mapBirthdayCalendar(b, theme)).filter(Boolean);
  const calItems = React.useMemo(
    () => [...eventos, ...avisos, ...cumpleanos],
    [eventRows, annRows, bdayRows, theme]
  );
  const types = ['todos', 'avisos', 'torneos', 'cumpleaños', 'reuniones', 'pagos', 'entrenamientos'];
  const typeMap = { torneos: 'torneo', cumpleaños: 'cumpleaños', reuniones: 'reunión', pagos: 'pago', entrenamientos: 'entrenamiento' };
  const filtered = React.useMemo(() => {
    if (filter === 'todos') return calItems;
    if (filter === 'avisos') return avisos;
    if (filter === 'cumpleaños') {
      return calItems.filter(e => e.itemKind === 'birthday' || e.type === 'cumpleaños' || e.type === 'cumpleanos');
    }
    return eventos.filter(e => e.type === typeMap[filter]);
  }, [filter, calItems, avisos, eventos, cumpleanos]);

  React.useEffect(() => {
    if (!highlightDayKey) return;
    const [y, m] = highlightDayKey.split('-').map(Number);
    if (y && m) {
      setViewYear(y);
      setViewMonth(m - 1);
    }
  }, [highlightDayKey]);

  const itemsOnDay = (dayKey) => calItems.filter(e => e.dayKeys?.includes(dayKey));
  const cells = buildMonthCells(viewYear, viewMonth);
  const prevMonth = () => {
    if (viewMonth === 0) { setViewMonth(11); setViewYear(y => y - 1); }
    else setViewMonth(m => m - 1);
  };
  const nextMonth = () => {
    if (viewMonth === 11) { setViewMonth(0); setViewYear(y => y + 1); }
    else setViewMonth(m => m + 1);
  };

  const openCalendarItem = (item) => {
    if (!item) return;
    if (item.itemKind === 'birthday') {
      if (onBirthdayClick) onBirthdayClick(item);
      else if (onCalendarFocus) onCalendarFocus(item);
      else return;
    }
    if (item.itemKind === 'anuncio') onAnnouncementClick?.(item._raw || item);
    else onEventClick?.(item._raw || item);
  };

  const openDayModal = (dayKey, tab = 0) => {
    const list = itemsOnDay(dayKey);
    if (!list.length) return;
    setDayModal({ dayKey, items: list, tab: Math.min(tab, list.length - 1) });
  };

  /** Clic en miniatura del día o en imagen de la lista → modal del ítem. */
  const activateCalendarItem = (dayKey, dayItems, index) => {
    const item = dayItems?.[index];
    if (!item) return;
    if (item.itemKind === 'birthday') {
      if (onBirthdayClick) onBirthdayClick(item);
      else openDayModal(dayKey, index);
      return;
    }
    if (dayItems.length > 2) {
      openDayModal(dayKey, index);
      return;
    }
    openCalendarItem(item);
  };

  const handleDayCellClick = (dayKey, dayItems) => {
    if (!dayItems.length) return;
    if (dayItems.length > 2) openDayModal(dayKey, 0);
    else activateCalendarItem(dayKey, dayItems, 0);
  };

  const handleDayThumbClick = (e, dayKey, dayItems, index) => {
    e.stopPropagation();
    activateCalendarItem(dayKey, dayItems, index);
  };

  const handleDayModalOpenItem = (item) => {
    setDayModal(null);
    openCalendarItem(item);
  };

  const sectionPad = variant === 'portal' ? '32px' : '100px 40px';

  return (
    <section
      className={variant === 'landing' ? 'tecos-landing-section' : undefined}
      style={{ padding: sectionPad, background: theme.bg, position: 'relative' }}
    >
      <div style={{ maxWidth: 1280, margin: '0 auto' }}>
        <SectionTitle theme={theme} kicker="Calendario"
          title={variant === 'portal' ? 'EVENTOS Y AVISOS' : 'EVENTOS, AVISOS Y CUMPLEAÑOS'}
          sub={variant === 'portal'
            ? 'Mismo calendario que la página pública: fotos en cada día, avisos, eventos y cumpleaños del club.'
            : 'Eventos, avisos y cumpleaños de alumnos activos (según fecha de nacimiento en Admin → Alumnos).'} />

        {/* Filters */}
        <div style={{ display: 'flex', gap: 8, marginBottom: 24, flexWrap: 'wrap' }}>
          {types.map(t => (
            <button key={t} onClick={() => setFilter(t)} style={{
              padding: '8px 16px', borderRadius: 999,
              background: filter === t ? theme.primary : theme.bgInput,
              color: filter === t ? '#fff' : theme.textDim,
              border: `1px solid ${filter === t ? theme.primary : theme.border}`,
              fontSize: 12, fontWeight: 600, letterSpacing: 0.5,
              textTransform: 'uppercase', cursor: 'pointer',
              fontFamily: 'Inter, sans-serif', transition: 'all .2s',
            }}>{t}</button>
          ))}
        </div>

        <div
          className="tecos-landing-stack-grid"
          style={{
            display: 'grid',
            gridTemplateColumns: isMobile ? '1fr' : '1.4fr 1fr',
            gap: isMobile ? 20 : 24,
          }}
        >
          {/* Calendar */}
          <Card theme={theme} style={{ padding: isMobile ? 16 : 24 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
              <h3 style={{
                fontFamily: 'Bebas Neue, sans-serif', fontSize: 32, letterSpacing: 1.5,
                color: theme.text, margin: 0, fontWeight: 400,
              }}>{monthLabel(viewYear, viewMonth)}</h3>
              <div style={{ display: 'flex', gap: 8 }}>
                <button type="button" style={navBtn(theme)} onClick={prevMonth} aria-label="Mes anterior"><Icon name="arrowLeft" size={16} /></button>
                <button type="button" style={navBtn(theme)} onClick={nextMonth} aria-label="Mes siguiente"><Icon name="arrowRight" size={16} /></button>
              </div>
            </div>
            {/* Day headers */}
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 6, marginBottom: 8 }}>
              {['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'].map(d => (
                <div key={d} style={{
                  textAlign: 'center', fontSize: 11, letterSpacing: 1,
                  color: theme.textMute, fontWeight: 700, textTransform: 'uppercase',
                  fontFamily: 'Inter, sans-serif', padding: 6,
                }}>{d}</div>
              ))}
            </div>
            {/* Cells */}
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 6 }}>
              {cells.map((cell, i) => {
                if (!cell) return <div key={i} />;
                const { day, dayKey } = cell;
                const dayItems = itemsOnDay(dayKey);
                const hasItems = dayItems.length > 0;
                const neon = highlightDayKey === dayKey;
                const glowColor = neon ? (highlightColor || dayItems[0]?.color || theme.primary) : null;
                const isToday = dayKey === todayKey;
                const layout = hasItems ? calendarDayGridLayout(dayItems.length) : null;
                const visibleItems = hasItems ? dayItems.slice(0, layout.visible) : [];
                const overflowCount = hasItems ? Math.max(0, dayItems.length - layout.visible) : 0;
                return (
                  <div
                    key={dayKey}
                    role={hasItems ? 'button' : undefined}
                    tabIndex={hasItems ? 0 : undefined}
                    onClick={() => hasItems && handleDayCellClick(dayKey, dayItems)}
                    onKeyDown={e => {
                      if (hasItems && (e.key === 'Enter' || e.key === ' ')) {
                        e.preventDefault();
                        handleDayCellClick(dayKey, dayItems);
                      }
                    }}
                    style={{
                      aspectRatio: '1', borderRadius: 10,
                      background: neon ? `${glowColor}33` : (hasItems ? `${dayItems[0].color}12` : theme.bgInput),
                      border: `1px solid ${neon ? glowColor : (hasItems ? dayItems[0].color : theme.border)}`,
                      padding: 4, position: 'relative', overflow: 'hidden',
                      cursor: hasItems ? 'pointer' : 'default',
                      display: 'flex', flexDirection: 'column',
                      transition: 'all .25s',
                      boxShadow: neon ? `0 0 28px ${glowColor}88, inset 0 0 24px ${glowColor}33` : 'none',
                      animation: neon ? 'pulseGlow 2s ease-in-out infinite' : 'none',
                    }}
                  >
                    <span style={{
                      position: 'absolute', top: 4, left: 6, zIndex: 2,
                      fontSize: 11, fontWeight: isToday ? 800 : 600, lineHeight: 1,
                      color: isToday ? theme.primary : (neon ? glowColor : theme.text),
                      fontFamily: 'Inter, sans-serif',
                      textShadow: hasItems ? '0 1px 4px rgba(0,0,0,0.85)' : 'none',
                    }}>{day}</span>
                    {hasItems && (
                      <div style={{
                        flex: 1, marginTop: 16, minHeight: 0,
                        display: 'grid',
                        gridTemplateColumns: `repeat(${layout.cols}, 1fr)`,
                        gap: 2,
                        borderRadius: 6,
                        overflow: 'hidden',
                      }}>
                        {visibleItems.map((it, idx) => (
                          <button
                            key={`${it.itemKind}-${it.id}-${idx}`}
                            type="button"
                            title={it.name}
                            aria-label={it.name}
                            onClick={(e) => handleDayThumbClick(e, dayKey, dayItems, idx)}
                            style={{
                              minHeight: 0, height: '100%', width: '100%',
                              padding: 0, margin: 0, border: `1px solid ${it.color}44`,
                              borderRadius: 4, overflow: 'hidden',
                              cursor: 'pointer', background: 'transparent',
                              display: 'block',
                            }}
                          >
                            <CalendarDayMedia item={it} theme={theme} />
                          </button>
                        ))}
                      </div>
                    )}
                    {overflowCount > 0 && (
                      <span style={{
                        position: 'absolute', bottom: 3, right: 4, zIndex: 3,
                        fontSize: 9, fontWeight: 800, padding: '2px 5px', borderRadius: 4,
                        background: 'rgba(0,0,0,0.75)', color: '#fff', fontFamily: 'Inter, sans-serif',
                      }}>+{overflowCount}</span>
                    )}
                  </div>
                );
              })}
            </div>
          </Card>

          <div style={{ display: 'grid', gap: 10, alignContent: 'start' }}>
            {loading ? (
              <LoadingBlock theme={theme} label="Cargando calendario…" />
            ) : filtered.length === 0 ? (
              <EmptyState theme={theme} title="Sin eventos en este filtro" message="Agrega eventos, avisos o fecha de nacimiento en Admin → Alumnos para ver cumpleaños." icon="cal" />
            ) : filtered.map((ev) => (
              <Card key={`${ev.itemKind || 'event'}-${ev.id || ev.name}`} theme={theme} hover
                style={{
                  padding: 14, display: 'flex', gap: 14, alignItems: 'center',
                  ...(ev.itemKind === 'anuncio' ? {
                    backgroundImage: `repeating-linear-gradient(-45deg, ${ev.color}11, ${ev.color}11 2px, transparent 2px, transparent 10px)`,
                  } : {}),
                }}>
                <button
                  type="button"
                  title={ev.itemKind === 'birthday' ? 'Ver felicitación' : 'Ver detalle'}
                  aria-label={ev.name}
                  onClick={(e) => { e.stopPropagation(); openCalendarItem(ev); }}
                  style={{
                    width: 56, height: 56, borderRadius: 12, overflow: 'hidden', flexShrink: 0,
                    padding: 0, border: `1px solid ${ev.color}55`, cursor: 'pointer',
                    background: ev.imageUrl ? '#000' : `${ev.color}22`,
                    display: 'block',
                  }}
                >
                  {ev.imageUrl ? (
                    <img src={ev.imageUrl} alt="" draggable={false} style={{
                      width: '100%', height: '100%', objectFit: 'cover', display: 'block',
                      objectPosition: `${ev.imageFocusX}% ${ev.imageFocusY}%`,
                      pointerEvents: 'none',
                    }} />
                  ) : (
                    <div style={{ width: '100%', height: '100%', display: 'grid', placeItems: 'center' }}>
                      <div style={{ textAlign: 'center', lineHeight: 1 }}>
                        {ev.itemKind === 'anuncio' ? (
                          <Icon name="megaphone" size={22} style={{ color: ev.color }} />
                        ) : ev.itemKind === 'birthday' ? (
                          <Icon name="cake" size={26} style={{ color: ev.color }} />
                        ) : (
                          <>
                            <div style={{ fontSize: 9, color: ev.color, fontWeight: 700, letterSpacing: 1, fontFamily: 'Inter, sans-serif' }}>{ev.monthAbbr || '—'}</div>
                            <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 26, color: ev.color, marginTop: 2 }}>{ev.day}</div>
                          </>
                        )}
                      </div>
                    </div>
                  )}
                </button>
                <div
                  role="button"
                  tabIndex={0}
                  onClick={() => openCalendarItem(ev)}
                  onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openCalendarItem(ev); } }}
                  style={{ flex: 1, minWidth: 0, cursor: 'pointer' }}
                >
                  <div style={{ fontSize: 14, fontWeight: 600, color: theme.text, fontFamily: 'Inter, sans-serif' }}>{ev.name}</div>
                  <div style={{ fontSize: 12, color: theme.textDim, marginTop: 4, display: 'flex', gap: 10, fontFamily: 'Inter, sans-serif', flexWrap: 'wrap' }}>
                    <span>{ev.dateLabel || ev.time}</span>
                    <span>·</span>
                    <span style={{ textTransform: 'capitalize', color: ev.color }}>
                      {ev.itemKind === 'anuncio' ? 'Aviso' : ev.itemKind === 'birthday' ? 'Cumpleaños' : ev.type}
                    </span>
                  </div>
                </div>
                <Icon name="chev" size={16} style={{ color: theme.textMute, flexShrink: 0 }} />
              </Card>
            ))}
          </div>
        </div>
      </div>
      <CalendarDayModal
        open={!!dayModal}
        onClose={() => setDayModal(null)}
        items={dayModal?.items || []}
        tab={dayModal?.tab ?? 0}
        onTabChange={(i) => setDayModal((prev) => (prev ? { ...prev, tab: i } : null))}
        theme={theme}
        onOpenItem={handleDayModalOpenItem}
        onBirthdayClick={onBirthdayClick}
      />
    </section>
  );
};

const navBtn = (theme) => ({
  width: 36, height: 36, borderRadius: 8,
  background: theme.bgInput, border: `1px solid ${theme.border}`,
  color: theme.text, cursor: 'pointer',
  display: 'grid', placeItems: 'center',
});

/* ENTRENADORES */
const EntrenadoresSection = ({ theme, onCoachClick }) => {
  const { data: rows, loading, reload } = useLiveData(() => fetchActiveCoaches(), []);
  const coaches = rows.map(mapCoachCard);

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:coaches-changed', onChange);
    return () => window.removeEventListener('tecos:coaches-changed', onChange);
  }, [reload]);

  return (
    <section className="tecos-landing-section" style={{ padding: '80px 40px', background: theme.bgElev }}>
      <div style={{ maxWidth: 1280, margin: '0 auto' }}>
        <SectionTitle theme={theme} kicker="Cuerpo técnico"
          title="ENTRENADORES DE ÉLITE"
          sub="Profesionales certificados con experiencia internacional y formación de campeones." />
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(168px, 1fr))', gap: 14 }}>
          {loading ? (
            <LoadingBlock theme={theme} />
          ) : coaches.length === 0 ? (
            <EmptyState theme={theme} title="Sin entrenadores" message="Agrega entrenadores en Admin → Entrenadores." icon="whistle" />
          ) : coaches.map((c) => (
            <Card key={c.id || c.name} theme={theme} hover style={{ padding: 0, overflow: 'hidden' }}>
              <div style={{
                aspectRatio: '4/3', background: `linear-gradient(180deg, ${theme.primary}28, ${theme.bgInput})`,
                position: 'relative', overflow: 'hidden',
              }}>
                {c.photoUrl ? (
                  <img src={c.photoUrl} alt="" style={{
                    position: 'absolute', inset: 0, width: '100%', height: '100%',
                    objectFit: 'cover',
                    objectPosition: `${c.photoFocusX ?? 50}% ${c.photoFocusY ?? 50}%`,
                  }} />
                ) : (
                  <div style={{ position: 'absolute', inset: 0, display: 'grid', placeItems: 'center' }}>
                    <Avatar name={c.name} size={64} color={theme.primary} />
                  </div>
                )}
                {c.exp && (
                  <div style={{
                    position: 'absolute', top: 8, right: 8,
                    padding: '3px 8px', borderRadius: 999,
                    background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(6px)',
                    fontSize: 9, fontWeight: 700, color: '#fff', letterSpacing: 0.5,
                    fontFamily: 'Inter, sans-serif',
                  }}>{c.exp} EXP.</div>
                )}
                <div style={{
                  position: 'absolute', bottom: 8, left: 8,
                  width: 28, height: 28, borderRadius: 6,
                  background: theme.primary, color: '#fff',
                  display: 'grid', placeItems: 'center',
                }}><Icon name={c.icon} size={14} /></div>
              </div>
              <div style={{ padding: '12px 12px 14px' }}>
                <h4 style={{
                  fontFamily: 'Bebas Neue, sans-serif', fontSize: 18, letterSpacing: 0.5,
                  color: theme.text, margin: 0, fontWeight: 400, lineHeight: 1.1,
                }}>{c.name}</h4>
                <div style={{
                  fontSize: 10, color: theme.primary, marginTop: 2, fontWeight: 700,
                  letterSpacing: 0.8, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
                }}>{c.role}</div>
                {c.spec && (
                  <p style={{
                    fontSize: 11, color: theme.textDim, margin: '6px 0 10px', fontFamily: 'Inter, sans-serif',
                    lineHeight: 1.35, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden',
                  }}>{c.spec}</p>
                )}
                <Btn theme={theme} kind="soft" size="sm" icon="eye" onClick={() => onCoachClick(c)}>Ver perfil</Btn>
              </div>
            </Card>
          ))}
        </div>
      </div>
    </section>
  );
};

/* GALERÍA — álbumes compactos */
const GalleryAlbumModal = ({ open, onClose, theme, album }) => {
  const [idx, setIdx] = React.useState(0);
  React.useEffect(() => { if (open) setIdx(0); }, [open, album?.id]);
  if (!album) return null;
  const photos = album.photos?.length ? album.photos : (album.coverUrl ? [{ url: album.coverUrl }] : []);
  const cur = photos[idx] || photos[0];
  const prev = () => setIdx(i => (i <= 0 ? photos.length - 1 : i - 1));
  const next = () => setIdx(i => (i >= photos.length - 1 ? 0 : i + 1));

  return (
    <Modal open={open} onClose={onClose} theme={theme} title={album.title} width={720}>
      <div style={{ padding: 24 }}>
        {(album.dateLabel || album.eventTitle) && (
          <div style={{ fontSize: 12, color: theme.primary, fontWeight: 700, marginBottom: 12, fontFamily: 'Inter, sans-serif' }}>
            {album.dateLabel}{album.eventTitle ? ` · ${album.eventTitle}` : ''}
          </div>
        )}
        {cur?.url ? (
          <img src={cur.url} alt="" style={{
            width: '100%', aspectRatio: '4/3', maxHeight: 420, objectFit: 'cover',
            objectPosition: `${album.coverFocusX ?? 50}% ${album.coverFocusY ?? 50}%`,
            borderRadius: 12, background: theme.bgInput,
          }} />
        ) : (
          <PlaceholderImage icon="image" label={album.title} theme={theme} accent={theme.primary} ratio="16/9" />
        )}
        {photos.length > 1 && (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12, marginTop: 16 }}>
            <Btn theme={theme} kind="ghost" size="sm" onClick={prev}>‹</Btn>
            <span style={{ fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>{idx + 1} / {photos.length}</span>
            <Btn theme={theme} kind="ghost" size="sm" onClick={next}>›</Btn>
          </div>
        )}
        {photos.length > 1 && (
          <div style={{ display: 'flex', gap: 6, marginTop: 12, overflowX: 'auto', paddingBottom: 4 }}>
            {photos.map((p, i) => (
              <button key={p.id || i} type="button" onClick={() => setIdx(i)}
                style={{
                  flex: '0 0 56px', width: 56, height: 56, padding: 0, borderRadius: 8, overflow: 'hidden',
                  border: `2px solid ${i === idx ? theme.primary : theme.border}`, cursor: 'pointer', background: 'none',
                }}>
                <img src={p.url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
              </button>
            ))}
          </div>
        )}
      </div>
    </Modal>
  );
};

const GaleriaSection = ({ theme }) => {
  const [cat, setCat] = React.useState('todas');
  const [viewAlbum, setViewAlbum] = React.useState(null);
  const { data: albums, loading, reload } = useLiveData(() => fetchPublicGalleryAlbums(), []);
  const { data: categories } = useLiveData(() => fetchPublicGalleryCategories(), []);

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:gallery-changed', onChange);
    return () => window.removeEventListener('tecos:gallery-changed', onChange);
  }, [reload]);

  const catFilters = React.useMemo(() => {
    const fromDb = (categories || []).map(c => ({ slug: c.slug, label: c.name }));
    const slugs = new Set(fromDb.map(c => c.slug));
    (albums || []).forEach(a => {
      if (a.categorySlug && !slugs.has(a.categorySlug)) {
        fromDb.push({ slug: a.categorySlug, label: a.category });
        slugs.add(a.categorySlug);
      }
    });
    return [{ slug: 'todas', label: 'Todas' }, ...fromDb];
  }, [categories, albums]);

  const filtered = cat === 'todas'
    ? albums
    : (albums || []).filter(a => a.categorySlug === cat);

  return (
    <section className="tecos-landing-section" style={{ padding: '80px 40px', background: theme.bg }}>
      <div style={{ maxWidth: 1280, margin: '0 auto' }}>
        <SectionTitle theme={theme} kicker="Galería"
          title="MOMENTOS QUE INSPIRAN"
          sub="Álbumes de entrenamientos, torneos, eventos e instalaciones." />
        <div style={{ display: 'flex', gap: 8, marginBottom: 20, flexWrap: 'wrap' }}>
          {catFilters.map(t => (
            <button key={t.slug} onClick={() => setCat(t.slug)} style={{
              padding: '8px 14px', borderRadius: 999,
              background: cat === t.slug ? theme.primary : theme.bgInput,
              color: cat === t.slug ? '#fff' : theme.textDim,
              border: `1px solid ${cat === t.slug ? theme.primary : theme.border}`,
              fontSize: 11, fontWeight: 600, letterSpacing: 0.4,
              textTransform: 'uppercase', cursor: 'pointer',
              fontFamily: 'Inter, sans-serif',
            }}>{t.label}</button>
          ))}
        </div>
        {loading ? (
          <LoadingBlock theme={theme} label="Cargando galería…" />
        ) : filtered.length === 0 ? (
          <EmptyState theme={theme} title="Sin álbumes" message="Crea álbumes en Admin → Galería." icon="image" />
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(168px, 1fr))', gap: 14 }}>
            {filtered.map((alb) => (
              <Card key={alb.id} theme={theme} hover style={{ padding: 0, overflow: 'hidden', cursor: 'pointer' }}
                onClick={() => setViewAlbum(alb)}>
                <div style={{ aspectRatio: '4/3', position: 'relative', overflow: 'hidden', background: theme.bgInput }}>
                  {alb.coverUrl ? (
                    <img src={alb.coverUrl} alt="" style={{
                      width: '100%', height: '100%', objectFit: 'cover',
                      objectPosition: `${alb.coverFocusX ?? 50}% ${alb.coverFocusY ?? 50}%`,
                    }} />
                  ) : (
                    <PlaceholderImage icon="image" label={alb.title} theme={theme} accent={theme.primary} ratio="4/3" />
                  )}
                  {alb.photoCount > 1 && (
                    <div style={{
                      position: 'absolute', top: 8, right: 8, padding: '3px 8px', borderRadius: 999,
                      background: 'rgba(0,0,0,0.55)', fontSize: 9, fontWeight: 700, color: '#fff',
                      fontFamily: 'Inter, sans-serif',
                    }}>{alb.photoCount} fotos</div>
                  )}
                </div>
                <div style={{ padding: '10px 12px 12px' }}>
                  <h4 style={{
                    fontFamily: 'Bebas Neue, sans-serif', fontSize: 17, letterSpacing: 0.5,
                    color: theme.text, margin: 0, fontWeight: 400, lineHeight: 1.1,
                  }}>{alb.title}</h4>
                  <div style={{
                    fontSize: 10, color: theme.primary, marginTop: 4, fontWeight: 600,
                    fontFamily: 'Inter, sans-serif', lineHeight: 1.35,
                  }}>
                    {alb.eventTitle || alb.dateLabel || alb.category}
                  </div>
                </div>
              </Card>
            ))}
          </div>
        )}
      </div>
      <GalleryAlbumModal open={!!viewAlbum} onClose={() => setViewAlbum(null)} theme={theme} album={viewAlbum} />
    </section>
  );
};

/* MAPA / UBICACIÓN — datos desde academy_settings (admin → Ubicación) */
const MapaSection = ({ theme }) => {
  const isMobile = useIsMobile();
  const { settings, loading, reload } = typeof useAcademySettings === 'function'
    ? useAcademySettings()
    : { settings: normalizeAcademySettings(null), loading: true, reload: () => {} };
  const loc = settings;

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:settings-changed', onChange);
    return () => window.removeEventListener('tecos:settings-changed', onChange);
  }, [reload]);

  const addressHtml = formatLocationAddress(loc).split('\n').map((line, i) => (
    <React.Fragment key={i}>{i > 0 && <br />}{line}</React.Fragment>
  ));
  const mapsUrl = buildGoogleMapsOpenUrl(loc);

  const mapHeight = isMobile ? 280 : 420;

  return (
    <section className="tecos-landing-section" style={{
      padding: isMobile ? '48px 16px' : '100px 40px',
      background: theme.bgElev,
      scrollMarginTop: 80,
    }}>
      <div style={{ maxWidth: 1280, margin: '0 auto' }}>
        <SectionTitle theme={theme}
          kicker={loc.location_kicker}
          title={loc.location_title}
          sub={loc.location_subtitle} />
        <div
          className="tecos-landing-stack-grid"
          style={{
            display: 'grid',
            gridTemplateColumns: isMobile ? '1fr' : 'minmax(0, 1.6fr) minmax(0, 1fr)',
            gap: isMobile ? 16 : 20,
          }}
        >
          {loading ? (
            <Card theme={theme} style={{ minHeight: mapHeight, display: 'grid', placeItems: 'center' }}>
              <span style={{ color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>Cargando mapa…</span>
            </Card>
          ) : (
            <AcademyLocationMap theme={theme} location={loc} height={mapHeight} />
          )}
          <Card theme={theme}>
            <h3 style={{
              fontFamily: 'Bebas Neue, sans-serif', fontSize: 28, letterSpacing: 1,
              color: theme.text, margin: 0, fontWeight: 400,
            }}>{loc.venue_name}</h3>
            <div style={{ height: 1, background: theme.border, margin: '14px 0' }} />
            <div style={{ display: 'grid', gap: 16, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>
              <div>
                <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1.5, textTransform: 'uppercase', marginBottom: 4 }}>Dirección</div>
                <div style={{ color: theme.text }}>{addressHtml}</div>
              </div>
              <div>
                <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1.5, textTransform: 'uppercase', marginBottom: 4 }}>Horarios</div>
                <div style={{ color: theme.text }}>
                  {loc.schedule_weekdays}{loc.schedule_saturday ? <><br />{loc.schedule_saturday}</> : null}
                </div>
              </div>
              <div>
                <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1.5, textTransform: 'uppercase', marginBottom: 4 }}>Contacto</div>
                <div style={{ color: theme.text }}>
                  {loc.contact_phone}{loc.contact_email ? <><br />{loc.contact_email}</> : null}
                </div>
              </div>
              <a href={mapsUrl} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none' }}>
                <Btn theme={theme} icon="pin" size="md" style={{ width: '100%' }}>Abrir en Google Maps</Btn>
              </a>
            </div>
          </Card>
        </div>
        {!getGoogleMapsApiKey() && (
          <p style={{ marginTop: 14, fontSize: 12, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>
            Tip: agrega googleMapsApiKey en config.js para mapa interactivo con estilo neón.
          </p>
        )}
      </div>
    </section>
  );
};

/* CONTACTO / FORM INTERESADOS */
const EMPTY_INTEREST_FORM = { name: '', studentName: '', age: '', phone: '', email: '', message: '' };

const InterestedLeadFormContent = ({ theme, source = 'landing', onClose, columns = 2 }) => {
  const [sent, setSent] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [form, setForm] = React.useState({ ...EMPTY_INTEREST_FORM });
  const [phoneDial, setPhoneDial] = React.useState(DEFAULT_PHONE_DIAL);
  const [phoneNational, setPhoneNational] = React.useState('');

  const reset = () => {
    setForm({ ...EMPTY_INTEREST_FORM });
    setPhoneDial(DEFAULT_PHONE_DIAL);
    setPhoneNational('');
    setErr('');
    setSent(false);
  };

  const submit = async () => {
    if (!form.name.trim()) { setErr('Escribe tu nombre (tutor o responsable).'); return; }
    const phone = combinePhoneWithDial(phoneDial, phoneNational);
    if (!phone.trim()) { setErr('Escribe tu teléfono.'); return; }
    if (!isSupabaseReady()) { setErr('El formulario no está conectado a la base de datos. Contacta a la academia.'); return; }
    setBusy(true);
    setErr('');
    try {
      if (typeof window.submitInterestedLead !== 'function') {
        throw new Error('El envío no está disponible. Recarga la página (F5).');
      }
      await window.submitInterestedLead({
        name: form.name.trim(),
        phone: phone.trim(),
        email: form.email.trim(),
        message: form.message.trim(),
        student_name: form.studentName.trim(),
        student_age: form.age.trim(),
        source,
      });
      window.notifyLeadsChanged?.();
      window.dispatchNotificationsChanged?.();
      setSent(true);
      setForm({ ...EMPTY_INTEREST_FORM });
      setPhoneDial(DEFAULT_PHONE_DIAL);
      setPhoneNational('');
    } catch (e) { setErr(e.message || 'No se pudo enviar. Intenta de nuevo.'); }
    setBusy(false);
  };

  if (sent) {
    return (
      <div style={{ textAlign: 'center', padding: columns === 1 ? 24 : 40 }}>
        <div style={{
          width: 80, height: 80, borderRadius: '50%', margin: '0 auto 20px',
          background: `${theme.success}22`, color: theme.success,
          display: 'grid', placeItems: 'center', animation: 'pop .4s cubic-bezier(.2,.9,.3,1.4)',
        }}><Icon name="check" size={40} /></div>
        <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 36, color: theme.text, margin: 0, letterSpacing: 1 }}>¡SOLICITUD ENVIADA!</h3>
        <p style={{ color: theme.textDim, marginTop: 10, fontFamily: 'Inter, sans-serif' }}>Te contactaremos pronto al teléfono o correo que registraste.</p>
        <div style={{ marginTop: 20, display: 'flex', gap: 10, justifyContent: 'center', flexWrap: 'wrap' }}>
          {onClose && <Btn theme={theme} onClick={onClose}>Cerrar</Btn>}
          <Btn theme={theme} onClick={reset} kind="ghost">Enviar otra solicitud</Btn>
        </div>
      </div>
    );
  }

  return (
    <div
      className={columns === 1 ? 'tecos-lead-form-grid tecos-lead-form-grid--single' : 'tecos-lead-form-grid'}
      style={{ display: 'grid', gridTemplateColumns: columns === 1 ? '1fr' : '1fr 1fr', gap: 16 }}
    >
      <Input theme={theme} label="Tu nombre" placeholder="Nombre del padre/tutor" icon="user" value={form.name} onChange={e => setForm(f => ({ ...f, name: e.target.value }))} />
      <Input theme={theme} label="Nombre del alumno" placeholder="Nombre completo del alumno" icon="user" value={form.studentName || ''} onChange={e => setForm(f => ({ ...f, studentName: e.target.value }))} />
      <Input theme={theme} label="Edad" placeholder="Edad del alumno" value={form.age || ''} onChange={e => setForm(f => ({ ...f, age: e.target.value }))} />
      <PhoneInput theme={theme} label="Teléfono" icon="wa"
        dial={phoneDial} national={phoneNational}
        onDialChange={setPhoneDial} onNationalChange={setPhoneNational} />
      <Input theme={theme} label="Correo" placeholder="correo@ejemplo.com" value={form.email} onChange={e => setForm(f => ({ ...f, email: e.target.value }))} />
      <div style={{ gridColumn: '1 / -1', display: 'grid', gap: 6 }}>
        <label style={{ fontSize: 11, fontWeight: 600, letterSpacing: 0.6, color: theme.textDim, textTransform: 'uppercase' }}>Mensaje</label>
        <textarea placeholder="Cuéntanos sobre tu interés..." rows={4} value={form.message} onChange={e => setForm(f => ({ ...f, message: e.target.value }))} style={{
          background: theme.bgInput, border: `1px solid ${theme.border}`,
          borderRadius: 10, padding: 12, color: theme.text, fontFamily: 'Inter, sans-serif',
          fontSize: 14, outline: 'none', resize: 'vertical',
        }} />
      </div>
      {err && <div style={{ gridColumn: '1 / -1', color: theme.danger, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>{err}</div>}
      <div style={{ gridColumn: '1 / -1', display: 'flex', gap: 12, marginTop: 6, flexWrap: 'wrap' }}>
        <Btn theme={theme} size="lg" icon="send" onClick={submit} disabled={busy}>{busy ? 'Enviando…' : 'Enviar solicitud'}</Btn>
        <Btn theme={theme} size="lg" kind="ghost" onClick={onClose || reset} disabled={busy}>{onClose ? 'Cancelar' : 'Limpiar'}</Btn>
      </div>
    </div>
  );
};

const SolicitaInformacionModal = ({ open, onClose, theme }) => (
  <Modal open={open} onClose={onClose} theme={theme} width={640} innerScroll>
    <div style={{
      padding: '18px 22px', borderBottom: `1px solid ${theme.border}`,
      display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, flexShrink: 0,
    }}>
      <div>
        <div style={{ fontSize: 11, color: theme.textMute, fontWeight: 700, letterSpacing: 1.2, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>
          ¿Te interesa entrenar con nosotros?
        </div>
        <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 28, color: theme.text, margin: '4px 0 0', letterSpacing: 1, fontWeight: 400 }}>
          SOLICITA INFORMACIÓN
        </h3>
        <p style={{ margin: '6px 0 0', fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.45 }}>
          Déjanos tus datos y un asesor te contactará en menos de 24 horas.
        </p>
      </div>
      <button type="button" onClick={onClose} style={{
        width: 36, height: 36, borderRadius: 8, background: theme.bgInput,
        border: `1px solid ${theme.border}`, color: theme.text, cursor: 'pointer',
        display: 'grid', placeItems: 'center', flexShrink: 0,
      }}><Icon name="x" size={16} /></button>
    </div>
    <div style={{ padding: 24, overflowY: 'auto', flex: 1, minHeight: 0 }}>
      {open && (
        <InterestedLeadFormContent key="login-interest-form" theme={theme} source="login" onClose={onClose} columns={1} />
      )}
    </div>
  </Modal>
);

const FormSection = ({ theme }) => {
  const isMobile = useIsMobile();
  return (
    <section className="tecos-landing-section" style={{
      padding: isMobile ? '48px 16px' : '100px 40px',
      background: theme.bg,
      position: 'relative',
      overflow: 'hidden',
      scrollMarginTop: 88,
    }}>
      {!isMobile && (
        <div style={{ position: 'absolute', top: -100, right: -100, opacity: 0.08 }}>
          <Volleyball size={500} color={theme.primary} />
        </div>
      )}
      <div style={{ maxWidth: 1100, margin: '0 auto', position: 'relative' }}>
        <SectionTitle theme={theme} kicker="¿Te interesa entrenar con nosotros?"
          title="SOLICITA INFORMACIÓN"
          sub="Déjanos tus datos y un asesor te contactará en menos de 24 horas." />
        <Card theme={theme} style={{ padding: isMobile ? 20 : 36 }}>
          <InterestedLeadFormContent theme={theme} source="landing" columns={isMobile ? 1 : 2} />
        </Card>
      </div>
    </section>
  );
};

/* FOOTER */
const footerLinkStyle = (theme) => ({
  background: 'none', border: 'none', padding: 0, margin: 0,
  color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif',
  cursor: 'pointer', textAlign: 'left', textDecoration: 'none',
  display: 'inline-block', transition: 'color .2s',
});

const FooterLinkItem = ({ theme, item }) => {
  const style = footerLinkStyle(theme);
  const hover = (e, on) => { e.currentTarget.style.color = on ? theme.primary : theme.textDim; };
  if (item.href) {
    return (
      <li>
        <a href={item.href} style={style} onMouseEnter={e => hover(e, true)} onMouseLeave={e => hover(e, false)}>{item.label}</a>
      </li>
    );
  }
  return (
    <li>
      <button type="button" onClick={item.onClick} style={style} onMouseEnter={e => hover(e, true)} onMouseLeave={e => hover(e, false)}>
        {item.label}
      </button>
    </li>
  );
};

const FooterBottomLine = ({ theme, text, url }) => {
  const style = {
    color: theme.textMute, fontSize: 11, letterSpacing: 1, textTransform: 'uppercase',
    fontFamily: 'Inter, sans-serif', textDecoration: 'none', transition: 'color .2s',
  };
  if (url && String(url).trim()) {
    return (
      <a href={url.trim()} style={style} onMouseEnter={e => { e.currentTarget.style.color = theme.primary; }}
        onMouseLeave={e => { e.currentTarget.style.color = theme.textMute; }}>{text}</a>
    );
  }
  return <span style={style}>{text}</span>;
};

const Footer = ({ theme, onNav, onOpenInscripcion }) => {
  const { settings: s, reload } = typeof useAcademySettings === 'function'
    ? useAcademySettings()
    : { settings: normalizeAcademySettings(null), reload: () => {} };

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:settings-changed', onChange);
    return () => window.removeEventListener('tecos:settings-changed', onChange);
  }, [reload]);

  const academiaLinks = [
    { label: 'Inscripciones', onClick: () => onOpenInscripcion?.() },
    { label: 'Galería', onClick: () => onNav?.('galeria') },
    { label: 'Entrenadores', onClick: () => onNav?.('entrenadores') },
    { label: 'Login', onClick: () => onNav?.('login') },
  ];
  const serviciosLinks = [
    { label: 'Tienda online', href: 'tienda.html' },
    { label: 'Torneos', onClick: () => onNav?.('calendario') },
    { label: 'Campamentos', onClick: () => onNav?.('calendario') },
    { label: 'Eventos', onClick: () => onNav?.('calendario') },
    { label: 'Aviso de privacidad', href: 'aviso-de-privacidad.html' },
  ];

  const contactLines = [
    s.contact_phone ? { value: s.contact_phone, href: `tel:${String(s.contact_phone).replace(/\s/g, '')}` } : null,
    s.contact_email ? { value: s.contact_email, href: `mailto:${s.contact_email}` } : null,
    s.footer_address_line1 ? { value: s.footer_address_line1 } : null,
    s.footer_address_line2 ? { value: s.footer_address_line2 } : null,
  ].filter(Boolean);

  return (
    <footer className="tecos-site-footer" style={{
      background: theme.bgElev, borderTop: `1px solid ${theme.border}`,
      padding: '60px 40px 30px', position: 'relative', overflow: 'hidden',
    }}>
      <div style={{ position: 'absolute', top: 0, left: 0, right: 0, opacity: 0.2 }}>
        <Net color={theme.primary} height={40} />
      </div>
      <div className="tecos-footer-grid" style={{ maxWidth: 1280, margin: '0 auto', display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr 1fr', gap: 40 }}>
        <div>
          <Logo size={72} theme={theme} />
          <p style={{ color: theme.textDim, fontSize: 13, marginTop: 16, fontFamily: 'Inter, sans-serif', maxWidth: 320, lineHeight: 1.6 }}>
            {s.footer_tagline}
          </p>
        </div>
        <div>
          <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, letterSpacing: 1, color: theme.text, marginBottom: 14 }}>Academia</div>
          <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'grid', gap: 8 }}>
            {academiaLinks.map(item => <FooterLinkItem key={item.label} theme={theme} item={item} />)}
          </ul>
        </div>
        <div>
          <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, letterSpacing: 1, color: theme.text, marginBottom: 14 }}>Servicios</div>
          <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'grid', gap: 8 }}>
            {serviciosLinks.map(item => <FooterLinkItem key={item.label} theme={theme} item={item} />)}
          </ul>
        </div>
        <div>
          <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, letterSpacing: 1, color: theme.text, marginBottom: 14 }}>Contacto</div>
          <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'grid', gap: 8 }}>
            {contactLines.map((line, i) => (
              <li key={i}>
                {line.href ? (
                  <a href={line.href} style={footerLinkStyle(theme)}>{line.value}</a>
                ) : (
                  <span style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>{line.value}</span>
                )}
              </li>
            ))}
          </ul>
          <SocialLinksRow theme={theme} settings={s} />
        </div>
      </div>
      <div
        className="tecos-footer-bottom-bar"
        style={{
          maxWidth: 1280, margin: '40px auto 0', paddingTop: 20,
          borderTop: `1px solid ${theme.border}`,
          display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12,
        }}
      >
        <FooterCopyrightBadge theme={theme} text={s.footer_copyright_text} url={s.footer_copyright_url} />
        {isStudioCreditLink(s.footer_credit_text, s.footer_credit_url) ? (
          <FooterStudioCredit text={s.footer_credit_text} url={s.footer_credit_url} />
        ) : (
          <FooterBottomLine theme={theme} text={s.footer_credit_text} url={s.footer_credit_url} />
        )}
      </div>
    </footer>
  );
};

/* MODALS for event & coach */
const EventConfirmModal = ({ open, onClose, theme, eventId, eventColor, onConfirmed }) => {
  const [digits, setDigits] = React.useState('');
  const [pendingStudent, setPendingStudent] = React.useState(null);
  const [studentVerified, setStudentVerified] = React.useState(null);
  const [lookupBusy, setLookupBusy] = React.useState(false);
  const [checkBusy, setCheckBusy] = React.useState(false);
  const [alreadyRegistered, setAlreadyRegistered] = React.useState(false);
  const [registrationDone, setRegistrationDone] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [ok, setOk] = React.useState('');
  const lookupTimerRef = React.useRef(null);
  const glow = eventColor || theme.primary;

  const fullCode = typeof buildTecStudentCode === 'function'
    ? buildTecStudentCode(digits)
    : (digits ? `TEC${digits.padStart(3, '0')}` : '');
  const locked = alreadyRegistered || registrationDone;

  React.useEffect(() => {
    if (!open) return;
    setDigits('');
    setErr('');
    setOk('');
    setPendingStudent(null);
    setStudentVerified(null);
    setLookupBusy(false);
    setCheckBusy(false);
    setAlreadyRegistered(false);
    setRegistrationDone(false);
    if (lookupTimerRef.current) clearTimeout(lookupTimerRef.current);
  }, [open]);

  const verifyAlreadyRegistered = async (codeStr) => {
    if (!eventId || !codeStr || !isSupabaseReady()) return false;
    if (typeof checkEventAttendance !== 'function') return false;
    setCheckBusy(true);
    try {
      const exists = await checkEventAttendance(eventId, codeStr);
      setAlreadyRegistered(!!exists);
      return !!exists;
    } catch {
      return false;
    } finally {
      setCheckBusy(false);
    }
  };

  const lookupStudentByCode = async (rawCode) => {
    const normalized = normalizeTecStudentCode(rawCode);
    if (!normalized) {
      setPendingStudent(null);
      setAlreadyRegistered(false);
      return;
    }
    if (!isSupabaseReady()) return;
    setLookupBusy(true);
    setErr('');
    try {
      const exists = await verifyAlreadyRegistered(normalized);
      if (exists) {
        setPendingStudent(null);
        setStudentVerified(null);
        setLookupBusy(false);
        return;
      }
      const found = await lookupStudentCodePublic(rawCode);
      if (!found) {
        setPendingStudent(null);
        setStudentVerified(null);
        return;
      }
      if (found.status && found.status !== 'activo') {
        setPendingStudent(null);
        setStudentVerified(null);
        setErr('Ese alumno no está activo. Contacta a la academia.');
        return;
      }
      setPendingStudent(found);
      setStudentVerified(null);
    } catch (e) {
      setPendingStudent(null);
      setErr(e.message || 'No se pudo buscar el alumno.');
    }
    setLookupBusy(false);
  };

  const scheduleLookup = (d) => {
    if (lookupTimerRef.current) clearTimeout(lookupTimerRef.current);
    const code = typeof buildTecStudentCode === 'function' ? buildTecStudentCode(d) : '';
    if (!code) {
      setPendingStudent(null);
      setAlreadyRegistered(false);
      return;
    }
    lookupTimerRef.current = setTimeout(() => lookupStudentByCode(code), 450);
  };

  const onDigitsChange = (d) => {
    setDigits(d);
    setStudentVerified(null);
    setPendingStudent(null);
    setOk('');
    setErr('');
    setAlreadyRegistered(false);
    scheduleLookup(d);
  };

  const confirmYes = () => {
    if (!pendingStudent) return;
    setStudentVerified(pendingStudent);
    setDigits(digitsFromTecCode(pendingStudent.code));
    setPendingStudent(null);
  };

  const confirmNo = () => {
    setPendingStudent(null);
    setStudentVerified(null);
  };

  const submit = async () => {
    if (!fullCode) {
      setErr('Escribe el número de tu ID (ej. 001 o 067).');
      return;
    }
    if (locked) return;
    if (pendingStudent && !studentVerified) {
      setErr('Confirma con «Sí» o «No» si el ID te corresponde.');
      return;
    }
    const confirmCode = studentVerified?.code || fullCode;
    if (await verifyAlreadyRegistered(confirmCode)) {
      setErr('Este ID ya confirmó asistencia a este evento.');
      return;
    }
    setSaving(true);
    setErr('');
    setOk('');
    try {
      const data = await confirmEventAttendance(eventId, confirmCode);
      setRegistrationDone(true);
      setAlreadyRegistered(true);
      setOk(`Asistencia confirmada: ${data?.student_name || data?.student_code || confirmCode}`);
      window.dispatchNotificationsChanged?.();
      onConfirmed?.();
    } catch (e) { setErr(e.message || 'No se pudo confirmar'); }
    setSaving(false);
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} title="Confirmar asistencia" width={440}>
      <div style={{ padding: '8px 24px 24px', display: 'grid', gap: 14, fontFamily: 'Inter, sans-serif' }}>
        <p style={{ margin: 0, fontSize: 13, color: theme.textDim }}>
          Escribe solo el número de tu ID (prefijo TEC incluido). Si coincide con un registro, confirma tu identidad.
        </p>
        <TecStudentIdField
          theme={theme}
          label="Número de ID"
          icon="medal"
          digits={digits}
          onDigitsChange={onDigitsChange}
          onBlur={() => fullCode && lookupStudentByCode(fullCode)}
          disabled={locked}
        />
        {(lookupBusy || checkBusy) && (
          <p style={{ margin: 0, fontSize: 12, color: theme.textDim }}>
            {checkBusy ? 'Verificando registro…' : 'Buscando alumno…'}
          </p>
        )}
        {locked && !ok && (
          <div style={{
            padding: 12, borderRadius: 10, background: `${theme.warning}18`,
            border: `1px solid ${theme.warning}44`, fontSize: 13, color: theme.text,
          }}>
            Este ID ya confirmó asistencia a este evento. No se puede volver a enviar con el mismo número.
          </div>
        )}
        {pendingStudent && !studentVerified && !locked && (
          <StudentConfirmNeonBanner
            theme={theme}
            student={pendingStudent}
            glowColor={glow}
            onYes={confirmYes}
            onNo={confirmNo}
            subtitle="Si confirmas, registraremos tu asistencia a este evento."
          />
        )}
        {studentVerified && !locked && (
          <div style={{
            padding: 12, borderRadius: 10,
            background: `${theme.success}18`, border: `1px solid ${theme.success}44`,
            fontSize: 12, color: theme.text,
            boxShadow: `0 0 16px ${theme.success}33`,
          }}>
            Identidad confirmada: <strong>{studentVerified.code}</strong> — {studentVerified.full_name}
          </div>
        )}
        {err && <div style={{ color: theme.danger, fontSize: 13 }}>{err}</div>}
        {ok && <div style={{ color: theme.success, fontSize: 13 }}>{ok}</div>}
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
          <Btn theme={theme} kind="ghost" onClick={onClose}>Cerrar</Btn>
          <Btn theme={theme} icon="check" onClick={submit}
            disabled={saving || !eventId || locked || !fullCode}
            style={studentVerified && !locked ? { boxShadow: `0 0 18px ${glow}66` } : undefined}>
            {saving ? 'Confirmando…' : locked ? 'Ya confirmado' : 'Confirmar asistencia'}
          </Btn>
        </div>
      </div>
    </Modal>
  );
};

const EventModal = ({ open, onClose, ev, theme }) => {
  const closeModal = React.useCallback(bindModalClose(onClose), [onClose]);
  const [confirmOpen, setConfirmOpen] = React.useState(false);
  const display = React.useMemo(() => {
    if (!ev) return null;
    if (ev.title) return mapEventCalendar(ev, theme);
    if (ev._raw) return mapEventCalendar(ev._raw, theme);
    if (ev.name) return ev;
    return null;
  }, [ev, theme, open]);

  const eventId = display?.id || ev?.id;

  return (
    <>
      <Modal open={open} onClose={onClose} theme={theme} width={560}>
        {display && (
          <div>
            <div style={{
              height: 200, position: 'relative', overflow: 'hidden',
              background: display.imageUrl ? '#111' : `linear-gradient(135deg, ${display.color}, ${theme.bgElev})`,
            }}>
              {display.imageUrl ? (
                <img src={display.imageUrl} alt="" style={{
                  position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover',
                  objectPosition: `${display.imageFocusX}% ${display.imageFocusY}%`,
                }} />
              ) : (
                <>
                  <CourtLines color="#fff" opacity={0.2} />
                  <div style={{ position: 'absolute', bottom: -30, right: -30, opacity: 0.4 }}>
                    <Volleyball size={200} color="#fff" />
                  </div>
                </>
              )}
              <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.75))' }} />
              <button type="button" tabIndex={-1} onMouseDown={(e) => e.preventDefault()} onClick={closeModal} style={{
                position: 'absolute', top: 14, right: 14, width: 36, height: 36, borderRadius: 8,
                background: 'rgba(0,0,0,0.4)', border: 'none', color: '#fff',
                cursor: 'pointer', display: 'grid', placeItems: 'center', outline: 'none', boxShadow: 'none',
              }}><Icon name="x" size={18} /></button>
              <div style={{ position: 'absolute', bottom: 16, left: 24, right: 24 }}>
                <Badge theme={theme} color="primary" soft={false} style={{ background: 'rgba(0,0,0,0.4)', color: '#fff', border: 'none' }}>{display.type}</Badge>
                <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 32, letterSpacing: 1, color: '#fff', margin: '6px 0 0', fontWeight: 400 }}>{display.name}</h3>
              </div>
            </div>
            <div style={{ padding: 24 }}>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: 16, marginBottom: 20 }}>
                {[
                  { l: 'Fecha', v: display.dateLabel, i: 'cal' },
                  { l: 'Hora', v: display.time, i: 'clock' },
                  { l: 'Lugar', v: display.location || 'Por definir', i: 'pin' },
                ].map(it => (
                  <div key={it.l}>
                    <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: 6, fontFamily: 'Inter, sans-serif' }}>
                      <Icon name={it.i} size={12} />{it.l}
                    </div>
                    <div style={{ color: theme.text, fontSize: 14, marginTop: 4, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>{it.v}</div>
                  </div>
                ))}
              </div>
              {display.mapsUrl && (
                <p style={{ margin: '0 0 16px' }}>
                  <a href={display.mapsUrl} target="_blank" rel="noopener noreferrer" style={{ color: theme.primary, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>
                    Ver en Google Maps →
                  </a>
                </p>
              )}
              <h4 style={{ fontFamily: 'Inter, sans-serif', fontSize: 11, letterSpacing: 1, color: theme.textMute, textTransform: 'uppercase', margin: '0 0 8px' }}>Descripción</h4>
              <p style={{ color: theme.textDim, fontSize: 14, lineHeight: 1.5, margin: 0, fontFamily: 'Inter, sans-serif', whiteSpace: 'pre-wrap' }}>
                {display.description || 'Sin descripción adicional.'}
              </p>
              <div style={{ display: 'flex', gap: 10, marginTop: 24, flexWrap: 'wrap' }}>
                <Btn theme={theme} icon="check" onClick={() => setConfirmOpen(true)} disabled={!eventId}>Confirmar asistencia</Btn>
                <Btn theme={theme} kind="ghost" onClick={closeModal}>Cerrar</Btn>
              </div>
            </div>
          </div>
        )}
      </Modal>
      <EventConfirmModal open={confirmOpen} onClose={() => setConfirmOpen(false)} theme={theme} eventId={eventId} eventColor={display?.color} />
    </>
  );
};

const CoachModal = ({ open, onClose, coach, theme }) => (
  <Modal open={open} onClose={onClose} theme={theme} width={560}>
    {coach && (
      <div>
        <div style={{
          padding: 22, display: 'flex', gap: 16, alignItems: 'center',
          background: `linear-gradient(135deg, ${theme.primary}22, ${theme.bgElev})`,
          position: 'relative', overflow: 'hidden',
        }}>
          <CourtLines color={theme.primary} opacity={0.2} />
          {coach.photoUrl ? (
            <img src={coach.photoUrl} alt="" style={{
              width: 72, height: 72, borderRadius: 12, objectFit: 'cover', flexShrink: 0,
              objectPosition: `${coach.photoFocusX ?? 50}% ${coach.photoFocusY ?? 50}%`,
            }} />
          ) : (
            <Avatar name={coach.name} size={72} color={theme.primary} />
          )}
          <div style={{ position: 'relative', minWidth: 0 }}>
            <Badge theme={theme} color="primary">{coach.role}</Badge>
            <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 28, letterSpacing: 1, color: theme.text, margin: '6px 0 2px', fontWeight: 400 }}>{coach.name}</h3>
            <div style={{ fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
              {coach.exp ? `${coach.exp} años de experiencia` : (coach.spec || '')}
            </div>
          </div>
          <button onClick={onClose} style={{
            position: 'absolute', top: 12, right: 12, width: 32, height: 32, borderRadius: 8,
            background: theme.bgInput, border: `1px solid ${theme.border}`, color: theme.text,
            cursor: 'pointer', display: 'grid', placeItems: 'center',
          }}><Icon name="x" size={16} /></button>
        </div>
        <div style={{ padding: 20, display: 'grid', gap: 16 }}>
          {coach.trajectory && (
            <div>
              <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', marginBottom: 6, fontFamily: 'Inter, sans-serif' }}>Trayectoria</div>
              <p style={{ color: theme.textDim, fontSize: 13, lineHeight: 1.6, margin: 0, fontFamily: 'Inter, sans-serif', whiteSpace: 'pre-wrap' }}>{coach.trajectory}</p>
            </div>
          )}
          {(coach.certifications?.length > 0 || coach.assignedGroups?.length > 0) && (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
              {coach.certifications?.length > 0 && (
                <div>
                  <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', marginBottom: 6, fontFamily: 'Inter, sans-serif' }}>Certificaciones</div>
                  <ul style={{ color: theme.text, fontSize: 12, margin: 0, padding: '0 0 0 16px', lineHeight: 1.6, fontFamily: 'Inter, sans-serif' }}>
                    {coach.certifications.map((line, i) => <li key={i}>{line}</li>)}
                  </ul>
                </div>
              )}
              {coach.assignedGroups?.length > 0 && (
                <div>
                  <div style={{ color: theme.textMute, fontSize: 10, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', marginBottom: 6, fontFamily: 'Inter, sans-serif' }}>Grupos asignados</div>
                  <ul style={{ color: theme.text, fontSize: 12, margin: 0, padding: '0 0 0 16px', lineHeight: 1.6, fontFamily: 'Inter, sans-serif' }}>
                    {coach.assignedGroups.map((line, i) => <li key={i}>{line}</li>)}
                  </ul>
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    )}
  </Modal>
);

Object.assign(window, {
  TopNav, Hero, LandingSectionAnchor, LazyWhenVisible, AvisosSection, CalendarioSection, EntrenadoresSection,
  GaleriaSection, MapaSection, FormSection, Footer, EventModal, AvisoModal, CoachModal,
  BirthdayCongratsModal, birthdayDisplayFromItem,
  SolicitaInformacionModal, InterestedLeadFormContent,
});
