/* Las cuatro fases del home, hacia dentro.

   La hoja tiene DOS maquetaciones y el atributo `data-fases` decide cuál. Sin
   él —sin JS, con reduced-motion, con ahorro de datos, en una pantalla baja— la
   sección es un documento normal: cuatro bloques apilados, cada uno con su
   palabra, su resultado y sus piezas. Con él, la sección reserva su recorrido, el
   escenario se pega y las cuatro fases se apilan en el mismo sitio, cada una con
   su propia escala.

   La maquetación cuelga de la PRESENCIA del atributo, nunca de su valor.

   POR QUÉ LA PALABRA Y LA COPIA SE ANCLAN AL CENTRO Y NO VAN EN FLUJO. El origen
   del zoom es el centro exacto del escenario, y de ahí es de donde crece la fase
   que llega. Anclando la palabra por encima de ese punto y la copia por debajo
   —las dos contra el escenario— el centro geométrico cae siempre en el mismo
   sitio, pesen lo que pesen las dos piezas. En flujo, una fase con dos etiquetas
   y otra con cuatro tendrían orígenes distintos y el zoom daría un salto al
   cambiar de acto.

   POR QUÉ NO HAY NADA DIBUJADO EN EL CENTRO. Se generaron tres direcciones y las
   tres dibujaron por su cuenta una banda más oscura o unos filetes para señalar
   ese punto. Es exactamente el mueble que §6.4 prohíbe y por el que se retiró
   `.sim-overlay` —«el marco de una interfaz de grabación encima sólo lo
   contradice»—. La profundidad la dan dos cosas que no son adorno: la diferencia
   de escala, y que la palabra en foco se sale del cuadro por los cuatro lados en
   cuanto pasa de escala 1. Estar dentro de algo se ve porque no cabe.

   Y SIGUE SIN HABERLO. Desde el 15 de agosto de 2026 hay un FILM a sangre detrás
   de las cuatro palabras, con excepción declarada en §6.4 de la skill del
   sistema visual, y no contradice el párrafo de arriba: aquellas tres
   direcciones dibujaban un mueble para SEÑALAR el origen del zoom —una banda,
   unos filetes—, y eso sigue prohibido. El film no señala nada: es el mundo
   dentro del cual se entra. El centro del escenario sigue vacío.

   Y SE RASCA, NO SE REPRODUCE. Es un solo recorrido de 16 s que atraviesa el
   visor y la lectura del detector, y el scroll dice DÓNDE está la cámara dentro
   de él. Cuatro clips en bucle relevándose fue la primera versión y estaba mal
   por construcción: el mundo avanzaba por su cuenta mientras el motor decía que
   la cámara la mueve el lector. Un film rascado dice lo mismo que la escala.

   POR QUÉ EL CIELO NO ROMPE EL ARGUMENTO DEL `will-change`. Aquél es sobre el
   rasterizado del TIPO a 216 aumentos. El lienzo no se transforma nunca y es UNO,
   así que es una sola capa compuesta. Ver el bloque 2.

   POR QUÉ `overflow:clip` Y NO `hidden`. `hidden` deja un contenedor de scroll, y
   aquí las fases rebasan la caja en los dos ejes. Un Ctrl+F, un enlace a fragmento
   de texto o un `scrollIntoView` le escriben `scrollLeft`/`scrollTop` y la
   composición se queda torcida para el resto de la sesión. `clip` recorta igual y
   no se puede desplazar. La misma página ya lo usa en `home-sequence.css:22`.

   POR QUÉ NO HAY `will-change` EN NINGUNA PARTE. Es la decisión de la que depende
   que esto se lea. `will-change:transform` fija el rasterizado a la escala del
   momento en que se promociona la capa y luego lo estira: con un zoom de 216
   aumentos, el tipo llega deshecho al foco. Sin la pista, el navegador vuelve a
   rasterizar en cada escala y la palabra sale limpia justo donde importa. De paso
   son cuatro capas compuestas menos, en la línea de las tres que ya se retiraron
   en `pages.css:1049` y `pages.css:100`.

   EL BLOQUE DE REDUCED-MOTION VA AL FINAL Y CON LA MISMA ESPECIFICIDAD que las
   reglas encendidas, a propósito: así gana por orden de fuente y esta hoja no
   necesita un solo `!important`. */

/* ─────────────────────────────────────────────────────────────────────────────
   1. El documento. Lo que se ve sin JS, y la base sobre la que monta el resto.
   ───────────────────────────────────────────────────────────────────────────── */

