/* concursos.css — los concursos
   ─────────────────────────────────────────────────────────────────────────────
   NO ES UNA COLUMNA DE FOTOS. Es un RELEVO: la sección se queda clavada y el
   scroll cambia la pieza en su sitio, la que sale acercándose y desenfocándose,
   la que entra llegando desde el fondo. Es el mecanismo que el hero ya usa en un
   teléfono, traído aquí — y no es reciclaje: es que las ocho fotografías SON EL
   MISMO ENCUADRE. Juez a la izquierda, Liz y el perro a la derecha, la placa
   azul abajo al centro, ocho veces.

   Puestas en relevo, las figuras no se desplazan: SE RELEVAN. El juez se
   disuelve en otro juez, ella se queda, el perro cambia de manto. No es una
   transición entre fotos: es UNA fotografía a la que le van cambiando la gente,
   y eso es tiempo pasando sobre un mismo momento. Siete rectángulos en hilera
   eran un catálogo; esto es una página.

   Y NO HAY TELÓN. Las láminas llegaron partidas para poder moverlas por
   separado, y montar las dos devolvía cada pieza a ser un rectángulo de
   fotografía puesto sobre una página — que era justo lo que la hacía parecer un
   listado. Los sujetos flotan en el negro del sitio, como en el hero. La
   profundidad la pone la CÁMARA, no la separación de capas.

   ── LA TIPOGRAFÍA, Y EL BUG QUE LA HACÍA ILEGIBLE ──────────────────────────
   Tres causas apiladas, y las tres se arreglan aquí:

   ① `opsz` NO ES UN ADORNO. Es el eje que decide el grosor del fino de una
      didona, y en Bodoni Moda va de 6 a 96. El corte de DISPLAY (96) tiene el
      fino unas SIETE VECES más delgado que el de TEXTO (16). Eso es lo que la
      hace magnífica a 300 px en el masthead —que se dibuja en un CANVAS a
      resolución de dispositivo, donde ese fino se lleva dos píxeles físicos— y
      lo que la volvía ilegible a 55 px, y directamente rota a 11 px, en el DOM:
      el fino caía por debajo del píxel y la E perdía los brazos. Por eso
      `CRIADERO DE GRAN DANÉS` se leía perfecto y `CONCURSOS` no, siendo el mismo
      archivo y siendo el segundo MÁS GRANDE. Aquí cada tamaño pide su corte
      óptico, y para eso va la fuente VARIABLE y no una instancia.

   ② EL TEXTO VIVÍA DENTRO DE UN CONTENEDOR CON `filter` Y `transform`. Chrome
      rasteriza ese subárbol en una textura y la reescala: la didona salía
      remuestreada incluso quieta, y durante el scroll salía literalmente
      desenfocada, porque se comía el desenfoque de la lámina. Aquí la tipografía
      NUNCA vive dentro de nada filtrado ni escalado. Se le mueve la opacidad y
      se acabó.

   ③ LLEVABA UN HALO NEGRO. En el hero tiene sentido: sus palabras cruzan una
      fotografía iluminada. Aquí van sobre negro, así que el halo sólo ensuciaba
      los pocos finos que sobrevivían. Fuera. */

@font-face{
  font-family:"Bodoni Concursos";
  src:url("BodoniModa-var-subset.e8f33aa7.woff2") format("woff2-variations");
  font-weight:400 900;
  font-style:normal;
  font-display:swap;
}

.concursos{
  --didona:"Bodoni Concursos", Didot, "Bodoni 72", "Times New Roman", serif;
  /* CUÁNTO SCROLL LE TOCA AL VIAJE DEL TÍTULO. Una pantalla y media larga: la
     misma unidad que un relevo (115vh), porque el viaje ES un relevo más — el de
     la sección presentándose antes de enseñar nada. Dentro caben las tres cosas
     que pasan: las palabras llegando escalonadas (el primer tercio), un respiro
     con el rótulo grande y quieto, y el descenso hasta su sitio.

     ESTÁ AQUÍ, EN CSS, Y NO EN JAVASCRIPT, y no es preferencia de estilo: es la
     §⑫ del LEEME. `.hero__riel` nacía a `height:0` y su alto de verdad llegaba
     a los 3.7 s desde JS; eso metió esta sección dentro de la primera pantalla y
     costó un salto de 2 532 px y las siete láminas pedidas a destiempo. Un alto
     que decide el recorrido del documento se declara antes del primer layout o
     no se declara. */
  --viaje:115vh;
  /* OPACO: el hero es position:fixed y sigue vivo debajo */
  position:relative; z-index:1;
  background:var(--negro);
}

/* ── EL CINTILLO ──────────────────────────────────────────────────────────────
   Mayúsculas diminutas, tracking abierto, un filete encima. A 11 px el corte de
   texto no es una preferencia: es la diferencia entre que se lea y que no. */
.cintillo{
  font-family:var(--didona);
  font-variation-settings:"opsz" 12, "wght" 500;
  font-size: clamp(.62rem, .82vw, .78rem);
  letter-spacing:.34em;
  text-transform:uppercase;
  color:var(--tinta);
  line-height:1.7;
  margin:0;
}

/* ── EL RIEL Y LA VISTA ───────────────────────────────────────────────────────
   El riel es alto —una pantalla por pieza— y es lo único que da recorrido. La
   vista va `sticky` dentro de él: se queda clavada mientras el riel pasa, y el
   scroll sólo mueve un número que decide qué pieza se ve. La página nunca baja
   dentro de la sección; las piezas se reemplazan en su sitio. */
/* Una pantalla por pieza: con N piezas el recorrido útil son N−1 cruces, que es
   exactamente cuántos hay. Menos que eso y el relevo se siente atropellado;
   más, y el scroll se hace largo sin que pase nada.

   115vh Y NO 100: un 15 % más de aire, pedido porque las piezas «cambian muy
   rápido sin claramente parar en la foto». El recorrido útil pasa de 600vh a
   705vh, o sea de 100vh a 117.5vh por relevo. `donde()` no se entera: lee del
   rect y divide por el recorrido que mida, sea el que sea.

   OJO: esto estira el relevo entero, y lo que se estira es sobre todo el CRUCE,
   que es la parte que ya estaba bien resuelta. Lo que crea la PARADA es la
   meseta de `MESETA` en concursos.js, no esto — ver ahí. */
