/* ==========================================================================
   laverapizza.css — pagina de caso, en clave de galeria
   --------------------------------------------------------------------------
   Mismo patron que el resto del portafolio: galeria en blanco, radio 0, el
   color de marca como unico acento. Tres diferencias de este caso:

   1. SIN LAMINAS NI BANNERS A SANGRE. La Vera Pizza no tiene presentacion de
      Behance ni banners de sitio web reales, y se descarto componer una tira
      (ver seleccion.py): son las mismas cuatro piezas que sostienen el feed,
      y reusar tres de cuatro se lee como repeticion. La pagina se apoya en
      el feed y las historias.

   2. SIN SECCION DE REELS (22 ago 2026). Los seis videos eran metraje del
      cliente presentando producto: la seccion se quito entera. Quedan las
      reglas del VIDEO DEL VISOR, porque el <dialog> conserva su <video> y
      el JS lo referencia.

   3. HISTORIAS EN UNA SOLA GALERIA PLANA, no en pares lado a lado: las
      cuatro recomposiciones 9:16 y las seis piezas de la serie narrativa
      conviven bien como piezas sueltas, todas a la misma altura.

   PALETA — muestreada por pixel exacto de las propias piezas (nunca
   inventada), sobre 01/Post/24-01-23.jpg y 01/Post/28-01-23.jpg:
     #FFCD00  amarillo del rotulado (fill de los titulares en las piezas)
     #AA2D2B  rojo de las etiquetas ("¡GRATIS!", "¿ganadora?")
   El rojo YA pasa AA sobre blanco por su cuenta (6,73:1): a diferencia de
   otros casos, aqui no hizo falta bajar el vivo para sacar un token, el
   mismo rojo real sirve de acento e interfaz. El amarillo es decorativo,
   nunca interfaz: solo aparece en el fondo del hero y en la palabra
   resaltada del titular, sobre el hero oscuro.
   ========================================================================== */

.caso-laverapizza {
  color-scheme: light;

  --tinta:        #1c1512;
  --tinta-media:  #6b5f5a;   /* 7,1:1 sobre blanco */
  --tinta-suave:  #8a7d78;   /* 4,5:1 sobre blanco: el minimo para texto */
  --linea:        #ede0da;
  --papel:        #ffffff;
  --papel-alt:    #fdf8f6;
  --rojo:         #aa2d2b;   /* 6,73:1 sobre blanco */
  --rojo-oscuro:  #7c211f;   /* hover del boton */
  --rojo-hondo:   #4a1614;   /* velo del hero, mas hondo que el acento */
  --amarillo:     #ffcd00;   /* decorativo: nunca lleva texto encima en claro */

  --e1: .5rem;  --e2: 1rem;   --e3: 1.5rem;  --e4: 2.5rem;
  --e5: 4rem;   --e6: 6rem;   --e7: clamp(5rem, 12vw, 9rem);

  --ancho: 1240px;
  --margen: clamp(1.25rem, 5vw, 3.5rem);

  --display: "Luckiest Guy", ui-rounded, "Segoe UI", sans-serif;
  --texto:   "Nunito Sans", ui-sans-serif, system-ui, sans-serif;
  --dato:    "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --salida: cubic-bezier(.16, 1, .3, 1);
}

.caso-laverapizza, .caso-laverapizza *, .caso-laverapizza *::before, .caso-laverapizza *::after {
  box-sizing: border-box;
}

