/* ═══════════════════════════════════════════════════════════════════════════
   MOVIMIENTO — la capa espectacular

   Todo lo de aquí es prescindible: si este archivo no se carga, la página
   sigue funcionando y sigue estando bien. Por eso va aparte y va el último.

   Las tres reglas del proyecto no se rompen ni una vez en este archivo:

     1. Solo se animan `transform`, `translate`, `rotate`, `scale` y `opacity`.
        Las cinco las resuelve el compositor. Ni un `filter`, ni un `width`, ni
        un `background-position` animado.

     2. Ni un listener de `scroll`. Lo atado al scroll va con
        `animation-timeline`, que el navegador resuelve fuera del hilo
        principal. Lo que solo necesita saber "ya se ve" reutiliza el
        IntersectionObserver que ya existe (`[data-reveal]` → `.is-in`).

     3. Todo lo que dependa de líneas de tiempo de scroll vive dentro de un
        `@supports`. Sin soporte no se pierde contenido: se pierde el efecto.

   ── EL TRUCO QUE SOSTIENE MEDIO ARCHIVO ─────────────────────────────────────
   Varias cosas de aquí son una SEGUNDA animación encima de un elemento que ya
   tenía la suya. Dos animaciones que tocan la misma propiedad no se suman: la
   última de la lista gana y la otra desaparece.

   Por eso lo nuevo casi nunca usa `transform`, sino las propiedades
   independientes `translate`, `rotate` y `scale`. Son propiedades distintas,
   así que conviven con el `transform` que ya estaba y el navegador las compone
   solo (translate → rotate → scale → transform). Es lo que permite, por
   ejemplo, que cada letra de la portada mantenga su animación de entrada Y
   además se disperse con el scroll, sin que una pise a la otra.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ══ 1 · EL REVELADO, CON PROFUNDIDAD ════════════════════════════════════════
   El revelado de `base.css` es un desplazamiento plano hacia arriba. Aquí pasa
   a ser una carta que llega desde el fondo: se acerca en Z y se endereza.

   La perspectiva va DENTRO del propio `transform` —`perspective()` como
   función, no como propiedad del padre— a propósito: si la pusiera en el
   contenedor, todos los hijos compartirían un mismo punto de fuga y los de los
   bordes entrarían torcidos hacia el centro. Así cada elemento tiene su fuga
   en su propio centro y todos entran igual, estén donde estén.               */

.js [data-reveal] {
  transform: perspective(60rem) translate3d(0, 2.4rem, -7rem) rotateX(7deg);
  transform-origin: 50% 100%;
  transition:
    opacity   1.05s var(--ease-out) var(--reveal-delay, 0s),
    transform 1.05s var(--ease-out) var(--reveal-delay, 0s);
}

/* El estado final sigue viviendo en `base.css` (`.is-in { transform: none }`),
   que es más específico que esto y gana. No hace falta repetirlo. */


/* ══ 2 · LA PORTADA: EL NOMBRE SE DISPERSA ═══════════════════════════════════
   El nombre ya crecía y se abría con el scroll. Ahora, además, cada letra se
   separa de sus vecinas y las de los extremos se adelantan hacia el ojo: la
   palabra no se va, te atraviesa.

   El reparto sale de `--l`, el índice que cada letra ya trae en el marcado. La
   letra 3 es el centro, así que `(--l - 3)` da la distancia con signo, y ese
   mismo valor al cuadrado —CSS sabe multiplicar dos números— da la profundidad
   sin necesidad de un `abs()` que todavía no está en todas partes.           */