.home-fases{
  position:relative;background:var(--surface);
  /* El aire entre la palabra y su copia, repartido a los dos lados del centro
     exacto del escenario, que es el origen del zoom.
     Fue un hueco de 180px mientras la fase siguiente se veía ahí dentro, en
     miniatura. Ya no se ve —una fase por acto— así que el hueco vuelve a ser lo
     único que sigue siendo: la separación entre un titular y su bajada. Sigue
     estando CENTRADO en el origen y no debajo de la copia, porque de ahí es de
     donde crece la fase que llega. */
  --fase-apertura:clamp(28px,5.5vh,64px)}

.fases-stage{position:relative}

.fases-mundo{
  display:grid;gap:var(--s9);
  max-width:var(--maxw);margin:0 auto;padding:var(--s9) var(--gutter)}

.fase{position:relative}

/* EL HALO ES EL VELO DE LA PALABRA, y existe porque el velo de la sección no
   puede llegar donde falla.

   Lo que el velo sí resuelve, medido sobre los 64 fotogramas del film: el peor
   píxel posible de un fotograma es el blanco, y tras `--ink-a60` compone
   rgb(104,107,110), sobre el que `--text` da 5,00:1. Ése es el SUELO, no una
   media: ningún fotograma presente ni futuro puede bajar de ahí mientras el velo
   esté puesto. Medido fotograma a fotograma bajo la banda de versales de las
   cuatro palabras, en 1440x900 y en 390x844 y en las dos lenguas, el peor p99
   real es 5,02:1 (LEARN, fotograma 5). El mínimo AA para texto grande es 3,0:1.

   Lo que el velo NO resuelve es el propio glifo, y ahí es donde el 1 % del área
   se cae:

   · LA PALABRA SE DESVANECE. `--fase-op` lleva la fase entera de 0 a 1 al entrar
     y de 1 a 0 al salir, y en ese tramo la tinta efectiva es
     `op·--text + (1-op)·fondo`. Sobre el peor fondo velado eso da 2,91:1 a
     op=0,60 y 2,49:1 a op=0,50: por debajo de op=0,621 la palabra deja de pasar
     AA. Y no es un fotograma de animación que pase de largo, es una POSICIÓN DE
     SCROLL: quien pare ahí se queda ahí.
     CUÁNTO SCROLL ES, Y LA CUENTA QUE NO VALE. Aquí ponía 225 px por acto, el
     32 %, y estaba mal por 3,4 veces: sale de repartir la ventana de `d` sobre el
     acto como si `z` avanzara linealmente con `x`, y no lo hace. `calcular()`
     suma dos smoothstep por acto y le da al reposo sólo `DERIVA` = 0,08 del
     recorrido de `z`; el 92 % restante se comprime en el 39 % del acto que es el
     cruce, y el desvanecimiento ocurre entero ahí dentro. Invirtiendo el `z(x)`
     real y barriendo el recorrido en pasos de 0,05 px, contando una posición como
     sub-AA si CUALQUIER fase visible lo está: son **65 px por cruce**, 196 px de
     los 2.527 del recorrido, el 7,7 %.
   · EL ASTA ES CASI TODO BORDE. Montserrat 300 tiene un asta de ~0,052 em: a
     390 px, con el tipo en 41,3 px, son 2,15 px, de los que el antialias se come
     ~1 px por lado. El núcleo opaco del asta mide entonces ~0,15 px, y un
     escáner que muestrea píxeles bajo la palabra lee ese borde a medio camino
     entre la tinta y el fondo.

   LOS DOS DAN LA MISMA CUENTA, y es la que resuelve el 1 % que canta la
   auditoría: un píxel con fracción k de tinta del glifo vale 104 + 140·k. Los
   tres valores publicados salen exactos —1,59:1 es k=0,65, 1,92:1 es k=0,53 y
   1,39:1 es k=0,75—, y ninguno de los tres lo puede pintar el film: a través de
   `--ink-a60` ni el blanco puro pasa de 104, que es justamente el p95 que la
   misma auditoría mide (4,90..5,21:1). O sea que el 1 % no es fondo: es glifo.

   LA CURA, y por qué es un halo y no una banda. §6.4 prohíbe por su nombre
   dibujar una banda más oscura en el centro del escenario —el mueble que
   señalaría el origen del zoom—, y la cabecera de esta hoja cuenta que ya se
   retiró tres veces. Un `text-shadow` no es mueble: tiene la forma de la palabra,
   no hay nada que ver cuando no hay palabra, y no cuesta ni un elemento ni una
   capa compuesta ni un píxel de maquetación.

   LAS DOS CAPAS, y las dos están calibradas:
   · `0 0 .06em var(--ink-a90)` es la que posee los píxeles pegados al asta. Un
     `text-shadow` es la silueta desenfocada con una gaussiana de sigma=radio/2,
     y justo en el borde de una forma sólida la cobertura es 0,5; con `--ink-a90`
     eso deja alfa 0,45 ahí mismo. Sobre el peor fondo velado el borde del asta
     pasa de 5,00:1 a 12,48:1, y a .02 em del asta sigue en 9,36:1 y a .06 em en
     6,54:1.
   · `0 .03em .10em var(--ink-a55)` es la sombra que el sitio ya da a un titular
     sobre imagen —`0 4px 30px var(--ink-a45)` en `.fold-copy h1` y en
     `.sequence-chapter h2`—, escrita en `em` para que valga igual a 172 px y a
     41 px, y un escalón más densa porque este fondo no es una fotografía
     corregida sino un film que atraviesa el blanco dos veces.
     EL RADIO ES .10em Y NO .22em, Y LA RAZÓN ES DE PINTADO. Un `text-shadow` se
     rasteriza con el elemento, y aquí el elemento lo escala la cámara: la fase
     sigue teniendo `--fase-op > 0` hasta `d = SALE_B`, así que el radio efectivo
     es el declarado por `6^d`. Con .22em son 33 px en reposo y **150 px** en el
     peor fotograma con la palabra aún visible (d=0,84, escala 4,54), y esta hoja
     se niega a propocionar la capa a propósito —el argumento del `will-change` de
     la cabecera—, así que eso se re-rasteriza en cada fotograma de scroll de la
     interacción insignia del inicio. Con .10em son 15 px y 68 px, y el coste del
     desenfoque va con el cuadrado del radio: el 21 %.
     NO SE PAGA NADA EN CONTRASTE, y por eso es la salida y no un recorte. En el
     borde del asta la cobertura de una silueta desenfocada es 0,5 sea cual sea el
     radio, así que el borde sigue en 12,48:1 y el límite de opacidad sigue en
     0,394, idénticos. Lo único que baja es la cola: a .06 em del asta, 5,65:1 en
     vez de 6,54:1. Los dos siguen al doble del 3,0:1 que se exige.
     La otra salida estudiada era `calc(.22em / var(--fase-z))`, que deja el radio
     constante en píxeles de pantalla. Se descarta: `apagar()` no retira los
     `--fase-*` en línea, así que un cambio a movimiento reducido a media sesión
     dejaría el radio dividido por una escala fantasma, y además haría del
     `text-shadow` una propiedad que cambia en cada fotograma. Un número más
     pequeño no tiene ninguno de los dos problemas.

   CON EL HALO, LA PALABRA AGUANTA 3,0:1 HASTA `--fase-op` 0,394, contra 0,621
   sin él: los 65 px sub-AA de cada cruce bajan a 51 —196 px del recorrido bajan
   a 154, del 7,7 % al 6,1 %—, y lo que queda es la cola en la que la fase ya es
   la pared por la que se pasa y no una palabra que se lee.

   NO HACE FALTA APAGARLO EN NINGÚN ESTADO. Sin motor, con reduced-motion o en
   papel el fondo de la palabra es `--surface` (#03080d) o el blanco del papel:
   en el primer caso el halo es tinta sobre la misma tinta y no se ve, y en el
   segundo lo anula `print.css` en su reset universal, el
   `*,*::before,*::after` que apaga sombras, filtros y animaciones de golpe.

   Y EL PESO SUBE DE 300 A 400 por lo que dice esta misma hoja hermana: «el 300
   editorial queda para las secciones que se leen con la página quieta, no sobre
   un fotograma en movimiento» (el comentario de `.sequence-chapter h2` en
   `home-sequence.css`, que por eso pone el capítulo del film en 600). El 400 lleva el asta de ~0,052 em a ~0,070 em, o
   sea de 2,15 px a 2,89 px a 390 px: triplica el núcleo opaco que le queda al
   antialias. Montserrat es variable 300-900 (`fonts.css`), así que no cuesta una
   descarga más. */