.caso-laverapizza {
  background: var(--papel);
  color: var(--tinta);
  font-family: var(--texto);
  font-weight: 400;
  font-size: clamp(1rem, .96rem + .2vw, 1.075rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

body:has(> .caso-laverapizza) { margin: 0; background: var(--papel); }

.caso-laverapizza img, .caso-laverapizza video { display: block; max-width: 100%; }

.caso-laverapizza a { color: inherit; }

.caso-laverapizza ::selection { background: var(--amarillo); color: var(--tinta); }

.caso-laverapizza :focus-visible { outline: 2px solid var(--rojo); outline-offset: 3px; }

.contenedor { max-width: var(--ancho); margin-inline: auto; padding-inline: var(--margen); }

.salta { position: absolute; left: -9999px; }
.salta:focus {
  left: var(--margen); top: var(--e2); z-index: 60;
  padding: .7rem 1.1rem; background: var(--tinta); color: #fff;
  font-family: var(--dato); font-size: .82rem;
}

/* --- Apertura -------------------------------------------------------------
   El fondo son las piezas reales en cuatro tiras a distintas velocidades,
   igual que en Barkys y La Mera Mera. Sin lamina de presentacion propia: el
   feed ya trae suficiente caracter (amarillo, rojo, fotografia de producto). */

.portada {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: min(78dvh, 760px);
  overflow: hidden;
  background: var(--rojo-hondo);
  isolation: isolate;
}

.portada__fondo {
  position: absolute; inset: 0;
  z-index: -2;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(8px, 1.2vw, 16px);
  padding: clamp(8px, 1.2vw, 16px);
}
@media (max-width: 699px) {
  .portada__fondo { grid-template-columns: repeat(2, 1fr); }
  .portada__fondo > :nth-child(n+3) { display: none; }
}

.tira {
  display: grid;
  gap: clamp(8px, 1.2vw, 16px);
  align-content: start;
  will-change: transform;
}
.tira img { width: 100%; height: auto; }

.portada__velo {
  position: absolute; inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgb(20 6 4 / .65), transparent 62%),
    rgb(28 21 18 / .88);
}

.portada__texto {
  position: relative;
  width: 100%;
  padding-block: clamp(6rem, 14vw, 9rem) clamp(2.5rem, 6vw, 4.5rem);
  color: #fff;
}
.portada__rotulo {
  margin: 0 0 var(--e2);
  font-family: var(--dato);
  font-size: .74rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: rgb(255 255 255 / .82);
}
.portada__titulo {
  margin: 0;
  max-width: 22ch;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(2.1rem, 5.2vw, 3.9rem);
  line-height: 1.12; letter-spacing: .01em;
  color: #fff;
  text-wrap: balance;
}
.portada__bajada {
  margin: var(--e3) 0 0;
  max-width: 46ch;
  font-size: clamp(1.02rem, .95rem + .35vw, 1.2rem);
  color: rgb(255 255 255 / .92);
  text-wrap: balance;
}

/* Una palabra del titular, resaltada. El resto ya es blanco solido, asi que
   un cambio de color es lo unico que se lee como enfasis sin sumar peso ni
   subrayado (patron de La Mera Mera). El amarillo de marca es DECORATIVO
   -nunca se usa en interfaz sobre blanco, ver la nota de paleta arriba- y
   esta es la unica palabra de la pagina que lo lleva, porque solo aqui hay
   fondo oscuro para sostenerlo.
   Contraste contra el peor caso del velo (un pixel blanco de una pieza
   detras, a la opacidad del velo): 8,5:1. El titulo es texto grande (min.
   2,1rem, siempre por encima del umbral de 24px), asi que el minimo AA es
   3:1 y no 4,5:1 -y este pasa los dos con margen de sobra. */
.destaca { color: var(--amarillo); }

.marca {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 3;
  max-width: var(--ancho);
  margin-inline: auto;
  padding: clamp(var(--e2), 2.5vw, var(--e4)) var(--margen);
}
.marca a { display: inline-block; }
.marca img { width: clamp(104px, 11vw, 132px); height: auto; }

/* --- Bloques de texto ---------------------------------------------------- */

.bloque { margin-block: clamp(3.5rem, 7vw, 5.5rem); }

/* Titulos de seccion en versales con un filete corto, patron de Klear tos y
   Konempleo: el filete hereda `currentColor` (el rojo del titulo), asi que
   no hace falta un segundo token de color solo para la linea. */
.bloque__titulo {
  margin: 0 0 var(--e3);
  display: flex;
  align-items: center;
  gap: .85rem;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.6rem, 1.15rem + 1.8vw, 2.4rem);
  line-height: 1.2; letter-spacing: .01em;
  text-wrap: balance;
  color: var(--rojo);
  text-transform: uppercase;
}
/* EL BUG QUE INFLABA EL TITULO: el texto del h2 viajando como nodo de texto
   crudo dentro de un flex hereda `min-width: auto` y nunca se encoge por
   debajo de su ancho sin envolver, sin importar cuanto `text-wrap: balance`
   se le pida. Se evita envolviendo el texto en un <span>, que si puede
   recibir `min-width: 0`. Corregido de entrada, no encontrado despues. */