@supports (animation-timeline: view()) {

  .hero .wordmark {
    /* La fuga, aquí y no en el `.hero__inner`: el punto de fuga tiene que ser
       el centro de la palabra, no el de la pantalla. */
    perspective: 46rem;
  }

  .hero .wordmark span {
    /* Distancia al centro y su cuadrado. Se calculan una vez por letra. */
    --dx: calc(var(--l) - 3);
    --dz: calc((var(--l) - 3) * (var(--l) - 3));

    /* La entrada de siempre + la dispersión. La primera va en tiempo, la
       segunda en scroll: `animation-timeline` admite una lista, igual que
       todas las demás. */
    animation-name: letter-in, letter-spread;
    animation-duration: 1.05s, auto;
    animation-timing-function: var(--ease-out), linear;
    animation-fill-mode: both, both;
    animation-delay: calc(.35s + var(--l) * .055s), 0s;
    animation-timeline: auto, --hero;
    animation-range: normal, contain;
  }

  @keyframes letter-spread {
    to {
      translate: calc(var(--dx) * 16%) -4% calc(var(--dz) * .55rem);
      rotate: calc(var(--dx) * 1.1deg);
    }
  }
}


/* ══ 3 · EL MANIFIESTO: LAS PALABRAS SE LEVANTAN ═════════════════════════════
   Las palabras ya se encendían una a una. Encender es poco: ahora además suben
   y crecen ese último 7% mientras se encienden, que es lo que hace que la
   frase parezca escribirse sola en vez de aparecer.

   La animación de opacidad (`light`, en `sections.css`) se queda tal cual y
   esta se le monta al lado: una toca `opacity`, la otra `translate` y `scale`.
   El tramo va un poco por delante y termina un poco después, para que el
   movimiento arranque antes que la luz y frene después. */

@supports (animation-timeline: view()) {
  .manifesto__word {
    animation-name: light, word-rise;
    animation-timeline: --man, --man;
    animation-timing-function: linear, var(--ease-out);
    animation-fill-mode: both, both;
    animation-range-start: cover calc(20% + var(--w) * 2.4%), cover calc(17% + var(--w) * 2.4%);
    animation-range-end:   cover calc(32% + var(--w) * 2.4%), cover calc(42% + var(--w) * 2.4%);
  }

  @keyframes word-rise {
    from { translate: 0 .34em; scale: .93; }
  }
}


/* ══ 4 · LOS TELÉFONOS ═══════════════════════════════════════════════════════
   Un teléfono de frente es una captura de pantalla. Girado y llegando desde el
   fondo es un producto. Entran con 13° de giro y se enderezan.

   El giro va en `rotate` y la profundidad en `translate` —propiedades sueltas,
   no `transform`— porque `.device` ya usa `transform` para el hover. Así el
   levantar-al-pasar-por-encima sigue funcionando durante y después de la
   entrada, en vez de pelearse con ella.

   La perspectiva sí va en el contenedor: aquí hay UN teléfono por caja y el
   punto de fuga compartido es justo lo que se quiere.                        */

.stack__stage,
.duo__device { perspective: 90rem; }

/* a) El teléfono ES el elemento revelado (los paneles de la pila). */
.js .device[data-reveal] {
  /* Se anula el desplazamiento genérico: aquí la profundidad la pone el giro,
     y sumar los dos deja el aparato entrando de lado y desde abajo a la vez. */
  transform: none;
  rotate: y -13deg;
  translate: 0 0 -6rem;
  transition:
    opacity   1.05s var(--ease-out) var(--reveal-delay, 0s),
    rotate    1.25s var(--ease-out) var(--reveal-delay, 0s),
    translate 1.25s var(--ease-out) var(--reveal-delay, 0s),
    transform  .6s  var(--ease-out);
}

/* b) El teléfono va DENTRO del elemento revelado (los dúos de texto + foto). */
.js .duo__device[data-reveal] {
  /* El bloque entero sube plano: el 3D es del aparato, no de su rótulo. */
  transform: translate3d(0, 2.2rem, 0);
}

.js .duo__device[data-reveal] .device {
  rotate: y -13deg;
  translate: 0 0 -6rem;
  transition:
    rotate    1.25s var(--ease-out) var(--reveal-delay, 0s),
    translate 1.25s var(--ease-out) var(--reveal-delay, 0s),
    transform  .6s  var(--ease-out);
}