/* Y AHORA EL RIEL LLEVA UN TRAMO MÁS: `--viaje`, el del título. El recorrido
   útil pasa de 705vh a 820vh y `donde()` no se entera —lee del rect y divide por
   lo que mida—; lo único que cambia en JS es que el primer tramo no mueve el
   relevo, mueve el rótulo. Con `prefers-reduced-motion`, `--viaje` vale 0 y esta
   misma línea devuelve el riel EXACTAMENTE al alto que tenía: no hay una segunda
   fórmula que mantener. */
.concursos__riel{ position:relative; height:calc(var(--piezas) * 115vh + var(--viaje)); }

/* La regla del viaje: 0 de ancho, fuera de flujo, invisible. Su único trabajo es
   que `offsetHeight` devuelva `--viaje` resuelta a píxeles — ver el comentario
   en `index.html`. */
.concursos__regla{ position:absolute; top:0; left:0; width:0; height:var(--viaje); pointer-events:none; }

.concursos__vista{
  position:sticky; top:0;
  height:100vh; height:100dvh;
  display:grid;
  /* UNA COLUMNA, Y ESO ES LO QUE DEVUELVE LA FOTOGRAFÍA A SU SITIO. El raíl de
     fechas tuvo durante una tarde una columna propia del reparto, y una columna
     del reparto OCUPA: la escena se corría a la derecha para dejarle hueco —a
     1440×900, 95 px— sin que la fotografía ganara nada a cambio. Ahora el raíl
     va SUPERPUESTO sobre la escena (§ EL RAÍL, abajo) y no participa del
     reparto, así que aquí sólo hay una columna y la escena vuelve a disponer
     del ancho entero. Medido, y está en el informe: a 1440×900 el marco vuelve
     de x=354.4 a x=259.3 SIN CAMBIAR DE TAMAÑO —921.3 × 737.0 en los dos—, que
     es exactamente el sitio que ocupaba antes de que el raíl existiera. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows:auto minmax(0, 1fr) auto;
  row-gap: clamp(.7rem, 2vh, 1.4rem);
  padding: clamp(1rem, 3.4vh, 2.2rem) clamp(1.2rem, 4vw, 3.4rem) clamp(1rem, 3vh, 2rem);
  overflow:hidden;

  /* LOS DOS NÚMEROS DEL VIAJE, Y SUS VALORES POR DEFECTO SON EL ESTADO FINAL.
     Eso no es un detalle de implementación: es lo que hace que la sección sea
     correcta sin JavaScript, con `prefers-reduced-motion`, y en el primer cuadro
     de la página antes de que `concursos.js` haya escrito nada. Lo que se anima
     es lo que se APAGA, nunca lo que se enciende.

       --aterriza   el rótulo pequeño y su filete: 0 mientras el título viaja,
                    1 cuando ha aterrizado.
       --fotos      las láminas, el raíl y el pie: salen cuando el título ya está
                    en su sitio, que es la mitad de la idea. Es un efecto VISUAL
                    y nada más: la DESCARGA de las siete láminas no la toca nadie
                    —sigue donde estaba, en el HTML y en §«CUÁNDO SE PIDEN LAS
                    SIETE LÁMINAS»—, porque retrasarla reintroduciría el hueco
                    gris que costó medir la lane 📱 Hero-Movil. */
  --aterriza:1;
  --fotos:1;

  /* EL CARRIL DEL RAÍL, y son dos números separados a propósito.
       --carril  el ancho de la COLUMNA ESCRITA: la fecha más larga
                 (`27 May 2026`) con su muesca y su hueco. LO MIDE
                 `concursos.js` y lo escribe en `--carril-txt`, porque el cuerpo
                 de la fecha cuelga de un `clamp(…vw…)` y una constante escrita
                 aquí mentiría en cuanto la ventana pasara de 1287 px: medido,
                 el texto va de 91.1 px a 108.3 px según el ancho. El valor de
                 respaldo es para el primer cuadro y para si JS no llega.
       --velo    lo que el fondo del carril tarda en desvanecerse hacia la
                 izquierda. Es lo que hace que el velo no tenga un canto recto
                 sobre la fotografía —un canto recto se lee como un panel
                 pegado encima— y por eso es AIRE: el texto no entra ahí.
     Ninguno de los dos le quita un píxel a la escena: el raíl va absoluto. */
  --carril: var(--carril-txt, 7.2rem);
  --velo:   2.4rem;
  /* La tinta de las fechas apagadas del raíl. NO es `--tenue`, y la separación
     tiene la misma razón que la de `--tenue` y `--filete`: son sitios con
     fondos distintos. `--tenue` vive sobre negro plano —el cierre del pie— y
     ahí 5.11:1 es verdad; el raíl vive sobre SIETE fotografías, y su peor caso
     está medido contra el velo en el informe. */
  --rail-tinta: #b9ab97;
}

/* Mientras las fotos y el mobiliario están del todo apagados no deben poder
   pulsarse ni recibir el foco — una opacidad de 0 no impide ninguna de las dos
   cosas. Lo pone y lo quita `concursos.js` en `viaja()`, donde está el porqué. */
.concursos__vista[data-viaje] .concursos__escena,
.concursos__vista[data-viaje] .concursos__lado,
.concursos__vista[data-viaje] .concursos__pie{ visibility:hidden; }

.concursos__cabecera{
  grid-column:1; grid-row:1;
  border-top:1px solid var(--filete); padding-top:.85rem;
  /* El filete viaja con su rótulo: los dos aparecen a la vez, y por eso la
     opacidad va en la cabecera y no en el <h2>. */
  opacity:var(--aterriza);
}

