/*
 * motion.css — capa de animación compartida por las páginas del landing.
 * 2026-09-20.
 *
 * POR QUÉ EXISTE ESTE ARCHIVO
 * El `.reveal` del <style> de index.html hace UNA sola cosa —fade + translateY(48px)—
 * y está puesto en ~45 elementos, así que pasado el hero la página repite el mismo
 * truco cuarenta y cinco veces. Esto no lo sustituye: lo ENVUELVE. Sin ningún
 * atributo, todo sigue comportándose exactamente igual que antes; las variantes se
 * activan una por una con `data-anim`, `data-stagger`, `data-parallax` y
 * `data-spotlight`.
 *
 * ⚠️ ESPECIFICIDAD — por qué los selectores se ven redundantes
 * El `.reveal` original vive en un <style> INLINE del <head>; este archivo entra por
 * <link>, y a igualdad de especificidad gana el que va después en el documento (el
 * inline). Por eso el estado final se escribe `.reveal[data-anim].visible` (0,3,0) y
 * no `.reveal.visible` (0,2,0): así gana sin depender de dónde se cuelgue el <link>.
 * Si alguien "limpia" esos selectores, las variantes se quedan pegadas en su estado
 * inicial (invisibles) y la página aparece en blanco a media altura.
 *
 * ⚠️ NADA AQUÍ PARTE TEXTO EN PALABRAS/LETRAS, y no es por falta de ganas: `i18n.js`
 * cachea los nodos de texto (`collect()` corre UNA vez) y traduce por frase completa.
 * Envolver cada palabra en un <span> deja el nodo original huérfano → ese título no
 * se vuelve a traducir nunca, sin error y sin log. El efecto "por línea" se consigue
 * aquí con data-anim="mask", que recorta el elemento entero y no toca el DOM.
 */

/* ───────────────────────────────────────────────────────────
   1. VARIANTES DE ENTRADA  ·  data-anim="…"
   Se combinan con la clase .reveal que ya existe. El disparo lo sigue haciendo
   el IntersectionObserver de index.html (añade .visible); aquí solo cambia
   DESDE dónde entra cada cosa.
   ─────────────────────────────────────────────────────────── */

/* Estado final, común a todas las variantes. Una sola regla para no repetir el
   reset en cada una: lo que no se animó vale su valor inicial y no estorba.

   ⚠️ `clip-path: none`, NO `inset(0)`. Un inset —aunque sea de cero— deja el
   elemento recortado a su caja para siempre, y ahí la sombra que crece al pasar
   el cursor por una tarjeta sale cortada a ras del borde. La variante "mask", que
   sí necesita interpolar, redefine el suyo más abajo (`none` no es animable, y por
   eso termina en un inset negativo en vez de en none). */
.reveal[data-anim].visible {
  opacity: 1;
  transform: none;
  filter: none;
  clip-path: none;
}

/* Entradas laterales — para bloques que viven a un lado de la rejilla. */
.reveal[data-anim="left"]  { transform: translateX(-44px); }
.reveal[data-anim="right"] { transform: translateX(44px); }

/* Escala — la tarjeta "se acerca". Para piezas con peso propio (el recibo de
   precios, los mockups). El translateY corto evita que parezca un zoom puro. */
.reveal[data-anim="scale"] { transform: scale(0.94) translateY(18px); }

/* Desenfoque — entra como si enfocara una cámara. Caro en GPU si se abusa:
   reservado para 3 o 4 elementos grandes, nunca para una rejilla entera. */
.reveal[data-anim="blur"] {
  transform: translateY(22px);
  filter: blur(12px);
}

/* ⚰️ AQUÍ VIVÍA LA VARIANTE "mask" — el titular emergiendo desde detrás de una
   cortina. Se quitó el 2026-09-20, el mismo día que nació, por dos cosas:
   1. No gustó: Jesús pidió los títulos con la entrada de siempre.
   2. Antes de eso dejó INVISIBLES los siete titulares de la home. Terminaba en
      `clip-path: inset(-25% …)` y **`inset()` no admite valores negativos**: el
      navegador descarta la declaración entera, el elemento se queda con el clip
      inicial y no vuelve nunca. El `<h1>` del hero se salvó por no ser un `.reveal`.
      Si algún día hace falta recortar un texto, va con `polygon()` —que sí acepta
      coordenadas fuera de la caja, porque son puntos y no márgenes— y hay que darle
      aire: con `line-height: 1.15` los glifos sobresalen del content box, así que un
      recorte a ras corta las colas de la g y los acentos. */