.fase-palabra{
  margin:0;color:var(--text);
  font-family:var(--font-display);font-weight:400;
  font-size:clamp(34px,10.4vw,172px);line-height:.92;letter-spacing:-.012em;
  text-transform:uppercase;
  text-shadow:0 0 .06em var(--ink-a90), 0 .03em .10em var(--ink-a55)}

/* `--text` Y NO `--text-2`, QUE SERÍA EL ROL DE UNA BAJADA, porque esta bajada
   no se apoya en una superficie plana sino en un VÍDEO a través de un velo. El
   peor píxel de un clip es el blanco: compuesto con `--ink-a60` da rgb(104,107,110),
   sobre el que `--text-2` (#a6b7c6) mide 2,67:1 y no pasa el 4,5:1 que exigen
   estos cuerpos de 15-22px. `--text` mide 5,10:1 y pasa. La jerarquía frente al
   titular no la pierde: la lleva el cuerpo, 172px contra 15px.
   Mismo argumento y mismas palabras que en `pages.css` para `.band p`. */
.fase-logro{
  margin:var(--s4) 0 0;color:var(--text);
  font-size:clamp(17px,1.5vw,22px);line-height:1.2}

.fase-piezas{
  display:flex;flex-wrap:wrap;gap:var(--s5);
  margin:var(--s5) 0 0;padding:0;list-style:none}