.bloque__titulo > span { min-width: 0; }

.bloque__titulo::after {
  content: "";
  flex: 0 0 2.6rem;
  height: .35rem;
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  opacity: .55;
}
.bloque--centrado > .bloque__titulo::before,
.bloque__titulo--solo::before {
  content: "";
  flex: 0 0 2.6rem;
  height: .35rem;
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  opacity: .55;
}
.bloque--centrado > .bloque__titulo,
.bloque__titulo--solo { justify-content: center; }

.bloque__titulo--solo {
  margin-bottom: var(--e4);
  text-align: center;
}

.bloque--centrado > .bloque__titulo,
.bloque--centrado > .bloque__texto {
  text-align: center;
  margin-inline: auto;
}
.bloque__texto {
  margin: 0;
  max-width: 62ch;
  color: var(--tinta-media);
}
.bloque__texto + .bloque__texto { margin-top: var(--e2); }

.dos-columnas {
  display: grid; gap: var(--e3) var(--e5);
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .dos-columnas { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: start; }
}

.alcance { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--e2); }
.alcance li {
  padding-top: var(--e2);
  border-top: 1px solid var(--linea);
  color: var(--tinta-media);
}

/* --- Rejilla de galeria -------------------------------------------------- */

.galeria {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 20px);
  margin: 0; padding: 0; list-style: none;
  grid-auto-flow: dense;
}

/* La celda doble cada seis piezas es lo que le da ritmo a un feed largo. Con
   un feed CORTO estorba: desde el 22 ago 2026 quedan cuatro piezas, y una de
   ellas al doble de tamano deja la rejilla en dos filas con un hueco del
   tamano de una pieza al final. El `:has(> li:nth-child(6))` la activa solo
   cuando hay material para sostenerla, y se ajusta sola si algun dia vuelve a
   entrar contenido. */
@media (min-width: 900px) {
  .galeria:not(.galeria--historias):has(> li:nth-child(6)) > li:nth-child(6n + 1) {
    grid-column: span 2;
    grid-row: span 2;
  }
}

/* Cuatro piezas cuadradas: una sola fila de cuatro, que es como se leen
   mejor. Sin esto la rejilla de 4 columnas ya las dejaba asi, pero se
   escribe explicito para que no dependa de que el numero de piezas coincida
   con el de columnas. */
