/* Shared design tokens, icons, primitives for TECOS ELITE VOLLEYBALL */

const TECOS_MOBILE_BP = 768;

function syncTecosMobileHtmlClass (matches) {
  if (typeof document === 'undefined') return;
  document.documentElement.classList.toggle('tecos-is-mobile', !!matches);
}

function useMediaQuery (query) {
  const read = () => typeof window !== 'undefined' && window.matchMedia(query).matches;
  const [matches, setMatches] = React.useState(read);
  React.useEffect(() => {
    const mq = window.matchMedia(query);
    const onChange = () => {
      const next = mq.matches;
      syncTecosMobileHtmlClass(next);
      setMatches((prev) => (prev === next ? prev : next));
    };
    onChange();
    mq.addEventListener('change', onChange);
    return () => mq.removeEventListener('change', onChange);
  }, [query]);
  return matches;
}

function useIsMobile () {
  return useMediaQuery(`(max-width: ${TECOS_MOBILE_BP}px)`);
}

const MobileMenuButton = ({ theme, onClick, open, style, className = '' }) => (
  <button
    type="button"
    className={`tecos-mobile-menu-btn ${className}`.trim()}
    aria-label={open ? 'Cerrar menú' : 'Abrir menú'}
    aria-expanded={!!open}
    onClick={onClick}
    style={{
      width: 40,
      height: 40,
      borderRadius: 10,
      border: `1px solid ${theme.border}`,
      background: theme.bgInput,
      color: theme.text,
      display: 'grid',
      placeItems: 'center',
      cursor: 'pointer',
      flexShrink: 0,
      ...style,
    }}
  >
    <Icon name={open ? 'x' : 'menu'} size={18} />
  </button>
);

const DrawerBackdrop = ({ open, onClose }) => (
  open ? <div className="tecos-drawer-backdrop" role="presentation" onClick={onClose} /> : null
);

const PageShell = ({ children, style, className = '' }) => {
  const mobile = useIsMobile();
  return (
    <div
      className={`tecos-page-shell ${className}`.trim()}
      style={{
        padding: mobile ? 16 : 32,
        boxSizing: 'border-box',
        width: '100%',
        ...style,
      }}
    >
      {children}
    </div>
  );
};

const THEMES = {
  dark: {
    name: 'Oscuro',
    bg: '#06080f',
    bgElev: '#0d1220',
    bgCard: '#111830',
    bgInput: '#0a0f1c',
    border: 'rgba(255,255,255,0.08)',
    borderStrong: 'rgba(255,255,255,0.16)',
    text: '#f1f5ff',
    textDim: '#9aa6c4',
    textMute: '#5d6889',
    primary: '#3aa8ff',
    primaryDark: '#1e6fd9',
    primaryGlow: 'rgba(58,168,255,0.35)',
    accent: '#22d3ee',
    navy: '#0a2a6b',
    success: '#22c55e',
    warning: '#f59e0b',
    danger: '#ef4444',
    urgent: '#a855f7',
    info: '#3b82f6',
    wa: '#25d366',
    grid: 'rgba(58,168,255,0.06)',
    courtLine: 'rgba(255,255,255,0.06)',
  },
  light: {
    name: 'Claro',
    bg: '#f6f8fc',
    bgElev: '#ffffff',
    bgCard: '#ffffff',
    bgInput: '#f1f4fa',
    border: 'rgba(10,42,107,0.08)',
    borderStrong: 'rgba(10,42,107,0.18)',
    text: '#0a1a3a',
    textDim: '#4a5778',
    textMute: '#8893b0',
    primary: '#0a2a6b',
    primaryDark: '#001a4d',
    primaryGlow: 'rgba(30,144,255,0.22)',
    accent: '#1e90ff',
    navy: '#0a2a6b',
    success: '#16a34a',
    warning: '#d97706',
    danger: '#dc2626',
    urgent: '#9333ea',
    info: '#2563eb',
    wa: '#25d366',
    grid: 'rgba(10,42,107,0.05)',
    courtLine: 'rgba(10,42,107,0.06)',
  },
  blue: {
    name: 'Azul',
    bg: '#001a4d',
    bgElev: '#062869',
    bgCard: '#0a3580',
    bgInput: '#062358',
    border: 'rgba(160,212,255,0.16)',
    borderStrong: 'rgba(160,212,255,0.32)',
    text: '#eaf3ff',
    textDim: '#a0c6ff',
    textMute: '#6a8dc6',
    primary: '#4cb4ff',
    primaryDark: '#1e90ff',
    primaryGlow: 'rgba(76,180,255,0.45)',
    accent: '#a0d4ff',
    navy: '#001233',
    success: '#34d399',
    warning: '#fbbf24',
    danger: '#fb7185',
    urgent: '#c084fc',
    info: '#60a5fa',
    wa: '#4ade80',
    grid: 'rgba(160,212,255,0.06)',
    courtLine: 'rgba(160,212,255,0.10)',
  },
};