/* Inclinación — la tarjeta cae hacia su sitio. Necesita perspectiva propia
   porque el padre es una rejilla sin transform-style. */
.reveal[data-anim="tilt"] {
  transform: perspective(900px) rotateX(11deg) translateY(38px);
  transform-origin: center top;
}

/* ───────────────────────────────────────────────────────────
   2. STAGGER REAL  ·  data-stagger en el CONTENEDOR
   Antes el escalonado eran cuatro clases fijas (.d1….d4) repartidas a mano, así
   que en una rejilla de 7 tarjetas entraban de dos en dos y de tres en tres. Con
   esto, motion.js numera los hijos en `--i` y cada uno sale detrás del anterior.
   `--i` es una custom property: se HEREDA, y de ahí la usan los hijos internos
   (ver .step-num más abajo) para encadenarse sin más JS.
   ─────────────────────────────────────────────────────────── */
/* El valor es una LISTA de palabras, no una sola: `data-stagger="auto fast"` es
   válido y combina el modo (auto) con la velocidad. De ahí el selector `~=`, que
   busca una palabra dentro del atributo en vez de exigir el valor exacto. */
[data-stagger] { --stagger: 90ms; }
[data-stagger~="fast"] { --stagger: 55ms; }
[data-stagger~="slow"] { --stagger: 140ms; }

/* (0,2,0) igual que `.reveal.d1`, así que el orden decidiría. No se deja al
   azar: motion.js le quita las clases .d1–.d6 a estos hijos. */
[data-stagger] > .reveal {
  transition-delay: calc(var(--i, 0) * var(--stagger, 90ms));
}

/* ⚠️ TRAMPA DEL MODO "auto": no lo pongas en un contenedor cuyos hijos tengan un
   :hover con transform. Al promoverlos a .reveal heredan
   `.reveal.visible { transform: none }` (0,2,0), que le gana a un
   `.mi-clase:hover { transform: scale(…) }` (0,2,0) por ir después en el <style>
   inline — y ese hover deja de funcionar, sin error y sin nada que lo delate.
   Pasó al intentarlo con `.color-swatches`; el comentario está también allá, en
   el HTML, porque es ahí donde da la tentación de volver a ponerlo. */

/* ───────────────────────────────────────────────────────────
   3. PARALLAX  ·  data-parallax
   CSS scroll-driven nativo: sin JS, sin listener de scroll, sin un rAF más
   peleándole la GPU al globo del hero.
   Soporte ~84% (Chrome/Edge 115+, Safari 18+; Firefox es el rezagado). Va dentro
   de @supports a propósito: quien no lo tenga ve la página EXACTAMENTE como hoy.
   Es decoración — no puede ser la única forma de que algo se vea.

   Usa la propiedad `translate`, NO `transform`: así convive con el transform del
   .reveal en el mismo elemento sin que uno pise al otro.
   ─────────────────────────────────────────────────────────── */
@supports (animation-timeline: view()) {
  [data-parallax] {
    animation: cgParallax linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
    will-change: translate;
  }
  @keyframes cgParallax {
    from { translate: 0 var(--px-from, 48px); }
    to   { translate: 0 var(--px-to, -48px); }
  }
}

/* ───────────────────────────────────────────────────────────
   4. SPOTLIGHT  ·  data-spotlight
   Un brillo que sigue al cursor dentro de la tarjeta. Las coordenadas las
   escribe motion.js en --mx/--my.
   El ::after va con pointer-events:none o se come los clics de lo que haya
   dentro, y con border-radius:inherit o el brillo asoma por las esquinas.
   ─────────────────────────────────────────────────────────── */
[data-spotlight] { position: relative; }
[data-spotlight]::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  background: radial-gradient(
    420px circle at var(--mx, 50%) var(--my, 50%),
    oklch(100% 0 0 / 0.16),
    transparent 62%
  );
}
[data-spotlight]:hover::after { opacity: 1; }

