/* ==========================================================================
   eterna.css — pagina de caso, en clave de galeria
   --------------------------------------------------------------------------
   Misma arquitectura que las otras tres paginas de caso: hoja unica, sin
   dependencias, con TODO colgado de una clase para que sirva suelta y dentro
   del sitio.

   TRES DECISIONES QUE EXPLICAN TODO LO DEMAS

   1. Un solo tema, blanco. Una galeria se pinta de blanco por la misma razon
      que un museo, y aqui hacia mas falta que nunca: las piezas de Eterna son
      campos de color pleno —naranja, morado, azul, verde, amarillo— y sobre
      fondo oscuro se pelean entre ellas.

   2. La pagina calla para que el trabajo hable. Sin cajas, sin sombras, sin
      degradados, sin rotulos sobre las imagenes.

      Y AQUI ESO CUESTA MAS QUE EN LOS OTROS CASOS. Eterna no tuvo un sistema,
      tuvo una campana con nombre propio detras de otra, cada una con su paleta
      y hasta con su tipografia. La tentacion de darle a cada seccion el color
      de su campana era enorme. No se hizo, por lo mismo de siempre: si la
      pagina tambien cambia de color, se pierde la unica regla que sostiene el
      sistema. Morado = lo que dice Eterna. Negro = lo que dice el estudio.

   3. Radio 0 en todo, sin excepcion. Incluido el boton.

   PALETA — muestreada de las piezas y de los fotogramas de video:
     #dc6400 el naranja    #7800b4 el morado

   EL ACENTO ES EL MORADO, Y NO EL NARANJA, aunque el naranja sea el color con
   el que se reconoce a Eterna. La razon es medible: el naranja de marca da
   entre 2,6 y 3,4:1 sobre blanco, y para llevarlo a un contraste accesible hay
   que bajarlo hasta #b35400, que ya se lee marron y deja de ser el naranja de
   las piezas. El morado llega sin dejar de ser el morado: el token es #6b2a8a,
   con 9,04:1. El naranja vive dentro de las imagenes, que es donde el cliente
   lo puso.

   TIPOGRAFIAS — aqui no hubo que deducirlas: los .ttf estan DENTRO del
   proyecto, y delatan el reparto por campana (Rubik en Doble Check, IBM Plex
   Mono en Transparencia, Montserrat en Comite de Convivencia, dos manuscritas
   en Cumpleanos).

     Rubik 700/900        la unica que aparece con la familia completa, nueve
                          pesos, o sea la del sistema y no la de una campana.
                          Geometrica de esquinas apenas redondeadas, que es
                          justo el registro de la cuenta: institucional sin ser
                          dura
     Source Sans 3 400    humanista y neutra para el cuerpo. Contrasta con la
                          geometria de Rubik en vez de duplicarla
     IBM Plex Mono        para el dato. Es la que el estudio conserva de pagina
                          a pagina. OJO: la marca TAMBIEN la usa, en la campana
                          de Transparencia. Es coincidencia, y no molesta
                          porque aqui solo aparece en el rotulo del hero
   ========================================================================== */