/* ---------- Icons (stroke = currentColor) ---------- */
const Icon = ({ name, size = 18, stroke = 1.8, style }) => {
  const s = stroke;
  const paths = {
    home: <><path d="M3 11l9-7 9 7v9a2 2 0 0 1-2 2h-4v-6h-6v6H5a2 2 0 0 1-2-2z" /></>,
    bell: <><path d="M6 8a6 6 0 1 1 12 0c0 7 3 7 3 9H3c0-2 3-2 3-9z" /><path d="M10 21a2 2 0 0 0 4 0" /></>,
    cal: <><rect x="3" y="5" width="18" height="16" rx="2" /><path d="M3 9h18M8 3v4M16 3v4" /></>,
    user: <><circle cx="12" cy="8" r="4" /><path d="M4 21a8 8 0 0 1 16 0" /></>,
    users: <><circle cx="9" cy="8" r="3.5" /><path d="M2 20a7 7 0 0 1 14 0" /><circle cx="17" cy="7" r="3" /><path d="M22 19a5 5 0 0 0-5-5" /></>,
    money: <><rect x="2" y="6" width="20" height="12" rx="2" /><circle cx="12" cy="12" r="3" /><path d="M6 10v.01M18 14v.01" /></>,
    wa: <><path d="M3 21l1.6-4.5A8.5 8.5 0 1 1 8 20l-5 1z" /><path d="M8.5 9c0 4 3 6.5 6.5 6.5l1.5-1.5-2.5-1-1 1.2c-1.4-.6-2.2-1.4-2.7-2.7l1.2-1-1-2.5L9 9z" /></>,
    facebook: <><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v9h4v-9h3l1-4h-4V7a1 1 0 0 1 1-1h3z" /></>,
    cart: <><circle cx="9" cy="20" r="1.5" /><circle cx="17" cy="20" r="1.5" /><path d="M2 3h3l2.5 13h12l2-9H6" /></>,
    image: <><rect x="3" y="3" width="18" height="18" rx="2" /><circle cx="9" cy="9" r="2" /><path d="M3 17l5-5 5 5 3-3 5 5" /></>,
    pin: <><path d="M12 22s7-7 7-12a7 7 0 0 0-14 0c0 5 7 12 7 12z" /><circle cx="12" cy="10" r="2.5" /></>,
    form: <><rect x="4" y="3" width="16" height="18" rx="2" /><path d="M8 8h8M8 12h8M8 16h5" /></>,
    box: <><path d="M3 7l9-4 9 4-9 4z" /><path d="M3 7v10l9 4 9-4V7" /><path d="M12 11v10" /></>,
    chart: <><path d="M4 20V10M10 20V4M16 20v-8M22 20H2" /></>,
    settings: <><circle cx="12" cy="12" r="3" /><path d="M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1" /></>,
    search: <><circle cx="11" cy="11" r="7" /><path d="M21 21l-4-4" /></>,
    plus: <><path d="M12 5v14M5 12h14" /></>,
    edit: <><path d="M4 20h4l10-10-4-4L4 16z" /></>,
    trash: <><path d="M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14" /></>,
    eye: <><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z" /><circle cx="12" cy="12" r="3" /></>,
    arrowRight: <><path d="M5 12h14M13 5l7 7-7 7" /></>,
    arrowLeft: <><path d="M19 12H5M11 5l-7 7 7 7" /></>,
    chev: <><path d="M9 6l6 6-6 6" /></>,
    check: <><path d="M5 13l4 4 10-10" /></>,
    x: <><path d="M6 6l12 12M18 6L6 18" /></>,
    menu: <><path d="M3 6h18M3 12h18M3 18h18" /></>,
    grid: <><rect x="3" y="3" width="7" height="7" /><rect x="14" y="3" width="7" height="7" /><rect x="3" y="14" width="7" height="7" /><rect x="14" y="14" width="7" height="7" /></>,
    list: <><path d="M8 6h13M8 12h13M8 18h13" /><circle cx="3.5" cy="6" r="1" fill="currentColor" stroke="none" /><circle cx="3.5" cy="12" r="1" fill="currentColor" stroke="none" /><circle cx="3.5" cy="18" r="1" fill="currentColor" stroke="none" /></>,
    star: <><path d="M12 2l3 7 7 .5-5.5 4.5L18 21l-6-4-6 4 1.5-7L2 9.5 9 9z" /></>,
    play: <><path d="M6 4l14 8-14 8z" /></>,
    upload: <><path d="M12 17V5M6 11l6-6 6 6" /><path d="M3 19h18" /></>,
    download: <><path d="M12 4v12M6 10l6 6 6-6" /><path d="M3 20h18" /></>,
    sun: <><circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M4 12H2M22 12h-2M5 5l1.5 1.5M17.5 17.5L19 19M5 19l1.5-1.5M17.5 6.5L19 5" /></>,
    moon: <><path d="M21 13A9 9 0 1 1 11 3a7 7 0 0 0 10 10z" /></>,
    palette: <><path d="M12 22a10 10 0 1 1 10-10c0 3-3 4-6 4h-2a2 2 0 0 0 0 4c1 0 2 1 2 2a2 2 0 0 1-4 0z" /></>,
    flame: <><path d="M12 22c5 0 8-3.5 8-8 0-3-2-5-3-8-1 2-2 3-3 3-1-2-2-4-2-7-3 3-6 6-6 12 0 4.5 3 8 6 8z" /></>,
    clock: <><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></>,
    send: <><path d="M22 2L11 13M22 2l-7 20-4-9-9-4z" /></>,
    filter: <><path d="M3 5h18l-7 9v7l-4-2v-5z" /></>,
    doc: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /><path d="M14 2v6h6M9 13h6M9 17h4" /></>,
    medal: <><circle cx="12" cy="15" r="6" /><path d="M8 4l4 7 4-7M8 4h8" /></>,
    cake: <><rect x="3" y="11" width="18" height="9" rx="1" /><path d="M3 15c2 2 4 0 6 0s4 2 6 0 4 0 6 0M8 8c-1-1-1-2 0-3 1 1 1 2 0 3zM12 8c-1-1-1-2 0-3 1 1 1 2 0 3zM16 8c-1-1-1-2 0-3 1 1 1 2 0 3z" /></>,
    target: <><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="5" /><circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none" /></>,
    megaphone: <><path d="M3 11v3l13 5V6L3 11z" /><path d="M16 9v8M19 10v6" /></>,
    chat: <><path d="M21 12a8 8 0 1 1-3.2-6.4L21 4l-1 4.2A8 8 0 0 1 21 12z" /></>,
    template: <><rect x="3" y="3" width="18" height="18" rx="2" /><path d="M3 9h18M9 9v12" /></>,
    history: <><path d="M3 12a9 9 0 1 0 3-6.7L3 8" /><path d="M3 3v5h5" /><path d="M12 7v5l3 2" /></>,
    whistle: <><path d="M3 12a6 6 0 1 0 12 0H9V8h13l-2 4" /></>,
    trophy: <><path d="M8 21h8M12 17v4M6 4h12v4a6 6 0 0 1-12 0z" /><path d="M6 4H3v3a3 3 0 0 0 3 3M18 4h3v3a3 3 0 0 1-3 3" /></>,
    info: <><circle cx="12" cy="12" r="9" /><path d="M12 8v.01M11 12h1v5h1" /></>,
    warning: <><path d="M12 3l10 18H2z" /><path d="M12 10v5M12 18v.01" /></>,
    minus: <><path d="M5 12h14" /></>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth={s} strokeLinecap="round" strokeLinejoin="round"
      style={{ flexShrink: 0, ...style }}>
      {paths[name] || paths.info}
    </svg>
  );
};

