/* ═══════════════════════════════════════════════════════════════════════════
   VIDA · CABECERA DE SECCIÓN  ·  .c-cabecera (+ __kicker __titulo)
   Capa de vida — se carga DESPUÉS de las 5 hojas de época.
   Contrato: 07-contrato-vida-y-armazon.md §A · doctrina MATERIA §0.
   ═══════════════════════════════════════════════════════════════════════════

   ── POR QUÉ ESTA HOJA ES DISTINTA A LAS OTRAS SIETE ────────────────────────
   Una cabecera no se pulsa, no se enfoca, no se rellena y no se recorre: NO ES
   INTERACTIVA. No tiene hover que calentar ni estado que cambiar. Su único
   momento de vida legítimo es LLEGAR A LA PANTALLA, y una sola vez. Todo lo
   demás sería decoración perpetua, que es justo lo que la doctrina prohíbe
   (§0.5: una sola cosa encendida por pantalla; el reposo es quieto).

   ── EL CONTRATO DE `.is-entrando` ──────────────────────────────────────────
   La clase la pone LA PÁGINA, no esta hoja:

       <header class="c-cabecera is-entrando">
         <p class="c-cabecera__kicker">EL VIAJE</p>
         <h2 class="c-cabecera__titulo">¿Dónde estás en la historia?</h2>
       </header>

   1. SIN la clase, esta hoja no existe. Todos los selectores de aquí abajo la
      exigen: si nadie la pone, la cabecera se pinta EXACTAMENTE como la dejó
      la piel de época. La página nunca depende de que alguien anime nada, ni
      de que llegue este CSS, ni de JavaScript. Este es el punto que manda.
   2. Se escribe en el HTML de servidor, en la cabecera principal de la vista.
      Un observador de scroll también sirve, pero NO hace falta y no lo trae
      esta capa (cero JS, contrato §A).
   3. Un solo disparo, idempotente: todas las animaciones van con
      `animation-fill-mode: backwards` y una sola iteración, así que al acabar
      el elemento VUELVE a su estado natural — el de la piel. Por eso la clase
      puede quedarse en el marcado para siempre sin dejar residuo: no hay ni un
      `forwards` que congele un estado intermedio.
   4. Quitar y volver a poner la clase = volver a reproducir la entrada. No la
      pongas en cabeceras que ya estaban en pantalla ni la recicles en listas.
   5. Una por vista. Si dos cabeceras entran a la vez, ya no entra ninguna:
      entra la página, que es otra cosa (§0.5).

   ── LA IDEA, POR ÉPOCA (una por época, ni una más) ─────────────────────────
     1 · CUEVA   el kicker se SOPLA: el pigmento llega como nube y se asienta
                 de fuera adentro, hasta que la marca queda seca y nítida.
     2 · PIEDRA  el grabado SE ASIENTA: el bloque baja el último milímetro y
                 las sombras del relieve llegan a su sitio.
     3 · PAPEL   TIPOS MÓVILES → el pliego se imprime: el cilindro cruza de
                 izquierda a derecha y la tinta aparece a su paso.
     4 · HIERRO  la corriente llega al cartel: el filete doble SE TRAZA y el
                 latón del kicker CAPTA LA LUZ del filamento (con parpadeo).
     5 · CABINA  la rayita del kicker SE ENCIENDE y el titular sube su luz.
                 Seco: 130 ms, cero desplazamiento.

   ── REGLAS QUE ESTA HOJA CUMPLE (contrato §A) ──────────────────────────────
   · Scope: todo cuelga de `html[data-epoca="N"] .c-cabecera.is-entrando…`.
     Ni un selector fuera de este componente.
   · Cero JS, cero marcado nuevo, cero recursos cargados.
   · Solo se animan `transform`, `opacity`, `filter`, `box-shadow`,
     `background-*` y `mask-*`. Nada que dispare layout.
   · La CAJA no cambia: los desplazamientos son `transform` y son transitorios;
     en reposo, ±0 px. Ningún `width/height/margin/padding/top/left`.
   · Pieles respetadas: no se pisa ni un `::before`/`::after` que la época ya
     pinte. Donde se toca un pseudo de la piel (el filete de la 4, la rayita de
     la 5) es SOLO para animarlo hacia su propio estado final — el permiso
     explícito del contrato («o sobre transiciones de lo existente»).
   · Contraste: ningún fotograma deja texto informativo por debajo de 4.5:1.
     Los suelos van medidos y anotados en cada sección. La regla de oro usada
     aquí: donde hay MÁSCARA, el píxel de tinta visible está siempre a su color
     final (borde duro, sin banda translúcida), así que el contraste del texto
     que se lee no baja NUNCA; y donde hay opacidad o brillo, el valor mínimo
     está calculado para que el peor fotograma siga por encima de 4.5:1.
   · `prefers-reduced-motion` → estado final directo (bloque al final).
   ═══════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   ÉPOCA 1 · CUEVA — EL KICKER SE SOPLA
   ───────────────────────────────────────────────────────────────────────────
   El fenómeno: pigmento soplado por un hueso contra la pared. No aparece: se
   POSA. Primero es una nube ocre demasiado grande y demasiado difusa, y en
   720 ms se contrae sobre la letra hasta que solo queda la marca y su rescoldo.

   Cómo está hecho, y por qué así:
   · El halo se hace con `drop-shadow` y NO con desenfoque de la letra. Un
     `blur()` sobre texto de 11px se come el contraste y no hay forma honesta
     de medirlo; un `drop-shadow` añade luz ALREDEDOR y deja el glifo intacto y
     nítido en todos los fotogramas. El pigmento que se asienta es el halo, que
     es exactamente lo que pasa en la pared.
   · Quien cuenta la historia es la ESCALA (1.20 → 1): la mancha llega más
     ancha que la marca y se recoge sobre ella. `scale()` compone con el
     `rotate: -1.4deg` de la piel (son propiedades distintas: la piel usa
     `rotate`, aquí `transform`) y comparte su `transform-origin: left center`,
     así que la nube se recoge hacia el arranque de la palabra: de fuera
     adentro, también en horizontal.
   · El titular entra con el MISMO gesto, más tarde, sin escala y con la mitad
     de halo: es la misma mano soplando, no una idea nueva.

   Contraste MEDIDO sobre el render (no sobre los tokens — ver abajo por qué):
     en reposo, la caja del kicker mide 9.78:1 y la del titular 10.61:1. NO son
     los 12.6:1 y 15.5:1 que salen de #FFC08A/#F2E3D2 sobre #0B0705, porque el
     fondo real de esta época no es el token: es una pared con resplandor de
     hoguera, bastante más clara que el negro nominal.
     Y hay un efecto que solo aparece al medir: EL PROPIO HALO SUBE EL FONDO.
     Un `drop-shadow` fuerte aclara los alrededores del glifo y baja el
     contraste real del texto — con los valores de la primera versión (halo
     .55/.38 y opacidad .40) el fotograma inicial caía a 2.60:1 ✘. De ahí los
     valores de ahora: halos a la mitad y suelos de opacidad altos.
     Suelos medidos: kicker 4.72:1 ✔ · titular 4.99:1 ✔  (mínimo de todo el
     viaje: esta época es la que va más justa, y por eso es la más ajustada)
   ═══════════════════════════════════════════════════════════════════════════ */

