/* AZ Accessories + Palmare - caso de portafolio, en clave de galeria.
   Hoja unica, sin dependencias. Se escribe a mano; el HTML se genera.

   Mismo patron de movimiento que Barkys, Bonfibra y La Mera Mera: entradas al
   scroll con `data-entra` + `.visible` (JS/IntersectionObserver) y, donde el
   navegador lo soporta, scroll-driven animations con perspectiva 3D sobre las
   rejillas. Sin `.js` en <html> (JS desactivado o sin cargar), nada lleva
   opacidad 0: la pagina se ve completa igual.

   LA REGLA QUE ORDENA TODAS LAS GALERIAS
   --------------------------------------
   Toda celda de galeria tiene PROPORCION FIJA (`--ratio`) y la pieza va con
   `object-fit: contain` sobre un fondo neutro. El material real va de 0,50
   (bolsa de regalo) a 2,03 (acrilico de vitrina): con alturas naturales, una
   rejilla de esas proporciones sale dentada y las piezas parecen pegarse unas
   a otras. Con celda fija la rejilla queda a plomo y NADA se recorta -que es
   la razon por la que no se usa `cover`: son piezas de diseno, un recorte se
   come la composicion.

   LO PROPIO DE ESTE CASO: dos marcas de un mismo dueno, con temperaturas
   opuestas. El acento de la pagina vira con el scroll con una variable
   registrada (@property --acento) y un IntersectionObserver independiente. */

/* ------------------------------------------------------------------ tipos */
/* Bodoni Moda y Bebas Neue son de AZ; Montserrat es de Palmare. Las tres tienen
   licencia OFL. PF Baseline Pro, la de titular de Palmare, es comercial y NO se
   embebe: aqui la reemplaza Montserrat, que es la otra del manual. */
@font-face { font-family: "Bodoni Moda"; src: url("tipos/bodonimoda-400.woff2") format("woff2");
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Bodoni Moda"; src: url("tipos/bodonimoda-400i.woff2") format("woff2");
             font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: "Bodoni Moda"; src: url("tipos/bodonimoda-600.woff2") format("woff2");
             font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Bebas Neue"; src: url("tipos/bebasneue-400.woff2") format("woff2");
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("tipos/montserrat-400.woff2") format("woff2");
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("tipos/montserrat-500.woff2") format("woff2");
             font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("tipos/montserrat-600.woff2") format("woff2");
             font-weight: 600; font-style: normal; font-display: swap; }

/* --acento se registra para que pueda TRANSICIONAR. Sin @property, un custom
   property es solo texto y el cambio seria un salto. */
@property --acento {
  syntax: "<color>"; inherits: true; initial-value: #2f3a41;
}
@property --acento-vivo {
  syntax: "<color>"; inherits: true; initial-value: #2f3a41;
}

/* ----------------------------------------------------------------- tokens */
.caso-azpalmare {
  color-scheme: light;

  /* Neutros. Un solo tema, blanco: sobre fondo oscuro estas piezas pierden su
     propio fondo blanco y se ven sucias. */
  --papel: #ffffff;
  --papel-2: #f6f5f2;
  --tinta: #14171a;
  --tinta-2: #5b6167;
  --tinta-suave: #7d8288;
  --linea: #e3e1dc;

  /* Fondo de celda: lo que se ve en las bandas cuando una pieza no llena su
     proporcion. Cada seccion lo ajusta para que las bandas desaparezcan. */
  --celda: #f4f3f0;

  /* Acento neutro de apertura y de cierre. */
  --acento: #2f3a41;
  --acento-vivo: #2f3a41;

  /* AZ: teal profundo del manual (#143240) y taupe. 13,5:1 sobre blanco. */
  --az: #143240;
  --az-2: #a99e92;
  --az-3: #74713a;

  /* Palmare: el azul de marca es #5986BF y sobre blanco da 3,3:1 -no pasa AA
     para texto-. El token de acento es el mismo azul bajado hasta 6,4:1; el
     vivo se reserva para superficies grandes (velo del hero). */
  --pal: #35618f;
  --pal-vivo: #5986bf;
  --pal-2: #a0a604;
  --pal-3: #d9d1b8;

  --e1: .5rem; --e2: 1rem; --e3: 1.75rem; --e4: 3rem; --e5: 5rem; --e6: 8rem;
  --ancho: 1320px;
  --margen: clamp(1.5rem, 5vw, 3.5rem);
  --hueco: clamp(10px, 1.3vw, 20px);
  --salida: cubic-bezier(.16, 1, .3, 1);

  color: var(--tinta);
  background: var(--papel);
  font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
  font-size: clamp(15px, .55vw + 13px, 17px);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  transition: --acento .7s ease, --acento-vivo .7s ease;
}