.fase-piezas li{
  color:var(--text);
  font-size:15px;line-height:1.2}

/* EL CIELO NO EXISTE EN EL DOCUMENTO. No es optimización, es corrección: aquí
   `.fases-mundo` no está posicionada, así que un cielo `position:absolute`
   pintaría por encima de las cuatro fases apiladas en flujo. */
.fase-cielo{display:none}

/* ─────────────────────────────────────────────────────────────────────────────
   2. La profundidad.

   `--fases-run` lo escribe `fases.js` en su primera pasada síncrona, antes de que
   el observador exista. `--fase-z`, `--fase-op` y `--fase-copia` los escribe por
   fase y por fotograma, y son los tres únicos que quedan.
   ───────────────────────────────────────────────────────────────────────────── */

.home-fases[data-fases]{height:calc(100svh + var(--fases-run,2800px))}

.home-fases[data-fases] .fases-stage{
  position:sticky;top:0;height:100svh;
  overflow:clip;overflow-clip-margin:0;contain:layout paint}

/* EL CIELO: EL RECORRIDO DETRÁS DE LA PALABRA.
   Se declara aquí y no en el bloque 1 porque sólo existe con el motor puesto.

   NO SE TRANSFORMA NUNCA —`inset:0`, cero `transform`—, y por eso no toca el
   argumento del `will-change` de la cabecera: aquél es sobre el rasterizado del
   TIPO a 216 aumentos; aquí el compositor sólo presenta un lienzo en un cuadro
   fijo. Es UNA capa compuesta, no cuatro, y es la razón de que el cielo sea un
   lienzo rascado y no cuatro vídeos que se relevan.

   El orden de pintado sale del orden del documento y no hace falta un solo
   `z-index`: lienzo → `.fases-mundo` (que trae el velo en su fondo) → las fases. */
.home-fases[data-fases] .fase-cielo{
  position:absolute;inset:0;display:block;
  width:100%;height:100%}

/* EL VELO ES EL FONDO DE `.fases-mundo`, NI UNA CAPA NI UN PSEUDO-ELEMENTO, y
   hay tres razones. Es la única caja que ya está entre los cielos y las cuatro
   fases, así que sale gratis: cero elementos, cero capas compuestas, cero
   `z-index`. Es ANCESTRO del texto, que es lo único que `audit-contraste` sabe
   componer. Y es un COLOR PLANO y no un degradado porque el auditor descarta
   todo `background-image`: un degradado aquí sería un velo que nadie mide.

   TOKEN Y NO LITERAL. `rgba(3,8,13,var(--algo))` como el hero obligaría a un
   literal de color fuera de `tokens.css` con su escape declarado, y sería el
   SEGUNDO del repo — hoy `pages.css:958` presume de ser el único. El hero
   necesita alfa por ruta porque tiene 96 fotografías distintas; aquí son cuatro
   clips que se graban a la vez y se corrigen juntos.

   Y VA SIN PUERTA DE `[data-fases]`: sobre `--surface` (--ink-950, #03080d),
   `rgba(3,8,13,.60)` compone exactamente #03080d. En el documento estático el
   velo es invisible y cuesta cero, así que una sola pila para los dos estados
   es una sola medición. */