/* ---------- Volleyball — Mikasa azul / amarillo / blanco (referencia original) ---------- */
const VB_MIKASA_IMAGE = 'assets/volleyball-mikasa.png?v=1';

const VB_PANEL_WHITE = 'M50 6 Q38 50 50 94 Q62 50 50 6 Z';
const VB_PANEL_BLUE = 'M50 6 Q62 50 50 94 A46 46 0 0 1 92 48 Q74 20 50 6 Z';
const VB_PANEL_YELLOW = 'M50 6 Q38 50 50 94 A46 46 0 0 0 8 48 Q26 20 50 6 Z';

const VB_GROOVE_PATHS = [
  'M50 6 Q38 50 50 94',
  'M50 6 Q62 50 50 94',
  'M8 36 Q50 54 92 36',
  'M8 64 Q50 46 92 64',
  'M16 20 Q50 30 84 20',
  'M16 80 Q50 70 84 80',
];

const vbColorsFromTheme = (theme) => ({
  primary: theme?.primary || '#2563eb',
  primaryDark: theme?.primaryDark || '#1d4ed8',
  gold: theme?.warning || '#eab308',
  goldLight: '#facc15',
  light: '#ffffff',
  seam: '#334155',
});

const VolleyballMikasaSvg = ({ uid, detailed, colors }) => (
  <>
    <defs>
      <radialGradient id={`${uid}-shade`} cx="32%" cy="26%" r="72%">
        <stop offset="0%" stopColor="#ffffff" stopOpacity="0.35" />
        <stop offset="100%" stopColor="#64748b" stopOpacity="0.2" />
      </radialGradient>
      <linearGradient id={`${uid}-blue`} x1="20%" y1="10%" x2="90%" y2="95%">
        <stop offset="0%" stopColor={colors.primaryDark} />
        <stop offset="100%" stopColor={colors.primary} />
      </linearGradient>
      <linearGradient id={`${uid}-gold`} x1="80%" y1="10%" x2="10%" y2="95%">
        <stop offset="0%" stopColor={colors.gold} />
        <stop offset="100%" stopColor={colors.goldLight} />
      </linearGradient>
      <linearGradient id={`${uid}-white`} x1="40%" y1="0%" x2="60%" y2="100%">
        <stop offset="0%" stopColor={colors.light} />
        <stop offset="100%" stopColor="#e2e8f0" />
      </linearGradient>
      <pattern id={`${uid}-dimple`} width="3.2" height="3.2" patternUnits="userSpaceOnUse">
        <circle cx="1.1" cy="1.1" r="0.38" fill="#000" fillOpacity="0.07" />
        <circle cx="2.5" cy="2.5" r="0.28" fill="#fff" fillOpacity="0.06" />
      </pattern>
      <clipPath id={`${uid}-clip`}>
        <circle cx="50" cy="50" r="46" />
      </clipPath>
    </defs>
    <circle cx="50" cy="51" r="47" fill="#0f172a" fillOpacity="0.08" />
    <g clipPath={`url(#${uid}-clip)`}>
      <circle cx="50" cy="50" r="46" fill="#f8fafc" />
      <path d={VB_PANEL_YELLOW} fill={`url(#${uid}-gold)`} />
      <path d={VB_PANEL_BLUE} fill={`url(#${uid}-blue)`} />
      <path d={VB_PANEL_WHITE} fill={`url(#${uid}-white)`} />
      <rect x="4" y="4" width="92" height="92" fill={`url(#${uid}-dimple)`} />
      <circle cx="50" cy="50" r="46" fill={`url(#${uid}-shade)`} />
    </g>
    {VB_GROOVE_PATHS.map((d, i) => (
      <path
        key={i}
        d={d}
        fill="none"
        stroke={colors.seam}
        strokeWidth={detailed ? 1.8 : 1.35}
        strokeLinecap="round"
        strokeOpacity="0.5"
      />
    ))}
    {detailed && <ellipse cx="33" cy="30" rx="12" ry="8" fill="#fff" fillOpacity="0.45" />}
    <circle cx="50" cy="50" r="46" fill="none" stroke={colors.seam} strokeWidth="0.6" strokeOpacity="0.12" />
  </>
);

const VolleyballMikasaPhoto = ({ uid }) => (
  <>
    <defs>
      <clipPath id={`${uid}-clip`}>
        <circle cx="50" cy="50" r="46" />
      </clipPath>
      <radialGradient id={`${uid}-edge`} cx="50%" cy="50%" r="50%">
        <stop offset="82%" stopColor="#000" stopOpacity="0" />
        <stop offset="100%" stopColor="#64748b" stopOpacity="0.35" />
      </radialGradient>
    </defs>
    <circle cx="50" cy="51" r="47" fill="#0f172a" fillOpacity="0.1" />
    <g clipPath={`url(#${uid}-clip)`}>
      <image href={VB_MIKASA_IMAGE} x="0" y="0" width="100" height="100" preserveAspectRatio="xMidYMid slice" />
      <circle cx="50" cy="50" r="46" fill={`url(#${uid}-edge)`} />
    </g>
    <ellipse cx="32" cy="28" rx="11" ry="7" fill="#fff" fillOpacity="0.22" />
    <circle cx="50" cy="50" r="46" fill="none" stroke="#0f172a" strokeWidth="0.5" strokeOpacity="0.1" />
  </>
);