/* Las dos temperaturas. La clase la pone el JS sobre el contenedor del caso
   segun que seccion cruza la franja central de la ventana. */
.caso-azpalmare.tono-az   { --acento: var(--az);  --acento-vivo: var(--az); }
.caso-azpalmare.tono-pal  { --acento: var(--pal); --acento-vivo: var(--pal-vivo); }

.caso-azpalmare, .caso-azpalmare *, .caso-azpalmare *::before, .caso-azpalmare *::after {
  box-sizing: border-box;
}
body:has(> .caso-azpalmare) { margin: 0; background: var(--papel); }

.caso-azpalmare img, .caso-azpalmare video { display: block; max-width: 100%; height: auto; }
.caso-azpalmare a { color: inherit; }
.caso-azpalmare ul, .caso-azpalmare ol { list-style: none; margin: 0; padding: 0; }
.caso-azpalmare p { margin: 0 0 var(--e2); text-wrap: pretty; }
.caso-azpalmare h1, .caso-azpalmare h2, .caso-azpalmare h3 { margin: 0; font-weight: 400; }
.caso-azpalmare ::selection { background: var(--pal-vivo); color: #fff; }
.caso-azpalmare :focus-visible { outline: 2px solid var(--acento); outline-offset: 3px; }

/* Radio 0 en todo. La unica excepcion es el boton de cierre. */
.caso-azpalmare img, .caso-azpalmare video, .caso-azpalmare .pieza { border-radius: 0; }

.marco { 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-size: .82rem;
}

/* ------------------------------------------------------------------ hero */
/* Portada minimalista sobre papel blanco. El fondo NO es un collage de piezas
   -eso ya lo hacen las galerias, y a pantalla completa competia con ellas-,
   sino un campo de OLAS: el motivo del logotipo de Palmare, dibujado en los dos
   colores de marca. Es el mismo principio que rige el resto de la pagina -todo
   se muestrea del material, nada se inventa- aplicado al fondo.

   Cuatro capas de ola derivan a distintas velocidades y en sentidos opuestos.
   Solo se anima `transform`, asi que corre en el compositor y no repinta. */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: center;
  overflow: clip;
  background: var(--papel);
  isolation: isolate;
}

.hero__olas {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}
.hero__olas svg { width: 100%; height: 100%; display: block; }
.hero__ola { fill: none; stroke-width: 1.25; vector-effect: non-scaling-stroke; }
/* Cada onda mide 400 unidades y el trazo repite cuatro periodos, asi que
   correr exactamente -400 devuelve la figura a su sitio: bucle sin costura. */
.js .hero__grupo { animation: deriva var(--vel, 30s) linear infinite; }
.js .hero__grupo--atras { animation-direction: reverse; }
@keyframes deriva {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-400px, 0, 0); }
}

/* Difuminado en los bordes para que las olas nazcan y mueran sin corte. */
.hero__olas::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--papel), transparent 18%, transparent 82%, var(--papel));
}

.hero__contenido {
  position: relative; z-index: 2;
  max-width: var(--ancho); margin-inline: auto; width: 100%;
  padding: clamp(4rem, 10vh, 8rem) var(--margen);
}
.hero__ojo {
  font-family: "Bebas Neue", "Montserrat", sans-serif;
  font-size: clamp(.78rem, .3vw + .72rem, .9rem);
  letter-spacing: .34em; text-transform: uppercase;
  color: var(--tinta-suave);
  margin: 0 0 clamp(1.5rem, 4vh, 2.75rem);
}

/* Los dos nombres, apilados y alineados a la izquierda. Cada uno entra con un
   revelado por mascara: el <span> interior sube desde detras del borde del
   contenedor, que tiene overflow oculto. */