/* TODO cuelga de `.caso-eterna`, 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-eterna">. */
.caso-eterna {
  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;
  --morado:        #6b2a8a;  /* 9,04:1 sobre blanco. El unico acento */
  --morado-oscuro: #55206e;  /* 11,2:1. Solo el hover del boton */
  --hero:          #150f19;  /* fondo del hero, bajo las tiras */
  --realce:        #c9a3e6;  /* el morado ACLARADO, solo para el 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: "Rubik", ui-sans-serif, system-ui, sans-serif;
  --texto:   "Source Sans 3", "Source Sans Pro", 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-eterna, .caso-eterna *, .caso-eterna *::before,
.caso-eterna *::after {
  box-sizing: border-box;
}

.caso-eterna {
  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-eterna) { margin: 0; background: var(--papel); }

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

.caso-eterna a { color: inherit; }

.caso-eterna ::selection { background: var(--morado); color: #fff; }

.caso-eterna :focus-visible { outline: 2px solid var(--morado); 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(51 47 48), que con texto
   blanco encima da 12,4:1. Cualquier pixel mas oscuro da mas. No depende de
   que imagen toque en cada momento del bucle.

   El velo va a 0,90 y no a 0,88 como en Coltanques: las piezas de Eterna son
   campos de color pleno muy saturado, y a 0,88 el naranja y el amarillo se
   colaban tanto que el titular dejaba de leerse limpio. El morado del degradado
   sube solo desde el pie, para no tenir las piezas de arriba. */
.portada__velo {
  position: absolute; inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgb(90 20 130 / .55), transparent 64%),
    rgb(21 15 25 / .90);
}

.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: 900;
  font-size: clamp(2.1rem, 5.2vw, 3.9rem);
  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;
}
/* El realce del titular. Sobre el hero oscuro el morado del acento (#6b2a8a)
   no se ve: esta pensado para leerse sobre blanco. Aqui se usa el mismo morado
   aclarado hasta #c9a3e6, que sobre el velo da mas de 9:1 y se reconoce como el
   mismo color. Es el UNICO sitio de la pagina donde el acento cambia de valor,
   y cambia porque cambia el fondo, no por gusto. */
.portada__realce { color: var(--realce); }

.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. En Barkys y en Bonfibra
   este respiro lo daba una lamina de Behance; aqui las tiras se COMPONEN en
   `build_assets.py` con cuatro piezas cuadradas pegadas, sin calle entre
   ellas. Que se toquen es la clave: separadas volverian a leerse como una
   rejilla de galeria, que es justo de lo que la pagina viene a descansar.

   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 rojo de la marca. Es el sistema de acento de la
   pagina: rojo = lo que Coltanques pone, negro = lo que dice el estudio. Da
   5,36:1 sobre blanco, de sobra para AA incluso a este tamano.

   EN CAJA ALTA, que es la unica cosa de esta pagina que se aparta de Barkys y
   Bonfibra. La razon es el proyecto: casi todas las piezas de Coltanques
   rotulan en mayuscula, y el registro de la cuenta es normativo (seguridad,
   procedimiento, convocatoria). Un titulo en caja alta se lee como
   senalizacion, que es de lo que va el caso.

   DOS AJUSTES QUE LA CAJA ALTA OBLIGA, y sin los cuales se ve mal:
   - El `letter-spacing` pasa de NEGATIVO a positivo. El -.025em existe para
     cerrar el aire que dejan las minusculas con ascendentes y descendentes; en
     mayusculas, todas las letras miden lo mismo y ese mismo valor las apelmaza.
   - El tamano baja un punto arriba (de 2,75 a 2,5rem). Una linea en caja alta
     ocupa mas ancho a igual cuerpo, y a 2,75 los titulos largos como
     "FUERA DE PANTALLA" empezaban a partirse en dos. */
.bloque__titulo {
  margin: 0 0 var(--e3);
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.5rem);
  line-height: 1.14;
  text-transform: uppercase;
  letter-spacing: .025em;
  text-wrap: balance;
  color: var(--morado);
}

/* 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; }

/* --- El separador de los titulares centrados ---------------------------
   Dos filetes, uno a cada lado del titulo.

   NO son iconos, y la diferencia importa. Un icono figurativo mete un dibujo
   que no es de la marca presentada ni del estudio, y esta pagina se sostiene
   justo sobre no poner nada que no sea el trabajo. Un filete no dibuja: solo
   acompana, y deja el titulo como lo primero que se ve.

   Por eso van en el gris de linea y no en el acento, y por eso `flex: 0 1 auto`
   con un maximo: los filetes CEDEN antes que el texto. Si el titulo crece o la
   ventana se angosta, se encogen ellos y el titulo no se parte.

   Abajo de 700 px desaparecen: ahi el ancho ya no da para llevar nada a los
   lados. No hace falta `aria-hidden`, porque un lector de pantalla no lee el
   `content` de un pseudoelemento vacio. */
.bloque--centrado > .bloque__titulo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(.75rem, 2vw, 1.5rem);
}
.bloque--centrado > .bloque__titulo::before,
.bloque--centrado > .bloque__titulo::after {
  content: "";
  flex: 0 1 clamp(24px, 6vw, 88px);
  height: 1px;
  background: var(--linea);
}
@media (max-width: 699px) {
  .bloque--centrado > .bloque__titulo::before,
  .bloque--centrado > .bloque__titulo::after { display: none; }
}