/* Sobre fondo CLARO el brillo blanco no se ve: ahí tiñe con el azul de marca. */
[data-spotlight][data-spotlight-tone="ink"]::after {
  background: radial-gradient(
    420px circle at var(--mx, 50%) var(--my, 50%),
    oklch(50% 0.13 232 / 0.1),
    transparent 62%
  );
}

/* ───────────────────────────────────────────────────────────
   5. LA LÍNEA DE "CÓMO FUNCIONA" SE DIBUJA
   `.steps-grid::before` es la línea de tiempo que une los 4 pasos, y hasta ahora
   ya estaba dibujada antes de que llegaras. Ahora se traza de izquierda a
   derecha y los números van apareciendo detrás de ella.
   ─────────────────────────────────────────────────────────── */
.steps-grid[data-line]::before {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.2s cubic-bezier(0.22, 1, 0.36, 1) 0.15s;
}
.steps-grid[data-line].is-drawn::before { transform: scaleX(1); }

/* El número entra con un rebote corto, encadenado al --i que hereda de la
   tarjeta (+160ms para que salga DESPUÉS de que la tarjeta ocupe su sitio). */
.steps-grid[data-line] .step-num {
  transform: scale(0.35);
  opacity: 0;
  transition:
    transform 0.72s cubic-bezier(0.34, 1.56, 0.64, 1),
    opacity 0.35s ease;
  transition-delay: calc(var(--i, 0) * var(--stagger, 90ms) + 160ms);
}
.steps-grid[data-line] .reveal.visible .step-num {
  transform: scale(1);
  opacity: 1;
}

/* ───────────────────────────────────────────────────────────
   6. HOVER CON MÁS CUERPO
   El hover de hoy es translateY(-4px) en todo. Se le suma escala y una sombra
   que crece, y —lo que más se nota— una curva con rebote en vez de `ease`.
   ─────────────────────────────────────────────────────────── */
/* El atributo va repetido (`[data-lift][data-lift]`) para subir la especificidad
   a 0,3,0 sin recurrir a !important: las tarjetas ya traen su propio
   `.service-card:hover { transform: translateY(-4px) }` (0,2,0) en el <style>
   inline, que al empatar ganaría por ir después en el documento. */
[data-lift][data-lift] {
  transition:
    transform 0.45s cubic-bezier(0.34, 1.4, 0.64, 1),
    box-shadow 0.45s ease,
    border-color 0.45s ease,
    background 0.45s ease;
}
[data-lift][data-lift]:hover { transform: translateY(-7px) scale(1.015); }

/* ───────────────────────────────────────────────────────────
   7. SCROLL SUAVE (Lenis)
   Lenis pone la clase .lenis en <html> cuando arranca. El `scroll-behavior:
   smooth` del CSS original tiene que apagarse ahí: si no, cada scrollIntoView
   lo animan los DOS a la vez y el ancla llega temblando.
   Si el script de Lenis no carga (CDN caído, red del colegio bloqueándolo), esta
   clase nunca aparece, el scroll-behavior original sigue en pie y no se pierde
   nada. Es la razón de que la regla cuelgue de la clase y no del elemento.
   ─────────────────────────────────────────────────────────── */
html.lenis, html.lenis body { height: auto; }
html.lenis { scroll-behavior: auto; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }

/* ───────────────────────────────────────────────────────────
   8. ACCESIBILIDAD
   El .reveal original ya respeta prefers-reduced-motion; estas variantes tienen
   que hacerlo también, y con MÁS razón: el blur y la escala son de los efectos
   que peor caen a quien tiene sensibilidad al movimiento.
   Todo queda en su estado final, visible y quieto.
   ─────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .reveal[data-anim],
  .reveal[data-anim].visible {
    opacity: 1;
    transform: none;
    filter: none;
    clip-path: none;
    transition: none;
  }
  [data-stagger] > .reveal { transition-delay: 0s; }
  [data-parallax] { animation: none; translate: none; }
  [data-lift][data-lift]:hover { transform: none; }
  [data-spotlight]::after { display: none; }
  .steps-grid[data-line]::before { transform: scaleX(1); transition: none; }
  .steps-grid[data-line] .step-num {
    transform: scale(1);
    opacity: 1;
    transition: none;
  }
}