.hero__nombres {
  font-family: "Bodoni Moda", Georgia, serif;
  font-size: clamp(2.4rem, 8.5vw, 6.6rem);
  line-height: 1.02; letter-spacing: -.015em;
  margin: 0;
}
.hero__linea { display: block; overflow: hidden; padding-block: .04em; }
.hero__linea > span { display: block; }
.js .hero__linea > span { transform: translateY(105%); }
.js .hero__linea--1 > span { animation: revela 1.05s var(--salida) .12s both; }
.js .hero__linea--2 > span { animation: revela 1.05s var(--salida) .26s both; }
@keyframes revela {
  from { transform: translateY(105%); }
  to   { transform: translateY(0); }
}

/* La diagonal entre los dos nombres: se dibuja sola, de izquierda a derecha. */
.hero__regla {
  display: block; height: 1px; margin: clamp(.5rem, 1.2vh, 1rem) 0;
  background: var(--acento); opacity: .5;
  transform-origin: left center;
}
.js .hero__regla { animation: traza 1s var(--salida) .5s both; }
@keyframes traza {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.hero__bajada {
  max-width: 42ch; margin: clamp(1.5rem, 4vh, 2.5rem) 0 0;
  font-size: clamp(.98rem, .4vw + .88rem, 1.15rem);
  color: var(--tinta-2);
}
.hero__meta {
  margin-top: clamp(1.75rem, 5vh, 3rem);
  display: flex; flex-wrap: wrap;
  gap: var(--e1) var(--e3);
  font-family: "Bebas Neue", sans-serif;
  font-size: .88rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--tinta-suave);
}
.hero__meta li { display: flex; align-items: center; gap: var(--e3); }
.hero__meta li + li::before {
  content: ""; width: 4px; height: 4px; background: var(--acento); opacity: .7;
}

/* Señal de scroll: una línea que baja en bucle. */
.hero__baja {
  position: absolute; z-index: 2;
  left: var(--margen); bottom: clamp(1.25rem, 4vh, 2.5rem);
  display: flex; align-items: center; gap: var(--e2);
  font-family: "Bebas Neue", sans-serif; font-size: .74rem;
  letter-spacing: .26em; text-transform: uppercase; color: var(--tinta-suave);
}
.hero__baja::after {
  content: ""; width: 46px; height: 1px; background: currentColor;
  transform-origin: left center;
}
.js .hero__baja::after { animation: recorre 2.4s var(--salida) infinite; }
@keyframes recorre {
  0%   { transform: scaleX(0); transform-origin: left center; }
  45%  { transform: scaleX(1); transform-origin: left center; }
  55%  { transform: scaleX(1); transform-origin: right center; }
  100% { transform: scaleX(0); transform-origin: right center; }
}