@media (min-width: 900px) {
  .galeria:not(.galeria--historias):not(:has(> li:nth-child(5))) {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (max-width: 899px) {
  .galeria { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Dos de los once posts del feed son 9:16 reales (la pizza en bandeja
   rectangular, el postre), no 1:1: en vez de recortarlas para igualar la
   celda cuadrada, la celda se alarga a dos filas y se deja la imagen entera
   (mismo principio de "agrupar por forma" que ya resolvio Eterna con video).
   Se marca por proporcion real, no a mano (ver `pieza()` en
   generar_pagina.py), que es la MISMA funcion que arma las historias -por
   eso la clase se restringe con `:not(.galeria--historias)`: ahi TODAS las
   piezas son 9:16 y ya tienen su propia solucion de alto comun (mas abajo);
   sin el `:not()`, esta regla les ganaba por especificidad y les rompia esa
   fila. */
.galeria:not(.galeria--historias) > li.pieza--alta {
  grid-row: span 2;
}
.galeria:not(.galeria--historias) > li.pieza--alta .pieza {
  height: 100%;
}
.galeria:not(.galeria--historias) > li.pieza--alta .pieza img {
  height: 100%;
  width: 100%;
  object-fit: contain;
}
@media (max-width: 899px) {
  /* A dos columnas la celda deja de emparejarse con fotos cuadradas
     vecinas: la pieza vuelve a su alto natural, como cualquier otra. */
  .galeria:not(.galeria--historias) > li.pieza--alta { grid-row: auto; }
  .galeria:not(.galeria--historias) > li.pieza--alta .pieza { height: auto; }
  .galeria:not(.galeria--historias) > li.pieza--alta .pieza img { height: auto; width: 100%; object-fit: initial; }
}

/* --- Historias: rejilla pareja de 4 columnas -------------------------------
   Diez piezas, TODAS 9:16 (a diferencia de La Mera Mera, que mezclaba
   Storie con correos de hasta 1:2,45): al ser un solo formato, no hace
   falta ningun mecanismo especial de alturas mixtas. `.galeria--historias`
   hereda la rejilla base de `.galeria` sin tocar nada -4 columnas en
   escritorio, 2 en movil, la misma que usa el feed- asi que cada fila queda
   pareja y centrada sola, sin calculos de por medio.

   DOS VERSIONES ANTERIORES DE ESTA REGLA SE ROMPIERON, y las dos por la
   misma razon: fijarle una altura a mano en vez de dejar que la rejilla
   resuelva sola. La primera version (flex con `height: 100%` en cada
   pieza, resuelto contra la altura del CONTENEDOR) dejaba `object-fit` mal
   heredado desde la regla de "pieza alta" del feed. La segunda (altura fija
   en el contenedor, pensada para una sola fila) se desbordaba por fuera de
   su caja en cuanto el numero de piezas obligaba a una segunda fila, y
   quedaba pintada ENCIMA de la seccion de creditos -es la misma familia de
   fallo que ya paso en Magna Lux, una cadena de altura que se rompe.

   La leccion: en una rejilla de piezas TODAS con la misma proporcion, no
   hace falta fijar ninguna altura a mano. `display: grid` con columnas
   iguales ya reparte el alto de cada fila solo, a partir del contenido, y
   crece sin limite sin importar cuantas filas hagan falta. */

.pieza {
  position: relative;
  display: block; width: 100%; padding: 0;
  border: 0; background: var(--papel-alt);
  cursor: zoom-in;
  overflow: hidden;
}
.pieza img {
  width: 100%; height: auto;
  transition: transform .5s var(--salida), opacity .3s ease;
}
.pieza:hover img, .pieza:focus-visible img { transform: scale(1.06); }

/* --- Reels: seccion quitada el 22 ago 2026 -----------------------------------
   Aqui vivian `.reels`, `.reel` y `.reel video`. Se fueron con la seccion
   (ver seleccion.py: los seis videos eran metraje del cliente presentando
   producto, no trabajo de diseno). Las reglas del VISOR para `video` SI se
   conservan mas abajo: el `<video>` del `<dialog>` sigue en el HTML porque el
   JS lo referencia, y si algun dia vuelve a entrar video ya esta resuelto. */

/* --- Creditos y cierre, en una sola seccion --------------------------------
   Creditos a la izquierda, la pregunta de cierre a la derecha, el boton
   centrado abajo de las dos columnas. En movil cae a una sola columna. */

.creditos-cierre {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--e5);
  align-items: start;
  margin-block: clamp(2.5rem, 5vw, 4rem);
  padding-top: clamp(2.25rem, 4vw, 3.25rem);
  border-top: 1px solid var(--linea);
}
@media (max-width: 699px) {
  .creditos-cierre { grid-template-columns: 1fr; gap: var(--e4); }
  .creditos-cierre__boton { grid-column: auto; }
}

.creditos-cierre__columna .bloque__titulo { margin-bottom: var(--e3); }

.creditos {
  max-width: 68ch;
  margin: 0;
  padding-left: var(--e3);
  border-left: 2px solid var(--rojo);
  font-size: .95rem;
  color: var(--tinta-media);
}
.creditos strong { font-weight: 600; color: var(--tinta); }

.cierre__contenido h2 {
  margin: 0 0 var(--e2);
  color: var(--rojo);
  max-width: none;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.5rem, .95rem + 1.4vw, 1.95rem);
  line-height: 1.2; letter-spacing: .01em;
  text-wrap: balance;
  text-transform: uppercase;
}
.cierre__contenido p { margin: 0; max-width: 60ch; color: var(--tinta-media); }

.creditos-cierre__boton {
  grid-column: 1 / -1;
  justify-self: center;
  margin-top: var(--e2);
}

/* Mismo fallo que ya aparecio en Barkys, Coltanques, Bonfibra y La Mera Mera:
   el boton es un <a>, asi que `.caso-X a { color: inherit }` le gana por
   especificidad a `.boton { color: #fff }` si el selector del boton no lleva
   la clase del caso delante. Se evita desde el inicio. */
/* Unica excepcion al radio 0 de la pagina: el boton de cierre, en pildora,
   con la floritura "❧" que se repite en las piezas de la cuenta alrededor de
   palabras con enfasis. */
.caso-laverapizza .boton {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .95rem 2.1rem;
  background: var(--rojo); color: #fff;
  font-family: var(--texto); font-weight: 600; font-size: 1rem;
  text-decoration: none;
  border-radius: 999px;
  transition: background .2s var(--salida), transform .2s var(--salida);
}
.caso-laverapizza .boton::before,
.caso-laverapizza .boton::after {
  content: "❧";
  font-size: .85em;
  color: rgb(255 255 255 / .75);
}
.caso-laverapizza .boton::after { transform: scaleX(-1); }
.caso-laverapizza .boton:hover { background: var(--rojo-oscuro); }
.caso-laverapizza .boton:active { transform: translateY(1px); }

.pie {
  padding: clamp(2.5rem, 5vw, 3.5rem) var(--margen) clamp(2rem, 4vw, 3rem);
  background: var(--papel);
  text-align: center;
}
.pie > .contenedor { padding-inline: 0; }

.pie__filete {
  display: flex;
  align-items: center;
  gap: var(--e3);
  margin-bottom: var(--e4);
}
.pie__filete::before,
.pie__filete::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--linea);
}
.pie__filete img { height: 26px; width: auto; }