.js .device[data-reveal].is-in,
.js .duo__device[data-reveal].is-in .device {
  rotate: none;
  translate: none;
}

/* El destello del cristal. `.device::after` ya era el reflejo corto del marco;
   lo único que se añade es que ENTRE, deslizándose, en vez de estar puesto
   desde el principio. Un reflejo que se mueve una vez es lo que separa un
   marco dibujado de un cristal de verdad.

   `overflow: hidden` en el marco para que el reflejo no se salga mientras se
   desliza. No afecta al halo: una `box-shadow` es del propio elemento y no la
   recorta su propio desbordamiento. */
.device { overflow: hidden; }

.js .device[data-reveal].is-in::after,
.js [data-reveal].is-in .device::after {
  animation: glass-sweep 1.5s var(--ease-soft) calc(var(--reveal-delay, 0s) + .35s) both;
}

@keyframes glass-sweep {
  from { translate: -60% 0; opacity: 0; }
  40%  { opacity: 1; }
  to   { translate: 0 0; opacity: 1; }
}


/* ══ 5 · LAS SECCIONES RETROCEDEN ════════════════════════════════════════════
   Lo que se va no se va del todo: se aleja. Cada sección se encoge un 5% y se
   apaga mientras sale por arriba, así la siguiente parece pasar por delante en
   vez de empujarla. Es lo que hace que la página se lea como planos y no como
   una lista de bloques.

   El tramo empieza en el 55% de la SALIDA, no antes. Medido en pantalla: el
   apagado arranca cuando a la sección le queda menos de la mitad visible y ya
   se está yendo. Antes de eso estaría apagando algo que todavía se lee, que es
   exactamente lo que no hay que hacer.

   Solo `.section` —los bloques de contenido normal—. La portada, el
   manifiesto, la pila, el riel y el cierre ya tienen su propia coreografía
   atada al scroll y meterles otra encima las descoloca.                      */

@supports (animation-timeline: view()) {
  .section {
    view-timeline-name: --sec;
    animation: recede linear both;
    animation-timeline: --sec;
    animation-range: exit 55% exit 100%;
  }

  @keyframes recede {
    to { scale: .95; opacity: .3; }
  }
}


/* ══ 6 · EL CIERRE ═══════════════════════════════════════════════════════════
   La fotografía del final entra ya acercada y se abre despacio hasta su tamaño
   real mientras la sección sube. Es el gesto más viejo del cine y sigue
   funcionando: la imagen se asienta justo cuando terminas de llegar.

   Escala de 1.28 a 1 y nunca por debajo de 1: la foto va a `cover`, y en
   cuanto bajara de 1 aparecería el fondo por los cantos. El desplazamiento
   vertical del principio es de un 3%, muy por dentro del sobrante que deja esa
   escala.                                                                     */

@supports (animation-timeline: view()) {
  .cta { view-timeline-name: --cta; }

  .cta__photo img {
    animation: cta-open linear both;
    animation-timeline: --cta;
    animation-range: entry 0% entry 100%;
  }

  @keyframes cta-open {
    from { scale: 1.28; translate: 0 3%; }
    to   { scale: 1;    translate: 0 0;  }
  }
}

/* Y el nombre se levanta letra a letra. El bloque deja de moverse como una
   caja —se le quita el revelado genérico— y el movimiento pasa a las letras,
   que suben desde debajo escalonadas por su `--l`. El `overflow: hidden` que
   ya tenía la sección hace de línea de corte: las letras aparecen desde detrás
   del borde, no desde la nada. */

.js .cta .wordmark[data-reveal] {
  opacity: 1;
  transform: none;
}

