/* ==========================================================================
   gasolina.css — pagina de caso, en clave de galeria
   --------------------------------------------------------------------------
   Misma arquitectura que `barkys.css`, `bonfibra.css` y `coltanques.css`:
   hoja unica, sin dependencias, con TODO colgado de una clase para que sirva
   suelta y dentro del sitio. Lo que cambia es la marca que se presenta, no el
   sistema.

   TRES DECISIONES QUE EXPLICAN TODO LO DEMAS

   1. Un solo tema, blanco.  Y aqui pesa mas que en las cinco anteriores. Las
      piezas de Gasolina Extra son bloques de color saturado a sangre: magenta,
      turquesa, violeta, amarillo. Metidas en una pagina de color, cualquier
      color, se pelean con el fondo. Sobre blanco cada una se lee como lo que
      es. Ademas el caso se presenta como galeria, que se pinta de blanco por
      la misma razon que un museo.

   2. La pagina calla para que el trabajo hable.  Sin cajas, sin sombras, sin
      degradados, sin rotulos sobre las imagenes. El magenta de marca es el
      unico acento y tiene un solo trabajo: los titulos de seccion y el boton.
      Magenta = lo que pone Gasolina Extra. Negro = lo que dice el estudio.

      OJO EN ESTE CASO. La cuenta cambio de sistema grafico dos veces: 2021 son
      manchas organicas de color, mediados de 2022 son tramas geometricas
      (semitono, pixel, escalera) y 2023 es fotografia de estudio con el
      logotipo al pie. La tentacion era darle a cada seccion el color de su
      epoca. No se hizo, por lo mismo de siempre: esos colores viven dentro de
      las imagenes, que es donde el cliente los puso.

   3. Radio 0 en todo, con UNA excepcion: el boton de cierre, que es una
      pastilla. Se hereda de las cinco paginas anteriores y se deja igual a
      proposito, porque ese boton es el mismo en las landings del sitio y una
      version cuadrada aqui lo desemparejaria de todo lo demas.

      OJO: los LEEME de Barkys, Bonfibra, Coltanques y Famoc dicen «radio 0 en
      todo, incluido el boton», y no es lo que hacen las hojas. La regla real
      es la de arriba.

   PALETA — muestreada de los 1.025 JPG originales, no elegida de un catalogo:
     #2E2A8C  el violeta del logotipo, que es el color de marca mas
              reconocible. Da 11,50:1 sobre blanco, o sea que como acento se
              confundiria con la tinta (#131313, 18,58:1). NO es el acento: es
              el FONDO del hero, que es el trabajo que sabe hacer un color tan
              oscuro. Es el mismo caso que el marino de Famoc
     #D81858  el magenta tal como sale en las piezas de promocion. Da 5,01:1
              sobre blanco: pasa AA, pero por poco
     --magenta  #D21755, ese mismo magenta calibrado a 5,26:1, que es el
              contraste con el que trabajan las cinco paginas anteriores. Sobre
              el, el texto blanco tambien da 5,26:1, asi que el boton pasa AA
              sin tener que buscar otro color

   TIPOGRAFIAS — son de la marca presentada, no de Ballestastudio. Lo unico
   propio es el logo. Se sacaron AMPLIANDO LAS LETRAS DE LOS TITULARES, no del
   logotipo:
     Montserrat 700/800   grotesca geometrica de a de DOS pisos, O circular y
                          terminales cortadas en horizontal, con oblicua muy
                          usada. Es lo que se lee ampliando "MANANA 20% OFF",
                          "EN TODAS nuestras TIENDAS" y "Amor y la Amistad"
     Source Sans 3 400/600  humanista y neutra. Contrasta con la geometria del
                          titular en vez de duplicarla
     IBM Plex Mono        los datos son ficha tecnica, no titulo. Es la misma
                          que usan los demas casos: el unico rasgo tipografico
                          que el estudio conserva de pagina a pagina
   ========================================================================== */

/* TODO cuelga de `.caso-gasolina`, no de `:root` ni de `body`. Asi la misma
   hoja sirve para la pagina suelta y para el fragmento incrustado en el sitio,
   sin pisar los tokens ni la tipografia de ballestastudio.com. Integrar es
   enlazar esta hoja y envolver el cuerpo en <div class="caso-famoc">. */