.pie__menu {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--e2) 0;
  margin-bottom: var(--e4);
}
.pie__menu a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 clamp(9px, 1.4vw, 17px);
  font-size: .87rem;
  color: var(--tinta-suave);
  text-decoration: none;
  transition: color .2s;
}
.pie__menu a + a::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 1px; height: 11px;
  margin-top: -5px;
  background: var(--linea);
}
.pie__menu a:hover { color: var(--rojo); }

@media (max-width: 699px) {
  .pie__menu { gap: 0 var(--e1); }
  .pie__menu a::before { display: none; }
}

.pie__legal { margin: 0; font-size: .8rem; color: var(--tinta-suave); }
.pie__nota { margin: 6px 0 0; font-size: .78rem; color: var(--tinta-suave); opacity: .85; }

/* --- Visor (lightbox) ------------------------------------------------------ */

.visor {
  width: 100vw; max-width: 100vw;
  height: 100dvh; max-height: 100dvh;
  padding: 0; border: 0;
  background: transparent;
  overflow: hidden;
}
.visor::backdrop { background: rgb(17 15 16 / .95); }
/* `minmax(0, 1fr)` y no `1fr`: son cosas distintas. `1fr` equivale a
   `minmax(auto, 1fr)`, y ese minimo `auto` es el min-content de lo que hay
   dentro — o sea el alto INTRINSECO de la imagen. Con una pieza vertical la
   fila crecia hasta el tamano real del archivo, la imagen se salia por abajo
   del dialogo y tapaba la barra de navegacion. Medido en Eterna: en un visor
   de 257px de alto, una pieza de 600x1600 se pintaba a 981px.
   El `0` de minimo deja que la fila se encoja y la imagen se ajuste. */
.visor[open] { display: grid; grid-template-rows: minmax(0, 1fr) auto; }

/* Flex y no grid: con `place-items: center` la imagen quedaba de item de
   rejilla y su `max-height: 100%` se resolvia contra una altura que la propia
   imagen estaba estirando. En flex el marco manda y la imagen se ajusta.
   `overflow: hidden` es el cinturon de seguridad por si algo se escapa. */
.visor__marco {
  display: flex; align-items: center; justify-content: center;
  min-height: 0; min-width: 0;
  overflow: hidden;
  padding: clamp(var(--e3), 5vw, var(--e5));
}
/* EL BUG QUE HACIA VER MAL CUALQUIER PIEZA ABIERTA. `max-height: 100%` aqui
   NUNCA se resuelve: el padre (`.visor__marco`) recibe su alto del track
   `1fr` de `.visor[open]`, y ese alto resultante no cuenta como "definido"
   para que un descendiente resuelva un porcentaje contra el. El navegador
   entonces trata `max-height` como si no existiera y pinta la pieza a su
   tamano NATURAL, desbordando la ventana entera.

   La salida es no depender de un porcentaje: `dvh` es siempre un numero
   absoluto sin importar la cadena de contenedores. */
.visor__marco img {
  max-width: 100%; max-height: 78dvh;
  width: auto; height: auto;
  object-fit: contain;
  background: #fff;
}
.visor__marco video {
  max-width: 100%; max-height: 78dvh;
  width: auto; height: auto;
  background: #000;
}