/* 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); }

/* 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; }
}

/* EL AIRE DE ESTA LISTA ESTA CALCULADO, no elegido. La columna de la izquierda
   mide 333 px y la de la derecha tiene que terminar con ella; con cuatro items,
   la calle y el padding de antes la dejaban en 252, o sea 81 px corta.

   Se probaron las tres cantidades midiendo:

     6 items, calle .7rem   ->  351 px   (se pasa 18)
     5 items, calle 1rem    ->  312 px   (falta 21)
     4 items, calle 1rem    ->  252 px   (falta 81)

   Miguel pidió cuatro, así que los 81 px se recuperan con aire y no con
   relleno: la calle sube a 1.55rem y el respiro sobre el filete de cada item a
   1.95rem. Con eso las dos columnas terminan a menos de 5 px. Con menos items, una lista necesita mas aire para no leerse apretada
   al lado de un bloque de texto largo. */
.alcance {
  margin: 0; padding: 0; list-style: none;
  display: grid; gap: 1.55rem;
}
.alcance li {
  padding-top: 1.95rem;
  border-top: 1px solid var(--linea);
  color: var(--tinta-media);
}

/* --- 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. */

/* LAS CALLES DE TODA LA PAGINA se apretaron el 20 ago 2026, de 20 y 24 px a 14
   y 16. Con piezas que en su mayoria son 16:9, una calle ancha hacia que cada
   fila se leyera como tres objetos sueltos en vez de como una serie. Apretadas,
   la rejilla vuelve a leerse como un bloque. */