.caso-gasolina {
  color-scheme: light;

  --tinta:        #131313;   /* 18,58:1 sobre blanco */
  --tinta-media:  #5c5c5c;   /* 6,69:1 sobre blanco */
  --tinta-suave:  #767676;   /* 4,54:1 sobre blanco: el minimo para texto */
  --linea:        #e8e8e8;
  --papel:        #ffffff;
  --papel-alt:    #fafafa;
  --magenta:       #D21755;  /* 5,26:1 sobre blanco. El unico acento */
  --magenta-hondo: #A81044;  /* 7,52:1. Solo el hover del boton */
  --hero:          #17143f;  /* fondo del hero, bajo las tiras */
  --hero-brillo:   #2E2A8C;  /* el violeta del logotipo, desde el pie del hero */

  /* Escala de espacio. Todo el ritmo vertical sale de aqui. */
  --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: "Montserrat", ui-sans-serif, system-ui, sans-serif;
  --texto:   "Source Sans 3", 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-gasolina, .caso-gasolina *, .caso-gasolina *::before,
.caso-gasolina *::after {
  box-sizing: border-box;
}

/* EL TOQUE EN TELEFONO. Dos cosas que solo se ven en un movil de verdad y que
   en esta pagina pesan, porque es una galeria: casi todo lo que hay es algo
   que se toca.

   `touch-action: manipulation` quita los 300 ms que el navegador espera por si
   el toque era un doble toque para hacer zoom. Sin esto, abrir una pieza en el
   visor se siente lento y no hay nada que arreglar en el JS.

   `-webkit-tap-highlight-color` es el recuadro gris que iOS pinta encima de lo
   que tocas. Sobre una galeria en blanco se ve, y ademas duplica lo que ya
   dice el `:active`. Se apaga a proposito, no por descuido: el estado tactil
   sigue existiendo en `.pieza:active` y en `.boton:active`. */
.caso-gasolina .pieza,
.caso-gasolina .boton,
.caso-gasolina button,
.caso-gasolina a {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* El `:active` que reemplaza al recuadro de iOS. Es el mismo empujon fisico
   que ya tiene el boton de cierre, aplicado a la pieza. */
.caso-gasolina .pieza:active { opacity: .82; }

.caso-gasolina {
  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;
}

/* La pagina suelta no tiene mas contenido que el caso, asi que el papel sube
   al <body> para que cubra la ventana entera. En el sitio esta regla no
   aplica: alli el fondo lo pone el sitio. */
body:has(> .caso-gasolina) { margin: 0; background: var(--papel); }

.caso-gasolina img { display: block; max-width: 100%; }

.caso-gasolina a { color: inherit; }

.caso-gasolina ::selection { background: var(--magenta); color: #fff; }

.caso-gasolina :focus-visible { outline: 2px solid var(--magenta); 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 desplazandose en tiras a distintas
   velocidades, y encima va una frase corta que invita a bajar. El fondo es el
   trabajo, que es lo que se vino a ver, y no una animacion abstracta pegada
   encima.

   Cuatro tiras y no mas: pasada esa cantidad el efecto no gana profundidad y
   si multiplica el coste de pintado. */

.portada {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: min(78dvh, 760px);
  overflow: hidden;
  background: var(--hero);
  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;
  /* La tira se sale por arriba y por abajo a proposito: asi el bucle nunca
     ensena un borde. El contenedor recorta. */
  will-change: transform;
}
.tira img { width: 100%; height: auto; }

/* El velo. Va aparte del fondo y no como sombra sobre el texto, porque tiene
   que cubrir veinticuatro imagenes de valores muy distintos por igual.

   CONTRASTE GARANTIZADO POR CONSTRUCCION. A 0,88 sobre el peor caso posible
   —un pixel blanco de una pieza— el resultado es rgb(48 52 69), que con texto
   blanco encima da 12,3:1. Cualquier pixel mas oscuro da mas. No depende de
   que imagen toque en cada momento del bucle.

   Y aqui el peor caso NO es el blanco sino el color vivo: casi ninguna pieza
   de Gasolina Extra es blanca, pero un amarillo o un turquesa saturado tienen
   luminancia alta y se comportan como blanco frente al velo. Calcularlo sobre
   blanco puro cubre los dos casos de una vez.

   El violeta NO va en el velo plano sino en el degradado que sube desde el
   pie: un velo violeta cerrado sobre veinticuatro piezas de colores distintos
   se los tine a todos del mismo violeta y el hero deja de ensenar el trabajo,
   que es justamente el rango de color. */
.portada__velo {
  position: absolute; inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgb(36 48 96 / .60), transparent 64%),
    rgb(20 24 44 / .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: 44ch;
  /* 800 y no 900. Montserrat TIENE 900, pero a este cuerpo la diferencia entre
     800 y 900 es de un pelo y cada peso extra es una peticion mas de fuente.
     Ojo: si algun dia alguien vuelve a poner 900, hay que pedirlo tambien en
     la URL de Google Fonts o el navegador lo SINTETIZA engordando el 700, y
     eso si se ve. */
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.1rem, 4.6vw, 3.3rem);
  line-height: 1.06; letter-spacing: -.03em;
  color: #fff;
  /* `balance` reparte el titular en lineas parejas. Se prefiere a un <br>
     fijo, que corta bien en un idioma y mal en el otro y se rompe en cuanto
     cambia el ancho. */
  text-wrap: balance;
}
/* `nowrap` para que el navegador NO parta las dos lineas por su cuenta: el
   corte lo pone el <br> de `portada__salto`, que cae donde esta el sentido.
   En movil se suelta (ver la consulta de 699 px). */
.portada__titulo > span { display: inline; white-space: nowrap; }
.portada__titulo > span[lang="en"] { display: none; }
/* EL REMATE DEL TITULAR, en el acento de la marca.

   NO es `--magenta`. El acento de la pagina esta calibrado contra BLANCO
   (5,26:1) y aqui el fondo es el velo del hero: #D21755 sobre el peor caso del
   velo da 2,35:1 y no se lee. Es el mismo error que las cinco paginas tenian
   en el boton, al reves: calibrar un color contra un fondo no dice nada de su
   contraste contra otro.

   #FF7BA6 es ese mismo magenta subido de valor hasta que pasa AA sobre el
   fondo que le toca: 5,09:1 sobre el peor caso del velo (un pixel blanco de
   una pieza) y 6,85:1 sobre la zona violeta del pie. Cualquier pixel mas
   oscuro da mas.

   Sin subrayado: el titular ya va a 800 y en dos lineas, y una raya debajo de
   la segunda compite con el filete del rotulo que tiene justo encima. El color
   solo alcanza, y es lo que hacen las piezas de la marca. */
.palabra-clave { color: #FF7BA6; }
.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;
}

/* --- La marca -----------------------------------------------------------
   El logo y nada mas: sin barra, sin fondo, sin linea y sin quedarse pegado al
   hacer scroll. Va montado sobre el hero, alineado con el margen del
   contenedor. Mismo tratamiento que en Barkys y Bonfibra.

   Usa el lockup INVERTIDO a una tinta, porque sobre el hero oscuro el logo
   normal (negro con "STUDIO" en turquesa) se pierde. */

.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);
}
/* El enlace se cine al logo. Si la caja clicable fuera toda la franja, medio
   pantallazo llevaria al inicio sin avisar. */
.marca a { display: inline-block; }
.marca img { width: clamp(104px, 11vw, 132px); height: auto; }


/* --- Tira a sangre ------------------------------------------------------
   Ancho completo de la VENTANA, no del contenedor.

   ESTE PROYECTO NO TIENE LAMINAS DE PRESENTACION, pero tiene algo mejor: los
   cuatro mosaicos `Feed.jpg` que se le entregaban al cliente para aprobar la
   parrilla del mes. `build_assets.py` recorta UNA FILA de ese mosaico (ver
   `recortar_fila`) y la deja a sangre. No es una tira compuesta a posteriori:
   es la parrilla como se aprobo, con su propio orden y su propia calle.

   Cumple la condicion del 7.1 de `00_LEEME_Portafolio_MKT.md` sin discusion,
   porque lo que se ve son NUEVE PIEZAS DISENADAS y no fotos de la operacion.

   Sin pie de foto a proposito: una linea gris debajo de una imagen que ocupa
   la pantalla la convierte en una ilustracion con leyenda y le quita la
   fuerza.

   Sin trucos de 100vw: la figura es hija directa de <main>, que ya ocupa todo
   el ancho del documento. Un `width: 100vw` aqui solo serviria para sumar los
   pixeles de la barra de scroll y provocar desborde horizontal. */

.tira-sangre {
  margin-block: clamp(2.75rem, 5.5vw, 4.25rem);
  /* El navegador le da a <figure> un margen lateral de 40 px por su cuenta.
     `margin-block` no lo toca, asi que hay que anularlo a mano o la tira se
     queda 80 px angosta y deja de ir a sangre. */
  margin-inline: 0;
  overflow: hidden;
}
.tira-sangre img { width: 100%; height: auto; }

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

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

/* Los titulos van en el magenta de la marca. Es el sistema de acento de la
   pagina: magenta = lo que Gasolina Extra pone, negro = lo que dice el
   estudio. Da 5,26:1 sobre blanco, de sobra para AA incluso a este tamano.

   EN VERSALES, igual que en las cinco hojas anteriores. Y ojo, porque los
   LEEME de Barkys, Bonfibra y Famoc dicen que van en caja baja: no es lo que
   hace la regla, que lleva `text-transform: uppercase` desde la primera
   pagina. La que manda es la regla.

   Aqui ademas encaja con la marca sin forzarla. Los titulares de promocion de
   Gasolina Extra mezclan versal y caja baja dentro de la misma linea ("En
   TODAS nuestras TIENDAS"): ese contraste vive dentro de las piezas, y un
   rotulo de pagina en caja baja al lado competiria con el. En versales y de un
   solo peso, el rotulo se aparta.

   El `letter-spacing` es NEGATIVO porque a 44 px las versales de una geometrica
   dejan demasiado aire entre si. En caja baja este valor seria excesivo. */
.bloque__titulo {
  margin: 0 0 var(--e3);
  display: flex;
  align-items: center;
  gap: .85rem;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.7rem, 1.15rem + 2vw, 2.75rem);
  line-height: 1.12;
  letter-spacing: -.02em;
  text-wrap: balance;
  color: var(--magenta);
  text-transform: uppercase;
}
.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; }