const VolleyballThemedSvg = VolleyballMikasaSvg;

/** Balón Mikasa (foto en grande; SVG en chico). */
const Volleyball = ({ size = 80, glow = false, color = '#fff', detailed = false, theme: themeProp }) => {
  const uid = `vb-${size}-${detailed ? 'p' : 's'}`;
  const colors = vbColorsFromTheme(themeProp);
  const usePhoto = detailed || size >= 72;
  const filter = glow
    ? 'drop-shadow(0 0 18px rgba(234,179,8,0.45)) drop-shadow(0 8px 22px rgba(37,99,235,0.25)) drop-shadow(0 6px 16px rgba(15,23,42,0.2))'
    : 'drop-shadow(0 4px 12px rgba(15,23,42,0.16))';
  return (
    <svg
      width={size}
      height={size}
      viewBox="0 0 100 100"
      role="img"
      aria-label="Balón de voleibol"
      style={{ filter, display: 'block' }}
    >
      {usePhoto
        ? <VolleyballMikasaPhoto uid={uid} />
        : <VolleyballMikasaSvg uid={uid} detailed={detailed || size >= 72} colors={colors} />}
    </svg>
  );
};

const CourtLines = ({ color, opacity = 1 }) => (
  <svg viewBox="0 0 800 500" preserveAspectRatio="none" style={{
    position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', opacity
  }}>
    <rect x="50" y="50" width="700" height="400" fill="none" stroke={color} strokeWidth="2" />
    <line x1="400" y1="50" x2="400" y2="450" stroke={color} strokeWidth="2" />
    <line x1="270" y1="50" x2="270" y2="450" stroke={color} strokeWidth="1" strokeDasharray="6 6" />
    <line x1="530" y1="50" x2="530" y2="450" stroke={color} strokeWidth="1" strokeDasharray="6 6" />
    <line x1="400" y1="30" x2="400" y2="50" stroke={color} strokeWidth="3" />
    <line x1="400" y1="450" x2="400" y2="470" stroke={color} strokeWidth="3" />
  </svg>
);

const Net = ({ color, height = 60 }) => (
  <svg viewBox="0 0 400 60" preserveAspectRatio="none" style={{ width: '100%', height, display: 'block' }}>
    <defs>
      <pattern id="netp" x="0" y="0" width="14" height="14" patternUnits="userSpaceOnUse">
        <path d="M0 0L14 0M0 0L0 14" stroke={color} strokeWidth="0.8" opacity="0.5" />
      </pattern>
    </defs>
    <rect x="0" y="4" width="400" height="2" fill={color} opacity="0.8" />
    <rect x="0" y="6" width="400" height="48" fill="url(#netp)" />
    <rect x="0" y="54" width="400" height="2" fill={color} opacity="0.8" />
  </svg>
);

/** Logo oficial Tecos Elite Volleyball (PNG sin fondo). */
const TECOS_LOGO_PATH = 'assets/logo-tecos.png?v=no-bg-2';
const LOGO_ASPECT = 918 / 887;

const Logo = ({ size = 40, withText = false, theme, style: wrapStyle, prominent = false, neon = false, className = '' }) => {
  const imgH = size;
  const imgW = Math.round(size * LOGO_ASPECT * 10) / 10;
  return (
    <div
      className={`tecos-logo ${prominent ? 'tecos-logo-hero' : ''} ${neon ? 'tecos-logo-neon' : ''} ${className}`.trim()}
      style={{
        display: 'flex',
        alignItems: 'center',
        gap: withText ? 10 : 0,
        lineHeight: 0,
        ...wrapStyle,
      }}
    >
      <img
        src={TECOS_LOGO_PATH}
        alt="Tecos Elite Volleyball"
        style={{
          width: imgW,
          height: imgH,
          objectFit: 'contain',
          display: 'block',
          flexShrink: 0,
        }}
      />
      {withText && (
        <div style={{ lineHeight: 1, display: 'grid', gap: 2 }}>
          <div style={{
            fontFamily: 'Bebas Neue, sans-serif', fontSize: size * 0.55, letterSpacing: 1.5,
            color: theme?.text || '#fff', fontWeight: 400,
          }}>TECOS ELITE</div>
          <div style={{
            fontFamily: 'Inter, sans-serif', fontSize: size * 0.22, letterSpacing: 3,
            color: theme?.primary || '#1e90ff', fontWeight: 600, textTransform: 'uppercase',
          }}>Volleyball</div>
        </div>
      )}
    </div>
  );
};

/* ---------- UI Primitives ---------- */
const Card = ({ children, theme, style, hover, onClick, className = '' }) => (
  <div
    className={className ? `tecos-card ${className}` : 'tecos-card'}
    onClick={onClick}
    style={{
    background: theme.bgCard,
    border: `1px solid ${theme.border}`,
    borderRadius: 14,
    padding: 18,
    transition: 'all .25s',
    cursor: onClick ? 'pointer' : 'default',
    ...(style || {}),
  }}
    onMouseEnter={hover ? e => {
      e.currentTarget.style.borderColor = theme.borderStrong;
      e.currentTarget.style.transform = 'translateY(-2px)';
      e.currentTarget.style.boxShadow = `0 8px 30px ${theme.primaryGlow}`;
    } : undefined}
    onMouseLeave={hover ? e => {
      e.currentTarget.style.borderColor = theme.border;
      e.currentTarget.style.transform = 'translateY(0)';
      e.currentTarget.style.boxShadow = 'none';
    } : undefined}
  >{children}</div>
);

