/* ==========================================================================
   MINI UNIVERSO — MOTION
   --------------------------------------------------------------------------
   Capa de decoración. Nada de lo que hay aquí sostiene la lectura ni el
   layout: se puede borrar el <link> a este archivo y el sitio queda intacto,
   solo más quieto. Por eso vive separado de components.css.

   Todo lo que se anima está detrás de `[data-motion="on"]`, un atributo que
   motion.js pone en <html> únicamente cuando GSAP cargó Y el usuario no pidió
   menos movimiento. Sin ese atributo, este archivo no cambia ni un píxel.
   ========================================================================== */

/* Registrar las custom properties las hace interpolables de verdad (y evita
   que el navegador tenga que reparsear un string en cada frame). Donde no hay
   @property, el fallback del `var()` mantiene el estado de reposo. */
@property --card-glow { syntax: '<number>'; inherits: false; initial-value: 0; }
@property --btn-glow  { syntax: '<number>'; inherits: false; initial-value: 0; }
@property --btn-x     { syntax: '<number>'; inherits: false; initial-value: 50; }
@property --btn-y     { syntax: '<number>'; inherits: false; initial-value: 50; }
@property --shine     { syntax: '<number>'; inherits: false; initial-value: 0; }

/* ==========================================================================
   DESTELLOS
   Una sola capa fija para toda la página. Por encima del contenido (z-index 1)
   y por debajo del header (50) y del modal (100): un destello nunca tapa un
   control.
   ========================================================================== */
.sparkle-layer {
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  /* Aísla la capa en su propio contexto de composición: animar los destellos
     no vuelve a pintar el contenido que hay debajo. */
  contain: strict;
}

.sparkle {
  position: absolute;
  top: 0;
  left: 0;
  width: 18px;
  height: 18px;
  will-change: transform, opacity;
}

.sparkle svg {
  width: 100%;
  height: 100%;
  display: block;
  fill: #fff;
  filter: drop-shadow(0 0 6px rgb(152 105 238 / 0.9))
          drop-shadow(0 0 14px rgb(122 64 236 / 0.55));
}

/* ==========================================================================
   TARJETA DE PARTICIPANTE
   El foco del sitio. Al acercarse el puntero se enciende un halo y el retrato
   gana un punto de escala — el brillo rodea a la persona, no la tapa.
   ========================================================================== */
[data-motion='on'] .person-card {
  position: relative;
}

[data-motion='on'] .person-card::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  opacity: var(--card-glow, 0);
  box-shadow:
    0 0 0 1px rgb(152 105 238 / 0.55),
    0 16px 48px -12px rgb(122 64 236 / 0.7);
}

/* Luz cenital sobre el retrato, donde va la corona. */
[data-motion='on'] .person-card .person-portrait::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--card-glow, 0);
  background: radial-gradient(
    120% 60% at 50% -10%,
    rgb(152 105 238 / 0.45),
    transparent 70%
  );
}

/* El recorte tiene que estar aquí: sin él, el retrato al escalar se sale del
   radio de la tarjeta. */
[data-motion='on'] .person-portrait {
  overflow: hidden;
}

[data-motion='on'] .person-portrait img {
  will-change: transform;
}

/* ==========================================================================
   BOTONES
   Un reflejo que sigue al puntero. Es un background-image por encima del
   background-color, así que no toca el contraste del texto ni el borde.
   ========================================================================== */
[data-motion='on'] .btn,
[data-motion='on'] .btn-ghost {
  position: relative;
  background-image: radial-gradient(
    circle at calc(var(--btn-x, 50) * 1%) calc(var(--btn-y, 50) * 1%),
    rgb(255 255 255 / calc(var(--btn-glow, 0) * 0.28)),
    transparent 60%
  );
}

[data-motion='on'] .btn-ghost {
  background-image: radial-gradient(
    circle at calc(var(--btn-x, 50) * 1%) calc(var(--btn-y, 50) * 1%),
    rgb(152 105 238 / calc(var(--btn-glow, 0) * 0.34)),
    transparent 65%
  );
}

/* ==========================================================================
   BRILLO DE TITULAR
   Una pasada de luz, una sola vez, cuando el titular entra en pantalla.

   `background-clip: text` obliga a `color: transparent`. Si el navegador no
   lo soporta, el texto sería invisible — de ahí el @supports: sin soporte, no
   se aplica nada y el titular conserva su color.
   ========================================================================== */
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  [data-motion='on'] .hero-title__hard.has-shimmer {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    background-repeat: no-repeat;
    background-image: linear-gradient(
      100deg,
      var(--mu-white) calc(var(--shine, 0) * 180% - 60%),
      var(--mu-accent-soft) calc(var(--shine, 0) * 180% - 30%),
      var(--mu-white) calc(var(--shine, 0) * 180%)
    );
  }
}

/* En modo de colores forzados el sistema impone su propia paleta y el
   `color: transparent` del que depende `background-clip: text` deja de tener
   quien lo pinte: el titular desaparece. El brillo se apaga y vuelve el color
   normal, que es lo que ese modo espera. */
@media (forced-colors: active) {
  [data-motion='on'] .hero-title__hard.has-shimmer {
    background-image: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: CanvasText;
  }
}

/* ==========================================================================
   MÉTRICAS
   "Más de 25" en lugar de "25+": la aproximación se dice con palabras, no con
   un símbolo que el lector tenga que interpretar.
   ========================================================================== */
.stat__approx {
  display: block;
  /* Reserva la caja de línea aunque venga vacía: es lo que mantiene las tres
     cifras sobre la misma línea base. */
  min-height: 1.4em;
  font-family: var(--font-body);
  font-size: var(--text-meta);
  font-weight: 600;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--mu-accent-soft);
  margin-bottom: var(--space-1);
}

.stat__num {
  /* Ancho de dígito fijo: el contador sube de 0 a 150 sin que la cifra
     tiemble ni empuje a la etiqueta de al lado. */
  font-variant-numeric: tabular-nums;
}

.stat__plus {
  color: var(--mu-accent-soft);
}

/* ==========================================================================
   SPONSORS
   Logotipos blancos sobre transparente. En reposo van a 0.75 para que no
   compitan con el contenido; al pasar el puntero, a plena luz.
   ========================================================================== */
.sponsor-logo {
  width: auto;
  max-width: 100%;
  height: 3rem;
  object-fit: contain;
  opacity: 0.75;
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}

.sponsor-tile:hover .sponsor-logo {
  opacity: 1;
  transform: scale(1.04);
}

/* ==========================================================================
   RESPETO A LA PREFERENCIA DEL SISTEMA
   motion.js ya no arranca el motor si el usuario pide menos movimiento. Esto
   es el segundo cinturón: aunque el atributo llegara a estar puesto, aquí no
   se mueve nada.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .sparkle-layer { display: none; }

  [data-motion='on'] .btn,
  [data-motion='on'] .btn-ghost { background-image: none; }

  [data-motion='on'] .person-card::after,
  [data-motion='on'] .person-card .person-portrait::after { opacity: 0; }

  .sponsor-logo {
    opacity: 1;
    transition: none;
  }

  .sponsor-tile:hover .sponsor-logo { transform: none; }
}