/* Cuando el titulo es lo unico que lleva la seccion, va centrado: sin texto al
   lado, un titulo suelto pegado a la izquierda deja un vacio raro a su
   derecha. */
.bloque__titulo--solo { text-align: center; }

/* Los bloques con una linea de apoyo centran titulo y linea, pero NO las
   piezas: esas siguen su propia rejilla. */
.bloque--centrado > .bloque__titulo,
.bloque--centrado > .bloque__texto {
  text-align: center;
  margin-inline: auto;
}
/* `balance` reparte la linea de apoyo en renglones parejos. Sin el, la linea
   de la campana de seguridad rompia dejando "dice dos." solo en el segundo
   renglon: dos palabras sueltas debajo de una linea llena se leen como un
   error de maqueta. Es la misma regla que ya usaba el titular del hero, y aqui
   hacia mas falta todavia, porque estas lineas van centradas y un huerfano
   centrado se nota el doble. */
.bloque__texto {
  margin: 0;
  max-width: 62ch;
  color: var(--tinta-media);
  text-wrap: balance;
}
.bloque__texto + .bloque__texto { margin-top: var(--e2); }
.bloque--centrado > .bloque__texto { margin-bottom: var(--e4); }
.bloque--centrado > .bloque__texto,
.portada__bajada {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* EL RECORTE ES UN PROBLEMA DE ANCHO, ASI QUE SE AFLOJA CUANDO EL ANCHO BAJA.

   Dos lineas es lo correcto a 1240 px, donde la columna mide 530 px y una
   tercera linea de tres palabras se lee como huerfano centrado. A 375 px la
   MISMA frase mide cuatro lineas, y ahi el recorte deja de evitar un huerfano
   y empieza a esconder la mitad de la frase.

   Las lineas de seccion de esta pagina estan escritas para caber en dos a
   1240 px (unos 112 caracteres). Cinco es el techo que necesitan en un
   telefono, con una de margen. */
@media (max-width: 899px) {
  .bloque--centrado > .bloque__texto,
  .portada__bajada { -webkit-line-clamp: 5; }
}

/* Dos columnas para el encargo. Es el unico texto largo de la pagina y en una
   sola columna a 1240 px quedaria una linea de 140 caracteres. */
.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);
}