const Btn = ({ children, theme, kind = 'primary', size = 'md', onClick, icon, style, disabled }) => {
  const sizes = {
    sm: { padding: '7px 12px', fontSize: 12, gap: 6, height: 32 },
    md: { padding: '10px 18px', fontSize: 13, gap: 8, height: 40 },
    lg: { padding: '14px 26px', fontSize: 15, gap: 10, height: 52 },
  };
  const kinds = {
    primary: {
      background: `linear-gradient(135deg, ${theme.primary}, ${theme.primaryDark})`,
      color: '#fff',
      border: 'none',
      boxShadow: `0 6px 20px ${theme.primaryGlow}`,
    },
    ghost: {
      background: 'transparent',
      color: theme.text,
      border: `1px solid ${theme.borderStrong}`,
    },
    soft: {
      background: theme.bgInput,
      color: theme.text,
      border: `1px solid ${theme.border}`,
    },
    danger: {
      background: theme.danger, color: '#fff', border: 'none',
    },
    success: {
      background: theme.success, color: '#fff', border: 'none',
    },
    wa: {
      background: '#25d366', color: '#fff', border: 'none',
      boxShadow: '0 4px 14px rgba(37,211,102,.35)'
    },
  };
  return (
    <button onClick={onClick} disabled={disabled} style={{
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      borderRadius: 10, fontWeight: 600, letterSpacing: 0.3,
      cursor: disabled ? 'not-allowed' : 'pointer', fontFamily: 'Inter, sans-serif',
      transition: 'all .2s', whiteSpace: 'nowrap',
      opacity: disabled ? 0.5 : 1,
      ...sizes[size], ...kinds[kind], ...(style || {}),
    }}
      onMouseEnter={e => !disabled && (e.currentTarget.style.transform = 'translateY(-1px)')}
      onMouseLeave={e => (e.currentTarget.style.transform = 'translateY(0)')}
    >
      {icon && <Icon name={icon} size={size === 'sm' ? 14 : size === 'lg' ? 20 : 16} />}
      {children}
    </button>
  );
};

/** Texto con degradado (evita el “rectángulo” si falta background-clip) */
const gradientTextStyle = (theme, angle = '135deg') => ({
  display: 'inline-block',
  backgroundImage: `linear-gradient(${angle}, ${theme.primary}, ${theme.accent})`,
  WebkitBackgroundClip: 'text',
  backgroundClip: 'text',
  WebkitTextFillColor: 'transparent',
  color: theme.primary,
});

/** Botón luna/sol — mismo estilo que el panel administrador */
const ThemeToggleButton = ({ theme, themeKey = 'light', onToggle, size = 18, style }) => {
  if (!onToggle) return null;
  const isDark = themeKey === 'dark';
  return (
    <button
      type="button"
      onClick={onToggle}
      style={{
        width: 40, height: 40, borderRadius: 10,
        background: theme.bgInput, border: `1px solid ${theme.border}`,
        color: theme.text, cursor: 'pointer', display: 'grid', placeItems: 'center',
        flexShrink: 0, ...style,
      }}
      title={isDark ? 'Cambiar a tema claro' : 'Cambiar a tema oscuro'}
      aria-label={isDark ? 'Tema claro' : 'Tema oscuro'}
    >
      <Icon name={isDark ? 'sun' : 'moon'} size={size} />
    </button>
  );
};

const Badge = ({ children, theme, color = 'primary', soft = true, style }) => {
  const c = theme[color] || color;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      padding: '3px 9px', borderRadius: 999,
      fontSize: 11, fontWeight: 600, letterSpacing: 0.4,
      textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
      background: soft ? `${c}22` : c,
      color: soft ? c : '#fff',
      border: soft ? `1px solid ${c}44` : 'none',
      ...style,
    }}>{children}</span>
  );
};

const inputStyle = (theme) => ({
  width: '100%',
  padding: '10px 12px',
  borderRadius: 8,
  border: `1px solid ${theme.border}`,
  background: theme.bgInput,
  color: theme.text,
  fontSize: 14,
  fontFamily: 'Inter, sans-serif',
  outline: 'none',
});

/** Banner «¿Eres tú?» con brillo neón (tienda, eventos, etc.) */
const StudentConfirmNeonBanner = ({
  theme, student, glowColor, onYes, onNo,
  subtitle = 'Si eres esta persona, confirma para continuar.',
}) => {
  if (!student) return null;
  const c = glowColor || theme.primary;
  return (
    <div
      style={{
        marginTop: 14,
        padding: '18px 16px',
        borderRadius: 12,
        background: `${c}14`,
        border: `1px solid ${c}`,
        fontFamily: 'Inter, sans-serif',
        boxShadow: `0 0 28px ${c}88, inset 0 0 24px ${c}33`,
        animation: 'pulseGlow 2s ease-in-out infinite',
        ['--g']: `${c}66`,
      }}
    >
      <div style={{
        fontFamily: 'Bebas Neue, sans-serif',
        fontSize: 26,
        letterSpacing: 1.5,
        color: c,
        marginBottom: 8,
        textShadow: `0 0 20px ${c}aa`,
      }}>¿ERES TÚ?</div>
      <p style={{ margin: '0 0 14px', fontSize: 13, color: theme.text, lineHeight: 1.55 }}>
        Encontramos a <strong>{student.full_name}</strong>
        {' '}({student.code}
        {student.category ? ` · ${student.category}` : ''}).
        {' '}{subtitle}
      </p>
      <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
        <Btn theme={theme} icon="check" size="sm" onClick={onYes} style={{ boxShadow: `0 0 16px ${c}55` }}>Sí, soy yo</Btn>
        <Btn theme={theme} kind="ghost" size="sm" onClick={onNo}>No</Btn>
      </div>
    </div>
  );
};

const Field = ({ label, theme, children }) => (
  <label style={{ display: 'grid', gap: 6 }}>
    {label && (
      <span style={{
        fontSize: 11, fontWeight: 600, letterSpacing: 0.6,
        color: theme.textDim, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
      }}>{label}</span>
    )}
    {children}
  </label>
);