.js .hero__ojo    { animation: sube .8s var(--salida) both; animation-delay: .05s; }
.js .hero__bajada { animation: sube .9s var(--salida) both; animation-delay: .62s; }
.js .hero__meta   { animation: sube .8s var(--salida) both; animation-delay: .76s; }
.js .hero__baja   { animation: sube .8s var(--salida) both; animation-delay: .95s; }
@keyframes sube {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------- estructura */
.bloque { padding-block: clamp(3.5rem, 7vw, 7rem); }
.bloque--ajustado { padding-block: clamp(2.5rem, 5vw, 5rem); }
.bloque--gris { background: var(--papel-2); --celda: #edece8; }

.bloque__cabeza { margin-bottom: var(--e4); max-width: 62ch; }
.bloque__titulo {
  display: flex; align-items: center; gap: .85rem;
  font-family: "Bebas Neue", "Montserrat", sans-serif;
  font-size: clamp(1.05rem, .6vw + .92rem, 1.4rem);
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--acento);
}
/* El <span> es obligatorio: sin el, el texto crudo dentro de un flex es un item
   anonimo con min-width:auto y un titulo largo se sale de la pagina en vez de
   partirse en dos lineas. */
.bloque__titulo > span { min-width: 0; }
.bloque__titulo::after {
  content: ""; flex: 0 0 2.6rem; height: .35rem;
  border-top: 1px solid currentColor; border-bottom: 1px solid currentColor;
  opacity: .55;
}
/* Nota de seccion: una linea, y solo donde de verdad aclara algo. */
.bloque__nota { margin: var(--e2) 0 0; color: var(--tinta-2); font-size: .95rem; max-width: 56ch; }

/* El encargo: el UNICO texto largo de la pagina, entre el hero y la primera
   galeria. De ahi en adelante, la pagina la llevan las piezas. */
.encargo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--e5); align-items: start; }
.encargo__lema {
  font-family: "Bodoni Moda", Georgia, serif;
  font-size: clamp(1.4rem, 2.3vw, 2.15rem);
  line-height: 1.26; text-wrap: balance;
  margin: 0;
}
.encargo__lema em { color: var(--acento); font-style: italic; }
.encargo__cuerpo p { color: var(--tinta-2); max-width: 56ch; }
.encargo__cuerpo p:last-child { margin-bottom: 0; }
.encargo__datos {
  margin-top: var(--e3); border-top: 1px solid var(--linea);
  font-family: "Bebas Neue", sans-serif; letter-spacing: .1em; text-transform: uppercase;
  font-size: .88rem;
}
.encargo__datos div {
  display: flex; justify-content: space-between; gap: var(--e2);
  padding: .65rem 0; border-bottom: 1px solid var(--linea);
}
.encargo__datos span:first-child { color: var(--tinta-suave); }
@media (max-width: 799px) { .encargo { grid-template-columns: 1fr; gap: var(--e4); } }

/* ------------------------------------------------------- antes y despues */
/* Solo visual: la comparacion habla sola, sin parrafo de por medio. */
.mudanza { display: grid; grid-template-columns: 1fr 1fr; gap: var(--e5); }
@media (max-width: 899px) { .mudanza { grid-template-columns: 1fr; gap: var(--e4); } }

.mudanza__marca {
  font-family: "Bebas Neue", sans-serif; font-size: .8rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--acento);
  margin: 0 0 var(--e2);
}
.mudanza__par { display: grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--linea); }
.mudanza__lado {
  padding: clamp(1.5rem, 3vw, 2.5rem);
  display: grid; gap: var(--e3); align-content: center; justify-items: center;
}
.mudanza__lado + .mudanza__lado { border-left: 1px solid var(--linea); }
.mudanza__lado--antes { background: var(--celda); }
.mudanza__lado img { width: 100%; height: auto; max-height: 120px; object-fit: contain; }
.mudanza__rotulo {
  font-family: "Bebas Neue", sans-serif; font-size: .75rem;
  letter-spacing: .24em; text-transform: uppercase; color: var(--tinta-suave);
}
.mudanza__lado--despues .mudanza__rotulo { color: var(--acento); }

/* ==========================================================================
   GALERIAS - todas con celda de proporcion fija. Ver la nota de arriba.

   FLEX, NO GRID -por lo que le pasaba a "Amor, sol y gratitud". Con CSS Grid,
   las columnas son tracks fijos para TODA la rejilla: si el ultimo elemento
   cae solo en su fila, las columnas que quedan a su derecha existen igual
   -son parte del grid- y se ven como una franja vacia, del ancho de la seccion,
   pegada a la ultima pieza. Ninguna combinacion de `justify-content` en Grid
   corrige esto: esa propiedad alinea la rejilla ENTERA, no una fila suelta.

   Con `display: flex; flex-wrap: wrap; justify-content: center`, cada FILA se
   centra por separado -es una propiedad de Flexbox, no de Grid-. La ultima
   fila incompleta queda centrada, con la misma huella que las piezas de al
   lado, en vez de pegada a la izquierda con un vacio al costado. El ancho de
   celda ahora es un valor fijo (`--celda-w`) en vez de `1fr`: es lo que hace
   que la pieza no se estire cuando le sobra espacio en su fila. */
.galeria { display: flex; flex-wrap: wrap; gap: var(--hueco); justify-content: center; }
.galeria > li { flex: 0 0 auto; width: var(--celda-w); max-width: 100%; }