/* ── LA ESCENA ────────────────────────────────────────────────────────────────
   Las siete láminas apiladas en el mismo rect. Como las siete son casi el mismo
   lienzo (1.234 a 1.333), plantarlas en un rect común con `object-fit:contain`
   las deja intactas —ninguna se deforma ni se recorta— y a cambio las figuras se
   relevan sin dar un salto de encuadre. Es el compromiso opuesto al del
   tríptico, y aquí es el bueno: allí cada panel se veía SOLO y su aspecto propio
   salía gratis; aquí se ven una ENCIMA DE OTRA, y lo que importa es que el juez
   caiga donde estaba el juez. */
.concursos__escena{
  position:relative;
  grid-column:1; grid-row:2;
  /* Las fotos salen cuando el título ha aterrizado. `opacity` y NADA MÁS: es la
     única propiedad que puede apagar esto sin meter el subárbol en una textura
     reescalada, que es el bug ② de arriba y el que dejaba la didona borrosa.
     Cuando `--fotos` vale exactamente 1 —o sea, siempre salvo durante el viaje—
     `opacity:1` no crea contexto de apilamiento ni capa, así que el relevo pinta
     igual que antes de que esta línea existiera. */
  opacity:var(--fotos);
  /* LOS DOS CEROS SON OBLIGATORIOS. Un item de rejilla tiene `min-width:auto`, o
     sea que crece hasta caber su contenido: con el marco pidiendo su ancho por
     `aspect-ratio`, la escena se estiraba a 837 px dentro de una vista de 366 en
     un teléfono, y el `overflow:hidden` se comía media fotografía y media
     palabra. Medido. Con los ceros manda la escena y el marco obedece. */
  min-width:0;
  min-height:0;
}

/* EL ENCUADRE LO CALCULA concursos.js, no el CSS, y es la misma regla de tres
   topes del hero: que quepa de alto, que quepa de ancho, y que no pase del alto
   nativo de la lámina — cero upscale. En CSS no se puede escribir «el menor de
   estos dos» cuando uno cuelga del alto de la fila y el otro del ancho, y los
   dos intentos salieron mal: por ancho, la placa azul quedaba cortada abajo; por
   alto, la rejilla se desbordaba en un teléfono.

   Va CENTRADO CON `margin:auto`, no con `transform:translate`, y eso no es
   estilo: un `transform` en un ancestro del rótulo lo mete otra vez en una
   textura rasterizada, que es justo el bug que hacía ilegible la didona. */
.concursos__marco{
  position:absolute; inset:0; margin:auto;
  container-type:size;
}


/* ── EL TÍTULO QUE VIAJA ──────────────────────────────────────────────────────
   Al entrar en la sección el rótulo está GRANDE y en mitad de la pantalla, y a
   base de scroll encoge y se va a vivir donde vive hoy: arriba a la izquierda,
   sobre su filete, en versales de 11 px. El final del viaje ES EL ESTADO ACTUAL
   —mismo sitio, mismo cuerpo, mismo tracking—, no un estado nuevo.

   LO MUEVE EL DEDO, NO UN RELOJ. Aquí no hay `transition` ni `animation` ni un
   temporizador: la posición del scroll es el único parámetro. Si el visitante
   para, el título para; si sube, el título vuelve. Ésa es la idea entera —que se
   vea que el scroll conduce— y una animación con reloj la rompería.

   ── POR QUÉ PALABRA POR PALABRA Y NO UN `scale` DEL BLOQUE ──────────────────
   Porque los dos estados no son el mismo dibujo a dos tamaños. `CONCURSOS ·
   FEDERACIÓN CANÓFILA MEXICANA` aterriza en UNA línea; a 390 px de ancho, el
   estado grande son TRES. Ninguna escala lleva de tres líneas a una, así que un
   bloque escalado tendría que dar un salto al final —justo en el fotograma en el
   que el visitante está mirando el título— o renunciar a ser grande en un
   teléfono, que es donde más falta hace.

   Con una palabra por <span>, cada una sabe de dónde sale y a dónde va, y el
   navegador no vuelve a maquetar NADA: son cinco `transform` y cinco `opacity`
   por cuadro. `concursos.js` mide las dos maquetaciones una vez —la grande, con
   un Range sobre cada <span>; la aterrizada, con un Range sobre el texto del
   <h2> que ya está en la página— y de ahí salen el desplazamiento y la escala.
   El <h2> no se toca: no se le mete un <span> dentro ni se le cambia una coma.

   ── Y POR QUÉ SON DOS ELEMENTOS Y NO UNO ────────────────────────────────────
   Por `opsz`, que en esta casa ya tiene su propia sección arriba. El viajero se
   dibuja a 46 px y pide el corte de DISPLAY; el rótulo aterrizado se dibuja a
   11 px y pide el de TEXTO, con los finos siete veces más gruesos, o la E pierde
   los brazos. Un solo elemento tendría que cambiar `font-variation-settings` a
   mitad de vuelo, que es volver a maquetar en cada cuadro.

   Así que el viajero rasteriza a su tamaño grande y BAJA por escala —un
   remuestreo hacia abajo, que es el que sale bien— y en el último 6 % del
   recorrido se cruza con el rótulo de verdad, que ya está exactamente en su
   sitio y con su corte óptico. No se ve un cambio de posición porque no lo hay:
   se ve cómo se afila. */
.concursos__viajero{
  position:absolute; inset:0;
  display:flex; align-items:center; justify-content:center;
  pointer-events:none;
  /* POR DEFECTO NO SE VE. Sin JavaScript esto es un <p> vacío, y con JavaScript
     no se enciende hasta que `viaja()` escribe `--vive`: en ningún caso hay un
     fotograma con el título pintado dos veces. */
  opacity:var(--vive, 0);
}
/* Aterrizado, deja de existir para el compositor: ni capa, ni pintado, ni
   `will-change` reservando memoria de textura durante todo el relevo. Se apaga
   con `visibility` y no con `display` a propósito — un elemento sin caja no se
   puede medir, y hay que poder volver a medirlo en cada `resize`. */