.galeria {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
  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: dieciseis 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 campana de seguridad usa la misma rejilla SIN la celda doble. En una
   serie numerada, sacar una pieza al doble de tamano dice que esa importa mas
   que las otras, y no es cierto: las ocho pesan lo mismo y por eso se
   publicaron juntas. */
.galeria--serie { grid-auto-flow: row; }

/* --- Capsulas de Buen Trato --------------------------------------------
   Ocho verticales de 930x1280 en dos filas de cuatro. Van completas y en
   orden: son una escala de color y quitar una rompe la escala. La rejilla es
   fija en cuatro columnas y no `auto-fit`, para que las dos filas queden
   iguales y se vea que es una serie cerrada. */

.capsulas {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
  margin: 0; padding: 0; list-style: none;
}

/* Las cuatro portadas de cumpleanos son cuadradas, y una de ellas es el VIDEO
   de agosto, que tambien lo es. Que la celda sea cuadrada es lo que permite
   meter el video en la cuadricula sin que se note que es de otra naturaleza:
   ocupa lo mismo que sus vecinas y se lee como una portada mas. */
.capsulas > li { aspect-ratio: 1; }
.capsulas > li > .pieza { height: 100%; }
.capsulas > li > .pieza > img { height: 100%; object-fit: contain; }

/* El <video> dentro de la cuadricula. No lleva las clases de forma de
   `.videos`, que reparten anchos dentro de un flex; aqui la celda ya la da el
   grid y lo unico que hace falta es que llene su caja. */
.celda-video { min-width: 0; }
.celda-video video {
  display: block;
  width: 100%; height: 100%;
  object-fit: contain;
  background: #000;
}

/* Las tarjetas de amor y amistad son 16:9. Misma idea que en `.aplicaciones`:
   cada forma con las de su forma, para no dejar papel de mas. */
.capsulas--ancha > li { aspect-ratio: 16 / 9; }

/* Aire entre las dos rejillas de la seccion, por lo mismo que en
   `.aplicaciones--separa`: sin esto se leen como una sola revuelta. */
.capsulas--separa { margin-bottom: clamp(9px, 1.2vw, 16px); }

/* --- Aplicaciones: Ponle Freno, convocatorias y POP ---------------------
   Tres columnas para piezas de formas MEZCLADAS: cuadrados, verticales,
   mockups horizontales. Por eso las celdas se alinean arriba (`start`) y no se
   estiran: estirar obligaria a recortar, y recortar el mockup de un pendon
   deja de ensenar el pendon. */

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

/* TODAS LAS CELDAS DE UNA SECCION, EL MISMO ALTO. Sin esto, cada pieza trae la
   suya y la fila queda escalonada, que se lee como un error de maqueta.

   `contain` y no `cover`: `cover` igualaria el alto recortando, y una pieza de
   pantalla recortada deja de ser la pieza que se entrego. Con `contain` entra
   entera y lo que sobra es papel.

   LA PROPORCION LA DECIDE CADA SECCION, no una constante. Es la diferencia con
   Coltanques, donde todo era 1:1 y bastaba con un cuadrado. Aqui conviven tres
   formas y forzarlas a una sola dejaba bandas de papel enormes:

     por defecto  16/9  las piezas de pantalla, que son la mayoria de la cuenta
     --cuadrada   1/1   sin uso hoy. Fue el compromiso que se probo para
                        Transparencia y se descarto: dejaba el 44 % de papel en
                        las seis piezas. Ahora esa seccion lleva dos rejillas,
                        una por forma. Se queda por si hace falta
     --alta       9/16  todo lo vertical: las stories de producto, las de
                        WhatsApp de Doble Check y las verticales de
                        Transparencia. La proporcion es la de las piezas, medida:
                        con 3/4, que fue el primer intento, perdian el 25 % */
.aplicaciones > li { aspect-ratio: 16 / 9; }
.aplicaciones--cuadrada > li { aspect-ratio: 1; }

/* Cuando una seccion lleva DOS rejillas seguidas, la de arriba deja aire. Sin
   esto, la ultima fila de la primera y la primera de la segunda quedan a la
   misma distancia que dos piezas entre si, y las dos rejillas se leen como una
   sola revuelta. */
.aplicaciones--separa { margin-bottom: clamp(9px, 1.2vw, 16px); }
.aplicaciones--alta > li { aspect-ratio: 9 / 16; }
.aplicaciones > li > .pieza { height: 100%; }
.aplicaciones > li > .pieza > img { height: 100%; object-fit: contain; }

/* La variante del POP. Tres celdas y no cuatro: las dos gorras son la misma
   pieza vista de dos maneras, asi que comparten celda, apiladas. Con cuatro
   celdas en tres columnas, la cuarta caia sola en una segunda fila y dejaba dos
   huecos del tamano de una pieza.

   La celda apilada respeta el mismo cuadrado que las otras dos, y dentro se
   parte en dos mitades iguales con la misma calle que separa las columnas. Asi
   las tres celdas terminan a la misma altura y no hay nada que cuadrar a ojo. */
.pop-par {
  display: grid;
  grid-template-rows: 1fr 1fr;
  gap: clamp(9px, 1.2vw, 16px);
  height: 100%;
}
.pop-par__lado { min-height: 0; }
.pop-par__lado .pieza { height: 100%; }
.pop-par__lado .pieza img { height: 100%; object-fit: contain; }

/* --- Correo interno -----------------------------------------------------
   REJILLA DE CELDAS IGUALES, con la pieza asomando desde arriba.

   La primera version usaba `columns: 4`, o sea multicolumna, para que cada
   correo cayera con su alto real sin recortarse. Tecnicamente correcto y
   visualmente malo: son ocho piezas de proporciones muy distintas repartidas en
   cuatro columnas de dos, asi que las columnas terminaban a alturas muy
   diferentes (la del correo de EPP, que mide 650x1442, casi doblaba a las
   demas) y al pie de la seccion quedaba un vacio de varios cientos de pixeles.
   El balanceo de `columns` no lo arregla: reparte por orden de documento, no
   por altura.

   Ahora la celda manda: 3/4 fija, y la imagen entra con `cover` anclada arriba.
   Ocho celdas iguales en dos filas de cuatro, cero hueco.

   POR QUE 3/4 Y NO OTRA. No es un numero de gusto: SIETE de los ocho correos
   estan entre 0,70 y 0,83 de proporcion, asi que a 0,75 se recortan unos pocos
   pixeles o ninguno. El unico que pierde de verdad es el de EPP, que es una
   infografia larga y ensena su mitad de arriba.

   Y SI, ESTO RECORTA, que es justo lo que la version anterior evitaba. El
   cambio es defendible por una razon concreta: el correo completo sigue estando
   a un clic, entero y sin recortar, en el visor. Lo que se recorta es la
   miniatura, no la pieza. Un correo de 2.900 px de alto metido en una columna
   de 269 px tampoco se leia: ya era un asomo, solo que uno que descuadraba la
   seccion. */

.correos {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
  margin: 0; padding: 0; list-style: none;
}
.correos > li { aspect-ratio: 3 / 4; }
.correos > li > .pieza { height: 100%; }
.correos > li > .pieza > img {
  height: 100%;
  object-fit: cover;
  /* Anclada ARRIBA, no al centro. Un correo se lee de arriba abajo y lo que
     identifica la pieza es su encabezado; centrado, la miniatura ensenaria un
     trozo del medio que no dice de que correo se trata. */
  object-position: top;
}

@media (max-width: 1099px) { .correos { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* --- Video --------------------------------------------------------------
   ESTA ES LA SECCION QUE MAS CAMBIA RESPECTO A LOS OTROS CASOS, porque aqui el
   video no es un anexo: es el trabajo. Nueve de los diez son 16:9.

   La clase de cada uno la escribe `generar_pagina.py` mirando las medidas
   REALES que dejo el build, y decide cuantos caben por fila:

     --solo      un 16:9 que es el unico de su seccion. Ancho completo, porque
                 un video solo a media pagina parece que le falta el de al lado
     --par       un 16:9 cuando hay varios. Dos por fila
     --cuadrado  un 1:1. Cuatro por fila

   Es flex y no grid por lo mismo que en Coltanques: agrupando por forma, cada
   fila cuadra sola y no hay que recortar un solo fotograma. */

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

/* `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
   varios videos alineados arriba, esos 8 px desalinean la fila entera. */
.video video {
  display: block;
  width: 100%; height: auto;
  background: #000;
}

/* El video que va SOLO en su seccion no ocupa el ancho entero. A 100 % pesaba
   mas que las nueve graficas juntas y rompia la escala de la seccion; a dos
   tercios se lee como una pieza mas del conjunto, que es lo que es. */
.video--solo     { flex: 0 0 min(100%, 66%); }
.video--par      { flex: 0 0 calc((100% - var(--calle)) / 2); }
.video--cuadrado { flex: 0 0 calc((100% - var(--calle) * 3) / 4); }

/* Cuando el video ABRE una seccion mixta y debajo vienen las graficas, se le
   deja aire por debajo. Sin esto, el video y la primera fila de piezas quedan
   a la misma distancia que dos piezas entre si, y la seccion se lee como una
   sola rejilla revuelta en vez de como «primero el video, despues las piezas». */
/* Aire entre el video y las graficas de su seccion. Es MAS grande que la calle
   de la rejilla a proposito: el video y las piezas son cosas distintas, y si se
   separan lo mismo que dos piezas entre si, la seccion se lee como una sola
   cuadricula revuelta. */
.videos--abre  { margin-bottom: clamp(1.75rem, 3.5vw, 3rem); }
.videos--cierra { margin-top: clamp(1.75rem, 3.5vw, 3rem); }

@media (max-width: 899px) {
  .video--par      { flex-basis: 100%; }
  .video--cuadrado { flex-basis: calc((100% - var(--calle)) / 2); }
  /* El 66 % de `--solo` existe para que un video no se coma una seccion ancha.
     En una columna de 335 px ese 66 % lo dejaba en 221, que es un sello de
     correos: a partir de aqui vuelve a ocupar el ancho que hay. */
  .video--solo     { flex-basis: 100%; }
}

/* En movil las rejillas de cuatro caen a dos columnas. Las de tres caen a una
   sola: un mockup de libreta a 46vw en un telefono se queda en 170 px y deja
   de verse lo que dice. */
@media (max-width: 899px) {
  .galeria, .capsulas, .correos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 699px) {
  .aplicaciones { grid-template-columns: 1fr; }
  .video--cuadrado { flex-basis: 100%; }

  /* 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,
  .aplicaciones--cuadrada > li,
  .aplicaciones--alta > li,
  .capsulas > li,
  .capsulas--ancha > li { aspect-ratio: auto; }
  .capsulas > li > .pieza > img { height: auto; object-fit: fill; }
  .celda-video video { height: auto; }
  .aplicaciones > li > .pieza > img { height: auto; object-fit: fill; }
  .pop-par { grid-template-rows: auto auto; height: auto; }
  .pop-par__lado .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(8px, 1vw, 14px); align-items: end; }
@media (min-width: 700px) { .par__visor { grid-template-columns: 1fr .5625fr; } }
@media (max-width: 699px) { .par__lado--story { max-width: 58%; } }

.par__lado { min-width: 0; }

/* --- Nota de creditos ---------------------------------------------------- */

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

/* --- El remate: creditos y cierre, juntos -------------------------------
   UNA sola seccion a dos columnas, y no dos apiladas. Antes el final de la
   pagina gastaba dos pantallas en decir dos cosas cortas: de quien es el
   trabajo, y que hacer si te interesa. Juntas se leen de un golpe, que es como
   funcionan.

   La columna de la derecha es la que invita, asi que lleva el boton; la de la
   izquierda es la nota legal y se queda quieta. En movil se apilan y el orden
   se conserva: primero los creditos, despues la invitacion. */

/* SIN FILETE. Lo llevaba, como las otras tres paginas de caso, y se quito el
   20 ago 2026: en esta el remate ya viene despues de una rejilla de piezas
   sobre blanco, y la linea sumaba un borde a una pagina que no tiene ninguno
   mas. El aire separa igual y no dibuja nada.

   Al quitarla, el aire de arriba SUBE. Un filete separa por si mismo aunque
   haya poco espacio; el espacio solo, no: necesita ser suficiente para que se
   lea como un cambio de bloque y no como un parrafo mas abajo. */
.remate {
  margin-block: 0;
  padding-block: clamp(2rem, 4vw, 3rem) clamp(1.25rem, 2vw, 1.75rem);
}

/* El bloque que va justo antes 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`. */
.bloque:has(+ .remate) { margin-bottom: clamp(1.75rem, 3vw, 2.5rem); }

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

/* Los creditos pierden el filete lateral: dentro de una reja de dos columnas,
   una linea vertical a la izquierda compite con la separacion entre columnas y
   la seccion se llena de rayas. El tamano de letra ya los distingue. */
.remate .creditos {
  max-width: none;
  padding-left: 0;
  border-left: 0;
}

.remate__cta h2 {
  margin: 0 0 var(--e2);
  color: var(--morado);
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.5rem);
  line-height: 1.14;
  text-transform: uppercase;
  letter-spacing: .025em;
  text-wrap: balance;
}
.remate__cta p { margin: 0 0 var(--e3); color: var(--tinta-media); }

.boton {
  display: inline-block;
  padding: .95rem 2rem;
  background: var(--morado); color: #fff;
  font-family: var(--texto); font-weight: 600; font-size: 1rem;
  text-decoration: none;
  transition: background .2s var(--salida), transform .2s var(--salida);
}
.caso-eterna .boton { color: #fff; }
.caso-eterna .boton:hover { background: var(--morado-oscuro); }
.caso-eterna .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(--morado); }

/* 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;
}
.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
   (600x1600 y parecidas) 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 un visor de 257px de alto, la imagen 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));
}
.visor__marco img {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
  background: #fff;
}
.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: #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: var(--e2); 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 `eterna.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) {

    /* `.correos` va en la lista aunque sea multicolumna y no rejilla. 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, .capsulas, .aplicaciones, .videos { perspective: 1500px; }
    .galeria > li, .capsulas > li, .aplicaciones > li,
    .videos > li { 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) .capsulas > li,
    .js:not(.sin-timeline) .aplicaciones > 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 /* El realce del titular. Sobre el hero oscuro el morado del acento (#6b2a8a)
   no se ve: esta pensado para leerse sobre blanco. Aqui se usa el mismo morado
   aclarado hasta #c9a3e6, que sobre el velo da mas de 9:1 y se reconoce como el
   mismo color. Es el UNICO sitio de la pagina donde el acento cambia de valor,
   y cambia porque cambia el fondo, no por gusto. */
.portada__realce { color: var(--realce); }

.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 .capsulas > li, .js .aplicaciones > li,
  .js .videos > li, .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, .remate .boton { display: none; }
  .js [data-entra] { opacity: 1; transform: none; }
  .galeria { grid-template-columns: repeat(3, 1fr); }
  /* `.videos` es flex, no grid. Al imprimir, todo va de a dos por fila. */
  .video--par, .video--cuadrado { flex-basis: calc((100% - var(--calle)) / 2); }
}