.galeria--feed       { --celda-w: 216px; --ratio: 4 / 5; }
/* Los dos manuales: TRES por fila, ocupando el ancho completo del marco.
   Antes la celda era un ancho fijo de 330px y, aunque tambien caian tres,
   sobraban ~180px de margen muerto a los lados y las laminas se veian
   pequenas. Con un ancho porcentual la celda pasa de 330px a ~389px en
   escritorio y la fila cierra contra el mismo margen que el resto de la
   pagina. Se conserva Flexbox -no Grid- porque son 14 laminas: la ultima
   fila trae dos y con `justify-content: center` queda centrada, no pegada
   a la izquierda con un hueco al lado (que es el problema que Grid tiene
   con las columnas vacias). */
.galeria--sistema    { --ratio: 16 / 9; }
.galeria--sistema > li { width: calc(100% / 3 - var(--hueco) * 2 / 3); }
.galeria--banners    { --celda-w: 440px; --ratio: 8 / 3; }
.galeria--pop        { --ratio: 1 / 1; }

/* Categorias y Eventos son la excepcion: aqui SI se quiere Grid, a proposito.
   Categorias son exactamente 8 piezas -"dos filas de 4"-, y Eventos son 10
   -"dos filas de 5"-. En los dos casos el pedido es una cuadricula que cierre
   PAREJA, no una que se acomode sola: con `auto-fill` a veces caian 5+3 o 6+2,
   y la fila corta quedaba con el mismo problema de espacio suelto que el resto
   de las galerias resuelve con flex-center. Con columnas fijas -4 y 5- las
   piezas SIEMPRE completan filas enteras, nunca hay una fila corta que
   resolver. Por eso pisan el `display: flex` de la regla `.galeria` de arriba:
   misma especificidad, pero estas van despues en la hoja. */
.galeria--categorias { display: grid; grid-template-columns: repeat(4, 1fr); --ratio: 2 / 3; }
.galeria--eventos    { display: grid; grid-template-columns: repeat(5, 1fr); --ratio: 4 / 5; }

/* Punto de venta se suma a la excepcion (ago 2026, pedido de Miguel: "cada fila
   de cuatro imagenes y maximo tres filas"). Tras la limpieza quedaban 11 piezas
   y en flex caian 4+4+3, con la ultima fila corta; se sumo el Black Friday
   impreso para llegar a 12 y que la cuadricula cierre exacta: 4 x 3, sin hueco.
   Es la misma logica de Categorias (8 = 4x2) y Eventos (10 = 5x2). */
.galeria--pop        { display: grid; grid-template-columns: repeat(4, 1fr); }

/* La celda. `contain` sobre fondo neutro: rejilla a plomo y ninguna pieza
   recortada. `place-items` centra la pieza dentro de su celda. */
.pieza {
  display: grid; place-items: center;
  width: 100%; padding: 0; margin: 0;
  border: 0; background: var(--celda);
  cursor: zoom-in; overflow: hidden; position: relative;
  font: inherit; color: inherit; text-align: left;
  aspect-ratio: var(--ratio, 1);
}
.pieza img, .pieza video {
  width: 100%; height: 100%; object-fit: contain;
  transition: transform .5s var(--salida);
}
.pieza:hover img, .pieza:focus-visible img,
.pieza:hover video, .pieza:focus-visible video { transform: scale(1.04); }
.pieza:focus-visible { outline: 2px solid var(--acento); outline-offset: 3px; }

/* ------------------------------------------------------ hileras en movimiento */
/* Las historias van en dos hileras que corren en sentidos opuestos: la de AZ
   hacia la izquierda, la de Palmare hacia la derecha. Se PARAN al pasar el
   raton por encima y al enfocar con teclado, porque un blanco en movimiento es
   dificil de acertar (y con el teclado seria imposible seguir el foco).
   Sin `.js`, o con `prefers-reduced-motion`, quedan quietas y se recorren a
   mano con scroll horizontal. */
.hileras { display: grid; gap: var(--hueco); }

.hilera { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); }
.hilera__pista { display: flex; gap: var(--hueco); width: max-content; will-change: transform; }
.hilera__grupo { display: flex; gap: var(--hueco); }
.hilera .pieza {
  flex: 0 0 auto;
  height: clamp(210px, 24vw, 290px);
  width: auto; aspect-ratio: 9 / 16;
}