.bloque--encargo { position: relative; isolation: isolate; overflow: hidden; }
.bloque--encargo::before,
.bloque--encargo::after {
  position: absolute;
  z-index: -1;
  color: var(--magenta);
  opacity: .08;
  font-family: var(--display);
  font-size: clamp(5rem, 12vw, 10rem);
  line-height: 1;
  pointer-events: none;
}
.bloque--encargo::before { content: "✦"; right: 4%; top: 2%; transform: rotate(12deg); }
.bloque--encargo::after { content: "＋"; left: 2%; bottom: -8%; transform: rotate(-16deg); }

/* --- Rejilla de galeria -------------------------------------------------
   Ni pies, ni nombres, ni fechas: solo la imagen y el zoom. Lo que hace una
   galeria es dejar mirar; cada rotulo que se agrega es una interrupcion. La
   descripcion sigue existiendo en el `alt` de cada imagen, o sea que un lector
   de pantalla la lee aunque no se vea. */

.galeria {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 20px);
  margin: 0; padding: 0; list-style: none;
  /* `dense` es obligatorio con spans mezclados: sin el, la rejilla deja el
     hueco donde la pieza grande no cabia y se lee como celda vacia. */
  grid-auto-flow: dense;
}

/* Una de cada seis ocupa 2x2. No es decoracion: veinte cuadrados iguales en
   fila se leen como hoja de contactos, y lo que se quiere es una galeria. La
   estructura sale de "Bento Box Grid"; su decoracion (radio 24 px, sombras,
   fondo gris) NO se toma, porque esta pagina va a radio 0 y sin sombras. */
@media (min-width: 900px) {
  .galeria:not(.galeria--serie) > li:nth-child(6n + 1) {
    grid-column: span 2;
    grid-row: span 2;
  }
  /* La última pieza cierra el feed: debe completar la fila, no convertirse
     en otro bloque 2x2 y dejar el final visualmente desbalanceado. */
  .galeria:not(.galeria--serie) > li:last-child {
    grid-column: span 1;
    grid-row: span 1;
  }
}

/* La campana del Dia sin IVA usa la misma rejilla SIN la celda doble. En una
   secuencia que se publico en orden, sacar una pieza al doble de tamano dice
   que esa importa mas que las otras, y no es cierto: las cuatro son el mismo
   anuncio partido en cuatro momentos del dia. */
.galeria--serie { grid-auto-flow: row; }

/* --- Aplicaciones: el sistema y las dos marcas --------------------------
   Tres columnas y celda cuadrada. La usan dos secciones y las dos por la misma
   razon: sus piezas son 1:1 y ninguna pierde un pixel.

   En «Dos marcas» las tres columnas hacen ademas el trabajo de la seccion. Las
   tres piezas de Gasolina Extra van en la primera fila y las tres de Republica
   justo debajo, en el mismo orden: la comparacion queda en vertical y no hay
   que rotular cual es cual, porque la pieza lo dice. */

.aplicaciones {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.8vw, 24px);
  margin: 0; padding: 0; list-style: none;
}

/* TODAS LAS CELDAS, EL MISMO ALTO.

   Cuadrado y `contain`, no `cover`. `cover` igualaria el alto recortando, y
   una pieza recortada deja de ser la pieza que se publico. Con `contain` entra
   entera y lo que sobra es papel, que es exactamente lo que hay detras. */
.aplicaciones > li { aspect-ratio: 1; }
.aplicaciones > li > .pieza { height: 100%; }
.aplicaciones > li > .pieza > img { height: 100%; object-fit: contain; }

/* --- Historias 9:16 -----------------------------------------------------
   Cuatro verticales en una fila. Es la unica rejilla de la pagina cuya celda
   NO es cuadrada, y no podia serlo: una story dentro de un cuadrado con
   `contain` se queda en la mitad del ancho de la celda y la fila se lee como
   cuatro sellos con mucho papel al lado.

   La celda es 9/16 exacta, que es la proporcion de las cuatro piezas, asi que
   `contain` no deja ni un pixel de papel. Se queda escrito de todos modos por
   si algun dia entra una que no lo sea. */

.historias {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 20px);
  margin: 0; padding: 0; list-style: none;
}
.historias > li { aspect-ratio: 9 / 16; }
.historias > li > .pieza { height: 100%; }
.historias > li > .pieza > img { height: 100%; object-fit: contain; }

/* --- Video --------------------------------------------------------------
   AGRUPADA POR FORMA, y por eso es flex y no grid.

   En esta pagina hay DOS secciones de video y dos formas: ocho reels 9:16 en
   la seccion de reels, y un solo institucional 16:9 en la de San Joaquin. En
   una misma rejilla quedarian escalonados; separados por forma, cada fila
   cuadra sola.

   La salida NO es recortarlos a todos al mismo cuadrado: un vertical es
   vertical, y meterlo en una caja cuadrada con bandas negras a los lados es
   enseñar el reproductor en vez de la pieza. La salida es AGRUPAR: como
   `seleccion.py` ya lista los videos por forma (cuadrados, verticales,
   horizontal), basta con darle a cada forma su ancho y dejar que el flex
   reparta. Salen tres filas y cada una cuadra sola:

     [vertical] [vertical] [vertical] [vertical]
     [vertical] [vertical] [vertical] [vertical]
     [        el 16:9, a ancho completo        ]

   Ocho verticales llenan dos filas de cuatro exactas. `justify-content:
   center` solo se nota si algun dia entra un numero que no sea multiplo de
   cuatro, y entonces la fila corta queda centrada en vez de coja. */

.videos {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(12px, 1.8vw, 24px);
  margin: 0; padding: 0; list-style: none;
  --calle: clamp(12px, 1.8vw, 24px);
}
.videos > li { margin: 0; min-width: 0; }