/* EL FALLO QUE INFLABA LA IMAGEN. `.caso-laverapizza img, .caso-laverapizza
   video { display: block; }`, mas arriba en esta hoja, le gana en
   especificidad a la regla del navegador para `[hidden]`. Sin esto, el
   elemento oculto del visor sigue contando como una fila mas dentro del grid
   de `.visor__marco`, y el `max-height` de la imagen visible queda
   resolviendo contra una referencia circular. Corregido de entrada. */
.visor__marco img[hidden], .visor__marco video[hidden] { display: none; }
.visor__barra {
  display: flex; align-items: center; gap: var(--e3);
  padding: var(--e2) clamp(var(--e2), 4vw, var(--e4)) var(--e4);
  color: #f2f2f2;
}
.visor__cuenta {
  flex: 1;
  font-family: var(--dato); font-size: .78rem;
  color: #b0a29c; white-space: nowrap;
}
.visor__nav { display: flex; gap: .5rem; }

.visor button {
  display: grid; place-items: center;
  width: 2.6rem; height: 2.6rem;
  color: #fff; background: rgb(255 255 255 / .1);
  border: 0; cursor: pointer;
  font-size: 1.1rem; line-height: 1;
  transition: background .2s var(--salida);
}
.visor button:hover:not(:disabled) { background: rgb(255 255 255 / .24); }
.visor button:disabled { opacity: .3; cursor: default; }
.visor__cerrar {
  position: absolute; top: var(--e2); right: clamp(var(--e2), 4vw, var(--e4));
}

/* ==========================================================================
   MOVIMIENTO — mismo patron que el resto del portafolio.
   ========================================================================== */

.js [data-entra] {
  opacity: 0;
  transform: translateY(20px) scale(.985);
}
.js [data-entra].visible {
  opacity: 1;
  transform: none;
  transition:
    opacity .75s var(--salida) var(--retraso, 0ms),
    transform .75s var(--salida) var(--retraso, 0ms);
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    .galeria { perspective: 1500px; }
    .galeria > li { transform-style: preserve-3d; }

    .js:not(.sin-timeline) .galeria > li {
      opacity: 1;
      transform: none;
      animation: llega linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 34%;
    }

    .js:not(.sin-timeline) .bloque__titulo[data-entra],
    .js:not(.sin-timeline) .bloque__texto[data-entra] {
      opacity: 1;
      transform: none;
      animation: asoma linear both;
      animation-timeline: view();
      animation-range: entry 8% cover 26%;
    }
  }
}

@keyframes llega {
  from { opacity: 0; transform: translate3d(0, 44px, -150px) rotateX(11deg); }
  to   { opacity: 1; transform: none; }
}
@keyframes asoma {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: none; }
}

.js .tira {
  animation: desfilar var(--vel, 70s) linear infinite;
}
.js .tira:nth-child(even) { animation-direction: reverse; }
.js .tira:nth-child(1) { --vel: 68s; }
.js .tira:nth-child(2) { --vel: 84s; }
.js .tira:nth-child(3) { --vel: 74s; }
.js .tira:nth-child(4) { --vel: 92s; }

@keyframes desfilar {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -50%, 0); }
}

.js .portada__rotulo { animation: sube .8s var(--salida) both; animation-delay: .1s; }
.js .portada__titulo { animation: sube .9s var(--salida) both; animation-delay: .22s; }
.js .portada__bajada { animation: sube .9s var(--salida) both; animation-delay: .38s; }
.js .marca { animation: sube .7s var(--salida) both; animation-delay: .05s; }

@keyframes sube {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js [data-entra] { opacity: 1; transform: none; transition: none; }
  .js .tira { animation: none; }
  .js .marca, .js .portada__rotulo, .js .portada__titulo,
  .js .portada__bajada,
  .js .galeria > li {
    animation: none;
    opacity: 1;
    transform: none;
    clip-path: none;
  }
  .pieza img, .boton { transition: none; }
  .pieza:hover img, .pieza:focus-visible img { transform: none; }
}

@media print {
  .visor, .creditos-cierre__boton { display: none; }
  .js [data-entra] { opacity: 1; transform: none; }
  .galeria { grid-template-columns: repeat(3, 1fr); }
}