/* Sin JS la hilera no se mueve: se deja recorrer a mano en vez de esconder
   la mitad del contenido. */
.hilera { overflow-x: auto; }
.js .hilera { overflow-x: hidden; }

/* La pista lleva N grupos identicos (`--copias`) separados por un hueco. Un
   ciclo completo mide UN grupo mas UN hueco, y eso vale sea cual sea N:

       pista  = N*grupo + (N-1)*hueco
       ciclo  = grupo + hueco = (pista + hueco) / N

   De ahi el `calc((-100% - var(--hueco)) / var(--copias))`. Con N=2 da lo mismo
   que un `-50%` corregido, pero N=2 NO siempre alcanza: al final del ciclo lo
   que queda cubriendo la ventana son (N-1) grupos, asi que un grupo mas angosto
   que la ventana dejaria un hueco a la derecha. Palmare tiene 7 historias y su
   grupo mide ~1.240 px: con dos copias se veia el corte en cualquier pantalla
   de 1.280 px. `generar_pagina.py` calcula N por hilera para que (N-1) grupos
   cubran hasta una pantalla ultraancha. */
.js .hilera__pista { animation: correr-izq var(--vel, 70s) linear infinite; }
.js .hilera--der .hilera__pista { animation-name: correr-der; }
.js .hilera:hover .hilera__pista,
.js .hilera:focus-within .hilera__pista { animation-play-state: paused; }