/* Un cuarto del ancho, descontando las tres calles que hay entre cuatro
   piezas. Los cuatro cuadrados llenan la primera fila exacta. */
.video--cuadrado { flex: 0 0 calc((100% - var(--calle) * 3) / 4); }

/* Los verticales van al MISMO ancho que un cuadrado, no mas anchos: asi la
   fila de abajo se lee como continuacion de la de arriba y no como otra cosa.
   Al ser solo dos, `justify-content: center` los deja centrados. */
.video--vertical { flex: 0 0 calc((100% - var(--calle) * 3) / 4); }

/* Ancho completo para un horizontal. Aqui SI se usa: el manifiesto de Ventanas
   de San Joaquin es 1920x1080 y es la unica pieza de la pagina que pide
   pantalla. Al ser flex y no grid, no hay columna que recalcular. */
.video--ancho { flex: 0 0 100%; }
/* `display: block` no es cosmetico: <video> es inline por defecto y deja 8 px
   de hueco bajo la caja, el mismo del descendente de una linea de texto. Con
   cuatro videos alineados arriba, esos 8 px desalinean la fila entera. */
.video video {
  display: block;
  width: 100%; height: auto;
  background: #000;
}
.video--ancho { grid-column: span 2; }

/* A dos por fila: los ocho reels quedan en cuatro filas de dos y el 16:9 sigue
   solo. Todas las filas siguen cuadrando. */
@media (max-width: 899px) {
  .video--cuadrado,
  .video--vertical { flex-basis: calc((100% - var(--calle)) / 2); }
  .videos--sanjoaquin .video--vertical { flex-basis: clamp(150px, 27%, 220px); }
}

/* En movil las rejillas de cuatro caen a dos columnas. Las de tres caen a una
   sola: una pieza del sistema de 2022 a 46vw en un telefono se queda en 170 px
   y su parrafo deja de leerse. */
@media (max-width: 899px) {
  .galeria, .historias { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 699px) {
  .portada__titulo > span { white-space: normal; }
  .aplicaciones { grid-template-columns: 1fr; }
  .video--cuadrado,
  .video--vertical { flex-basis: 100%; }
  .videos--sanjoaquin .video--vertical { flex-basis: min(42vw, 190px); }
  .creditos-cierre { grid-template-columns: 1fr; gap: var(--e4); }
  .creditos-cierre__boton { grid-column: auto; }

  /* A una columna se suelta el cuadrado. Ya no hay fila que cuadrar, y forzar
     `contain` dentro de un cuadrado a ancho completo dejaria bandas de papel de
     100 px arriba y abajo de cada pieza. Cada una vuelve a su proporcion. */
  .aplicaciones > li { aspect-ratio: auto; }
  .aplicaciones > li > .pieza > img { height: auto; object-fit: fill; }
}

/* La pieza es un boton que envuelve la imagen. Sin JS sigue siendo un boton
   inerte y la imagen se ve igual: la galeria nunca depende del script para
   mostrar el trabajo. */
.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); }

/* --- Pares 1:1 y 9:16 ---------------------------------------------------
   Las dos columnas estan en proporcion 1 : 0.5625, o sea la del 9:16 frente al
   cuadrado: asi las dos imagenes terminan exactamente a la misma altura y la
   comparacion es honesta. Si se dejaran a anchos iguales, la story se veria
   mas alta y pareceria otra cosa. En movil se apilan y la story se topa,
   porque a ancho completo mide casi dos pantallas ella sola. */

.pares {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--e5);
  margin: 0; padding: 0; list-style: none;
}
@media (min-width: 900px) {
  .pares {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--e4) var(--e5);
  }
}
.par { margin: 0; }
.par__visor { display: grid; gap: clamp(10px, 1.4vw, 20px); align-items: end; }
@media (min-width: 700px) { .par__visor { grid-template-columns: 1fr .5625fr; } }
@media (max-width: 699px) { .par__lado--story { max-width: 58%; } }

/* La variante de «Dos marcas»: los dos lados son 1:1, asi que van a anchos
   IGUALES. La proporcion 1 : 0.5625 de arriba existe para que un cuadrado y un
   9:16 terminen a la misma altura; con dos cuadrados sobra, y aplicada aqui
   haria que la pieza de Republica se viera mas chica que la de Gasolina Extra,
   que es justo lo contrario de lo que la seccion afirma. */
@media (min-width: 700px) {
  .par__visor--marcas { grid-template-columns: 1fr 1fr; }
}

.par__lado { min-width: 0; }

/* --- Nota de creditos ----------------------------------------------------
   SIN RECORTE, y es la unica diferencia deliberada con las cinco hojas
   anteriores.

   Las otras cinco le ponen `-webkit-line-clamp: 4` a este parrafo. En Barkys y
   Bonfibra el texto cabia en cuatro lineas y no se notaba; en Coltanques,
   Famoc y aqui no cabe, asi que la mitad del bloque de derechos se estaba
   ocultando en silencio, con puntos suspensivos y sin forma de desplegarlo.

   Un recorte tipografico sirve para que una linea de seccion no deje un
   huerfano. No sirve para un bloque legal: ahi lo que no se lee es lo que no
   se dijo. Se quita el clamp y el parrafo ocupa lo que ocupe. */

.creditos-cierre {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--e5);
  align-items: start;
  margin-block: clamp(2.75rem, 5.5vw, 4.25rem);
}
.creditos {
  max-width: 68ch;
  margin: 0;
  padding-left: var(--e3);
  border-left: 2px solid var(--magenta);
  font-size: .95rem;
  color: var(--tinta-media);
}
.creditos strong { font-weight: 600; color: var(--tinta); }