.concursos__viajero[data-fin]{ visibility:hidden; }
.concursos__viajero[data-fin] .concursos__lockup span{ will-change:auto; }

.concursos__lockup{
  /* El tracking abierto de las versales deja un hueco colgando a la derecha de
     la última letra de cada línea, y con el texto centrado ese hueco descuadra
     el bloque media letra hacia la izquierda. El margen negativo se lo come, y
     vale lo que valga el tracking de AQUÍ —.20em—, no el del cintillo. */
  margin:0 -.20em 0 0;
  /* 74rem, y el número lo decidió la MEDIDA del corte semántico, no el gusto.
     Con el corte puesto, el segundo renglón es `FEDERACIÓN CANÓFILA MEXICANA`
     ENTERO —28 caracteres— y tiene que caber en una sola línea o el bloque
     vuelve a ser de tres renglones y el corte no habrá servido de nada. Mide
     1 066 px a 46.4 px de cuerpo; con 64rem (1 024 px) NO CABÍA. */
  max-width:min(88vw, 74rem);
  text-align:center;
  font-family:var(--didona);
  /* opsz 48: a este cuerpo la didona pide su corte de display, que es lo que la
     hace magnífica. Es el mismo argumento del masthead, al revés que el del
     cintillo de 11 px. */
  font-variation-settings:"opsz" 48, "wght" 400;
  font-size: clamp(1.5rem, 3.4vw, 2.9rem);
  /* .20em Y NO .34, Y ESTO LO PIDIÓ EL CORTE. Con el tracking del cintillo el
     segundo renglón medía 1 248 px y sólo cabía de una pieza en una ventana de
     1 418 px para arriba: a 1440 sobraban 19 px, o sea que el corte semántico
     habría durado hasta el primer navegador con otra métrica. A .20em mide
     1 066 px y entra de una línea desde los 626 px de ancho — medido.

     Y de paso es lo correcto: el tracking de una versal se ABRE al bajar de
     cuerpo y se CIERRA al subir, que es por lo que el cintillo de 11 px lleva
     .34em. Un rótulo de 46 px con el tracking de uno de 11 no es coherencia,
     es la misma cifra puesta dos veces.

     NO DESCUADRA EL ATERRIZAJE: la escala de cada palabra sale de su ANCHO
     MEDIDO (`mideViaje`), así que el tracking entra en la cuenta; y dentro de
     la palabra el tracking es un sumando IGUAL por letra, que al normalizar por
     el ancho total se cancela. Medido: el desvío de aterrizaje sigue en 0.01 px
     y 0.00 % de ancho. */
  letter-spacing:.20em;
  text-transform:uppercase;
  /* El MISMO interlineado que el cintillo aterrizado (1.7). No es estética: cada
     palabra se coloca por la esquina de su caja de texto, y dos interlineados
     distintos meterían un desfase vertical constante entre lo medido y lo
     pintado. */
  line-height:1.7;
  color:var(--tinta);
}
/* EL SEPARADOR SALE DEL FLUJO MIENTRAS EL BLOQUE ES DE DOS RENGLONES, y ése es
   todo el mecanismo del corte. El `·` no es una palabra: es la MARCA del corte,
   y `concursos.js` mete el <br> justo detrás. Si además contara en el renglón,
   «CONCURSOS ·» se centraría COMO UNA UNIDAD y CONCURSOS quedaría fuera de eje
   media letra —el `·` con su espacio y su tracking mide 58 px a 46 px de
   cuerpo—, que es exactamente el descuadre que una portada no perdona. Fuera
   del flujo, CONCURSOS queda solo y centrado de verdad.

   SIGUE SIENDO MEDIBLE Y SIGUE VIAJANDO: tiene caja y tiene rects, así que
   `mideViaje()` le saca su origen y su salto como a cualquier otra palabra, y
   aterriza en su sitio del <h2> al centésimo de píxel como las demás. Lo único
   que cambia es CUÁNDO se ve, y eso lo decide `viaja()` con `junta` — el último
   cuarto del descenso, cuando el punto ya está donde va a quedarse. */
.concursos__lockup{ position:relative; }
.concursos__lockup .sep{ position:absolute; left:50%; top:0; }

/* PALABRA POR RENGLÓN. Lo pone `mideViaje()` cuando el rótulo ATERRIZA en más
   de un renglón, y el porqué está ahí: dos palabras juntas arriba que aterrizan
   separadas se pisan a mitad del descenso. `display:block` y no un <br> porque
   así la decisión es reversible en un `resize` sin tocar el DOM, y porque el
   <br> del separador tiene que seguir estando donde está pase lo que pase. */
.concursos__lockup[data-suelto] span{ display:block; }

.concursos__lockup span{
  display:inline-block;   /* un inline no admite `transform` */
  /* El origen se lo pone `concursos.js` en píxeles: la esquina del TEXTO, que no
     es la de la caja del <span>. Con el origen ahí, escalar no mueve el punto
     que se está llevando a su sitio y las dos cuentas —trasladar y encoger— no
     se estorban. */
  transform-origin:0 0;
  will-change:transform, opacity;
}

/* LA PALABRA VA DETRÁS DE LAS FIGURAS, y vive aquí —fuera de todo lo que se
   filtra o se escala—. Se cuelga del CRÁNEO con la regla del hero
   (`GRAN_ENCIMA = 0.36`): un 36 % de la altura de mayúscula por encima de donde
   empieza el recorte y el 64 % restante detrás de las cabezas. `--craneo` es el
   MÁS ALTO de las siete, medido sobre el alfa, para que la palabra no dé un
   salto vertical al cambiar de pieza. */
.concursos__premios{
  position:absolute; left:0; right:0;
  top: calc((var(--craneo) - .36 * var(--cap)) * 100cqh);
  z-index:1;
  display:grid;                 /* las siete en la misma celda, cruzándose */
}
.premio{
  grid-area:1 / 1;
  margin:0;
  font-family:var(--didona);
  /* opsz 34: bastante didona para que siga siendo una didona, bastante corte de
     texto para que la E tenga brazos a este cuerpo en el DOM */
  font-variation-settings:"opsz" 34, "wght" 400;
  font-size: calc(var(--cap) / .75 * 100cqh);
  line-height:.94;
  letter-spacing: var(--track, .26em);
  text-transform:uppercase;
  text-align:center;
  color:#fff;
  white-space:pre;
  opacity: var(--w, 0);
}