@keyframes correr-izq {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc((-100% - var(--hueco)) / var(--copias, 2)), 0, 0); }
}
@keyframes correr-der {
  from { transform: translate3d(calc((-100% - var(--hueco)) / var(--copias, 2)), 0, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

/* --------------------------------------------------------------- reels */
/* Tres reels y, en el hueco que queda a la derecha, la nota de la seccion.
   Antes ese hueco quedaba vacio. */
.reels { display: grid; gap: var(--hueco); grid-template-columns: repeat(4, 1fr); --ratio: 9 / 16; }
.reels__nota {
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: var(--e2); padding: var(--e2) 0 var(--e2) var(--e2);
  border-left: 2px solid var(--acento);
}
.reels__nota p { margin: 0; color: var(--tinta-2); font-size: .95rem; }
.reels__nota strong { display: block; color: var(--tinta); font-weight: 600; }
@media (max-width: 1099px) { .reels { grid-template-columns: repeat(3, 1fr); } .reels__nota { grid-column: 1 / -1; border-left: 0; border-top: 2px solid var(--acento); padding: var(--e2) 0 0; } }
@media (max-width: 699px)  { .reels { grid-template-columns: repeat(2, 1fr); } }

.reel video { background: #0d1418; }
.reel .pieza::after {
  content: ""; position: absolute; inset: auto var(--e2) var(--e2) auto;
  width: 26px; height: 26px; border-radius: 999px;
  background: rgba(255,255,255,.9);
  pointer-events: none; opacity: .85;
}

/* --------------------------------------------------------------- creditos */
/* Mismo patron que Klear tos, Konempleo y La Mera Mera: creditos a la
   izquierda, la pregunta de cierre a la derecha, boton centrado abajo de las
   dos columnas. El texto de creditos se mantiene corto a proposito -unas
   cuatro lineas-, o desequilibra la columna de al lado, que es mas corta. */
.creditos-cierre {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr);
  gap: var(--e5); align-items: start;
  padding-top: clamp(2.25rem, 4vw, 3.5rem);
  border-top: 1px solid var(--linea);
}
@media (max-width: 799px) {
  .creditos-cierre { grid-template-columns: 1fr; gap: var(--e4); }
  .creditos-cierre__boton { grid-column: auto; }
}
.creditos-cierre__columna .bloque__titulo { margin-bottom: var(--e3); }
.creditos {
  max-width: 66ch; margin: 0; padding-left: var(--e3);
  border-left: 2px solid var(--acento);
  font-size: .94rem; color: var(--tinta-2);
}
.creditos strong { font-weight: 600; color: var(--tinta); }

.cierre__contenido h2 {
  margin: 0 0 var(--e2); color: var(--acento);
  font-family: "Bodoni Moda", Georgia, serif; font-weight: 400;
  font-size: clamp(1.5rem, 1rem + 1.3vw, 2rem);
  line-height: 1.16; text-wrap: balance;
}
.cierre__contenido p { margin: 0; max-width: 58ch; color: var(--tinta-2); }

.creditos-cierre__boton { grid-column: 1 / -1; justify-self: center; margin-top: var(--e2); }

/* La unica excepcion al radio 0: el boton, en pildora. El ornamento "\301C"
   es la ola del logotipo de Palmare, el signo que las dos marcas comparten en
   el punto de venta -se copia de ahi, no se inventa uno nuevo. */
.caso-azpalmare .boton {
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .9rem 2rem;
  border-radius: 999px;
  background: var(--acento);
  color: #fff;
  text-decoration: none;
  font-family: "Bebas Neue", sans-serif;
  letter-spacing: .16em; text-transform: uppercase; font-size: 1rem;
  transition: background .25s var(--salida), transform .2s var(--salida);
}
.caso-azpalmare .boton::before, .caso-azpalmare .boton::after { content: "\301C"; font-size: .85em; opacity: .75; }
.caso-azpalmare .boton::after { transform: scaleX(-1); }
.caso-azpalmare .boton:hover, .caso-azpalmare .boton:focus-visible { background: var(--tinta); transform: translateY(-2px); }
.caso-azpalmare .boton:active { transform: translateY(0); }

/* ------------------------------------------------------------- pie del sitio */
.pie {
  padding: clamp(2.5rem, 5vw, 3.5rem) var(--margen) clamp(2rem, 4vw, 3rem);
  background: var(--papel);
  text-align: center;
}
.pie > .marco { padding-inline: 0; }
.pie__filete { display: flex; align-items: center; gap: var(--e3); margin-bottom: var(--e4); }
.pie__filete::before, .pie__filete::after { content: ""; flex: 1; height: 1px; background: var(--linea); }
.pie__filete img { height: 26px; width: auto; }

.pie__menu { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--e2) 0; margin-bottom: var(--e4); }
.pie__menu a {
  position: relative; display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 clamp(9px, 1.4vw, 17px);
  font-size: .87rem; color: var(--tinta-suave); text-decoration: none;
  transition: color .2s;
}
.pie__menu a + a::before {
  content: ""; position: absolute; left: 0; top: 50%;
  width: 1px; height: 11px; margin-top: -5px; background: var(--linea);
}
.pie__menu a:hover, .pie__menu a:focus-visible { color: var(--acento); }
@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 */
.visor { border: 0; padding: 0; max-width: none; max-height: none; width: 100%; height: 100%; background: transparent; }
.visor::backdrop { background: rgba(10, 14, 17, .94); }
.visor[open] {
  display: grid; grid-template-rows: 1fr auto;
  place-items: center; gap: var(--e2);
  padding: clamp(3.5rem, 6vw, 5rem) clamp(1rem, 4vw, 3rem) clamp(1.25rem, 3vw, 2rem);
}
.visor__marco { display: grid; place-items: center; width: 100%; min-height: 0; }

/* max-height: 100% NO se resuelve aqui: el alto del padre viene de un track 1fr
   de grid y eso no cuenta como alto definido para un porcentaje de un
   descendiente. El navegador ignora el max-height y pinta la pieza a su tamano
   NATURAL, desbordando la ventana. dvh es un numero absoluto y no depende de la
   cadena de contenedores. */
.visor__marco img, .visor__marco video {
  max-width: 100%; max-height: 78dvh;
  width: auto; height: auto;
  object-fit: contain;
}
/* Tiene que ganarle a la regla `display: block` de mas arriba, o el elemento
   oculto sigue ocupando su celda del grid. */
.visor__marco img[hidden], .visor__marco video[hidden] { display: none; }

.visor__barra { display: flex; align-items: center; gap: var(--e3); color: rgba(255,255,255,.85); }
.visor__cuenta { font-family: "Bebas Neue", sans-serif; letter-spacing: .12em; font-size: .9rem; }
.visor__nav { display: flex; gap: var(--e1); }
.visor button {
  display: grid; place-items: center;
  width: 2.5rem; height: 2.5rem;
  color: #fff; background: rgba(255,255,255,.1);
  border: 0; cursor: pointer; font-size: 1.05rem; line-height: 1;
  transition: background .2s var(--salida);
}
.visor button:hover:not(:disabled) { background: rgba(255,255,255,.22); }
.visor button:disabled { opacity: .3; cursor: default; }
.visor__cerrar {
  position: fixed; top: clamp(.75rem, 2vw, 1.5rem); right: clamp(.75rem, 2vw, 1.5rem);
  width: 44px; height: 44px; border-radius: 999px;
  border: 1px solid rgba(255,255,255,.35);
}

/* ==========================================================================
   MOVIMIENTO
   ========================================================================== */

/* Sin JS, nada lleva opacidad 0: la pagina se ve completa igual. */
.js [data-entra] { opacity: 0; transform: translateY(20px) scale(.985); }
.js [data-entra].visible {
  opacity: 1; transform: none;
  transition: opacity .75s var(--salida) var(--retraso, 0ms),
              transform .75s var(--salida) var(--retraso, 0ms);
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    .galeria, .reels { perspective: 1500px; }
    .galeria > li, .reels > li { transform-style: preserve-3d; }

    .js:not(.sin-timeline) .galeria > li,
    .js:not(.sin-timeline) .reels > li {
      opacity: 1; transform: none;
      animation: llega linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 32%;
    }

    .js:not(.sin-timeline) .bloque__cabeza[data-entra],
    .js:not(.sin-timeline) .mudanza__bloque[data-entra] {
      opacity: 1; transform: none;
      animation: asoma linear both;
      animation-timeline: view();
      animation-range: entry 8% cover 26%;
    }
  }
}

@keyframes llega {
  from { opacity: 0; transform: translate3d(0, 40px, -140px) rotateX(10deg); }
  to   { opacity: 1; transform: none; }
}
@keyframes asoma {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js [data-entra] { opacity: 1; transform: none; transition: none; }
  .js .hero__grupo, .js .hero__baja::after { animation: none; }
  .js .hero__ojo, .js .hero__bajada, .js .hero__meta, .js .hero__baja,
  .js .hero__regla, .js .hero__linea > span {
    animation: none; opacity: 1; transform: none;
  }
  .js .galeria > li, .js .reels > li, .js .mudanza__bloque { animation: none; opacity: 1; transform: none; }
  /* Las hileras se paran y se vuelven recorribles a mano. */
  .js .hilera__pista { animation: none; transform: none; }
  .js .hilera { overflow-x: auto; }
  .hilera { -webkit-mask-image: none; mask-image: none; }
  .pieza img, .pieza video, .boton { transition: none; }
  .pieza:hover img, .pieza:focus-visible img { transform: none; }
}

@media print {
  .visor, .creditos-cierre__boton, .hero__olas, .hero__baja { display: none; }
  .js [data-entra] { opacity: 1; transform: none; }
  .galeria > li { width: calc(100% / 3 - var(--hueco) * 2 / 3); }
}

/* ------------------------------------------------------------------ movil */
@media (max-width: 699px) {
  .hero { min-height: 88svh; }
  .mudanza__par { grid-template-columns: 1fr; }
  .mudanza__lado + .mudanza__lado { border-left: 0; border-top: 1px solid var(--linea); }

  /* En un telefono de 375 px, la celda fija de escritorio (216-440px) ya no
     cabe: se recalcula el ancho para dos columnas exactas (o una, en las
     galerias de piezas anchas). El calc resta un hueco de por medio, para que
     dos celdas mas el espacio entre ellas sumen el 100% del contenedor. */
  .galeria--feed > li { width: calc(50% - var(--hueco) / 2); }
  .galeria--sistema > li,
  .galeria--banners > li { width: 100%; }

  /* Categorias y Eventos siguen siendo Grid, pero aqui SI se sueltan sus
     columnas fijas de escritorio: 4 o 5 columnas de piezas verticales quedarian
     minusculas en un telefono. Con dos columnas, 8 y 10 piezas siguen sin dejar
     fila corta -cuatro y cinco filas parejas, respectivamente. */
  .galeria--categorias,
  .galeria--eventos,
  .galeria--pop { grid-template-columns: 1fr 1fr; }
}