.creditos-cierre__columna .bloque__titulo { margin-bottom: var(--e3); }
.cierre__contenido h2 {
  margin: 0 0 var(--e2);
  color: var(--magenta);
  font-family: var(--display);
  font-weight: 700;
  /* MAS CHICO QUE EN LAS OTRAS PAGINAS, y es por el largo de esta pregunta.
     A 2,75rem el titular del cierre salia en CUATRO lineas y se comia la mitad
     de la columna; el bloque de creditos de al lado mide seis. A 2,15rem cae en
     tres, que es lo que equilibra las dos columnas.

     El tamano se baja aqui y no se acorta la pregunta: la pregunta es el
     llamado a la accion y ya dice lo justo. */
  font-size: clamp(1.6rem, .95rem + 1.55vw, 2.15rem);
  line-height: 1.12;
  letter-spacing: -.02em;
  text-transform: uppercase;
  text-wrap: balance;
}
.cierre__contenido p {
  max-width: 60ch;
  margin: 0;
  color: var(--tinta-media);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* En el telefono el cierre pasa a una sola columna estrecha y esta frase mide
   nueve lineas. Recortarla a dos deja el llamado a la accion sin la mitad de
   lo que dice, asi que abajo de 900 px no se recorta: se deja fluir. La regla
   va AQUI y no arriba con las otras por orden de cascada, que a igual
   especificidad gana la ultima. */
@media (max-width: 899px) {
  .cierre__contenido p { display: block; overflow: visible; }
}

.creditos-cierre__boton {
  grid-column: 1 / -1;
  justify-self: center;
}

/* --- Cierre -------------------------------------------------------------- */

/* EL AIRE ALREDEDOR DEL FILETE, medido y recortado el 20 ago 2026.

   Antes sobraban unos 120 px repartidos a los dos lados de la linea y el cierre
   se leia despegado del caso, como si fuera otra pagina. Y venian de dos sitios
   distintos, que es por lo que no bastaba con tocar aqui:

     - ARRIBA lo ponia el `margin-bottom` del bloque de creditos (4,25rem), que
       COLAPSA con el `margin-top` de esta seccion y gana el mas grande de los
       dos. Bajar solo el de aqui no hacia nada: seguia mandando el del bloque.
       Por eso el margen de arriba se pone en 0 y el espacio lo decide el bloque
       anterior, con la regla `:has(+ .cierre)` de mas abajo.
     - ABAJO lo pone el `padding-top` de esta seccion, que si se controla aqui.

   Queda en unos 40 px arriba y 32 abajo: suficiente para que el filete separe,
   y poco para que el cierre siga perteneciendo al caso. */
.cierre {
  margin-block: 0;
  /* El de abajo baja de 3rem a 1,75rem: entre el boton y el filete del pie
     habia 104 px (48 de aqui y 56 del pie) y sobraban. Ver `.pie`. */
  padding-block: clamp(1.5rem, 2.5vw, 2rem) clamp(1.25rem, 2vw, 1.75rem);
  border-top: 1px solid var(--linea);
}

/* El bloque que va justo antes del cierre acorta su margen inferior. `:has` es
   lo que permite que la regla viva aca y no haya que marcar ese bloque a mano
   en `generar_pagina.py`: si algun dia se agrega una seccion antes del cierre,
   el espacio se ajusta solo. */
.bloque:has(+ .cierre) { margin-bottom: clamp(1.75rem, 3vw, 2.5rem); }
/* Mismo tratamiento que los titulos de seccion: es uno mas, aunque viva en
   otro bloque. */
.cierre h2 {
  margin: 0 0 var(--e2);
  color: var(--magenta);
  max-width: none;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.7rem, 1.15rem + 2vw, 2.75rem);
  line-height: 1.12;
  letter-spacing: -.02em;
  text-wrap: balance;
}
.cierre p { margin: 0 0 var(--e3); max-width: 60ch; color: var(--tinta-media); }

/* OJO CON EL SELECTOR, que aqui hubo un fallo de verdad.

   Este boton salia con el texto NEGRO sobre el color, no blanco. La causa es de
   especificidad y no se ve leyendo esta regla sola:

     .caso-gasolina a { color: inherit; }   ->  (0,1,1)  GANA
     .boton             { color: #fff; }      ->  (0,1,0)

   El boton es un <a>, asi que la regla generica de enlaces le ganaba y le metia
   `inherit`, o sea la tinta del cierre. Y no era solo feo: #131313 sobre
   #d60724 da 3,5:1, que NO pasa AA. En blanco da 5,36:1.

   Por eso el selector lleva la clase del caso delante. No se arregla con
   `!important`: lo que hay que hacer es empatarle la especificidad al selector
   que estaba ganando.

   ESTE MISMO FALLO ESTABA EN LAS TRES HOJAS ANTERIORES, que tienen la
   pareja de reglas identica. Corregidos los dos en ago 2026, igual que aqui.
   Los tres daban practicamente el mismo contraste, 3,5:1, y no por casualidad:
   los tres acentos estan calibrados a 5,26-5,36:1 contra blanco, asi que contra
   la tinta #131313 (18,58:1 con blanco) los tres caen en 18,58/5,3 = 3,5. */
.caso-gasolina .boton {
  display: inline-block;
  padding: .95rem 2rem;
  background: var(--magenta); color: #fff;
  font-family: var(--texto); font-weight: 600; font-size: 1rem;
  text-decoration: none;
  transition: background .2s var(--salida), transform .2s var(--salida);
  border-radius: 999px;
}
.caso-gasolina .boton:hover { background: var(--magenta-hondo); }
.caso-gasolina .boton:active { transform: translateY(1px); }