html[data-epoca="1"] .c-cabecera.is-entrando .c-cabecera__kicker{
  animation: vc1-soplado calc(var(--e-vel) * 1.15) var(--e-curva) backwards;
}
@keyframes vc1-soplado{
  0%{
    opacity: .68;                        /* con el halo puesto: 4.72:1 medido */
    transform: scale(1.20);
    filter:
      drop-shadow(0 0 9px rgba(255, 106, 31, .26))
      drop-shadow(0 0 22px rgba(201, 122, 61, .16));
  }
  52%{
    opacity: 1;
    filter:
      drop-shadow(0 0 5px rgba(255, 106, 31, .18))
      drop-shadow(0 0 12px rgba(201, 122, 61, .10));
  }
  100%{
    opacity: 1;
    transform: scale(1);
    filter:
      drop-shadow(0 0 0 rgba(255, 106, 31, 0))
      drop-shadow(0 0 0 rgba(201, 122, 61, 0));
  }
}

html[data-epoca="1"] .c-cabecera.is-entrando .c-cabecera__titulo{
  animation: vc1-posa var(--e-vel) var(--e-curva) calc(var(--e-vel) * .34) backwards;
}
@keyframes vc1-posa{
  0%{
    opacity: .66;                        /* con el halo puesto: 4.99:1 medido */
    filter: drop-shadow(0 0 13px rgba(255, 106, 31, .20));
  }
  100%{
    opacity: 1;
    filter: drop-shadow(0 0 0 rgba(255, 106, 31, 0));
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉPOCA 2 · PIEDRA — EL GRABADO SE ASIENTA
   ───────────────────────────────────────────────────────────────────────────
   El fenómeno: la inscripción no llega, se COLOCA. El bloque baja los últimos
   milímetros y, al apoyar, la sombra que proyectaba sobre la pared se acorta,
   se endurece y desaparece bajo la pieza: el relieve que la piel ya tiene
   grabado (`text-shadow` de 3 capas) queda como única sombra. Las sombras del
   relieve llegan a su sitio.

   Cómo está hecho:
   · La sombra que viaja es un `drop-shadow` propio (la que proyecta el bloque
     en el aire); la del relieve es la de la piel y no se toca ni un valor.
   · El recorrido es de 5px y de 3px: en esta época «apenas se mueven».
   · El brillo sube de .80 a 1 SOLO en el titular. El kicker de bronce NO se
     atenúa: medido, un brightness(.70) lo dejaría en 3.45:1 ✘. Se le mueve la
     sombra y nada más.

   Contraste medido sobre el render:
     titular 15.05:1 en reposo · suelo brightness(.80) → 9.51:1 ✔
     kicker  6.28:1 EN LOS CINCO FOTOGRAMAS, sin una décima de variación: no se
     le toca el color, solo la sombra. Seguro por construcción, no por ajuste.
   ═══════════════════════════════════════════════════════════════════════════ */

html[data-epoca="2"] .c-cabecera.is-entrando .c-cabecera__kicker{
  animation: vc2-apoya-kicker calc(var(--e-vel) * .9) var(--e-curva) backwards;
}
@keyframes vc2-apoya-kicker{
  0%{
    transform: translateY(-3px);
    filter: drop-shadow(0 5px 4px rgba(0, 0, 0, .80));
  }
  100%{
    transform: translateY(0);
    filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0));
  }
}