const Input = ({ theme, icon, label, ...props }) => (
  <label style={{ display: 'grid', gap: 6 }}>
    {label && <span style={{
      fontSize: 11, fontWeight: 600, letterSpacing: 0.6,
      color: theme.textDim, textTransform: 'uppercase',
    }}>{label}</span>}
    <div style={{
      display: 'flex', alignItems: 'center', gap: 8,
      background: theme.bgInput,
      border: `1px solid ${theme.border}`,
      borderRadius: 10, padding: '0 12px', height: 42,
      transition: 'border-color .2s',
    }}
      onFocus={e => e.currentTarget.style.borderColor = theme.primary}
      onBlur={e => e.currentTarget.style.borderColor = theme.border}
    >
      {icon && <span style={{ color: theme.textMute }}><Icon name={icon} size={16} /></span>}
      <input {...props} style={{
        flex: 1, background: 'transparent', border: 'none', outline: 'none',
        color: theme.text, fontSize: 14, fontFamily: 'Inter, sans-serif',
        height: '100%',
      }} />
    </div>
  </label>
);

/** ID alumno: prefijo TEC fijo + solo dígitos (001, 067…) */
const TecStudentIdField = ({
  theme, label = 'ID del alumno', digits, onDigitsChange, onBlur, disabled, icon,
}) => {
  const fmtIn = typeof formatTecCodeDigitsInput === 'function'
    ? formatTecCodeDigitsInput
    : (v) => String(v || '').replace(/\D/g, '').slice(0, 3);
  const preview = typeof buildTecStudentCode === 'function'
    ? buildTecStudentCode(digits)
    : (digits ? `TEC${digits}` : '');

  return (
    <label style={{ display: 'grid', gap: 6, opacity: disabled ? 0.6 : 1 }}>
      {label && (
        <span style={{
          fontSize: 11, fontWeight: 600, letterSpacing: 0.6,
          color: theme.textDim, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
          display: 'flex', alignItems: 'center', gap: 6,
        }}>
          {icon && <Icon name={icon} size={14} />}{label}
        </span>
      )}
      <div style={{ display: 'flex', gap: 0 }}>
        <span style={{
          padding: '12px 14px', background: theme.bgElev, border: `1px solid ${theme.border}`,
          borderRight: 'none', borderRadius: '10px 0 0 10px', fontWeight: 800, color: theme.primary,
          fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, letterSpacing: 1, lineHeight: 1,
        }}>TEC</span>
        <input
          type="text"
          inputMode="numeric"
          autoComplete="off"
          value={digits}
          disabled={disabled}
          onChange={(e) => onDigitsChange(fmtIn(e.target.value))}
          onBlur={onBlur}
          placeholder="001"
          maxLength={3}
          style={{
            flex: 1, padding: '12px 14px', borderRadius: '0 10px 10px 0',
            border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text,
            fontFamily: 'Bebas Neue, sans-serif', fontSize: 24, letterSpacing: 2, outline: 'none',
          }}
        />
      </div>
      {preview && (
        <span style={{ fontSize: 11, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
          ID completo: <strong style={{ color: theme.primary }}>{preview}</strong>
        </span>
      )}
    </label>
  );
};

const SocialLinksRow = ({ theme, settings, size = 40, iconSize = 20 }) => {
  const s = typeof normalizeAcademySettings === 'function' ? normalizeAcademySettings(settings) : (settings || {});
  const waUrl = typeof buildWhatsAppLink === 'function' ? buildWhatsAppLink(s) : '';
  const fbUrl = String(s.facebook_url || '').trim();
  if (!fbUrl && !waUrl) return null;
  return (
    <div style={{ display: 'flex', gap: 10, marginTop: 14 }}>
      {fbUrl && (
        <a href={fbUrl} target="_blank" rel="noopener noreferrer" title="Facebook" style={{
          width: size, height: size, borderRadius: 10, display: 'grid', placeItems: 'center',
          background: theme.bgInput, border: `1px solid ${theme.border}`, color: theme.primary,
          textDecoration: 'none',
        }}><Icon name="facebook" size={iconSize} /></a>
      )}
      {waUrl && (
        <a href={waUrl} target="_blank" rel="noopener noreferrer" title="WhatsApp" style={{
          width: size, height: size, borderRadius: 10, display: 'grid', placeItems: 'center',
          background: `${theme.wa}18`, border: `1px solid ${theme.wa}44`, color: theme.wa,
          textDecoration: 'none',
        }}><Icon name="wa" size={iconSize} /></a>
      )}
    </div>
  );
};

const PhoneInput = ({ theme, label, dial, national, onDialChange, onNationalChange, icon = 'wa' }) => {
  const options = typeof PHONE_DIAL_OPTIONS !== 'undefined' ? PHONE_DIAL_OPTIONS : [{ code: '+52', label: 'México (+52)' }];
  const defaultDial = typeof DEFAULT_PHONE_DIAL !== 'undefined' ? DEFAULT_PHONE_DIAL : '+52';
  const shellRef = React.useRef(null);
  const setShellFocus = (on) => {
    if (!shellRef.current) return;
    shellRef.current.style.borderColor = on ? theme.primary : theme.border;
  };
  return (
    <label style={{ display: 'grid', gap: 6, minWidth: 0 }}>
      {label && <span style={{
        fontSize: 11, fontWeight: 600, letterSpacing: 0.6,
        color: theme.textDim, textTransform: 'uppercase',
      }}>{label}</span>}
      <div
        ref={shellRef}
        style={{
          display: 'flex', alignItems: 'stretch', width: '100%', minWidth: 0,
          height: 42, borderRadius: 10, overflow: 'hidden',
          border: `1px solid ${theme.border}`,
          background: theme.bgInput,
          transition: 'border-color .2s',
        }}
      >
        <select
          value={dial || defaultDial}
          onChange={e => onDialChange?.(e.target.value)}
          onFocus={() => setShellFocus(true)}
          onBlur={() => setShellFocus(false)}
          title="Prefijo internacional (LADA)"
          aria-label="Prefijo telefónico"
          style={{
            width: 118,
            maxWidth: '38%',
            flexShrink: 0,
            height: '100%',
            padding: '0 6px',
            border: 'none',
            borderRight: `1px solid ${theme.border}`,
            background: theme.bgElev,
            color: theme.text,
            fontSize: 13,
            fontFamily: 'Inter, sans-serif',
            fontWeight: 600,
            cursor: 'pointer',
            outline: 'none',
          }}
        >
          {options.map(o => (
            <option key={o.code} value={o.code}>{o.label}</option>
          ))}
        </select>
        <div style={{
          flex: 1, minWidth: 0, display: 'flex', alignItems: 'center', gap: 8, padding: '0 10px',
        }}>
          {icon && <span style={{ color: theme.textMute, flexShrink: 0 }}><Icon name={icon} size={16} /></span>}
          <input
            type="tel"
            inputMode="tel"
            autoComplete="tel-national"
            value={national || ''}
            onChange={e => onNationalChange?.(e.target.value.replace(/[^\d\s\-()]/g, ''))}
            onFocus={() => setShellFocus(true)}
            onBlur={() => setShellFocus(false)}
            placeholder="33 1234 5678"
            style={{
              flex: 1, minWidth: 0, width: '100%', background: 'transparent', border: 'none', outline: 'none',
              color: theme.text, fontSize: 14, fontFamily: 'Inter, sans-serif', height: '100%',
            }}
          />
        </div>
      </div>
    </label>
  );
};

const Avatar = ({ name = '?', size = 36, color, theme, src }) => {
  const initials = name.split(' ').map(s => s[0]).slice(0, 2).join('').toUpperCase();
  const colors = ['#1e90ff', '#22d3ee', '#a855f7', '#22c55e', '#f59e0b', '#ef4444', '#0a2a6b'];
  const bgc = color || colors[name.charCodeAt(0) % colors.length];
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      background: src ? 'transparent' : `linear-gradient(135deg, ${bgc}, ${bgc}cc)`,
      display: 'grid', placeItems: 'center', overflow: 'hidden',
      fontFamily: 'Bebas Neue, sans-serif', fontSize: size * 0.42,
      color: '#fff', letterSpacing: 1, fontWeight: 400,
      flexShrink: 0,
      border: theme ? `2px solid ${theme.bgCard}` : 'none',
    }}>{src ? <img src={src} style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : initials}</div>
  );
};