/* --- El pie -------------------------------------------------------------
   El mismo patron de Barkys y Bonfibra, que es el pie de las paginas de caso y
   de las landings: filete con la marca partiendo la linea, menu del sitio
   separado por filetes verticales, linea legal y nota de que trabajo fue y de
   que ano.

   Viaja DENTRO del fragmento, no en `shared-components`: al integrar, esta
   pagina conserva el suyo. */

/* El `padding-top` baja de 3,5rem a 2rem. Sumado al `padding-bottom` del
   cierre, el hueco entre el boton y el filete de la marca pasa de 104 px a 60.
   El de abajo NO se toca: es el aire al final del documento y sostiene la linea
   legal. */
.pie {
  padding: clamp(1.5rem, 2.5vw, 2rem) var(--margen) clamp(2rem, 4vw, 3rem);
  background: var(--papel);
  text-align: center;
}
.pie > .contenedor { padding-inline: 0; }

/* Filete con la marca partiendo la linea. */
.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; }

/* Menu del pie: enlaces separados por un filete vertical. */
.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;              /* objetivo tactil */
  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(--magenta); }

/* En movil el menu cae en dos filas, y el filete de `a + a` deja una rayita
   suelta al empezar la segunda: el selector no sabe donde envuelve el flex.
   Abajo de 700 px se quitan los filetes y separa el espacio. */
@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) ---------------------------------------------------
   <dialog> nativo: trae el foco atrapado, el Esc y el fondo inerte sin una
   linea de JS. Lo unico que agrega el script es la navegacion con flechas.
   El fondo va oscuro aunque la pagina sea blanca: es la unica manera de que
   una pieza con fondo blanco se lea como pieza y no como parte de la pagina. */

.visor {
  width: 100vw; max-width: 100vw;
  height: 100dvh; max-height: 100dvh;
  padding: 0; border: 0;
  background: transparent;
  overflow: hidden;
  /* Sin esto, al llegar al final del visor el gesto SIGUE y arrastra la pagina
     que hay detras: se cierra el visor y apareces treinta piezas mas abajo.
     Es el fallo clasico de cualquier capa a pantalla completa en telefono. */
  overscroll-behavior: contain;
}

.visor::backdrop { background: rgb(17 17 17 / .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));
  overflow: hidden;
}
.visor__marco img {
  display: block;
  max-width: calc(100vw - 6rem);
  max-height: calc(100dvh - 10rem);
  width: auto; height: auto;
  object-fit: contain;
  background: #fff;
}

/* MIENTRAS LA IMAGEN GRANDE VIAJA. El visor pide la variante de 1600 al
   abrirse, y hasta que llega el marco se queda vacio: medido en Chrome, unos
   dos segundos la primera vez. Sin esto no hay forma de saber si la pagina
   esta trabajando o se rompio.

   No es un girador. Es un cuadrado gris del tamano que va a ocupar la imagen,
   que es lo mismo que hace la celda de la rejilla mientras carga: el estado de
   carga se parece al resultado y no a un simbolo aparte.

   Se dimensiona por ALTURA y no por anchura: la imagen todavia no mide nada,
   y con `aspect-ratio` mas una altura el ancho sale solo, tanto para una pieza
   cuadrada como para una story 9:16. */
.visor--cargando .visor__marco::before {
  content: "";
  grid-area: 1 / 1;
  /* La proporcion la pone el JS desde la miniatura que ya esta en pantalla
     (`--proporcion`); el 1 es el respaldo por si el script no llego. Con el
     hueco a la proporcion correcta, la imagen aparece donde estaba el gris y
     no salta. */
  aspect-ratio: var(--proporcion, 1);
  /* Los MISMOS topes que `.visor__marco img`, no otros parecidos: asi el hueco
     y la imagen que lo sustituye miden exactamente igual y no hay salto. Con
     un `72vmin` puesto a ojo el desfase era de 24 px en las piezas 9:16. */
  height: calc(100dvh - 10rem);
  max-width: calc(100vw - 6rem);
  background: #2a2a2a;
}
.visor--cargando .visor__marco img { visibility: hidden; }
/* El visor ocupa la ventana ENTERA, asi que en un telefono con muesca sus dos
   extremos caen debajo del hardware: la barra de navegacion bajo el indicador
   de inicio y el boton de cerrar bajo la camara. `env()` vale 0 en cualquier
   pantalla sin muesca, asi que en escritorio no cambia nada.

   Van DENTRO de las reglas que ya existian y no en una regla aparte mas
   arriba: a igual especificidad gana la ultima, y una regla suelta antes de
   estas se la comerian ellas sin avisar. */
.visor__barra {
  display: flex; align-items: center; gap: var(--e3);
  padding: var(--e2) clamp(var(--e2), 4vw, var(--e4))
           calc(var(--e4) + env(safe-area-inset-bottom));
  color: #f2f2f2;
}
.visor__cuenta {
  flex: 1;
  font-family: var(--dato); font-size: .78rem;
  color: #9a9a9a; 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: calc(var(--e2) + env(safe-area-inset-top));
  right: clamp(var(--e2), 4vw, var(--e4));
}

/* ==========================================================================
   MOVIMIENTO
   --------------------------------------------------------------------------
   Hay tres capas y cada una responde a una pregunta distinta. Ninguna esta por
   verse bonita: si no se puede decir en una frase que comunica, sobra.

     1. Entrada al hacer scroll     ¿esto es nuevo? ¿de donde venia?
     2. Profundidad en la galeria   las piezas llegan desde atras y se posan
     3. Secuencia de apertura       jerarquia: marca, titular, contexto

   El motor preferido es `animation-timeline: view()`, o sea animacion ligada
   al scroll HECHA POR EL NAVEGADOR. No hay un solo listener de scroll ni un
   requestAnimationFrame: el compositor la corre fuera del hilo principal y no
   se cae aunque la pagina tenga sesenta imagenes. Donde no exista, el
   observador de `famoc.js` hace la version simple y nadie se entera.
   ========================================================================== */