html[data-epoca="2"] .c-cabecera.is-entrando .c-cabecera__titulo{
  animation: vc2-asienta calc(var(--e-vel) * 1.1) var(--e-curva) calc(var(--e-vel) * .26) backwards;
}
@keyframes vc2-asienta{
  0%{
    transform: translateY(-5px);
    filter: brightness(.80) drop-shadow(0 9px 7px rgba(0, 0, 0, .85));  /* 9.51:1 medido */
  }
  64%{
    filter: brightness(1.02) drop-shadow(0 3px 2px rgba(0, 0, 0, .55));
  }
  100%{
    transform: translateY(0);
    filter: brightness(1) drop-shadow(0 0 0 rgba(0, 0, 0, 0));
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉPOCA 3 · PAPEL — TIPOS MÓVILES, TRADUCIDOS
   ───────────────────────────────────────────────────────────────────────────
   El encargo pedía «tipos móviles» y el original de MATERIA (demo 12) los hace
   con una letra por nodo: `.d12-impresa i` con `--i`, `--pr` y `--bl` propios.
   Aquí NO hay marcado nuevo (contrato §A), así que no hay letras sueltas que
   caer al componedor. La traducción honesta no es imitar el resultado con
   trucos: es contar la MISMA historia desde el otro lado de la prensa.

   En el taller pasan dos cosas. En el componedor caen los tipos uno a uno —eso
   necesita nodos—. En la prensa, el cilindro cruza la forma y la hoja sale
   impresa de izquierda a derecha —eso es una sola superficie y CSS puro lo hace
   entero—. Se elige la segunda: lo que ve el lector no es el componedor, es el
   pliego. Por eso la entrada de esta época es UNA PASADA DE RODILLO sobre toda
   la cabecera (kicker, filetes, losange y titular a la vez): es la forma
   completa entrando en contacto con el papel, que es literalmente lo que
   compone una página de tipos móviles.

   Cómo está hecho:
   · Máscara de BORDE DURO (`#000 0 50%, transparent 50%` a `mask-size: 200%`)
     desplazada con `mask-position` de 100% a 0%. Borde duro y no degradado a
     propósito: la prensa no difumina, imprime o no imprime. Y como efecto
     colateral el contraste queda blindado — no existe un solo píxel de tinta a
     opacidad intermedia: o no ha llegado el cilindro, o está a 14.5:1.
   · La máscara se pone sobre `.c-cabecera` y no sobre cada pieza porque los
     ornatos del kicker (el filete fino en `bottom:-6px` y el losange con su
     `box-shadow` de 5px) viven FUERA de la caja del kicker: una máscara sobre
     el kicker los decapitaría. Sobre la cabecera caen todos dentro.
   · TODAS las propiedades de máscara viven DENTRO del keyframe, ninguna en una
     regla estática. Medido: dejar la máscara puesta (aunque fuera opaca al
     100%) obliga a una pasada de composición extra y mueve el antialiasing del
     texto 1/255 respecto a la cabecera sin animar. Declarándola solo en el
     keyframe, al terminar la animación la máscara DESAPARECE y el pliego queda
     píxel a píxel idéntico al de la piel. El `mask-image` es el mismo en 0% y
     100%, así que da igual que el navegador lo interpole o lo conmute.
   · Segunda hebra: el pliego se asienta (`translateY` de 3px + `scaleY(.994)`,
     la mordida de la platina) durante toda la pasada, con la curva de la época.
     No es una idea nueva: es la misma prensa.
   · Duraciones sobre `--e-vel` (400 ms): pasada 520 ms lineal —un cilindro va a
     velocidad constante— y asiento 700 ms con `--e-curva`.

   Contraste medido sobre el render:
     14.53:1 en el kicker y en el titular, EN TODOS LOS FOTOGRAMAS y clavado al
     valor de reposo: la máscara no toca el color, solo decide si el píxel
     existe. Es la única época del viaje cuyo suelo de entrada ES su techo ✔
     (En t=0 exacto no hay tinta que medir: es papel en blanco, no texto flojo.)
   ═══════════════════════════════════════════════════════════════════════════ */

html[data-epoca="3"] .c-cabecera.is-entrando{
  animation:
    vc3-rodillo calc(var(--e-vel) * 1.3) linear backwards,
    vc3-pliego  calc(var(--e-vel) * 1.75) var(--e-curva) backwards;
}
@keyframes vc3-rodillo{
  0%{
    -webkit-mask-image: linear-gradient(90deg, #000 0 50%, transparent 50%);
            mask-image: linear-gradient(90deg, #000 0 50%, transparent 50%);
    -webkit-mask-size: 200% 100%;      mask-size: 200% 100%;
    -webkit-mask-repeat: no-repeat;    mask-repeat: no-repeat;
    -webkit-mask-position: 100% 0;     mask-position: 100% 0;
  }
  100%{
    -webkit-mask-image: linear-gradient(90deg, #000 0 50%, transparent 50%);
            mask-image: linear-gradient(90deg, #000 0 50%, transparent 50%);
    -webkit-mask-size: 200% 100%;      mask-size: 200% 100%;
    -webkit-mask-repeat: no-repeat;    mask-repeat: no-repeat;
    -webkit-mask-position: 0% 0;       mask-position: 0% 0;
  }
}
@keyframes vc3-pliego{
  0%{   transform: translateY(-3px) scaleY(.994); }
  100%{ transform: translateY(0) scaleY(1); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉPOCA 4 · HIERRO — EL FILETE SE FORJA Y EL LATÓN CAPTA LA LUZ
   ───────────────────────────────────────────────────────────────────────────
   El fenómeno: llega la corriente al cartel. Primero se traza la regla —el
   filete doble del cartel de 1879 crece de izquierda a derecha, como sale de
   la máquina— y en cuanto está puesta, el filamento arranca: dos titubeos y
   estable, y el latón del kicker devuelve esa luz.

   Cómo está hecho:
   · El filete es `.c-cabecera::after`, pseudo DE LA PIEL. No se repinta: se
     anima su `background-size` de 0 a su propio valor final. Es el permiso
     explícito del contrato («sobre transiciones de lo existente»). El
     `background-position` se fija a `left` en los dos extremos del keyframe
     porque la piel lo tiene en `top`/`bottom` (= centrado): a ancho 100% son
     idénticos, pero a ancho 0 la diferencia es crecer desde el centro o
     trazarse desde la izquierda. Se traza desde la izquierda.
   · Al terminar (fill `backwards`, sin `forwards`) el pseudo vuelve a los
     valores de la piel — mismos píxeles.
   · El parpadeo del filamento se hace con `brightness()`, que es lo que hace
     una bombilla: modula la luz, no mueve la pieza. Ritmo mecánico de
     `--e-vel` (240 ms), ni orgánico ni seco.

   Contraste medido (el punto delicado de toda la hoja):
     kicker 7.96:1 en reposo. El latón es el color más comprometido del viaje:
     un brightness(.55) lo dejaría en 2.99:1 ✘ (calculado) y el límite exacto
     está en .72 — por eso el suelo del parpadeo es .75 → 4.66:1 medido ✔.
     Los picos por encima de 1 solo suman luz, nunca restan.
     titular 14.96:1 en reposo · suelo brightness(.78) → 8.92:1 medido ✔
   ═══════════════════════════════════════════════════════════════════════════ */

html[data-epoca="4"] .c-cabecera.is-entrando::after{
  animation: vc4-filete calc(var(--e-vel) * 1.6) var(--e-curva) backwards;
}
@keyframes vc4-filete{
  0%{
    background-size: 0% 3px, 0% 1px;
    background-position: left top, left bottom;
  }
  100%{
    background-size: 100% 3px, 100% 1px;
    background-position: left top, left bottom;
  }
}

html[data-epoca="4"] .c-cabecera.is-entrando .c-cabecera__kicker{
  animation: vc4-laton calc(var(--e-vel) * 2.2) linear calc(var(--e-vel) * 1.2) backwards;
}
@keyframes vc4-laton{
  0%{   filter: brightness(.75); }        /* 4.66:1 — el suelo medido */
  16%{  filter: brightness(1.30); }
  24%{  filter: brightness(.82); }
  44%{  filter: brightness(1.24); }
  56%{  filter: brightness(.90); }
  74%{  filter: brightness(1.08); }
  100%{ filter: brightness(1); }
}

/* El titular acompaña con UNA sola rampa, sin parpadeo: la pieza encendida de
   esta cabecera es el kicker de latón, y solo puede haber una (§0.5). */
html[data-epoca="4"] .c-cabecera.is-entrando .c-cabecera__titulo{
  animation: vc4-corriente calc(var(--e-vel) * 1.6) var(--e-curva) backwards;
}
@keyframes vc4-corriente{
  0%{   filter: brightness(.78); }        /* 8.92:1 medido */
  100%{ filter: brightness(1); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉPOCA 5 · CABINA — LA RAYITA SE ENCIENDE, EL TITULAR SUBE SU LUZ
   ───────────────────────────────────────────────────────────────────────────
   El fenómeno: alguien ha pulsado el interruptor. La rayita de brasa del
   kicker se enciende de izquierda a derecha con un destello de encendido, y el
   titular sube su nivel de luz. Seco: 130 ms, sin un solo píxel de recorrido.
   Aquí «lo demás ni se inmuta» — el texto del kicker no se toca.

   Cómo está hecho:
   · La rayita es `.c-cabecera__kicker::before`, pseudo DE LA PIEL: no se
     repinta su degradado, solo se le anima la escala y un halo de encendido.
     `transform-origin` se declara fuera del keyframe (es inerte sin transform,
     así que no deja residuo).
   · El halo de arranque se apaga solo: en reposo la cabina no brilla.
   · El titular usa `brightness()` y no `opacity` a propósito: sobre grafito,
     subir la luz es exactamente lo que hace un panel al encenderse, y además
     el valor es medible con precisión.

   Contraste medido sobre el render:
     titular 17.58:1 en reposo · suelo brightness(.72) → 8.93:1 ✔
     kicker  6.83:1 en los cinco fotogramas: NO se modula, porque la brasa a
     brightness(.72) caería a 3.91:1 ✘ (calculado). La rayita sí puede partir
     de opacidad 0: es luz, no texto.
   ═══════════════════════════════════════════════════════════════════════════ */

html[data-epoca="5"] .c-cabecera.is-entrando .c-cabecera__kicker::before{
  transform-origin: left center;
  animation: vc5-rayita calc(var(--e-vel) * 1.2) cubic-bezier(.2, .7, .3, 1) backwards;
}
@keyframes vc5-rayita{
  0%{
    opacity: 0;
    transform: scaleX(.12);
    box-shadow: 0 0 0 0 rgba(255, 106, 31, 0);
  }
  55%{
    opacity: 1;
    box-shadow: 0 0 7px 1px var(--e-halo);
  }
  100%{
    opacity: 1;
    transform: scaleX(1);
    box-shadow: 0 0 0 0 rgba(255, 106, 31, 0);
  }
}

html[data-epoca="5"] .c-cabecera.is-entrando .c-cabecera__titulo{
  animation: vc5-luz var(--e-vel) cubic-bezier(.2, .7, .3, 1) calc(var(--e-vel) * .35) backwards;
}
@keyframes vc5-luz{
  0%{   filter: brightness(.72); }        /* 8.93:1 medido */
  100%{ filter: brightness(1); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   MOVIMIENTO REDUCIDO — la cabecera ya está puesta
   ───────────────────────────────────────────────────────────────────────────
   No se degrada la entrada: se elimina. Sin animación, cada elemento se pinta
   con su estado natural, que es el de la piel — que es el estado final. No se
   pierde ni un dato ni un ornato: ni el pliego se queda a medio imprimir (la
   máscara de la 3 vive dentro del keyframe, así que se va con él) ni el filete
   de la 4 se queda corto (su `background-size` es el de la piel).
   (base.css §12 ya recorta duraciones globalmente; esto lo hace explícito y no
   depende del orden de carga.)
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce){
  html .c-cabecera.is-entrando,
  html .c-cabecera.is-entrando::after,
  html .c-cabecera.is-entrando .c-cabecera__kicker,
  html .c-cabecera.is-entrando .c-cabecera__kicker::before,
  html .c-cabecera.is-entrando .c-cabecera__titulo{
    animation: none !important;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   VERIFICADO (2026-08-11, Chrome headless, 5 épocas × 5-6 fotogramas exactos
   sembrados con la línea de tiempo de la Web Animations API)
   ───────────────────────────────────────────────────────────────────────────
   · SIN RESIDUO: `sin .is-entrando` vs `con la clase y la entrada terminada`
     → sha256 idéntico y 0/255 de diferencia de canal en las CINCO épocas.
   · CAJA INTACTA: `offsetLeft/Top/Width/Height` de cabecera, kicker y titular
     idénticos con y sin la clase, a mitad de entrada, a 900px y a 390px.
     Sin desbordes: `scrollWidth == clientWidth` en las cinco.
   · CONTRASTE: 27 fotogramas medidos sobre el píxel renderizado, ninguno por
     debajo de 4.5:1. El más justo de todo el viaje es la Cueva a t=0
     (kicker 4.72:1) y el segundo, el latón de la Fragua (4.66:1).
   · REDUCED-MOTION: con la clase puesta y `--force-prefers-reduced-motion`,
     la cabecera sale idéntica (0/255) a la cabecera sin clase. La entrada no
     se degrada: no existe.
   ═══════════════════════════════════════════════════════════════════════════ */