const SectionTitle = ({ kicker, title, sub, theme, align = 'left' }) => (
  <div style={{ textAlign: align, marginBottom: 28 }}>
    {kicker && <div style={{
      fontSize: 12, fontWeight: 700, letterSpacing: 3,
      color: theme.primary, textTransform: 'uppercase', marginBottom: 10,
      display: 'flex', alignItems: 'center', gap: 10, justifyContent: align === 'center' ? 'center' : 'flex-start',
    }}>
      <span style={{ width: 28, height: 2, background: theme.primary }}></span>
      {kicker}
    </div>}
    <h2 style={{
      fontFamily: 'Bebas Neue, sans-serif', fontSize: 44, letterSpacing: 1.5,
      color: theme.text, margin: 0, lineHeight: 1, fontWeight: 400,
    }}>{title}</h2>
    {sub && <p style={{
      color: theme.textDim, fontSize: 15, marginTop: 10,
      maxWidth: 560, fontFamily: 'Inter, sans-serif',
      marginLeft: align === 'center' ? 'auto' : 0,
      marginRight: align === 'center' ? 'auto' : 0,
    }}>{sub}</p>}
  </div>
);

/* Placeholder image with icon */
const PlaceholderImage = ({ icon = 'image', label, theme, ratio = '4/3', accent }) => {
  const c = accent || theme.primary;
  return (
    <div style={{
      aspectRatio: ratio, width: '100%',
      background: `linear-gradient(135deg, ${c}22, ${c}05)`,
      border: `1px solid ${c}33`,
      borderRadius: 12, position: 'relative', overflow: 'hidden',
      display: 'grid', placeItems: 'center',
    }}>
      <svg viewBox="0 0 200 150" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: .4 }}>
        <defs>
          <pattern id={`dot-${label || icon}`} x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse">
            <circle cx="1" cy="1" r="1" fill={c} opacity="0.4" />
          </pattern>
        </defs>
        <rect width="200" height="150" fill={`url(#dot-${label || icon})`} />
      </svg>
      <div style={{ position: 'relative', display: 'grid', placeItems: 'center', gap: 6, color: c }}>
        <Icon name={icon} size={32} />
        {label && <span style={{
          fontSize: 11, fontWeight: 600, letterSpacing: 1, textTransform: 'uppercase',
          fontFamily: 'Inter, sans-serif',
        }}>{label}</span>}
      </div>
    </div>
  );
};

const isStudioCreditLink = (text, url) => {
  const t = String(text || '').toLowerCase().replace(/\s+/g, '');
  const u = String(url || '').toLowerCase();
  return u.includes('diseñador') || u.includes('disenador') || u.includes('desarrollador')
    || t.includes('luna') || t.includes('creativa') || t.includes('studio') || t.includes('lunacreativa');
};

const FooterStudioCredit = ({ text, url }) => {
  const href = String(url || '').trim() || 'diseñador.html';
  const label = String(text || '').trim() || 'Luna Creativa Studio';
  return (
    <a href={href} className="tecos-studio-credit" title="Conoce al equipo de diseño y desarrollo">
      <span className="tecos-studio-credit__spark" aria-hidden="true">✦</span>
      <span className="tecos-studio-credit__text">{label}</span>
      <span className="tecos-studio-credit__spark" aria-hidden="true">✦</span>
    </a>
  );
};