/* --- Capa 1: entrada, version de respaldo -------------------------------
   Se oculta SOLO si el JS ya corrio (clase .js en el <html>): sin script no se
   esconde nada, porque una animacion no puede ser el motivo por el que alguien
   vea una pagina en blanco. */

.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);
}

/* --- Capa 2: profundidad ligada al scroll -------------------------------
   Las piezas no aparecen: se acercan. Entran inclinadas y desde el fondo, y
   terminan de posarse cuando estan bien dentro de la pantalla, asi que el
   efecto se siente al scrollear y no como un parpadeo al cruzar un umbral.

   Se queda corto a proposito. La pieza se endereza y para: con sesenta
   imagenes, mantenerlas girando convierte la galeria en un carrusel y deja de
   poderse mirar el trabajo, que es para lo que existe la pagina. */

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

    /* Las cuatro rejillas de la pagina llevan la misma perspectiva. Se quedo
       fuera en la primera version y se detecto leyendo `getComputedStyle`: sin
       perspective en el padre, el translate3d en Z y el rotateX de `llega` se
       aplanan, o sea que las piezas de correo subian sin profundidad mientras
       las otras cinco rejillas si la tenian. Se veia como si esa seccion
       estuviera a medio hacer. */
    .galeria, .aplicaciones, .historias, .videos, .pares { perspective: 1500px; }
    .galeria > li, .aplicaciones > li, .historias > li, .videos > li,
    .pares > .par { transform-style: preserve-3d; }

    /* La animacion manda sobre el estado de la capa 1: una animacion en curso
       gana sobre el valor declarado y sobre la transicion.
       Solo las IMAGENES llegan con profundidad. El texto sube y ya: una linea
       de texto girando en perspectiva se lee peor y no comunica nada. */
    .js:not(.sin-timeline) .galeria > li,
    .js:not(.sin-timeline) .aplicaciones > li,
    .js:not(.sin-timeline) .historias > li,
    .js:not(.sin-timeline) .videos > li,
    .js:not(.sin-timeline) .par {
      opacity: 1;
      transform: none;
      animation: llega linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 24%;
    }

    .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%;
    }

    /* Las dos mitades del par llegan desde profundidades distintas y se posan
       en el mismo plano. Es literal: son dos versiones de la misma pieza. */
    .js:not(.sin-timeline) .par__lado--story {
      animation: llega-story linear both;
      animation-timeline: view(); animation-range: entry 0% cover 28%;
    }

    /* Las tiras hacen zoom de salida dentro de su marco durante todo el
       recorrido. Da profundidad sin mover nada de sitio, que es lo unico que
       se puede permitir un elemento que ocupa el ancho completo. La figura
       solo aparece: dos movimientos encima de la misma imagen se pelean. */
    .js:not(.sin-timeline) .tira-sangre {
      opacity: 1; transform: none;
      animation: asoma linear both;
      animation-timeline: view(); animation-range: entry 4% cover 22%;
    }

    .js:not(.sin-timeline) .tira-sangre img {
      animation: acerca linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
      will-change: transform;
    }
  }
}

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

/* --- Capa 3: secuencia de apertura --------------------------------------
   Las tiras del fondo del hero. Cada una recorre la mitad de su propia altura
   y vuelve a empezar; como el contenido esta duplicado, el salto es invisible.
   Velocidades distintas por tira: es lo que vende la profundidad. Las pares
   van al reves para que el conjunto no parezca una sola cortina bajando.

   Se anima `transform`, nunca `top`: solo asi el trabajo se queda en el
   compositor y no obliga a recalcular la maqueta en cada fotograma. */

.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); }
}

/* El texto del hero entra escalonado al cargar: la marca, el rotulo, el
   titular y la bajada. Es la jerarquia de quien mira: de quien es la pagina,
   que trabajo es y de quien es el cliente. */
.js .marca           { animation: sube .7s var(--salida) both; animation-delay: .05s; }
.js .portada__rotulo { animation: sube .8s var(--salida) both; animation-delay: .10s; }
.js .portada__titulo { animation: sube .9s var(--salida) both; animation-delay: .22s; }
.js .portada__bajada { animation: sube .9s var(--salida) both; animation-delay: .38s; }

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

/* --- El interruptor -----------------------------------------------------
   Una sola regla apaga las tres capas. Va al final para que gane siempre. */

@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, .js .aplicaciones > li, .js .historias > li,
  .js .videos > li, .js .par,
  .js .par__lado--story, .js .tira-sangre, .js .tira-sangre img {
    animation: none;
    opacity: 1;
    transform: none;
    clip-path: none;
  }
  .pieza img, .boton { transition: none; }
  .pieza:hover img, .pieza:focus-visible img { transform: none; }
}

/* --- Impresion ----------------------------------------------------------
   Un caso de portafolio se imprime a PDF mas seguido de lo que parece. */
@media print {
  .visor, .cierre .boton { display: none; }
  .js [data-entra] { opacity: 1; transform: none; }
  .galeria { grid-template-columns: repeat(3, 1fr); }
  /* `.videos` es flex, no grid: `grid-template-columns` aqui no hacia nada. Al
     imprimir, los cuadrados van de a dos y el 16:9 se queda con su fila. */
  .video--cuadrado, .video--vertical { flex-basis: calc((100% - var(--calle)) / 2); }
}