.js .cta .wordmark[data-reveal] span {
  opacity: 0;
  translate: 0 .32em;
  transition:
    opacity    .9s var(--ease-out) calc(var(--reveal-delay, 0s) + var(--l) * .06s),
    translate 1.1s var(--ease-out) calc(var(--reveal-delay, 0s) + var(--l) * .06s);
}

.js .cta .wordmark[data-reveal].is-in span {
  opacity: 1;
  translate: none;
}


/* ══ 7 · EL FONDO VIVO ═══════════════════════════════════════════════════════
   Una aurora fija detrás de toda la página que gira y crece con el scroll. No
   se ve en las secciones que traen su propio fondo; se ve justo en las que son
   negras, que son las que corrían el riesgo de parecer vacías.

   Va en `html::before` y no en `body::before` por dos motivos: el `body` lleva
   `overflow-x: clip`, que en algunos navegadores también recorta a los hijos
   fijos, y su fondo se propaga al lienzo, así que una capa suya podría acabar
   pintada por debajo de él.

   El desenfoque es ESTÁTICO. Se rasteriza una vez y lo que se anima encima es
   `rotate` y `scale`, que el compositor mueve sin volver a desenfocar nada. Un
   `blur` animado sobre una capa de este tamaño sería repintar media pantalla
   por fotograma.                                                              */

html::before {
  content: "";
  position: fixed;
  inset: -25vmax;
  z-index: -1;
  pointer-events: none;
  background: var(--aurora-halo);
  filter: blur(90px);
  opacity: .2;
}

@supports (animation-timeline: scroll()) {
  /* Solo de tablet para arriba. En un móvil, una capa fija y desenfocada del
     tamaño de la pantalla que además se mueve es el tipo de cosa que se paga
     en fotogramas justo mientras el dedo arrastra. Ahí se queda quieta. */
  @media (min-width: 861px) {
    html::before {
      will-change: transform;
      animation: aurora linear both;
      animation-timeline: scroll(root block);
    }

    @keyframes aurora {
      to { rotate: 150deg; scale: 1.35; }
    }
  }
}


/* ══ 8 · MOVIMIENTO REDUCIDO ═════════════════════════════════════════════════
   `base.css` ya recorta las duraciones a .01ms, y eso NO basta: una animación
   con `fill: both` y duración cero salta a su último fotograma y se queda ahí.
   Con las de esta página eso significa el nombre de la portada en `opacity: 0`
   y el riel del feed desplazado hasta el final. O sea: contenido que
   desaparece justo para quien ha pedido que nada se mueva.

   La única forma de apagarlas de verdad es `animation: none`, que además
   descarta el relleno. Y con las animaciones apagadas hay que devolver a su
   sitio lo que dependía de ellas: las secciones clavadas dejan de tener
   sentido —clavado y sin avanzar es contenido que no llega nunca— y el riel
   necesita recuperar su arrastre.                                            */

@media (prefers-reduced-motion: reduce) {

  *, *::before, *::after {
    animation: none !important;
    rotate: none !important;
    translate: none !important;
    scale: none !important;
  }

  .js [data-reveal],
  .js [data-reveal] span {
    opacity: 1 !important;
    transform: none !important;
  }

  /* Las secciones clavadas vuelven a ser secciones normales. */
  .hero,
  .manifesto,
  .rail { block-size: auto !important; }

  .hero__stage,
  .manifesto__pin,
  .rail__stage { position: relative !important; }

  /* El alto sí se suelta en los dos que lo reciben DENTRO del `@supports` —el
     escenario del riel y el del manifiesto—, pero no en el de la portada: ese
     100svh es su alto de siempre, no parte de la coreografía. */
  .manifesto__pin,
  .rail__stage { block-size: auto !important; }

  .manifesto__word { opacity: 1 !important; }

  /* Y el feed vuelve a ser un carrusel que se arrastra con el dedo. */
  .rail__pin {
    overflow-x: auto !important;
    scroll-snap-type: x mandatory !important;
  }
}