const footerCopyrightThemeVars = (theme) => ({
  '--tc-primary': theme.primary,
  '--tc-accent': theme.accent,
  '--tc-navy': theme.navy,
  '--tc-glow': theme.primaryGlow,
  '--tc-border': `${theme.primary}33`,
  '--tc-border-hover': `${theme.accent}55`,
  '--tc-bg-a': `${theme.navy}0a`,
  '--tc-bg-b': `${theme.accent}14`,
});

const FooterCopyrightBadge = ({ theme, text, url }) => {
  const label = String(text || '').trim();
  if (!label) return null;
  const href = String(url || '').trim();
  const vars = footerCopyrightThemeVars(theme);
  const inner = (
    <>
      <span className="tecos-copyright-badge__dot" aria-hidden="true" />
      <span className="tecos-copyright-badge__text">{label}</span>
      <span className="tecos-copyright-badge__dot" aria-hidden="true" />
    </>
  );
  if (href) {
    return (
      <a href={href} className="tecos-copyright-badge" style={vars} title={label}>
        {inner}
      </a>
    );
  }
  return (
    <span className="tecos-copyright-badge" style={vars} role="note">
      {inner}
    </span>
  );
};

/* Status pill (semáforo de pagos) */
const StatusPill = ({ status, theme }) => {
  const map = {
    pagado: { c: theme.success, label: 'Pagado' },
    pendiente: { c: theme.warning, label: 'Por pagar' },
    en_revision: { c: theme.warning, label: 'Pendiente' },
    rechazado: { c: theme.danger, label: 'Rechazado' },
    vencido: { c: theme.danger, label: 'Vencido' },
    revision: { c: theme.warning, label: 'Pendiente' },
    urgente: { c: theme.urgent, label: 'Urgente' },
    activo: { c: theme.success, label: 'Activo' },
    inactivo: { c: theme.textMute, label: 'Inactivo' },
    enviado: { c: theme.success, label: 'Enviado' },
    programado: { c: theme.info, label: 'Programado' },
    fallido: { c: theme.danger, label: 'Fallido' },
    borrador: { c: theme.textMute, label: 'Borrador' },
    confirmado: { c: theme.success, label: 'Confirmado' },
    entregado: { c: theme.success, label: 'Entregado' },
    cancelado: { c: theme.danger, label: 'Cancelado' },
    nuevo: { c: theme.info, label: 'Nuevo' },
    contactado: { c: theme.warning, label: 'Contactado' },
    inscrito: { c: theme.success, label: 'Inscrito' },
    descartado: { c: theme.textMute, label: 'Descartado' },
  };
  const s = map[status] || { c: theme.textMute, label: status };
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '4px 10px', borderRadius: 999,
      fontSize: 11, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase',
      fontFamily: 'Inter, sans-serif',
      background: `${s.c}1a`, color: s.c, border: `1px solid ${s.c}44`,
    }}>
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: s.c, boxShadow: `0 0 8px ${s.c}` }}></span>
      {s.label}
    </span>
  );
};

/* Quita foco activo (evita contorno 1px al cerrar modales). */
const blurActiveFocus = () => {
  const el = document.activeElement;
  if (el && el !== document.body && typeof el.blur === 'function') el.blur();
  try {
    document.body.focus({ preventScroll: true });
  } catch (_) {
    document.body.focus();
  }
};

const scheduleBlurCleanup = () => {
  blurActiveFocus();
  requestAnimationFrame(() => {
    blurActiveFocus();
    requestAnimationFrame(blurActiveFocus);
  });
};

/** Mismo cierre que el overlay del Modal (blur + callback). */
const bindModalClose = (onClose) => () => {
  scheduleBlurCleanup();
  onClose?.();
};

/* Modal shell — sin backdrop-filter ni sombras (línea fantasma 1px en WebKit). */
const Modal = ({ open, onClose, children, theme, width = 560, innerScroll = false, borderless = false }) => {
  const handleClose = React.useCallback(bindModalClose(onClose), [onClose]);

  React.useEffect(() => {
    if (!open) {
      scheduleBlurCleanup();
      return undefined;
    }
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = prevOverflow;
      scheduleBlurCleanup();
    };
  }, [open]);

  if (!open) return null;

  return (
    <div
      data-tecos-modal-overlay
      onClick={handleClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 1000,
        background: 'rgba(2,6,15,0.88)',
        display: 'grid', placeItems: 'center', padding: 20,
        overflow: 'hidden',
      }}
    >
      <div className="tecos-modal-panel" onClick={e => e.stopPropagation()} style={{
        background: theme.bgElev,
        border: borderless ? 'none' : `1px solid ${theme.borderStrong}`,
        borderRadius: 18, width: '100%', maxWidth: width,
        maxHeight: '90vh',
        overflow: innerScroll ? 'hidden' : 'auto',
        display: innerScroll ? 'flex' : undefined,
        flexDirection: innerScroll ? 'column' : undefined,
        outline: 'none',
        boxShadow: 'none',
      }}>{children}</div>
    </div>
  );
};

Object.assign(window, {
  THEMES, Icon, Volleyball, VolleyballThemedSvg, vbColorsFromTheme, CourtLines, Net, Logo, TECOS_LOGO_PATH, LOGO_ASPECT,
  Card, Btn, ThemeToggleButton, gradientTextStyle, Badge, inputStyle, Field, Input, TecStudentIdField, PhoneInput, SocialLinksRow, Avatar, SectionTitle, StudentConfirmNeonBanner,
  PlaceholderImage, StatusPill, Modal, bindModalClose, scheduleBlurCleanup, isStudioCreditLink, FooterStudioCredit, FooterCopyrightBadge,
  useMediaQuery, useIsMobile, MobileMenuButton, DrawerBackdrop, PageShell, TECOS_MOBILE_BP,
});