.fases-mundo{background:var(--ink-a60)}

.home-fases[data-fases] .fases-mundo{
  position:absolute;inset:0;
  display:block;max-width:none;margin:0;padding:0}

/* Las cuatro ocupan el mismo sitio y se distinguen sólo por su escala. El orden
   del documento ya las apila bien: la fase más honda va después, así que pinta
   encima de aquella dentro de la cual está. No hace falta un solo `z-index`.
   `scale()` en 2D y no `scale3d`: promocionar la capa es justo lo que arruina el
   rasterizado del tipo. */
/* Sin `visibility`, y es una corrección y no una limpieza: ocultaba tres de las
   cuatro fases del árbol de accesibilidad y de la búsqueda en página. La razón
   larga está en `fases.ts`, donde se retiró la variable que la alimentaba. */
.home-fases[data-fases] .fase{
  position:absolute;inset:0;
  transform-origin:50% 50%;
  transform:scale(var(--fase-z,1));
  opacity:var(--fase-op,1)}

.home-fases[data-fases] .fase-palabra{
  position:absolute;left:var(--gutter);right:var(--gutter);
  bottom:calc(50% + var(--fase-apertura) / 2);
  text-align:center}

.home-fases[data-fases] .fase-copia{
  position:absolute;left:var(--gutter);right:var(--gutter);
  top:calc(50% + var(--fase-apertura) / 2);
  text-align:center;
  opacity:var(--fase-copia,1);
  transform:translateY(calc((1 - var(--fase-copia,1)) * 12px))}

.home-fases[data-fases] .fase-logro{margin:0}
.home-fases[data-fases] .fase-piezas{justify-content:center}

/* ─────────────────────────────────────────────────────────────────────────────
   3. Compacto. El gutter ya baja a 22px en `tokens.css:544`.
   ───────────────────────────────────────────────────────────────────────────── */

@media (max-width:760px){
  .fase-palabra{font-size:clamp(28px,10.6vw,64px)}
  .fases-mundo{gap:var(--s8);padding:var(--s8) var(--gutter)}
  .home-fases[data-fases] .fase-piezas{gap:var(--s4)}
}

/* ─────────────────────────────────────────────────────────────────────────────
   4. Sin movimiento. Misma especificidad que el bloque 2 y más abajo en la hoja,
      así que gana por orden. `fases.js` ya no enciende con esta preferencia; este
      bloque cubre el cambio a media sesión, cuando el atributo ya está puesto.
   ───────────────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion:reduce){
  .home-fases[data-fases]{height:auto}
  .home-fases[data-fases] .fases-stage{
    position:relative;height:auto;overflow:visible;contain:none}
  .home-fases[data-fases] .fases-mundo{
    position:static;display:grid;gap:var(--s9);
    max-width:var(--maxw);margin:0 auto;padding:var(--s9) var(--gutter)}
  .home-fases[data-fases] .fase{
    position:relative;inset:auto;transform:none;opacity:1}
  .home-fases[data-fases] .fase-palabra{
    position:static;text-align:left}
  .home-fases[data-fases] .fase-copia{
    position:static;text-align:left;opacity:1;transform:none}
  .home-fases[data-fases] .fase-logro{margin:var(--s4) 0 0}
  .home-fases[data-fases] .fase-piezas{justify-content:flex-start}
  /* El cielo se va con el gesto. `nivel()` ya devuelve `static` con esta
     preferencia y `apagar()` retira `data-fases`, así que esta regla es el
     cinturón del tirante: cubre el instante entre que la consulta cambia y
     `apagar()` corre.
     Y AQUÍ NO SE PAUSA NADA, PORQUE NO HAY NADA QUE PAUSAR. Esta línea decía que
     `display:none` no para un vídeo y que la pausa la hacían dos líneas de
     `apagar()`. Las dos mitades eran falsas y venían de la primera versión, la de
     cuatro clips relevándose: el cielo es un `<canvas>` rascado, no un medio, no
     tiene reloj, y `apagar()` no para nada —lo único que hace con el lienzo es
     olvidar el fotograma pintado, y su propio comentario lo dice—. Ocultarlo aquí
     es todo lo que hay que hacer: sin fotograma que dibujar, `pintar` no vuelve a
     tocar el lienzo mientras el atributo no esté. */
  .home-fases[data-fases] .fase-cielo{display:none}
}