/* LAS LÁMINAS. Sólo ellas se escalan y se desenfocan — la cámara del relevo.
   La que sale se ACERCA y se va de foco (queda demasiado cerca); la que entra
   llega DESDE EL FONDO y se enfoca. Es el lenguaje de cámara del guion de
   entrada del hero, no una transición pegada encima. */
/* `<picture>` no dibuja nada: sólo elige el formato. Sin esto genera una caja
   en línea que no pinta pero sí cuenta para el flujo. */
.concursos__marco picture{ display:contents; }

.lamina{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:contain;
  z-index:2;
  /* LA LÁMINA SE DISUELVE EN EL NEGRO POR SUS TRES CANTOS DUROS, que es la regla
     de la casa desde el hero: «la foto se disuelve EN el negro, no sobre él».

     Sin telón detrás, el suelo que cada recorte trae pegado —la tarima, el
     pedestal de cantera, la losa de pasto de la pieza 8— llega al borde del
     lienzo y termina en un CANTO RECTO. Un canto recto sobre negro se lee como
     un recorte pegado encima de la página, y era justo lo que rompía la
     coherencia con el tríptico de arriba, donde la fotografía nunca tiene borde.

     Se desvanece por los DOS COSTADOS y por el PIE. Arriba no hace falta: ahí el
     recorte ya empieza vacío —su alfa no arranca hasta el 5.5 % del alto— así que
     no hay nada que cortar. Las figuras viven entre el 13 % y el 88 % del ancho,
     así que un 9 % de fundido lateral se come el suelo y no toca a nadie. */
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0, #000 9%, #000 91%, transparent 100%),
    linear-gradient(to bottom, #000 84%, transparent 100%);
          mask-image:
    linear-gradient(to right,  transparent 0, #000 9%, #000 91%, transparent 100%),
    linear-gradient(to bottom, #000 84%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
  opacity: var(--w, 0);
  transform: scale(var(--z, 1));
  filter: blur(calc(var(--soft, 0) * 1px));
  will-change: opacity, transform;
}

/* ── EL RAÍL DE FECHAS · UNA CAPA SOBRE LA ESCENA, A LA IZQUIERDA ────────────
   La única pieza de mobiliario de la sección, y hace dos trabajos: dice DÓNDE
   estás en el registro —que es lo que un relevo no sabe decir solo— y dice que
   el registro tiene fondo. Sin él, un relevo es un pase de diapositivas.

   ESTÁ EN VERTICAL, y el porqué lo dijo Victor de una vez: «para que la gente
   entienda más que el scroll es lo que los está bajando». Una fila horizontal no
   puede decir eso —el dedo baja y la fila no se mueve en esa dirección—; una
   columna sí, porque la marca DESCIENDE con el dedo y el gesto y el dibujo van
   por fin en el mismo eje.

   Y LA FUNCIÓN MANDA SOBRE EL ADORNO: de un vistazo tienen que caber las dos
   preguntas que nombró —CUÁNTAS fotos hay y EN CUÁL voy—. Por eso hay una marca
   por foto y no una barra continua, y por eso la marca viva se enciende de
   golpe: siete marcas se cuentan de un vistazo, un porcentaje no.

   ── Y NO OCUPA. ÉSTE ES EL ARREGLO DE ESTA VUELTA ──────────────────────────
   Durante una tarde el raíl fue una COLUMNA DEL REPARTO, y una columna del
   reparto le quita ancho a la fotografía: «aquí nada más estás empujando todas
   las imágenes mucho más a la derecha sin razón alguna». Tenía razón, y estaba
   medido sin verse: a 1440×900 el marco se iba de x=259.3 a x=354.4.

   Ahora es una CAPA. Y el truco de que arranque exactamente donde arranca la
   fotografía —o sea justo debajo del cintillo del título, que es la altura que
   pidió— no cuesta ni una medida en JavaScript: un hijo ABSOLUTO de una rejilla
   que declara su `grid-area` toma ESA CELDA como bloque contenedor, no la
   rejilla entera. Se le declara la celda de la escena y `top:0` es el borde de
   arriba de la escena. Es la misma técnica que la regleta del teléfono ya usaba
   desde la lane anterior, ahora en las dos formas y por el mismo motivo.

   ── EL VELO, Y POR QUÉ NO ES UN ADORNO PEGADO ENCIMA ───────────────────────
   Una capa sobre la fotografía tiene un problema que una columna no tenía: su
   contraste ya no es contra negro plano, es contra lo que haya debajo, y debajo
   hay siete fotografías distintas. Un 5.11:1 medido sobre negro no significa
   NADA ahí.

   Así que el carril lleva su propio fondo: un degradado que va de transparente
   a casi negro hacia el canto izquierdo. Sobre negro es invisible —negro sobre
   negro—, así que en las ventanas donde la lámina no llega hasta el raíl no se
   ve nada de esto; y donde la lámina sí llega, garantiza el peor caso. No es un
   panel pegado encima: es LA MISMA REGLA DE LA CASA que ya gobierna la lámina,
   que se disuelve en el negro por sus costados con un fundido del 9 %. El velo
   continúa esa disolvencia unos píxeles más, que es donde vive el raíl.

   El peor caso está medido contra las SIETE láminas y escrito en el informe.

   Tres dibujos, y ninguno cuesta maquetación:
     · la regleta apagada, entera, de la primera marca a la última;
     · el hilo encendido, que crece con `scaleY` — cuánto llevas;
     · la aguja, que baja con `translateY` — dónde estás, entre marca y marca.
   `concursos.js` mide los centros una vez por maquetación y escribe `--riel-y0`
   y `--riel-h` en píxeles; por cuadro sólo toca dos `transform`. */
.concursos__lado{
  /* FUERA DEL REPARTO: absoluto, con su celda declarada. La rejilla no le
     reserva un solo píxel, y su bloque contenedor es la celda de la escena. */
  /* LAS DOS LÍNEAS DE CADA EJE, ESCRITAS ENTERAS, y no es redundancia: para un
     hijo ABSOLUTO de una rejilla, una línea `auto` no significa «una celda»
     —eso es para los items de verdad— significa EL BORDE DE LA REJILLA. Con
     `grid-row:2` el carril arrancaba bien y terminaba 80 px por debajo de la
     fotografía, metido en el pie. Se veía poco y estaba mal. */
  position:absolute; grid-column:1 / 2; grid-row:2 / 3;
  left:0; top:0; bottom:0;
  width:calc(var(--carril) + var(--velo));
  padding-right:var(--velo);
  /* POR ENCIMA DE LA LÁMINA, que lleva `z-index:2`. Sin esta línea el raíl se
     pinta DEBAJO de la fotografía y no por un capricho del navegador: en el DOM
     va antes que la escena —ahí es donde se lee y donde se tabula— y dos
     hermanos posicionados sin `z-index` se pintan en orden de documento. */
  z-index:3;
  display:flex; align-items:flex-start; justify-content:flex-start;
  min-width:0;
  opacity:var(--fotos);
  /* EL NÚMERO DEL VELO SALE DE UNA CUENTA CON EL PEOR FONDO POSIBLE, no del
     gusto: un BLANCO SATURADO debajo. Para que `--rail-tinta` —9.33:1 contra
     negro— siga pasando de 4.5:1, el fondo compuesto tiene que quedarse por
     debajo de 65 de gris, y sobre un 255 eso pide un velo de .74 justo donde
     EMPIEZA el texto. Va a .80, con margen.

     Los dos escalones de en medio son una entrada suave. Un degradado de dos
     paradas deja un canto visible donde arranca, y un canto recto sobre la
     fotografía es exactamente el «panel pegado encima» que la casa no usa: la
     lámina se disuelve en el negro por sus costados, y el velo continúa esa
     disolvencia unos píxeles más. El escalón de `.12` a un 34 % del desvanecido
     es además el LÍMITE que `ajusta()` le pone al rótulo del premio —ahí un
     blanco baja de 255 a 224 y en mitad de un degradado no se ve—; si se mueve
     ese 34 %, se mueve también `VELO_SUAVE` en `concursos.js`.

     DÓNDE HACE FALTA DE VERDAD: con las fechas escritas, no —la regla del raíl
     es que sólo se escriben si el carril entero cabe en el HUECO NEGRO, así que
     ahí el fondo es negro por geometría y el velo es negro sobre negro, o sea
     invisible—. Hace falta con la REGLETA, que sí va sobre la lámina. Medido
     contra las siete, a 390×844: peor caso 8.04:1 en la marca apagada y 18.10:1
     en la viva, y el velo toca el 0.6 % del ancho del marco. El velo se queda
     también en el caso escrito porque cuesta cero y es el cinturón de una regla
     geométrica que un día podría quedarse a un píxel. */
  background:linear-gradient(to left,
    rgba(0,0,0,0)   0,
    rgba(0,0,0,.12) calc(var(--velo) * .34),
    rgba(0,0,0,.44) calc(var(--velo) * .68),
    rgba(0,0,0,.80) var(--velo),
    rgba(0,0,0,.95) 100%);
  --riel-y0:0px; --riel-h:0px;
}
/* la regleta apagada — pegada al canto IZQUIERDO de la escena */
.concursos__lado::before{
  content:""; position:absolute; left:0;
  top:var(--riel-y0); height:var(--riel-h); width:1px;
  background:var(--filete);
}
.rail__hilo, .rail__aguja{ position:absolute; left:0; pointer-events:none; }
.rail__hilo{
  top:var(--riel-y0); height:var(--riel-h); width:1px;
  background:var(--brasa);
  /* el origen es el ALTO; la x da igual en un `scaleY` y se deja en 0 */
  transform-origin:0 0;
  transform:scaleY(var(--llena, 0));
}
.rail__aguja{
  top:var(--riel-y0); width:5px; height:5px; margin:-2px 0 0 -2px;
  border-radius:50%; background:var(--brasa);
  transform:translateY(var(--y, 0px));
}

/* LAS SIETE FECHAS SE REPARTEN EL ALTO, no se apelotonan arriba: es lo que
   convierte una lista en un mapa del recorrido. Apiladas con su interlineado
   ocupaban 230 px de una pantalla de 900 y la marca viva recorría 196 px en toda
   la sección — un movimiento tan corto que no se lee como un descenso, que era
   justo lo que había que enseñar. Repartidas, la marca baja 500 px y se ve que
   la baja el dedo.

   El tope de 32rem es para que no se desperdiguen en una pantalla muy alta: con
   `space-between` sobre 900 px las fechas quedarían a 130 px unas de otras y
   dejarían de leerse como una sola columna.

   Y ARRANCAN ARRIBA, no centradas en la escena: «como abajo de CONCURSOS, esa
   altura me refiero». Con `flex-start` la primera fecha queda a la altura del
   borde de arriba de la fotografía, que es la línea que el cintillo del título
   ya marca justo encima.

   ── POR QUÉ ESTE TEXTO NO SE LEÍA, Y NO ERA EL COLOR ────────────────────────
   Por la mañana se subió `--tenue` de 2.83:1 a 5.11:1 y las fechas seguían sin
   leerse. La causa eran otras dos, y las dos se miden:

     ① EL CUERPO. `.cintillo` es `clamp(.62rem, .82vw, .78rem)`, y ese `clamp`
        se pensó para un rótulo de una línea, no para una columna de datos: a
        1024 px de ancho manda el SUELO y las fechas se dibujan a 9.92 px. Una
        versal de Bodoni a 9.92 px tiene 6.9 px de altura de mayúscula y un fino
        por debajo del píxel — a ese cuerpo la didona no pierde contraste,
        pierde TRAZO. Aquí el suelo sube a .74rem (11.84 px) y el techo a
        .88rem, que es el rango en el que la versal conserva su asta fina.

     ② EL TRACKING. `.34em` a 9.92 px son 3.4 px de aire entre letra y letra,
        más que el grueso del asta gruesa: a esa proporción la palabra deja de
        ser una palabra y pasa a ser una hilera de letras sueltas, y el ojo la
        deletrea en vez de leerla. Un rótulo de una línea aguanta ese aire
        —está solo y se mira—; una columna de siete datos que se recorre de un
        vistazo, no. Baja a `.16em`, que sigue siendo un cintillo abierto.

     ③ Y EL `wght` SUBE A 560 porque el raíl ahora va SOBRE LA FOTOGRAFÍA: un
        asta de un píxel sobre un fondo que cambia se pierde aunque el color
        cumpla. El velo pone el contraste, esto pone el trazo.

   El tercer sospechoso, el color, se queda como está: `--tenue` es del cierre
   del pie —que sí va sobre negro plano y ahí sus 5.11:1 son verdad— y del
   trazo de las muescas. Lo que el raíl necesitaba sobre imagen no era otro
   número de contraste sino un fondo que lo garantice; ver el velo, arriba.
   `--filete` no se toca: es trazo de 1 px y está separado a propósito. */
.rail{
  align-self:flex-start;
  width:100%; min-width:0;
  height:min(100%, 32rem);
  display:flex; flex-direction:column; justify-content:space-between;
  list-style:none; margin:0; padding:0;
  /* gana a `.cintillo` por orden, no por peso: las dos reglas son una clase */
  font-size: clamp(.74rem, .92vw, .88rem);
  letter-spacing:.16em;
  font-variation-settings:"opsz" 12, "wght" 560;
  text-align:left;
}
.rail li{
  color:var(--rail-tinta);
  transition:color 240ms ease;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
.rail li[data-viva]{ color:#fff; }

/* EL BOTÓN NO SE VE. La fecha pulsable tiene que seguir leyéndose como un dato
   del cintillo y no como un botón de formulario: el navegador le trae fuente
   propia, fondo, borde y relleno, y los cuatro hay que devolverlos. `color` va
   heredado A PROPÓSITO — así el estado vivo lo sigue mandando `.rail
   li[data-viva]`, que es quien ya lo tenía, y el pulsable no pelea con él.
   Un color heredado sigue la transición del padre, así que el encendido de la
   fecha viva se mantiene igual.

   El foco no se dibuja aquí: `base.css` ya tiene su `:focus-visible` con
   `--brasa`, y es el mismo anillo que en todo el sitio. */
.rail button{
  font:inherit; letter-spacing:inherit; color:inherit;
  background:none; border:0; padding:0; margin:0;
  cursor:pointer;
  /* La muesca es el PRIMER hijo de la caja —el `::before`— y con `row` (el
     orden natural) queda pegada al canto izquierdo, sobre la regleta, con la
     fecha a su derecha. La regleta se mudó a la izquierda con el raíl. */
  display:flex; flex-direction:row; align-items:center; gap:.5rem;
  width:100%;
  transition:color 240ms ease;
}
/* LA MARCA DE CADA FOTO. Arranca en el 0 del raíl, o sea encima de la regleta:
   el trazo NACE de la línea, que es lo que hace que se lea como una muesca de
   una regla y no como un guion delante de una fecha.

   Se ensancha con `scaleX` y no con `width`, y eso no es coquetería: la fecha
   viva cambia siete veces mientras se recorre la sección, y una transición de
   `width` de 240 ms es una maquetación de la columna entera en cada uno de sus
   cuadros, justo mientras el relevo está cruzando dos láminas. */
.rail button::before{
  content:""; flex:none;
  width:.9rem; height:1px; background:currentColor;
  transform:scaleX(.5); transform-origin:0 50%;
  transition:transform 240ms ease;
}
.rail li[data-viva] button::before{ transform:scaleX(1); }

/* ── VARIANTE B · LAS FECHAS EN LA LÍNEA, NO LA MUESCA ───────────────────────
   Victor: «lo quería JUSTIFICADO CON CONCURSOS, CON EL TÍTULO. No lo quería
   más a la izquierda del título.» A ya cumple eso —§ EL RAÍL DE FECHAS, arriba,
   y medido en el informe: x del <h2> y x de la tinta del raíl son el MISMO
   número en dos ventanas—, pero hay dos piezas que podían apoyarse en esa
   línea y no es decisión de esta lane cuál: la muesca (A, hoy activo) o la
   fecha misma (B). `concursos.js` pone `data-rail="b"` con `?rail=b`; por
   defecto esto no existe y el raíl es A.

   B invierte el orden del botón —la fecha va primero, pegada a la línea; la
   muesca queda última, contra el carril— y desplaza la regleta, el hilo y la
   aguja del canto EXTERIOR de `.concursos__lado` (`left:0`, el de A) al canto
   INTERIOR del CARRIL (`right:var(--velo)`, antes de donde empieza el velo):
   ahí es donde cae la muesca ahora, y el trazo tiene que seguir naciendo de la
   misma línea que dibuja el hilo, o la aguja y la muesca dejan de leerse como
   la misma marca. */
[data-rail="b"] .concursos__lado::before,
[data-rail="b"] .rail__hilo,
[data-rail="b"] .rail__aguja{ left:auto; right:var(--velo); }
/* `justify-content` no se hereda del default de A: en `row-reverse` el
   main-start es el canto DERECHO, así que un `flex-start` sin escribir dejaría
   el texto pegado ahí —flotando lejos de la línea en cuanto la fecha es más
   corta que la más larga del carril— en vez de pegado a la línea del título,
   que es lo que B viene a demostrar. `flex-end` empuja hacia el main-end, que
   en `row-reverse` es la IZQUIERDA. */
[data-rail="b"] .rail button{ flex-direction:row-reverse; justify-content:flex-end; }
[data-rail="b"] .rail button::before{ transform-origin:100% 50%; }

.concursos__pie{
  grid-column:1; grid-row:3;
  display:flex; align-items:baseline; justify-content:space-between;
  gap:1.5rem;
  border-top:1px solid var(--filete); padding-top:.85rem;
  opacity:var(--fotos);
}
/* La fecha viva sólo existe donde la columna no las trae escritas — ver la
   regleta, abajo. En escritorio sobra, y el cierre se queda solo: con
   `order:-1` va delante en el pintado aunque siga siendo el SEGUNDO hijo del
   DOM —el que anuncia `aria-current` no cambia—, así que un `space-between` de
   un único hijo visible ya lo deja donde debe estar, al canto izquierdo, sin
   necesitar un margen que lo empuje. Si algún día vuelve la fecha viva junto a
   él, `space-between` los reparte solo: cierre a la izquierda, viva a la
   derecha. */
.concursos__viva{ display:none; }
.concursos__cierre{ order:-1; color:var(--tenue); text-align:left; white-space:nowrap; }

/* ── LA REGLETA · CUANDO LAS FECHAS ESCRITAS NO CABEN ────────────────────────
   Siete marcas, siete concursos, la viva encendida y descendiendo —la función
   intacta— pero SIN LAS FECHAS ESCRITAS, y la de la foto que se está viendo se
   lee en el pie, que es donde ya se leía.

   ── Y LA CONDICIÓN YA NO ES UN ANCHO DE VENTANA, ES UNA MEDIDA ─────────────
   Esto era una media query de 1000 px, y un número de ancho es el proxy
   equivocado: lo que decide no es cuánto mide la ventana, es cuánto HUECO NEGRO
   deja la fotografía a su izquierda. El encuadre se calcula con tres topes y en
   una ventana ancha y baja sobra hueco de sobra —a 1440×900, 205 px a cada
   lado— mientras que en una alta y estrecha del mismo ancho no sobra ninguno:
   a 1024×1366 el marco ocupa el ancho ENTERO. Con el corte por ancho, la
   tableta en vertical se llevaba las fechas escritas encima del perro. Está
   capturado: el velo se comía el cuarto trasero del arlequín.

   Así que lo decide `concursos.js` midiendo —§«¿CABEN LAS FECHAS ESCRITAS?»— y
   lo escribe en `data-regleta`. La regla en una línea: **fechas escritas sólo
   si el carril entero cabe en el hueco negro**. Donde no cabe, regleta; y la
   fotografía no cede un píxel en ningún caso, que es de lo que iba esta lane.

   El velo se estrecha con ella —1.5rem de carril en vez de 7.2— pero NO se
   apaga: aquí la lámina llega hasta el canto, así que el fondo que garantiza el
   contraste hace más falta que en escritorio, no menos. Medido: en un teléfono
   toca el 0.9 % del ancho del marco y le baja como mucho 0.010 de luminancia,
   porque cae sobre el fundido del 9 % que la lámina ya trae.

   Que no haga falta una red por si JavaScript no llega lo dice la propia
   sección: `.lamina` nace en `opacity:var(--w, 0)` y `--w` lo escribe
   `pinta()`. Sin JavaScript no hay fotografías, así que no hay nada que tapar.

   El texto se esconde A LA VISTA Y NUNCA A LA ACCESIBILIDAD: la fecha sigue en
   el árbol, el botón conserva su nombre accesible con el premio pegado, y con el
   tabulador la columna se recorre igual que con las fechas escritas. */
/* EL CARRIL DE LA REGLETA ES EL DE LA MUESCA Y NADA MÁS —.62rem de trazo en un
   carril de 1rem— y el velo se estrecha con él. No es tacañería: aquí la lámina
   llega hasta el canto, así que cada píxel de carril es fotografía tapada, y
   además el campo del rótulo del premio se mide contra este borde. Con los
   1.5rem + 1.6rem de escritorio, el premio perdía un 15 % de cuerpo en un
   teléfono; con 1rem + .8rem pierde un 4 %, y el velo entero cabe dentro del
   fundido del 9 % que la lámina ya trae. */
.concursos__vista[data-regleta]{ --carril:1rem; --velo:.8rem; }
[data-regleta] .concursos__lado{ align-items:stretch; }
[data-regleta] .rail{ height:100%; text-align:left; }
[data-regleta] .rail li{ display:flex; }
[data-regleta] .rail button{ padding:.5rem 0; }
[data-regleta] .rail button::before{ width:.62rem; }
[data-regleta] .rail__txt{
  position:absolute; width:1px; height:1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
[data-regleta] .concursos__viva{ display:block; color:#fff; }

@media (max-width: 820px){
  .concursos__vista{ padding-inline:1.1rem; }
  .concursos__pie{ flex-direction:column; align-items:flex-start; gap:.45rem; }
  /* Apilado no hay canto que reflejar —sólo arriba y abajo—, así que el
     `order:-1` que manda al cierre a la izquierda en la fila de escritorio se
     deshace aquí: la columna vuelve al orden del DOM, viva encima, cierre
     debajo, que es el que tenía antes de esta lane. */
  .concursos__cierre{ order:0; white-space:normal; }
}

/* ── MENOS MOVIMIENTO ─────────────────────────────────────────────────────────
   No se mueve nada: ni el acercamiento, ni el desenfoque. El relevo se queda en
   un cruce de opacidad, que sigue siendo el mecanismo entero —una pieza da paso
   a la siguiente— sin un solo píxel viajando.

   Y EL TÍTULO NO VIAJA: `--viaje` a 0 le quita al riel el tramo del viaje, así
   que la sección abre directamente en su estado final —el rótulo en su sitio,
   las fotos puestas— y no queda ni un píxel de scroll muerto donde antes estaba
   el recorrido. Una línea, y `concursos.js` no necesita saber nada: mide la
   regla, le sale 0 y no hay viaje que pintar. */
@media (prefers-reduced-motion: reduce){
  .concursos{ --viaje:0px; }
  .lamina{ transform:none; filter:none; }
}
