/* 24/09/2026 (décima corrección), pedido de Gus: "NO quiero que cualquier persona
   pueda dar click derecho y guardar la imagen... de forma que puedan ellos imprimir
   nuestros diseños" -- se desactiva, en TODAS las imágenes del sitio (catálogo, ficha
   de producto -- las 3 fotos --, carrito, checkout, portada), el menú de click derecho
   ("Guardar imagen como...", ver el listener global "contextmenu" en el JS, cerca del
   final del archivo) y el arrastre nativo de la imagen (que en Chrome/Firefox permite
   soltarla en el escritorio o en otra pestaña para guardarla sin pasar por ningún
   menú) -- "-webkit-user-drag:none" cubre el arrastre, "-webkit-touch-callout:none"
   el menú de "Guardar imagen"/"Copiar imagen" que aparece en iPhone/iPad al mantener
   el dedo presionado sobre una imagen (no existe un "click derecho" en touch, ese menú
   largo-presionar es el equivalente). "user-select:none" evita además que se pueda
   seleccionar la imagen como si fuera texto (otra vía, menos común, para copiarla).
   OJO -- límite honesto, para no prometer algo que esta técnica no puede cumplir:
   esto sube bastante la valla para cualquier persona común (que es lo que se pidió),
   pero ninguna medida que corra en el navegador del visitante puede impedir del todo
   que alguien decidido descargue una imagen que ya se le envió a su pantalla -- con
   las herramientas de desarrollador del navegador (F12), una captura de pantalla, o
   inspeccionando el tráfico de red, siempre se puede llegar al archivo. La única
   protección real contra eso sería nunca enviar el archivo en su resolución completa
   (ej. una marca de agua, o servir siempre una versión más chica/comprimida que la
   que se usa para imprimir) -- ninguna de las dos cosas se pidió ni se implementa acá,
   sólo el bloqueo de click derecho / arrastre / menú táctil que Gus pidió
   explícitamente. */
img {
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
:root {
  --cream: #efeee5;
  --bone: #f8f7f5;
  --hero-bg: #e4dfd7;
  --paper: #ffffff;
  --ink: #000000;
  --burgundy: #501a19;
  --burgundy-deep: #3d1312;
  --butter: #f3e9be;
  --babyblue: #8eaaba;
  --black: #000000;
  --muted: #000000;
  --line: #e2dfd3;
  --radius: 2px;
}
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
/* Reversión 25/09/2026, pedido de Gus ("al darle click en 'Catálogo', en lugar de
   desplegarse el menú de opciones, baja y deja ver los primeros diseños del catálogo...
   debe ser como estaba antes, que se desplegaba el menú"): el "overflow-x:hidden" que se
   había agregado acá en "html" el 24/09/2026 (ver corrección de consistencia entre
   pantallas) rompía el "position:sticky" de ".site-header" -- un efecto secundario real de
   ese cambio que no se había detectado en la verificación de ese día (solo se había medido
   que ya no hubiera scroll horizontal, no que el header siguiera pegado arriba al
   scrollear). Con el header ya no pegado arriba, el menú desplegable de "Catálogo"
   (".catalogo-dropdown", que vive dentro del header y depende de que este se quede fijo
   para seguir viéndose) se abría igual por dentro (su clase "open" sí se activaba) pero
   quedaba fuera de la pantalla en cuanto el clic también hacía scroll hacia la sección del
   catálogo -- se veía como si el menú ya no se desplegara. Se revierte por completo ese
   "overflow-x:hidden" de "html" (queda solo el de "body", que ya existía desde antes y
   nunca causó este problema) para devolver el header a su comportamiento de siempre. Para
   revertir esta reversión: ver el historial de este mismo comentario en versiones
   anteriores del archivo. */
/* 25/09/2026, pedido explícito de Gus ("al hacer click en el catálogo, no bajes a los
   cuadros del principio... solo despliega las opciones"): además de haber quitado ya
   el "scrollTo" manual del JS (ver "catalogoBtn.onclick"), se descubrió con Playwright
   que el scroll seguía moviéndose solo -- de 234px a 690px -- SIN ningún scrollTo en el
   código. La causa real es el "scroll anchoring" nativo del navegador: cuando el panel
   del catálogo (que vive en el flujo normal de la página, encima del contenido) crece
   de 0 a su altura final, el navegador detecta que el contenido de más abajo "saltaría"
   hacia arriba en la pantalla y compensa automáticamente aumentando el scroll para que
   ese contenido no se mueva -- exactamente el efecto de "bajar hacia el catálogo" que
   Gus seguía viendo aunque ya no hubiera ningún scroll programado a mano. Se desactiva
   este comportamiento automático con "overflow-anchor:none" para que abrir/cerrar el
   panel no mueva el scroll de la página en absoluto, tal como se pidió. */
/* 25/09/2026, otra causa posible del mismo reporte de Gus ("se sacuden esas tres
   opciones"): "openAboutOverlay()" (botón "Sobre Morello") hace
   "document.body.style.overflow = 'hidden'" en el instante mismo del click, ANTES de que
   empiece a subir el panel. En Windows, con la barra de scroll "clásica" (la que sí ocupa
   espacio real a la derecha de la pantalla, a diferencia de la barra "flotante" de Mac),
   ocultar el overflow así hace desaparecer esa barra al instante -- y al desaparecer, el
   ancho útil de la página crece de golpe por el espacio que la barra ocupaba, corriendo
   todo el contenido centrado del header (incluidos los tres botones del menú) unos
   pixeles hacia la derecha en ese mismo instante. Como el panel todavía no se ve (sigue
   fuera de pantalla, recién empieza a subir), lo único visible en ese momento es ese
   salto lateral del menú -- exactamente "se mueven las cosas antes de llevarte a la
   opción". "scrollbar-gutter:stable" reserva ese espacio de forma permanente (haya o no
   overflow:hidden en un momento dado), así la barra nunca aparece ni desaparece y ese
   ancho nunca cambia. No tiene efecto en navegadores con barra de scroll flotante (Mac, o
   la mayoría de móviles), así que no cambia nada ahí. */
html {
  scroll-behavior: smooth;
  overflow-anchor: none;
  scrollbar-gutter: stable;
}
body {
  font-family: "Cormorant Garamond", serif;
  font-weight: 500;
  color: var(--ink);
  background: #f8f7f5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img {
  max-width: 100%;
  display: block;
}
a {
  color: inherit;
  text-decoration: none;
}
button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
  color: inherit;
}
.wordmark {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  color: var(--burgundy);
  letter-spacing: -0.01em;
} /* unused now, logo renders as image */
.logo-img {
  height: 30px;
  display: block;
}
.footer-logo {
  height: 26px;
}
.eyebrow {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 0.864rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #000;
}
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 32px;
}
@media (max-width: 640px) {
  .container {
    padding: 0 20px;
  }
}

/* ---------- Header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(239, 238, 229, 0.94);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.site-header .container {
  max-width: 1400px;
  padding: 0 20px;
}
.site-header > .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64.6px;
}
.header-left {
  display: flex;
  align-items: center;
  gap: 52px;
}
.logo {
  font-size: 1.5rem;
}
.nav-links {
  display: flex;
  gap: 32px;
  font-size: 1.104rem;
  font-weight: 600;
}
.nav-links a {
  position: relative;
  padding: 4px 0;
}
.nav-links a:hover {
  color: var(--burgundy);
  font-weight: 700;
  -webkit-text-stroke: 0.4px currentColor;
}
.header-right {
  display: flex;
  align-items: center;
  gap: 22px;
}
.lang-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.96rem;
  font-weight: 700;
}
.lang-btn {
  color: var(--muted);
  padding: 2px 3px;
  letter-spacing: 0.02em;
}
.lang-btn.active {
  color: #000;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.lang-sep {
  color: var(--line);
  font-size: 0.9rem;
}
.cart-btn {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1.08rem;
  font-weight: 600;
}
.cart-count {
  background: var(--burgundy);
  color: #fff;
  font-size: 0.816rem;
  font-weight: 700;
  border-radius: 50%;
  width: 19px;
  height: 19px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "EB Garamond", serif;
}
.hamburger-btn {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 8px 4px;
  flex-shrink: 0;
}
.hamburger-btn span {
  width: 22px;
  height: 2px;
  background: #000;
  display: block;
  border-radius: 1px;
}
@media (max-width: 895px) {
  .hamburger-btn {
    display: flex;
  }
  .header-left {
    gap: 16px;
  }
  .nav-links {
    display: none;
    position: absolute;
    top: 64.6px;
    left: 0;
    right: 0;
    background: var(--cream);
    flex-direction: column;
    gap: 0;
    padding: 8px 20px 16px;
    border-bottom: 1px solid var(--line);
    box-shadow: 0 16px 28px -18px rgba(0, 0, 0, 0.2);
  }
  .nav-links.mobile-open {
    display: flex;
  }
  .nav-links a,
  .nav-links .nav-link-btn {
    padding: 14px 0;
    border-top: 1px solid var(--line);
    width: 100%;
    text-align: left;
    font-size: 1.2rem;
  }
}

.nav-link-btn {
  font-family: inherit;
  font-size: 1.104rem;
  font-weight: 600;
  padding: 4px 0;
  position: relative;
  /* 25/09/2026: ver el comentario grande de abajo -- se agrega una transición suave de
     "color" para que el cambio de color al pasar el cursor sea gradual y no un "snap"
     instantáneo en el mismo cuadro en que se hace click (que podía leerse como un
     parpadeo justo antes de que se abriera el panel). */
  transition: color 0.15s ease;
}
/* 25/09/2026, pedido de Gus ("cuando haces clic en catálogo, en sobre Morello o en
   contacto, se sacuden esas tres opciones y luego recién se abre la opción, quítale
   esa porquería"): la causa principal era el "font-weight:700" de esta misma regla --
   al pasar el cursor (o marcar ".active") el texto pasaba de 600 a 700, se ensanchaba, y
   como estos tres botones viven en ".nav-links", que es un contenedor "display:flex" sin
   anchos fijos, ese ensanche empujaba a los botones de al lado, sacudiendo todo el menú
   justo antes de que se abriera el panel/overlay correspondiente. Se quitó el cambio de
   "font-weight". Gus reportó que el temblor seguía sintiéndose, así que además se quita
   ahora también el "-webkit-text-stroke" (que aparecía de golpe, sin transición, y podía
   verse como un pequeño destello al engrosar el trazo de las letras en el mismo instante
   del click) y se deja SOLO el cambio de color, ahora con una transición suave (ver
   arriba) en vez de un cambio instantáneo. Sin cambio de ancho, sin cambio de grosor de
   trazo, sin cambio brusco de ningún tipo -- lo único que cambia es el color, y de forma
   gradual. */
.nav-link-btn:hover,
.nav-link-btn.active {
  color: var(--burgundy);
}
.catalogo-dropdown {
  display: grid;
  grid-template-rows: 0fr;
  background: var(--bone);
  border-bottom: 1px solid transparent;
  box-shadow: 0 12px 24px -16px rgba(0, 0, 0, 0.15);
  /* 25/09/2026, pedido de Gus ("quiero que sea más smooth, más fluido"): antes usaba
     ".3s ease" (una curva genérica, algo golpeada al arrancar) -- se cambia a la misma
     curva "cubic-bezier(.4,0,.2,1)" ya usada en el resto del sitio para los movimientos
     más elegantes (ver MODAL_TRANSITION_EASE en el JS), y se alarga un poco la duración
     (.3s a .45s) para que se sienta más pausado/elegante en vez de rápido y seco.
     25/09/2026 (segunda vuelta, mismo día), pedido de Gus: "aplica la misma velocidad de
     transición de 'Sobre Morello' a 'Catálogo', esta segunda es mucho más veloz" -- el
     desplegable de "Sobre Morello" (".checkout-overlay", más abajo) usa ".55s", quedando
     el de "Catálogo" (".45s") perceptiblemente más rápido a pesar de compartir la misma
     curva. Se iguala a ".55s" para que las dos transiciones se sientan igual de
     pausadas/elegantes.
     25/09/2026 (tercera vuelta, mismo día), pedido de Gus: "que la transición de
     'catálogo' sea más lenta, 25% más lenta". Se toma la última duración (.55s) como
     base y se alarga un 25% (.55 * 1.25 = .6875s). Esto separa a "Catálogo" de "Sobre
     Morello" otra vez (que se queda en .55s) -- ahora "Catálogo" es el más lento de los
     dos, tal como se pidió.
     25/09/2026 (cuarta vuelta, mismo día), pedido de Gus: "haz 25% más lento (quiero que
     se vea fluido, pero no de golpe) el despliegue de las opciones del catálogo. Sigue
     siendo muy brusco, quiero que se parezca a la transición de 'sobre morello'.
     CORRIGE ESO SOLAMENTE, NO MUEVAS NADA MÁS." Se toma la última duración (.6875s) como
     base y se alarga otro 25% (.6875 * 1.25 = .859375s). No se toca ningún otro valor de
     esta regla (misma curva, mismo color, mismo box-shadow) según lo pedido. */
  transition:
    grid-template-rows 0.859375s cubic-bezier(0.4, 0, 0.2, 1),
    border-color 0.859375s;
}
.catalogo-dropdown.open {
  grid-template-rows: 1fr;
  border-bottom-color: var(--line);
}
.catalogo-dropdown-inner {
  overflow: hidden;
  min-height: 0;
}
.catalogo-dropdown .filter-bar {
  padding: 44px 0 22px;
}

/* ---------- Hero ---------- */
.hero {
  padding: 0;
  background: var(--hero-bg);
  overflow: hidden;
}
.hero-flex {
  display: flex;
  align-items: center;
}
.hero-copy {
  flex: 1;
  min-width: 260px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  text-align: center;
  /* 14/09/2026, pedido de Gus: alinear el texto del hero con el borde
     izquierdo del logo. El logo vive en .site-header .container, que tiene
     max-width:1400px, padding:0 20px y se centra con margin:0 auto -- su
     borde izquierdo real es max(20px, (100vw - 1400px)/2 + 20px), que
     simplifica a max(20px, calc(50vw - 680px)). Antes .hero-copy centraba
     el bloque de texto (align-items:center) dentro de su propio ancho, lo
     que no coincide con esa posición. Ahora se alinea a la izquierda
     (align-items:flex-start, arriba) y el padding-left reproduce esa misma
     fórmula, verificado con Playwright en 1280/1440/1600/1920px de ancho:
     el borde izquierdo del texto queda exactamente igual al del logo. */
  padding: 40px 40px 40px max(20px, calc(50vw - 680px));
}
.hero-copy-inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: 520px;
}
.hero-copy h1 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
  font-size: clamp(2.5rem, calc(7.06vw - 21.6px), 4.1rem);
  line-height: 1.12;
  margin: 0 0 22px;
  letter-spacing: -0.01em;
  color: #000;
  max-width: 520px;
  white-space: pre-line;
  text-align: left;
}
.hero-copy p {
  font-size: 1.38rem;
  font-weight: 500;
  color: #000;
  line-height: 1.6;
  max-width: 520px;
  white-space: pre-line;
  text-align: left;
}
@media (min-width: 901px) and (max-width: 935px) {
  .hero-copy p {
    font-size: 1.25rem;
  }
}
.hero-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 26px;
  flex-wrap: wrap;
}
.hero-image {
  /* 14/09/2026, pedido de Gus: se quita aspect-ratio:1/1 (forzaba un recuadro
     cuadrado). Con fotos horizontales como las 8 nuevas, el cuadrado obligaba
     a recortar mucho más de los lados para cubrir el alto, dando sensación de
     "zoom". Con solo la altura, el recuadro queda panorámico.
     14/09/2026 (2do ajuste), pedido de Gus: la altura se reduce más -- medí su
     captura de referencia (ventana de 888px de alto) y el recuadro ahí mide
     ~687px, dejando ~116px visibles de "Nuestra colección" sin scroll. Antes
     usaba solo "100svh - 92px" (altura casi completa); ahora se le restan
     116px extra para que ese asome quede igual que en su referencia.
     14/09/2026 (3er ajuste), pedido de Gus: el ancho también quedó más grande
     que su referencia -- medí el borde izquierdo real de la foto en su
     captura (columna x=671 de 1600, comparando la misma ventana blanca en
     ambas imágenes) contra el mío (x=565) y confirmé que mi recuadro se
     extendía ~100px de más hacia la izquierda. 65% quedaba ancho; el valor
     que reproduce su referencia es 58%. */
  position: relative;
  width: 58%;
  max-width: 1320px;
  min-width: 340px;
  height: calc(100svh - 208px);
  flex-shrink: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 18%);
  mask-image: linear-gradient(to right, transparent 0%, #000 18%);
}
.hero-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center center;
  /* 15/09/2026, pedido de Gus: las fotos no deben recortarse/ampliarse -- se
     cambia de object-fit:cover a contain para que se vea la foto completa,
     en su mismo formato y proporción originales, sin zoom. El espacio que
     quede libre en el recuadro se ve del color de fondo del hero (beige).
     14/09/2026, pedido de Gus: sin zoom -- se quitó la animación heroKenBurns
     (el tamaño de la foto se queda fijo). La transición entre una foto y otra
     dura exactamente 4s. */
  opacity: 0;
  transition: opacity 4s ease;
}
.hero-slide.active {
  opacity: 1;
}
@media (max-width: 900px) {
  .hero-flex {
    flex-direction: column;
    align-items: center;
    max-height: 100svh;
  }
  /* alineación con el logo es solo para escritorio (arriba, >900px); en
     mobile el hero-copy se apila y se mantiene centrado como antes. */
  .hero-copy {
    padding: 24px 32px 16px;
    align-items: center;
  }
  .hero-copy h1 {
    margin: 12px 0 12px;
  }
  .hero-image {
    width: 100%;
    max-width: none;
    min-width: 0;
    height: calc(100svh - 360px);
    margin: 0 auto;
    -webkit-mask-image: none;
    mask-image: none;
  }
}
@media (max-width: 640px) {
  .hero-copy {
    padding: 16px 20px 8px;
  }
  .hero-image {
    height: calc(100svh - 340px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-slide {
    animation: none;
  }
}
.btn-primary {
  background: var(--burgundy);
  color: #fff;
  padding: 15px 30px;
  font-size: 1.104rem;
  font-weight: 700;
  border-radius: var(--radius);
  transition: background 0.2s;
  display: inline-block;
}
.btn-primary:hover {
  background: var(--burgundy-deep);
}
.link-ghost {
  font-size: 1.08rem;
  font-weight: 700;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 2px;
}

/* ---------- Essence / Brand pillars ---------- */
.essence {
  padding: 96px 0;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.essence .container {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 64px;
  align-items: start;
}
.essence-overlay-content {
  padding: 56px 0 100px;
}
.terms-section {
  padding: 18px 0;
  background: var(--bone);
  border-top: 1px solid var(--line);
}
.container-narrow {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 32px;
}
.terms-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  text-align: left;
  padding: 6px 0;
}
.terms-toggle-icon {
  font-size: 2.28rem;
  font-weight: 500;
  color: #000;
  transition: transform 0.3s ease;
  flex-shrink: 0;
  line-height: 1;
}
.terms-toggle.open .terms-toggle-icon {
  transform: rotate(45deg);
}
.terms-collapse {
  max-height: 0;
  overflow: hidden;
}
.terms-collapse-inner {
  opacity: 0;
  transition: opacity 0.5s ease;
}
.terms-collapse.open .terms-collapse-inner {
  opacity: 1;
  transition: opacity 0.55s ease 0.08s;
}
.terms-updated {
  color: var(--muted);
  font-size: 0.984rem;
  margin: 0;
}
.terms-top-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin: 28px 0 24px;
}
.terms-close-top {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.68rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s;
}
.terms-close-top:hover {
  background: var(--burgundy);
}
.terms-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 36px 40px;
  padding-bottom: 16px;
}
.terms-block h3 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.2rem;
  margin-bottom: 10px;
}
.terms-block p {
  color: var(--muted);
  line-height: 1.7;
  font-size: 1.08rem;
  margin-bottom: 8px;
  text-align: justify;
}
@media (max-width: 860px) {
  .terms-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 560px) {
  .terms-grid {
    grid-template-columns: 1fr;
  }
}
.essence-overlay-content .container {
  max-width: 640px;
  margin: 0 auto;
  padding: 0 32px;
  text-align: center;
}
.essence h2,
.essence-overlay-content h2 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: clamp(1.92rem, 3.36vw, 2.4rem);
  line-height: 1.25;
  margin-top: 14px;
  max-width: 480px;
  text-align: justify;
}
.essence-overlay-content h2 {
  max-width: none;
  text-align: center;
}
.essence-lead {
  color: var(--muted);
  line-height: 1.7;
  font-size: 1.2rem;
  margin-top: 20px;
  max-width: 480px;
  text-align: justify;
}
#aboutTextBlock .essence-lead {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}
.pillars {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.pillar {
  display: flex;
  gap: 20px;
  padding: 26px 0;
}
.pillar-num {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  color: #000;
  font-size: 1.02rem;
  flex-shrink: 0;
  width: 26px;
  padding-top: 2px;
}
.pillar h3 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.26rem;
  margin-bottom: 6px;
}
.pillar p {
  color: var(--muted);
  font-size: 1.08rem;
  line-height: 1.55;
}
@media (max-width: 860px) {
  .essence .container {
    grid-template-columns: 1fr;
  }
}

/* ---------- Trust strip ---------- */
.trust-strip {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  margin-top: 64px;
}
/* Corrección 08/09/2026, pedida por Gus: quiere que las celdas de los extremos ("Hecho en
   Perú" y "Marcos de madera") absorban el margen vacío que queda a cada lado en pantallas
   anchas (donde ".container" llega a su ancho máximo de 1200px y se centra, dejando espacio
   en blanco fuera de él) y centren su texto en ese espacio más ancho -- mientras que la
   celda del medio ("Lienzo premium") se queda exactamente donde está, sin cambiar de tamaño
   ni posición ("no modifiques nada más", instrucción explícita).
   Para lograrlo, esta regla ya NO usa el ".container" de ancho máximo 1200px (se le quita
   el max-width y el padding con las dos líneas de abajo) -- el grid pasa a ocupar TODO el
   ancho de ".trust-strip" (que sí es de borde a borde). Sus 3 columnas son "1fr / ancho fijo
   / 1fr": la columna del medio usa una fórmula (calc con min()) que da exactamente el mismo
   ancho que tenía antes como tercio de un contenedor de máximo 1200px -- así "Lienzo
   premium" no cambia ni un píxel a ningún ancho de pantalla -- y las columnas 1 y 3 (1fr)
   se reparten TODO lo que sobra a cada lado, exactamente el efecto de "fusionar" la celda
   con el margen vacío contiguo. */
.trust-strip .container {
  display: grid;
  grid-template-columns: 1fr calc((min(1200px, 100vw) - 64px) / 3) 1fr;
  text-align: center;
  max-width: none;
  padding: 0;
}
/* Corrección 08/09/2026, pedida por Gus ("hecho en Perú" / "marcos de madera" sin saber
   respecto a qué estaban centrados): el selector de esta regla era ".trust-strip div" (sin
   ">"), un selector descendiente que -- sin querer -- también hace match con
   ".trust-strip .container" mismo (que también es un <div>, aunque la intención era
   apuntar solo a los 3 bloques de texto de adentro). Como ".trust-strip div" tiene más
   especificidad que la regla base ".container{padding:0 32px}" (ver más arriba), el
   padding de ESE contenedor quedaba pisado a "22px 16px" -- 16px de margen a los lados en
   vez de los 32px que usa el resto de la página (encabezado, grilla de productos, etc.).
   El texto SÍ estaba centrado dentro de su propia columna, pero las tres columnas en
   conjunto quedaban corridas 16px hacia la izquierda y ensanchadas 16px hacia la derecha
   respecto al resto del contenido de la página -- de ahí que no calzaran visualmente con
   nada más. Se corrige acotando el selector a "> div" (solo los hijos directos del
   contenedor), para que ya no alcance al contenedor mismo. */
.trust-strip .container > div {
  padding: 22px 16px;
  font-size: 1.032rem;
  font-weight: 600;
  color: var(--muted);
  border-left: 1px solid var(--line);
  white-space: pre-line;
}
.trust-strip .container > div:first-child {
  border-left: none;
}
.trust-strip strong {
  color: var(--ink);
  font-weight: 700;
}
@media (max-width: 640px) {
  .trust-strip .container {
    grid-template-columns: 1fr;
  }
  .trust-strip .container > div {
    border-left: none;
    border-top: 1px solid var(--line);
  }
  .trust-strip .container > div:first-child {
    border-top: none;
  }
}

/* ---------- Collection ---------- */
.collection {
  padding: 64px 0 96px;
  background: var(--bone);
}
.collection-title {
  text-align: center;
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.collection-title h2 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: clamp(2.04rem, 3.84vw, 2.76rem);
  letter-spacing: -0.01em;
}
.collection-arrow {
  font-size: 1.68rem;
  color: #000;
  animation: bounce 2.2s infinite;
}
@keyframes bounce {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(6px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .collection-arrow {
    animation: none;
  }
}

/* ---------- Filter toolbar ---------- */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 28px;
  padding: 20px 0 24px 0;
}
.filter-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.filter-section-label {
  font-size: 0.864rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  -webkit-text-stroke: 0.4px currentColor;
}
.filter-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.filter-divider {
  width: 1px;
  align-self: stretch;
  background: var(--line);
  flex-shrink: 0;
  margin-top: 24px;
}
.chip {
  border: none;
  background: none;
  padding: 4px 0;
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 8px;
  transition: color 0.15s;
  white-space: nowrap;
  text-align: left;
}
.chip:hover {
  color: var(--burgundy);
  font-weight: 700;
  -webkit-text-stroke: 0.4px currentColor;
}
.chip.active {
  color: var(--burgundy);
  font-weight: 700;
  -webkit-text-stroke: 0.4px currentColor;
}
.chip.chip-all.active {
  color: var(--ink);
  font-weight: 600;
  -webkit-text-stroke: 0;
}
.chip.chip-all.active:hover {
  color: var(--burgundy);
  font-weight: 700;
  -webkit-text-stroke: 0.4px currentColor;
}
.results-count {
  font-size: 20px;
  color: var(--muted);
  text-align: center;
  margin-bottom: 36px;
  font-family: "EB Garamond", serif;
}
.no-results {
  padding: 60px 20px;
  text-align: center;
  color: var(--muted);
  font-size: 1.104rem;
}
.composition-banner {
  text-align: center;
  font-size: 1.08rem;
  color: #000;
  background: var(--butter);
  border-radius: 2px;
  padding: 14px 20px;
  margin-bottom: 32px;
}
@media (max-width: 640px) {
  .filter-bar {
    flex-direction: column;
    gap: 20px;
  }
  .filter-divider {
    display: none;
  }
}

/* ---------- Compact header search (siempre visible, integrada en el header) ---------- */
.header-search-pill {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid #8c8c8c;
  border-radius: 3px;
  background: transparent;
  padding: 8px 12px;
  width: 316px;
  flex-shrink: 0;
  transition:
    border-color 0.15s,
    width 0.2s ease;
}
.header-search-pill:focus-within {
  border-color: #6b6b6b;
  width: 360px;
}
.header-search-pill input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 1.056rem;
  color: #000;
  outline: none;
}
.header-search-pill input::placeholder {
  color: rgba(0, 0, 0, 0.45);
}
.header-search-icon {
  width: 16px;
  height: 16px;
  background: none;
  color: #000;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.close-x-small {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.56rem;
  color: var(--muted);
  flex-shrink: 0;
  transition: background 0.15s;
}
.close-x-small:hover {
  background: rgba(0, 0, 0, 0.06);
}
.dropdown-close {
  position: absolute;
  top: 14px;
  right: 32px;
}
.catalogo-dropdown-inner .container {
  position: relative;
}
@media (max-width: 1060px) {
  .header-search-pill {
    width: 226px;
  }
  .header-search-pill:focus-within {
    width: 256px;
  }
  .nav-links {
    gap: 18px;
  }
  .header-left {
    gap: 28px;
  }
}
@media (max-width: 768px) {
  .header-search-pill input {
    font-size: 16px;
  }
}
@media (max-width: 640px) {
  .header-search-pill {
    width: 150px;
  }
  .header-search-pill:focus-within {
    width: 196px;
  }
  .header-search-icon {
    width: 16px;
    height: 16px;
  }
}
@media (max-width: 480px) {
  .header-search-pill {
    width: 100px;
    padding: 6px 8px;
  }
  .header-search-pill:focus-within {
    width: 150px;
  }
}
/* 24/09/2026, pedido de Gus ("que se vea exactamente igual... en todo tipo de celulares
   Android y iOS"): verificado con Playwright a distintos anchos de pantalla -- por debajo
   de ~327px de ancho (el celular más angosto real que sigue existiendo es el iPhone SE de
   primera generación, 320px; ningún Android actual baja de 360px) el header ya no entraba:
   ".header-search-pill" se quedaba fijo en 100px (el breakpoint de arriba, para 480px, no
   tiene ninguno más angosto después) y esos 100px, sumados al resto del header (logo,
   idioma ES/EN, carrito), no cabían en el ancho real de pantalla -- el ícono del carrito
   quedaba parcialmente cortado fuera del viewport. Se agrega este breakpoint extra,
   siguiendo el mismo patrón que los tres de arriba (1060px/640px/480px), solo para achicar
   un poco más la barra de búsqueda en pantallas angostas -- nada más del header cambia, y
   en pantallas de 361px o más (donde ya no hacía falta) el comportamiento queda exactamente
   igual que antes. */
@media (max-width: 360px) {
  .header-search-pill {
    width: 70px;
  }
  .header-search-pill:focus-within {
    width: 120px;
  }
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(135px, 1fr));
  gap: 118px 130px;
  align-items: stretch;
}
@media (max-width: 640px) {
  /* 03/10/2026, pedido de Gus: el diseño debe verse un 40% MAS CHICO que como se veia
     ANTES de que tocaramos nada hoy (no un 40% mas chico que la primera corrección que
     le mostramos) -- recalculado desde cero: antes de hoy, el margen lateral de la
     cuadricula era 20px por lado y el espacio entre columnas ("gap") 20px (regla
     original de Maria), asi que el ancho de cada tarjeta era (ancho de pantalla - 40 de
     margenes - 20 de gap) / 2. En el iPhone 15 Pro de la esposa de Gus (393px, el
     celular de referencia de este pedido) eso da (393-40-20)/2 = 166.5px por tarjeta.
     Un 40% mas chico = 166.5 * 0.6 = 99.9 ≈ 100px por tarjeta. Como el margen lateral NO
     se toca (nadie pidio tocarlo) y solo hay 2 columnas, el espacio sobrante de achicar
     las tarjetas tiene que ir al gap entre ellas: resolviendo "(393-40-gap)/2 = 100"
     da gap = 153px. Verificado con Playwright: a 393px de ancho cada tarjeta queda en
     ~99-100px (99.9px exacto), un 40% mas chica que los 166.5px de antes. NOTA para
     Gus: con el margen fijo y solo 2 columnas, ese 40% de reduccion en el ANCHO de la
     imagen obliga a un espacio vacio entre columnas mas grande que las tarjetas mismas
     (153px de gap vs ~100px de tarjeta) -- mandamos captura para que lo veas tal cual
     antes de dejarlo asi. */
  .grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 68px 54.33px;
  }
}
/* 03/10/2026, pedido de Gus: separar la distancia VERTICAL entre filas (no la horizontal,
   que queda igual) para que en pantalla se vean "solo 4" disenos a la vez en vez de 6.
   Se probo +10% (42px->46.2px, no alcanzaba) y despues 60px (3ra fila a 16px del borde,
   todavia entraba). Valor final pedido por Gus: 68px -- aplicado arriba. Verificado con
   Playwright en el iPhone 15 Pro (393x852): con 68px la 3ra fila queda JUSTO en el limite
   del alto de pantalla (852px), a menos de 1px de diferencia -- practicamente el punto
   exacto donde deja de entrar completa. En un telefono real (con la barra del navegador
   ocupando algo de espacio, distinto por navegador/modelo) lo normal es que esa 3ra fila
   ya no se vea completa y queden las 4 primeras tarjetas visibles, pero al ser un margen
   tan al limite (menos de 1px en este calculo) puede variar un poco segun el dispositivo
   exacto. */
/* 03/10/2026, pedido de Gus (segunda vuelta, mismo dia): "el espacio lateral izquierdo
   y derecho deberian ser el mismo que el espacio central" -- el margen lateral (20px,
   de ".container", sin tocar) y el gap central (153px, de arriba) quedaban muy
   desparejos. Gus pidio mantener el tamaño de tarjeta de la correccion anterior
   (~100px), asi que el espacio total disponible para repartir no cambia (193px =
   20+153+20 de antes) -- solo se reparte distinto: en 3 partes IGUALES de 64.33px en
   vez de 20/153/20. ".grid" (arriba) ya baja el gap central a 64.33px; esta regla le
   agrega a "#productGrid" los 44.33px que le faltan a cada lado (20px que ya pone
   ".container" + 44.33px de aqui = 64.33px, igual que el gap) -- justo a este grid, sin
   tocar ".container" (que tambien lo usa el header, el hero, etc. -- cambiarlo ahi
   afectaria todo el sitio, no solo el catalogo). Verificado con Playwright: ancho de
   tarjeta sigue en ~100px, igual que antes, ahora con los 3 espacios iguales. */
@media (max-width: 640px) {
  #productGrid {
    padding: 0 34.33px;
  }
}
/* 03/10/2026, pedido de Gus (con 3 capturas de su celular y el de su esposa, iPhone 15
   Pro -- 393px de ancho CSS): a ese ancho (y al de la mayoria de celulares actuales,
   ≈360-430px) estaba cayendo en este breakpoint de "1 columna", pensado originalmente
   para pantallas MUY angostas -- el resultado: en vez de 2 diseños por fila (como pide
   Gus) aparecía solo 1, enorme. Bajamos el límite a 300px (ningún celular real a la
   venta hoy es tan angosto; el iPhone SE de 1ra gen, el más chico que existe, mide
   320px) -- así TODOS los celulares actuales mantienen 2 columnas (regla de arriba,
   "@media max-width:640px"), y este breakpoint queda solo como red de seguridad para
   un hipotético dispositivo diminuto, sin afectar a nadie real. NO se toca nada más de
   esta regla de María (mismo "1fr"/"gap" si algún día sí aplica). */
@media (max-width: 300px) {
  .grid {
    grid-template-columns: 1fr;
    gap: 36px;
  }
}
.card {
  cursor: pointer;
  display: flex;
  flex-direction: column;
  height: 100%;
}
.card-media-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  min-height: 0;
}
/* 03/10/2026, pedido de Gus: "en algunos cuadros la distancia entre el nombre del diseño
   y el diseño es mayor que en otros (Moka vs Bossa), necesito que se respete un mismo
   estilo". Causa: ".grid{align-items:stretch}" (mas arriba) hace que todas las tarjetas
   de una misma fila midan lo mismo que la mas alta de esa fila (las piezas "square" y
   "rect" tienen proporciones distintas, 1:1 vs 4:5, asi que su alto natural difiere) --
   ese espacio de sobra lo absorbe ".card-media-wrap" (flex:1), y "align-items:center"
   (arriba) lo reparte MITAD arriba, MITAD abajo de la foto -- la mitad de ABAJO es
   exactamente la distancia hasta el nombre, y como esa mitad depende de cuanto mas alta
   sea la tarjeta vecina en esa fila especifica, el espacio al nombre termina siendo
   distinto fila por fila (y pieza por pieza) en vez de siempre igual. Solo en celular
   (la base de arriba, sin media query, no se toca -- en desktop el gap de 130px ya hace
   que esto casi no se note y Gus no reporto el problema ahi) se cambia a
   "align-items:flex-end": ahora la foto queda SIEMPRE pegada al fondo de su propio
   contenedor (y por lo tanto a una distancia fija del nombre, igual en todas las
   tarjetas) -- el espacio de sobra por la fila mas alta se va arriba de la foto en vez
   de abajo, donde no se nota ni se reporto como problema. */
@media (max-width: 640px) {
  .card-media-wrap {
    align-items: flex-end;
  }
}
.card-media {
  /* Fondo = --cream (igual que el resto de la página, y que la "versión 20"). Antes este
     fondo era del color del marco (#533b28) para disimular un margen residual que dejaba
     object-fit:contain cuando la proporción de una foto no calzaba exacta con el recuadro.
     Ya no hace falta: fixCatalogAspectRatios() (más abajo) ajusta ese recuadro para que
     calce exacto con cada foto, así que el margen desaparece por completo en vez de solo
     disimularse. Un fondo café aquí tenía además un efecto secundario no deseado en las
     piezas "square": junto con transform:scale(1.13) (ver abajo), hacía que el marco de
     esas piezas se viera más grueso que el de las piezas "rect" (el suavizado del navegador
     al escalar mezclaba el fondo café con el marco, ensanchándolo visualmente). Con fondo
     crema ese efecto desaparece y el marco mide igual en ambos formatos. */
  position: relative;
  overflow: hidden;
  background: var(--cream);
  border-radius: var(--radius);
  aspect-ratio: 4/5;
  width: 100%;
  flex-shrink: 0;
}
.card-media.square {
  aspect-ratio: 1/1;
  transform: scale(1.13);
  transform-origin: center;
}
/* 03/10/2026, segunda causa de la distancia-al-nombre desigual (ver el comentario grande
   junto a ".card-media-wrap" mas arriba, que arregla la causa PRINCIPAL): el
   "transform:scale(1.13)" de esta regla (centrado) agranda la caja de las piezas
   "square" para los 4 lados por igual, incluido hacia ABAJO -- un transform no mueve el
   layout (flexbox sigue viendo la caja de su tamaño original), pero SI mueve lo que se
   ve y lo que mide "getBoundingClientRect()", asi que, aun con ".card-media-wrap"
   pegando la caja original al fondo del contenedor (fix de arriba), lo que se VE de una
   pieza "square" sigue sobresaliendo por debajo de ese fondo -- las piezas "rect" (sin
   este transform) no tienen ese problema. "margin-bottom" (positivo) en un flex item
   con "align-items:flex-end" aleja su caja ese mismo tanto del fondo del contenedor --
   es decir, sube la caja original ANTES de que el transform la agrande hacia los 4
   lados, asi que, una vez agrandada, su borde de abajo cae exactamente donde el de una
   pieza "rect". Este valor depende del alto real de la tarjeta (es el 5.75% de
   "mediaH", la mitad del 13% del transform) asi que cambia cada vez que el tamaño de
   tarjeta cambia -- 6.5px cuando las tarjetas eran ~100px de ancho, ahora 7.47px con
   las tarjetas ~15% mas grandes (verificado con Playwright: los 2 formatos vuelven a
   dar el mismo gap hasta el nombre). SOLO en celular -- mismo criterio (compensar el
   transform) que ya usan ".card-media.square .card-art-frame" y hermanas mas abajo
   para el grosor del marco. */
@media (max-width: 640px) {
  .card-media.square {
    margin-bottom: 7.47px;
  }
}
.card-media img {
  /* object-fit:contain (no "cover") para garantizar que nunca se recorte ni un pixel del
     diseño, incluso cuando la proporción real de la foto no calza exacto con el recuadro
     cuadrado/rectangular de la tarjeta (una discrepancia mínima con "cover" sí recortaba
     un poco el borde del diseño al mirar de cerca). */
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition:
    transform 0.55s cubic-bezier(0.2, 0.7, 0.2, 1),
    opacity 0.45s ease;
  /* 25/09/2026, pista decisiva de Gus: "cuando haces zoom a los diseños del catálogo... no
     sucede ese desenfoque... necesito que eso se replique en el tamaño normal de la web".
     Esto apunta a una causa totalmente distinta de todo lo probado hasta ahora (secuencia
     de opacidad, instantáneo, etc.): estas fotos están MUY reducidas en la tarjeta del
     catálogo (una foto de 1200x1200px real mostrada a ~211x211px, ~5.7x más chica) -- ese
     nivel de reducción es justo el que hace notorio un comportamiento conocido de Chromium
     al usar "opacity" para mostrar/ocultar una imagen: la primera vez que una imagen pasa a
     ser visible (opacity>0) el navegador puede pintarla con un remuestreo rápido/tosco
     antes de terminar de generar la versión bien remuestreada (mipmap) un instante después
     -- eso se ve exactamente como "aparece y se reenfoca", SIN que tenga nada que ver con
     la superposición de dos fotos (por eso ninguna de las correcciones anteriores, todas
     sobre el TIEMPO de la transición, lo solucionó) y explica por qué al hacer zoom (menos
     reducción de tamaño real) el efecto se nota mucho menos. "will-change:opacity" fuerza
     al navegador a preparar la imagen en su propia capa, ya remuestreada, desde que carga
     la página -- no recién en el primer instante en que se vuelve visible -- así no hay
     ningún remuestreo de apuro que hacer al pasar el cursor. */
  will-change: opacity;
}
/* El marco va en el padding del contenedor (un anillo alrededor de la imagen), nunca
   encima de ella, para no tapar ni un solo pixel del diseño en los bordes. */
/* 25/09/2026, corrección de raíz (primer intento, INSUFICIENTE según Gus) pedida por Gus
   ("cursor sobre el hover y la imagen... se mueve, como que se desenfoca" -- reportado
   sobre Mosa y luego confirmado también en Drift, Offset I, Offset II, Moka, Marlo II,
   Cala I y Cala II, es decir, en TODAS las piezas que tienen foto de ambiente): la causa
   NO era el zoom de 4.5% (ya quitado el 25/09/2026 más arriba, verificado con
   "transform:none" en las 61 piezas) sino el propio cruce de opacidad hacia la foto de
   ambiente ("card-img-room"). Se corrigió primero haciendo el cruce SECUENCIAL en vez de
   simultáneo (foto plana se desvanece del todo, y RECIÉN DESPUÉS aparece la foto de
   ambiente, nunca superpuestas) -- verificado sin superposición en 8 piezas, en la
   entrada, en la salida, y con interrupciones a mitad de camino. Gus reportó que el
   problema seguía sintiéndose igual, así que se probó quitar la animación por completo
   (transition:none, cambio instantáneo) como siguiente paso.
   25/09/2026 (segunda vuelta, mismo día): Gus pidió expresamente que el hover SÍ tenga
   animación ("una animación que no impida que se vea bien... antes lo hemos estado
   haciendo") -- el cambio instantáneo no era lo que quería, sólo el "se mueve" sin
   corregir. Se vuelve al cruce SECUENCIAL sin superposición (ya verificado exhaustivamente
   antes de quitarlo): la foto plana se desvanece del todo (.2s, sin demora) y RECIÉN
   DESPUÉS aparece la foto de ambiente (.2s, con .2s de demora) -- nunca ambas visibles al
   mismo tiempo, en ningún punto de la animación, en ninguna dirección. Técnica: la
   "demora" (transition-delay) se define distinto en la regla base y en la regla ":hover"
   de cada elemento, así el navegador usa un valor al ENTRAR (base -> hover) y el opuesto
   al SALIR (hover -> base) -- ver "transition" repetido en ambas reglas de
   ".card-img-room"/".card-view-label"/".card-img-base" más abajo.
   25/09/2026 (tercera vuelta, corrección de un error propio detectado con Playwright
   ANTES de entregar): un navegador de CSS Transitions usa, para decidir "duration"/
   "delay", el valor de "transition" definido en el estado de LLEGADA del cambio (el
   `:hover` al entrar, la regla normal al salir) -- NO el estado de partida, que es lo que
   se había asumido al escribir la primera versión de este comentario, dejando los cuatro
   valores de demora exactamente invertidos (verificado con muestreo cuadro a cuadro: la
   foto plana se quedaba esperando .2s ANTES de empezar a desvanecerse, superponiéndose
   de lleno con la foto de ambiente ya visible). Los valores de abajo ya están corregidos:
   ".card-art-frame" (foto plana, estado normal/salida) lleva la DEMORA (porque al salir
   del hover, la foto plana debe esperar a que la de ambiente termine de desvanecerse);
   ".card:hover .card-img-base" (foto plana, estado hover/entrada) NO lleva demora (debe
   empezar a desvanecerse de inmediato al entrar el cursor). Mismo criterio, invertido,
   para ".card-img-room"/".card-view-label". */
/* 28/09/2026, pedido de Gus: "al hacer hover se pone blanco y luego muestra la foto del
   diseño en el fondo... me gustaría que la transición sea como la de la página de
   https://layers.pe/." Inspeccionado ese sitio (mismo mecanismo de "tarjeta con 2
   fotos" que usamos acá): ahí las dos fotos se funden SIMULTÁNEAMENTE, sin ninguna
   demora, la misma duración (.6s) tanto para la que desaparece como para la que
   aparece -- por eso nunca se ve un hueco. El "se pone blanco" que reportó Gus es
   justo lo contrario: el cruce SECUENCIAL de acá (foto plana desvanecida del todo
   ANTES de que la de ambiente empiece a aparecer, con demora entre una y otra --
   ver el comentario grande de arriba, 25/09/2026) deja, durante esa demora, un
   instante donde NINGUNA de las dos fotos es opaca -- ahí se ve el fondo de la
   tarjeta (blanco/crema) por debajo. Esa técnica secuencial se había elegido en su
   momento para evitar ver las dos fotos superpuestas -- pero la razón real de fondo
   de aquel problema (el zoom de hover, "transform:scale") ya se quitó por completo
   más abajo (".card:hover .card-media img:not(.card-img-room){transform:none}"),
   así que ya no hace falta evitar la superposición con una demora: alcanza con
   fundir ambas fotos al mismo tiempo, como en layers.pe. Se quita toda demora
   ("transition-delay") de las tres reglas de esta secuencia (foto plana, foto de
   ambiente, etiqueta de texto) y se iguala su duración a .6s en las dos direcciones
   (entrada y salida) -- ver ".card:hover .card-img-room"/".card:hover
   .card-view-label"/".card:hover .card-img-base" más abajo, mismo cambio. No se
   toca ningún zoom/transform: sigue exactamente igual que ya estaba. */
.card-art-frame {
  position: absolute;
  inset: 0;
  padding: var(--cat-frame-rect, 4.26525px);
  box-sizing: border-box;
  transition: opacity 0.6s ease;
}
/* 12/09/2026: ver nota de ".card-media.square.no-frame .card-art-frame" más abajo --
   esta es la misma excepción (piezas con marco ya en la foto) para el caso "rect". */
.card-art-frame.no-frame {
  padding: 0;
}
.card-frame-strip {
  position: absolute;
  background-repeat: repeat;
  background-position: center;
  z-index: 1;
}
.card-frame-strip.cfs-top {
  top: 0;
  left: 0;
  right: 0;
  height: var(--cat-frame-rect, 4.26525px);
  background-size: 50px auto;
}
.card-frame-strip.cfs-bottom {
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--cat-frame-rect, 4.26525px);
  background-size: 50px auto;
}
.card-frame-strip.cfs-left {
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--cat-frame-rect, 4.26525px);
  background-size: auto 50px;
}
.card-frame-strip.cfs-right {
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--cat-frame-rect, 4.26525px);
  background-size: auto 50px;
}
.card-frame-corner {
  position: absolute;
  width: var(--cat-frame-rect, 4.26525px);
  height: var(--cat-frame-rect, 4.26525px);
  z-index: 2;
  overflow: hidden;
}
.card-frame-corner.cfc-tl {
  top: 0;
  left: 0;
}
.card-frame-corner.cfc-tr {
  top: 0;
  right: 0;
}
.card-frame-corner.cfc-bl {
  bottom: 0;
  left: 0;
}
.card-frame-corner.cfc-br {
  bottom: 0;
  right: 0;
}
/* Encuentro de esquina a 45° (inglete): cada esquina se divide en dos triángulos —
   uno continúa la veta de la franja horizontal, el otro la de la franja vertical —
   igual que el corte a inglete de un marco real, en vez de un parche de color plano. */
.card-frame-corner::before,
.card-frame-corner::after,
.frame-corner::before,
.frame-corner::after,
.mini-frame-corner::before,
.mini-frame-corner::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--fc-color);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  /* Suaviza el encuentro a 45 grados entre las dos vetas (horizontal/vertical) que se
     unen en cada esquina, a pedido de Gus el 07/09/2026 -- sigue siendo un corte a 45°,
     solo que menos marcado/gráfico. */
  filter: blur(0.5px);
}
.card-frame-corner::before,
.frame-corner::before,
.mini-frame-corner::before {
  background-image: var(--fc-h);
  clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.card-frame-corner::after,
.frame-corner::after,
.mini-frame-corner::after {
  background-image: var(--fc-v);
  clip-path: polygon(0 0, 0 100%, 100% 100%);
}
.card-frame-corner.cfc-tl,
.frame-corner.fc-tl,
.mini-frame-corner.mfc-tl {
  transform: none;
}
.card-frame-corner.cfc-tr,
.frame-corner.fc-tr,
.mini-frame-corner.mfc-tr {
  transform: scaleX(-1);
}
.card-frame-corner.cfc-bl,
.frame-corner.fc-bl,
.mini-frame-corner.mfc-bl {
  transform: scaleY(-1);
}
.card-frame-corner.cfc-br,
.frame-corner.fc-br,
.mini-frame-corner.mfc-br {
  transform: scale(-1, -1);
}
/* .card-media.square se agranda 13% con transform:scale, así que el marco (dibujado
   antes de ese transform, junto con el padding que lo aloja) debe reducirse en la misma
   proporción para que, ya escalado, se vea del mismo grosor real que en las tarjetas
   rect (que no tienen ese transform). 3.77px × 1.13 ≈ 4.26525px (grosor base, +10% sobre
   el grosor anterior de 3.8775px, a pedido de Gus el 05/09/2026). Los valores reales se
   fijan como variables CSS (--cat-frame-rect / --cat-frame-square) calculadas en JS por
   snapCatalogFrameThickness() para que el grosor renderizado en pantalla sea un número
   entero de píxeles físicos del dispositivo de cada visitante — ver esa función para el
   porqué (evita que un lado del marco se redondee más grueso que el opuesto). */
.card-media.square .card-art-frame {
  padding: var(--cat-frame-square, 3.77px);
}
.card-media.square .card-frame-strip.cfs-top,
.card-media.square .card-frame-strip.cfs-bottom {
  height: var(--cat-frame-square, 3.77px);
}
.card-media.square .card-frame-strip.cfs-left,
.card-media.square .card-frame-strip.cfs-right {
  width: var(--cat-frame-square, 3.77px);
}
.card-media.square .card-frame-corner {
  width: var(--cat-frame-square, 3.77px);
  height: var(--cat-frame-square, 3.77px);
}
/* 12/09/2026, pedido de Gus: piezas con marco ya incluido en la foto misma
   (p.fixedFrame en cardTemplate/renderModal, "segunda colección" en adelante)
   no llevan el anillo de marco que esta web dibuja encima -- padding:0 quita
   el hueco que ese anillo reservaría (ver fixCatalogAspectRatios/
   computeImageTargets, que también saben de este caso). */
.card-media.square.no-frame .card-art-frame {
  padding: 0;
}
.card-media img.card-img-room {
  /* object-fit:cover (no "contain"): esta es la foto de ambiente que aparece al pasar el
     cursor, no el diseño de la pieza en sí (esa nunca se recorta, ver ".card-media img" más
     arriba) -- aquí sí conviene llenar todo el recuadro sin dejar franjas de fondo, igual
     en formato "square" y "rect". Antes usaba "contain" y en piezas "rect" (cuya foto de
     ambiente no calza 4:5 exacto) dejaba franjas color crema arriba y abajo; en "square" no
     se notaba porque esas fotos sí calzan casi exacto con 1:1. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--cream);
  /* 28/09/2026: ver el comentario grande junto a ".card-art-frame" (pedido de Gus, "como
     la transición de layers.pe") -- fundido simultáneo con la foto plana, sin demora,
     misma duración (.6s) en las dos direcciones. */
  opacity: 0;
  transition: opacity 0.6s ease;
}
.card-view-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  text-align: center;
  background: rgba(210, 205, 198, 0.72);
  color: #fff;
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.08rem;
  letter-spacing: 0.02em;
  padding: 10px 0;
  /* 28/09/2026: mismo criterio que ".card-img-room" -- ver el comentario grande junto a
     ".card-art-frame". */
  opacity: 0;
  transition: opacity 0.6s ease;
  pointer-events: none;
}
/* 25/09/2026, pedido de Gus: "cuando ponga el cursor sobre el hover, de forma un poquito
   más fluida y lenta, deje aparecer la imagen del fondo. Solamente un poco más." Se
   alarga SOLO la duración del desvanecimiento de ENTRADA de la foto de ambiente (de .2s a
   .3s) -- la demora antes de empezar (.2s, sin tocar) sigue igual, así como todo lo
   demás de esta secuencia (ver el comentario grande junto a ".card-art-frame" para el
   porqué de cada demora): la foto plana sigue desapareciendo exactamente igual que
   antes (sin este cambio, ver ".card-img-base" más abajo), sólo que una vez que la foto
   de ambiente empieza a aparecer, tarda un poco más en llegar a su opacidad final. Se
   aplica el mismo cambio a ".card-view-label" (el texto que aparece encima) porque
   ambos estaban sincronizados a propósito con la misma duración/demora -- cambiar sólo
   uno los hubiera desincronizado. */
.card:hover .card-img-room {
  opacity: 1;
  transition: opacity 0.6s ease;
}
.card:hover .card-view-label {
  opacity: 1;
  transition: opacity 0.6s ease;
}
.card:hover .card-img-base {
  opacity: 0;
  transition: opacity 0.6s ease;
}
/* Corrección 24/09/2026, pedido de Gus: la foto de ambiente (".card-img-room") no debe
   tener ningún movimiento propio -- tiene que "aparecer y quedarse igual" (su queja: "el
   cuadro aparece y luego se reenfoca... toda la imagen se mueve"). La causa encontrada:
   esta regla aplicaba el mismo zoom (transform:scale) a TODAS las imágenes de la tarjeta al
   pasar el cursor, incluida ".card-img-room" -- pero esa imagen tiene su propia regla más
   específica más arriba que solo define "transition:opacity" (sin "transform"), así que su
   zoom no se animaba de forma suave como el de la imagen base: saltaba de golpe, sin
   transición, al mismo tiempo que se desvanecía hacia adentro. Ese salto sin suavizado,
   ocurriendo justo mientras la foto se hace visible, es lo que se percibía como "aparece y
   se reenfoca". Se excluye ".card-img-room" de este zoom (queda del tamaño real, sin
   ningún transform).
   25/09/2026, corrección DE RAÍZ (reportado por Gus repetidas veces -- Mosa, Moka,
   Marlo II, y "en varios sigue sucediendo", con síntomas de "se mueve", "se descuadra" y
   "aparecen bordes blancos"): el intento de ir agregando, uno por uno, los ids de los
   diseños afectados a una lista de excepción (CARD_NO_HOVER_ZOOM_PRODUCT_IDS) fue un
   error de enfoque -- verificando los datos de las 61 piezas, TODAS usan una foto real
   ya enmarcada (fixedFrame:true, no solo un grupo chico), y CUALQUIERA de esas 61 fotos
   puede mostrar moiré/bordes al reescalarse 4.5% durante la transición del hover,
   dependiendo del detalle fino de su diseño y de cómo calce su proporción real con el
   recuadro cuadrado/rectangular (una foto que no calza exacto ya deja una franja del
   fondo crema visible por "object-fit:contain" -- al agrandar la imagen 4.5% esa franja
   se mueve/aparece de golpe, y eso es el "borde blanco" reportado). No hay forma
   confiable de listar de antemano qué diseños sí y cuáles no lo van a mostrar -- por
   eso Gus seguía encontrando más casos cada vez que revisaba otro diseño distinto. Se
   quita el zoom de esta foto también (iguales que ya se le quitó a ".card-img-room"
   arriba): la tarjeta sigue mostrando el mismo fundido de siempre hacia la foto de
   ambiente al pasar el cursor, solo que ninguna de las dos fotos cambia de tamaño, así
   que este tipo de problema no puede volver a aparecer en ningún diseño, actual o
   futuro. La lista `CARD_NO_HOVER_ZOOM_PRODUCT_IDS` y su uso en `cardTemplate()` quedan
   sin efecto (ya no hay zoom del que excluir a nadie) pero se dejan en el código sin
   quitar, documentados, por si en el futuro se quisiera reactivar el zoom solo para
   una lista corta de diseños ya confirmados como seguros. */
.card:hover .card-media img:not(.card-img-room) {
  transform: none;
}
.card-num {
  position: absolute;
  top: 46px;
  left: 12px;
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 0.864rem;
  color: #fff;
  background: rgba(42, 36, 34, 0.35);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  padding: 4px 9px;
  border-radius: 20px;
  letter-spacing: 0.04em;
  opacity: 0;
  transform: translateY(-4px);
  transition: all 0.3s;
}
.card:hover .card-num {
  opacity: 1;
  transform: translateY(0);
}
.new-in-badge {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 2;
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 0.864rem;
  letter-spacing: 0.06em;
  color: #fff;
  background: var(--burgundy);
  padding: 5px 10px;
  border-radius: 20px;
}
/* 03/10/2026, pedido de Gus: achicar un 25% el badge "New in" de los cuadros nuevos, SOLO
   en celular (regla de arriba, de Maria, queda intacta para desktop). -25% sobre
   font-size: 0.864rem * 0.75 = 0.648rem. -25% sobre el padding: 5px/10px * 0.75 =
   3.75px/7.5px. No se toca "letter-spacing" (ya esta en "em", escala solo junto con el
   font-size) ni la posicion ("top"/"left": 12px, Gus pidio reducir el TAMANO, no moverlo)
   ni "border-radius" (20px sigue dando una pildora totalmente redondeada con el badge mas
   chico, no hace falta bajarlo). */
@media (max-width: 640px) {
  .new-in-badge {
    font-size: 0.648rem;
    padding: 3.75px 7.5px;
  }
}
.card-badges {
  position: absolute;
  top: 12px;
  right: 12px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  z-index: 2;
}
.tag-badge {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: 20px;
  white-space: nowrap;
}
.tag-bestseller {
  background: var(--burgundy);
  color: #fff;
}
.card-info {
  padding-top: 14px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.card-name {
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
  font-size: 1.224rem;
}
.card-price {
  font-family: "EB Garamond", serif;
  font-weight: 500;
  font-size: 1.032rem;
  color: var(--muted);
}
/* 03/10/2026, pedido de Gus: con el diseño 40% mas chico en celular, el nombre y el
   precio (que no se habian tocado) quedaron desproporcionados -- letra grande para una
   tarjeta tan angosta, y como ".card-info" los pone LADO A LADO ("justify-content:
   space-between"), un nombre largo + el precio no entraban en una sola linea y se
   envolvian de forma apretada ("pegados"). Dos cambios, SOLO en celular (la regla base
   de arriba, sin media query, sigue igual en desktop):
   1) Letra 40% mas chica, mismo criterio que la foto: 1.224rem -> 0.7344rem (nombre),
      1.032rem -> 0.6192rem (precio).
   2) ".card-info" pasa de fila (nombre izquierda, precio derecha) a columna (nombre
      arriba, precio abajo, pedido explicito de Gus) -- con una tarjeta tan angosta ya
      no hay espacio para los dos lado a lado sin que se corten o se amontonen; en
      columna cada uno tiene todo el ancho de la tarjeta para si solo, sin envolverse. */
@media (max-width: 640px) {
  /* 03/10/2026, pedido de Gus (tercera vuelta, mismo dia): "pone el nombre y el precio
     alineados al centro de cada diseño" -- "align-items:center" centra el bloque
     nombre/precio respecto al ancho de la tarjeta (antes "flex-start" los pegaba a la
     izquierda), y "text-align:center" en cada uno asegura que, si el nombre de un
     diseño es largo y se envuelve en 2 lineas, ambas lineas queden centradas entre si
     (no solo el bloque completo centrado con el texto interno pegado a la izquierda). */
  /* 03/10/2026, pedido de Gus (cuarta vuelta, mismo dia): "acerca un 30% el nombre y el
     precio (ambos como un todo, como bloque) a los cuadros" -- el espacio entre la foto
     y el bloque nombre+precio es ".card-info{padding-top:14px}" (regla base, sin media
     query, se deja igual en desktop). Se acerca un 30% SOLO en celular: 14px * 0.7 =
     9.8px. No se toca el espacio interno entre nombre y precio (gap:2px, ya bien junto,
     Gus pidio acercar el BLOQUE a la foto, no el nombre al precio). */
  .card-info {
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding-top: 7.84px;
  }
  .card-name {
    font-size: 0.84456rem;
    text-align: center;
  }
  .card-price {
    font-size: 0.71208rem;
    text-align: center;
  }
}

/* ---------- Contact CTA ---------- */
.contact-cta {
  padding: 135px 0;
  text-align: center;
}
.contact-cta h2 {
  font-family: "Cormorant Garamond", serif;
  font-size: clamp(1.92rem, 3.6vw, 2.52rem);
  font-weight: 700;
  margin-bottom: 14px;
}
.contact-cta p {
  color: var(--muted);
  max-width: 480px;
  margin: 0 auto 30px;
}
.contact-buttons {
  display: flex;
  gap: 16px;
  justify-content: center;
  flex-wrap: wrap;
}
.contact-free-note {
  font-size: 0.936rem;
  color: var(--muted);
  text-align: center;
}
.btn-outline {
  border: 1.5px solid var(--ink);
  padding: 13px 26px;
  border-radius: var(--radius);
  font-weight: 700;
  font-size: 1.08rem;
}
.btn-outline:hover {
  background: var(--ink);
  color: #fff;
}

/* ---------- Footer ---------- */
footer {
  background: var(--burgundy-deep);
  color: #f3e9e5;
  padding: 64px 0 28px;
}
footer .container {
  max-width: 1320px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  gap: 40px;
  padding-bottom: 40px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}
footer .wordmark {
  color: #fff;
}
.footer-tag {
  color: rgba(243, 233, 229, 0.65);
  font-size: 1.056rem;
  margin-top: 14px;
  max-width: 280px;
  line-height: 1.6;
}
.footer-col h4 {
  font-size: 0.9rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone);
  margin-bottom: 16px;
}
.footer-col a,
.footer-col div {
  display: block;
  font-size: 1.056rem;
  color: rgba(243, 233, 229, 0.85);
  margin-bottom: 10px;
}
.footer-col a:hover {
  color: #fff;
}
.footer-col-end {
  text-align: right;
}
.footer-col-center {
  text-align: center;
  white-space: nowrap;
}
.footer-bottom {
  padding-top: 24px;
  font-size: 0.936rem;
  color: rgba(243, 233, 229, 0.55);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 40px;
}
.footer-bottom-left {
  text-align: left;
}
.footer-bottom-center {
  justify-self: center;
  text-align: center;
  white-space: nowrap;
}
.footer-bottom-right {
  justify-self: end;
  text-align: right;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
.footer-payment-badges {
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 5px;
}
@media (max-width: 640px) {
  .footer-bottom {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: 14px;
  }
  .footer-bottom-left,
  .footer-bottom-right,
  .footer-bottom-center {
    justify-self: center;
  }
}
.footer-terms-link {
  font-size: 0.936rem;
  color: rgba(243, 233, 229, 0.75);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.footer-secure-note {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.936rem;
  color: rgba(243, 233, 229, 0.55);
}
.footer-terms-link:hover {
  color: #fff;
}
@media (max-width: 700px) {
  .footer-grid {
    grid-template-columns: 1fr;
    gap: 30px;
  }
  .footer-col-end {
    text-align: left;
  }
  .footer-col-center {
    text-align: left;
    white-space: normal;
  }
}

/* ---------- Backdrop ---------- */
.backdrop {
  position: fixed;
  inset: 0;
  background: rgba(42, 36, 34, 0.45);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
  z-index: 50;
}
.backdrop.show {
  opacity: 1;
  pointer-events: auto;
}

/* ---------- Cart Drawer ---------- */
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  height: 100%;
  width: 420px;
  max-width: 92vw;
  background: var(--cream);
  z-index: 60;
  transform: translateX(100%);
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
  display: flex;
  flex-direction: column;
  box-shadow: -20px 0 40px rgba(0, 0, 0, 0.15);
}
.drawer.open {
  transform: translateX(0);
}
.drawer-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24px 26px;
  border-bottom: 1px solid var(--line);
}
.drawer-head h3 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.38rem;
}
.close-x {
  font-size: 1.68rem;
  line-height: 1;
  padding: 4px;
}
.drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: 20px 26px;
}
.cart-item {
  display: flex;
  gap: 14px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
}
.cart-item-thumb {
  width: 74px;
  height: 74px;
  flex-shrink: 0;
  border-radius: var(--radius);
  padding: 5px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--cream);
}
.frame-dot {
  display: inline-block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  margin-right: 5px;
  vertical-align: middle;
  border: 1px solid rgba(0, 0, 0, 0.12);
  background-repeat: repeat;
  background-size: 40px auto;
  background-position: center;
}
/* Miniatura del cuadro con el marco realmente elegido (roble/nogal/castaña/negro/humo),
   reutilizando el mismo criterio de "marco en anillo, nunca encima de la imagen": la
   imagen sin marco (canvasImage) va dentro del padding, y las franjas del marco caen
   exactamente en ese padding. Se usa tanto en el carrito como en el resumen de compra. */
.mini-art-frame {
  position: relative;
  height: 100%;
  width: auto;
  box-sizing: border-box;
  padding: var(--mini-frame-pad, 2px);
}
/* 12/09/2026: piezas con marco ya incluido en la foto (c.fixedFrame, ver renderCart/
   checkout) no llevan el mini-anillo de marco -- padding:0 (fixMiniFrameAspect()
   también sabe de este caso, más abajo). */
.mini-art-frame.no-frame {
  padding: 0;
}
.mini-art-frame.mf-square {
  aspect-ratio: 1/1;
}
.mini-art-frame.mf-rect {
  aspect-ratio: 4/5;
}
.mini-art-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
/* El grosor de este marco (2px) es tan delgado que dejar una dimensión del
   background-size en "auto" (proporcional al tamaño real de la textura) hacía que el
   navegador escale y luego recorte una imagen mucho más alta/ancha que el propio recuadro
   de 2px -- eso generaba una franja horizontal (arriba/abajo) visualmente más gruesa que
   la vertical (izquierda/derecha), aunque el CSS pedía 2px en las cuatro. Reportado por
   Gus el 08/09/2026: "Los bordes no se ven uniformes, se engrosan y se adelgazan por
   partes", con capturas del carrito ("Bossa" y "Romy"). Fijar las DOS dimensiones del
   background-size (en vez de dejar una en "auto") hace que el navegador ya no necesite
   escalar-y-recortar una imagen de tamaño distinto al del recuadro, y el grosor sale
   idéntico en los cuatro lados.
   Segunda corrección relacionada, el mismo día: aun con lo anterior, Gus seguía viendo un
   lado más grueso que otro (confirmado midiendo su propia captura: 4px arriba/izquierda
   contra 6px abajo/derecha). Esto era el mismo problema de redondeo de píxeles fraccionarios
   que ya se había resuelto para el marco del catálogo (ver CATALOG_FRAME_TARGET_RECT /
   snapCatalogFrameThickness() más abajo): "2px" es un valor fijo que, según la densidad de
   píxeles real de la pantalla y la posición fraccionaria exacta del recuadro, no siempre
   redondea igual en los cuatro lados. Ahora el grosor sale de la variable CSS
   `--mini-frame-pad`, calculada por snapMiniFrameThickness() (ver más abajo) para que
   siempre sea un número entero de píxeles físicos -- el padding, el grosor de las franjas
   y el de las esquinas usan todos la misma variable, así se mantienen sincronizados. */
.mini-frame-strip {
  position: absolute;
  background-repeat: repeat;
  background-position: center;
}
.mini-frame-strip.mfs-top {
  top: 0;
  left: 0;
  right: 0;
  height: var(--mini-frame-pad, 2px);
  background-size: 30px var(--mini-frame-pad, 2px);
}
.mini-frame-strip.mfs-bottom {
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--mini-frame-pad, 2px);
  background-size: 30px var(--mini-frame-pad, 2px);
}
.mini-frame-strip.mfs-left {
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--mini-frame-pad, 2px);
  background-size: var(--mini-frame-pad, 2px) 30px;
}
.mini-frame-strip.mfs-right {
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--mini-frame-pad, 2px);
  background-size: var(--mini-frame-pad, 2px) 30px;
}
.mini-frame-corner {
  position: absolute;
  width: var(--mini-frame-pad, 2px);
  height: var(--mini-frame-pad, 2px);
  overflow: hidden;
}
.mini-frame-corner.mfc-tl {
  top: 0;
  left: 0;
}
.mini-frame-corner.mfc-tr {
  top: 0;
  right: 0;
}
.mini-frame-corner.mfc-bl {
  bottom: 0;
  left: 0;
}
.mini-frame-corner.mfc-br {
  bottom: 0;
  right: 0;
}
.cart-item-info {
  flex: 1;
}
.cart-item-info .ci-name {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.104rem;
}
.cart-item-info .ci-meta {
  font-size: 0.936rem;
  color: var(--muted);
  margin: 3px 0 8px;
  line-height: 1.5;
}
.ci-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.qty-ctrl {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 3px 10px;
}
.qty-ctrl button {
  font-size: 1.14rem;
  width: 16px;
}
/* 15/09/2026, pedido de Gus: el número de cantidad, en la ficha de producto, se
   veía con el "1" de Cormorant Garamond (fuente del resto del sitio), que a este
   tamaño se confunde con una "I" mayúscula -- mismo motivo por el que Gus ya había
   pedido, el 14/09/2026, que los números de tamaño (.pill) y el precio
   (.price-line .big) usaran EB Garamond en vez de Cormorant Garamond. Se aplica
   aquí el mismo criterio, solo al número (no a los botones −/+, que se quedan
   como estaban), para que quede en el mismo formato que esos otros números. */
#qtyVal {
  font-family: "EB Garamond", serif;
}
.ci-price {
  font-family: "EB Garamond", serif;
  font-size: 1.056rem;
}
.ci-remove {
  font-size: 0.9rem;
  color: var(--muted);
  text-decoration: underline;
  margin-top: 6px;
}
.empty-cart {
  color: var(--muted);
  font-size: 1.104rem;
  text-align: center;
  padding: 60px 0;
}
.drawer-foot {
  padding: 22px 26px;
  border-top: 1px solid var(--line);
}
.subtotal-row {
  display: flex;
  justify-content: space-between;
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.32rem;
  margin-bottom: 16px;
}
/* poniendo numeros con EB */
.subtotal-row > span:nth-child(2),
.ci-meta,
.qty-ctrl,
.os-item,
.os-line > span:nth-child(2),
.os-total > span:nth-child(2),
#ckPhone,
.micro-note.delivery-note,
.pay-details,
.checkout-wrap .btn-primary.btn-block {
  font-family: "EB Garamond", serif;
  font-weight: 400;
}
.btn-block {
  width: 100%;
  text-align: center;
}
.modal-footer .btn-primary.cart-btn-compact {
  width: 50%;
  padding-top: 4.35px;
  padding-bottom: 4.35px;
  margin-bottom: 15px;
}

/* ---------- Product Modal ---------- */
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 70;
  overflow-y: auto;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}
.modal.open {
  opacity: 1;
  pointer-events: auto;
}
.modal-inner {
  width: 100%;
  height: 100vh;
  margin: 0;
  background: var(--paper);
  border-radius: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  overflow: hidden;
  position: relative;
  box-shadow: none;
}
.modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 5;
  background: var(--paper);
  border-radius: 50%;
  width: 36px;
  height: 36px;
  font-size: 1.44rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* 16/09/2026, pedido de Gus: "Volver" (arriba a la izquierda) baja para que
   su borde superior quede a la misma altura que el borde superior del marco
   de la foto principal (ambos ahora a 44px, ver .modal-media de arriba). El
   boton "X" (.modal-close) no se toca, se queda en 16px como antes. */
/* 16/09/2026, pedido de Gus (segunda vuelta): bajar aun mas la foto principal, 'Volver'
   y el titulo del diseno -- ya no alineados a 44px, sino a 140px, marcado por Gus con una
   linea de referencia sobre una captura de "Noma". Los tres (marco de la foto, 'Volver',
   y el titulo en la columna de al lado) se mueven juntos al mismo valor, preservando la
   misma relacion que ya existia (imagen y titulo a la misma altura). */
/* 16/09/2026, pedido de Gus (correccion): vuelve a subir -- ya no comparte altura con
   la imagen/titulo (eso se desacopla, ver ".modal-media"/".modal-details" mas abajo).
   Ahora "Volver" se alinea con el CENTRO vertical del boton "X" (cerrar), que esta fijo
   en top:16px, height:36px (centro en 34px) -- "Volver" mide 37px de alto, asi que con
   top:16px su propio centro cae en ~34.5px, prácticamente igual. */
.modal-back-link {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 5;
  background: var(--paper);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* 16/09/2026, pedido de Gus: bajar la foto principal para que su borde
   superior quede a la misma altura que el titulo del diseño (ej. "Nora") en
   la columna de al lado -- se sube el padding-top del contenedor de fotos de
   28px a 44px (deja el resto de paddings igual). */
/* 16/09/2026, pedido de Gus (segunda correccion): el intento anterior (16px, igual que
   "Volver") hizo que la foto ganara mucha mas altura disponible de la que tenia antes
   (perdio 124px de padding de golpe), y eso infla el calculo de computeImageTargets
   (bigH/roomBoxH dependen directo de este alto disponible) -- resultado: fotos
   "gigantescas", muy por encima de lo ya establecido. Gus marco con una linea a mano
   alzada, DIRECTAMENTE sobre la pagina ya renderizada (a 1872x1012, calibrado pixel a
   pixel), que la parte de arriba del marco debe bajar hasta ahi (~108px) -- ya NO a la
   misma altura que "Volver"/"X" (eso quedó mal entendido la vuelta anterior: "Volver
   parece estar bien ubicado", dijo Gus, o sea se queda en 16px, y es la FOTO la que
   tiene que bajar por separado, dejando de estar superpuesta con "Volver" en su
   esquina). El titulo (".modal-details" mas abajo) baja al mismo valor para seguir
   coincidiendo en altura con la foto, como ya estaba. */
/* 16/09/2026 (tercera correccion): Gus midio con una regla sobre pantalla (ej. "Noma",
   "Trama") y confirmo que el titulo del diseño quedaba un poco por encima de la parte
   superior del marco -- el "hueco" de -10px en el margin-top del h2 (ver ".modal-details
   h2" mas abajo) no alcanzaba a compensar del todo la diferencia real entre paddings.
   Pidio subir la foto (no bajar el titulo) hasta que ambas partes superiores coincidan
   exacto. El titulo visible queda a 108-10=98px (padding-top de ".modal-details" menos
   su propio margin-top negativo); bajando aqui el padding-top de 108 a 98, el marco de
   la foto queda tambien exacto en 98px, igualando al titulo sin tocar ".modal-details". */
/* 16/09/2026, reportado por Gus: al pasar de una foto a otra, la barra de scroll
   vertical de esta columna (".modal-media") aparecía/desaparecía A MITAD de la
   animación (justo cuando el contenido total pasa de necesitar scroll a no
   necesitarlo, o al revés, según el producto) -- ese cambio puntual de "hay barra
   / no hay barra" angosta o ensancha el área visible de golpe, y como las fotos
   están centradas dentro de esa área, se ven "saltar"/vibrar un instante. Gus pidió
   dejar la barra siempre presente para que nunca aparezca de la nada. "overflow-y:
   scroll" (en vez de "auto") reserva el espacio de la barra todo el tiempo, incluso
   cuando no hace falta scrollear -- así el ancho útil de la columna no cambia nunca
   durante la transición. */
/* 17/09/2026 (novena corrección), pedido de Gus (pantallazo con recuadro rojo + línea
   de referencia): quiere que TODO el bloque de arriba (foto principal + título +
   descripción + "Pieza hermana" + "Tamaño" + "Acabado de marco") suba en conjunto,
   como una unidad rígida, hasta que la parte de arriba de la foto/título toque esa
   línea -- sin mover nada de lo que queda por debajo (la miniatura de ambiente,
   "Cantidad", "Total", el botón, "Detalles"). Medido contra su pantallazo: 35px.
   padding-top pasa de 98px a 63px acá y de 108px a 73px en ".modal-details" (mismo
   valor en las dos columnas, para que sigan empezando a la misma altura como ya
   estaban). Para que la miniatura de ambiente NO se mueva con este cambio (queda
   fuera del recuadro que marcó Gus), se le suma esos mismos 35px a su separación con
   la foto principal (".room-preview{margin-top}" y su gemelo en JS,
   MEDIA_SPACER_ROOM_MARGIN, más abajo) -- así la foto principal sube pero el hueco
   que se abre debajo se rellena con ese margen extra y la miniatura queda exactamente
   donde estaba. "Cantidad"/"Total"/el botón tampoco se mueven porque ya dependían de
   la miniatura (centerCantidadOnRoomPreview()) o de elementos por debajo de ella, no
   de este bloque de arriba -- nada de esto necesitó tocarse aparte. */
/* 23/09/2026, pedido de Gus: al elegir el acabado "blanco humo" (el más claro,
   ver FRAMES/#E9E4DC) se perdía casi todo el contraste contra el fondo crema de
   este panel (--cream, #EFEEE5 -- un tono casi idéntico). Se oscurece SOLO el
   fondo de este panel (no la variable --cream global, que se sigue usando igual
   en el resto del sitio: tarjetas, carrito, checkout, etc.).
   23/09/2026 (corrección, mismo día): el primer intento (15% más oscuro, #CBCAC3)
   quedó muy oscuro para Gus -- pidió "un punto medio entre cómo estaba antes y
   cómo está ahora". Se promedia canal por canal el crema original (239,238,229) y
   ese primer intento (203,202,195): (239+203)/2=221, (238+202)/2=220,
   (229+195)/2=212 = #DDDCD4 -- la mitad de oscurecimiento del primer intento
   (~7.5% más oscuro que el crema original en vez de 15%), suficiente contraste
   para el acabado casi blanco sin verse tan cargado. */
/* 24/09/2026 (tercera corrección del mismo día), pedido de Gus: "que el espacio que
   actualmente existe entre la primera y la segunda foto sea el mismo que existe entre
   la primera foto y la parte superior de la página... corremos la primera foto un poco
   hacia arriba". El padding-top pasa de 63px a 36.6px (el mismo valor exacto de
   PAIR_ROW_GAP, el espacio ya usado entre 1ra-2da y 2da-3ra foto) -- así el hueco de
   arriba queda igual de chico que los otros dos, y como el espacio ENTRE las fotos no
   se toca (sigue en 36.6px), la 2da y 3ra foto suben exactamente lo mismo que la 1ra
   (están todas en el mismo flujo vertical, una debajo de otra) -- todo el conjunto sube
   en bloque, liberando el espacio de sobra que pedía Gus para poder ver las tres fotos
   sin scroll. El título/"Cantidad"/"Total" de la columna derecha NO necesitaron tocarse
   aparte: ya dependen de la posición REAL del marco (alignTitleToArtFrame(),
   centerCantidadOnRoomPreview(), pinTotalToRoomBottom(), etc. -- todas miden con
   getBoundingClientRect() en vez de usar un número fijo), así que se acomodan solas al
   nuevo lugar más arriba del diseño, cumpliendo "subir todo lo de la derecha de forma
   proporcional" sin necesitar ningún cálculo nuevo. */
/* 24/09/2026 (sexta corrección, pedido de Gus, sólo en esta "Opción 2"): "recorta el
   espacio entre la primera y la segunda foto, otro 20%, y lo mismo con la segunda y
   la tercera" -- 36.6px -> 29.28px (36.6 * 0.8). El padding-top de aquí abajo se
   reduce EXACTO igual (mismo criterio que la corrección anterior, "el espacio de
   arriba de la 1ra foto = el espacio entre fotos") para que esa invariante siga
   cumpliéndose sin que Gus tenga que pedirlo de nuevo cada vez que cambia el espacio
   entre fotos. Debe coincidir con "PAIR_ROW_GAP" (JS) y con ".room-preview-row:has(.room-preview-pair){gap;margin-top}" (CSS, más abajo). */
/* 25/09/2026, medida preventiva ante el reporte de Gus sobre "Detalles" abriendo "hacia
   arriba" en piezas individuales: ".modal-media" es su propio contenedor de scroll
   (overflow-y:scroll) y el acordeón de "Detalles" vive dentro de él -- exactamente el
   mismo tipo de contenedor donde ya se encontró y corrigió, más arriba en este archivo, un
   "scroll anchoring" nativo del navegador que compensaba por su cuenta el crecimiento de
   un panel, compitiendo contra el cálculo manual de scroll ya hecho en JS
   (toggleDetailsAccordion() ya calcula y anima el scroll con su propia lógica -- ver esa
   función). No se logró reproducir el problema exacto que describe Gus en las pruebas
   automatizadas (se probó con productos individuales y en pareja, en escritorio y en
   móvil, midiendo cuadro a cuadro la posición del botón "Detalles" -- se mantuvo fija en
   todos los casos), pero por las dudas se aplica la misma corrección ya usada para el
   catálogo (que si es un problema real y comprobado): "overflow-anchor:none" evita que el
   navegador ajuste el scroll de este contenedor por su cuenta, dejando que sea SOLO el
   cálculo de "toggleDetailsAccordion()" el que decida cuánto se mueve. */
.modal-media {
  background: #dddcd4;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  /* 03/10/2026, pedido de Gus (con captura, linea roja marcando la altura deseada):
     "sube un poco la imagen grande hasta que su extremo superior coincida con la
     linea roja... corre hacia arriba de forma proporcional las 3 fotos en miniatura
     de abajo". Solo CSS (pedido explicito de Gus) -- 73px -> 28px, igual a los otros
     tres lados (ya no hace falta que el de arriba sea distinto). Como todo lo de
     abajo (las 3 fotos, el relleno, el acordeon) vive en el mismo flujo vertical
     dentro de este padding, sube en bloque junto con la foto grande, sin tocar
     ningun espacio relativo entre ellos. OJO: a pesar del nombre, las funciones
     alignTitleToArtFrame()/distributeInfoColumn()/equalizeCantidadGap() etc. que
     existen en script.js NO se llaman desde ningun lado (confirmado por grep,
     codigo muerto de una version anterior) -- el titulo y el reparto de
     Tamaño/Acabado/Cantidad NO se recalculan solos en JS. Por eso este cambio se
     hizo en pareja con ".modal-details" (mismo padding-top, ver comentario ahi) y
     con los margin-top fijos de mas abajo (".option-group:first-of-type" /
     "#acabadoGroup" / "#cantidadGroup") -- todo calculado a mano para esta altura
     exacta (690px / 540px), no es dinamico. */
  padding: 28px 28px 28px 28px;
  height: 100%;
  box-sizing: border-box;
  /* 02/10/2026: ".modal-media" es una celda de ".modal-inner" (display:grid) -- un
     grid item SIN "overflow" propio (visible, el valor por defecto) usa su
     contenido para calcular un "alto minimo automatico" que puede estirar la celda
     entera mas alla de "height:100%" cuando el contenido (ahora dentro de
     ".modal-media-scroll") crece (acordeon abierto). Antes esto no pasaba porque
     ".modal-media" tenia su propio "overflow-y:scroll" (que anula ese minimo
     automatico, ver especificacion de CSS Grid/Flexbox, "min-size:auto"); al mover
     ese scroll al hijo nuevo, hacia falta algo equivalente aca para que la celda
     se mantenga siempre clavada en "height:100%" (y sea ".modal-media-scroll",
     adentro, el unico que crece/scrollea) -- "overflow:hidden" (nunca necesita
     scrollear por si misma) resuelve esto sin reintroducir ningun scroll propio. */
  overflow: hidden;
}
/* 02/10/2026, pedido de Gus ("DEBE MANTENERSE ASI... Mantenlo fijo"): nuevo
   contenedor que envuelve TODO lo que puede crecer dentro de ".modal-media" (las
   dos fotos, la fila de miniaturas, el relleno invisible y el panel del acordeon
   de "Detalles") -- ver el comentario grande junto a "#modalMediaScroll" en el
   HTML (script.js, renderModal()) para la explicacion completa de por que hacia
   falta. Hereda exactamente las mismas propiedades de scroll/centrado que antes
   tenia ".modal-media" directamente (mismos valores, ningun comportamiento de
   scroll nuevo) -- solo cambia QUIEN las tiene. "flex:1 1 auto" + "min-height:0"
   es lo que permite que este contenedor (y no ".modal-media" entero, ni la
   columna completa) sea el que efectivamente scrollea puertas adentro cuando el
   acordeon abierto no entra en el alto visible, dejando a ".modal-extra-details"
   (hermano de este bloque, ver mas abajo) completamente afuera de ese scroll. */
.modal-media-scroll {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: scroll;
  overflow-x: hidden;
  overflow-anchor: none;
}

.modal-media-inner {
  display: flex;
  gap: 14px;
  width: 100%;
  margin: 0 auto;
  min-height: 0;
  flex: 0 0 auto;
}
/* (Antes había aquí ".modal-media-inner.shrink{width:45%;}" -- quitada el 08/09/2026.)
   Esa regla era de una versión anterior del layout y ya no hace falta: hoy
   computeImageTargets()/animateModalFit() calculan el ancho de CADA foto (el diseño chico
   y la foto de ambiente grande) directo contra el ancho de ".modal-media" (el contenedor
   de afuera), no contra este ".modal-media-inner". Como ".art-frame" tiene
   "max-width:100%" relativo a ESTE contenedor, esa regla vieja lo achicaba a un 45% del
   ancho DE GOLPE (sin transición -- este contenedor no tiene "transition:width") en el
   mismo instante en que se agregaba la clase "shrink", antes incluso de que arrancara la
   animación por JS. El ancho de ".art-frame" quedaba recortado así un instante mientras
   su alto todavía no se había movido -- una foto angosta pero todavía alta -- y esa
   proporción incorrecta hacía que se viera, por una fracción de segundo, una franja del
   fondo crema de ".modal-media" asomando arriba y abajo del diseño, dentro del marco.
   Reportado por Gus el 08/09/2026 con capturas: "el diseño inicial aparece con un borde
   blanco... se dibuja el mismo borde e inmediatamente se desvanece". Quitando esta regla,
   ".modal-media-inner" se queda siempre al 100% (como ya estaba arriba) y el único que
   controla el ancho/alto real de cada foto, en todo momento, es el bucle de animación en
   JS -- sin nadie más recortando nada por su cuenta. */
/* El marco va en el padding del contenedor (un anillo alrededor de la imagen), nunca
   encima de ella, para no tapar ni un pixel del diseño en los bordes. */
/* 17/09/2026, pedido de Gus ("ha aparecido una franja blanca en la parte inferior de
   la primera foto... no importa si tú la ves, ahí está, el problema está en que tú no
   lo percibas"): esta sombra ("box-shadow") no traza un rectángulo sólido -- por su
   desenfoque (36px) es más alta que el hueco real entre esta foto y la de ambiente (26px,
   ver ".room-preview{margin-top:26px}"), así que se difumina hacia ARRIBA (aunque su
   desplazamiento sea hacia abajo) y se mezcla con el tono de esa miniatura, sumando un
   oscurecido extra justo en esa franja -- confirmado comparando capturas con y sin esta
   sombra (pixel a pixel, con ambas sombras desactivadas la franja se aclara). Ninguna de
   las dos sombras (esta y la de ".room-preview-frame", mismo valor, más abajo) fue jamás
   pedida por Gus -- son decoración heredada de una versión anterior. Se quitan ambas: es
   una remoción, no un agregado, y no cambia el tamaño de ninguna foto. */
.art-frame {
  flex: 0 1 auto;
  /* 03/10/2026, pedido de Gus (ver comentario grande junto a ".modal-media-inner"
     mas arriba, el que fija el alto 690px/540px): 80% -> 100%. Con el alto nuevo
     (mas grande), el ancho que le corresponde por la proporcion real de la foto
     (vía ".art-frame img", object-fit:contain) empezaba a chocar con este 80% --
     quedaba mas angosto de lo que le toca y aparecia un colchon vacio arriba/abajo
     dentro del marco (sin recortar nada, solo sin usar todo el alto reservado).
     ".art-frame" es el UNICO hijo de ".modal-media-inner" (no hay ninguna otra
     foto compartiendo esta fila -- las otras dos fotos viven aparte, en
     ".room-preview-row", mas abajo), asi que no hace falta reservar ningun
     porcentaje para nadie mas; 100% iguala ademas al mismo valor que ya usa la
     version movil de esta regla (ver "@media (max-width: 640px)" debajo). */
  max-width: 100%;
  height: 100%;
  position: relative;
  padding: var(--art-frame-thick, 6px);
  box-sizing: border-box;
  transition: padding 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

@media (max-width: 640px) {
  .art-frame {
    max-width: 100%;
  }
}

/* 12/09/2026, pedido de Gus: piezas con marco ya incluido en la foto (p.fixedFrame,
   "segunda colección" en adelante, ver renderModal) no llevan el anillo dinámico de
   marco -- padding:0 quita el hueco que ese anillo reservaría, y el filter:blur que
   ".art-frame img" aplica (pensado para disimular el empalme con ese anillo) se quita
   también, porque acá no hay empalme que disimular. */
.art-frame.no-frame {
  padding: 0;
}
.art-frame.no-frame img {
  filter: none;
  object-position: top;
}
/* Corrección 09/09/2026 (tercer seguimiento del mismo día): "--art-frame-thick" /
   "--art-frame-thick-shrink" son variables calculadas en JS por snapArtFrameThickness()
   (ver esa función) -- el mismo mecanismo ya usado para el marco del catálogo
   (CATALOG_FRAME_TARGET_RECT/snapCatalogFrameThickness()) y para el mini-marco del
   carrito/checkout (MINI_FRAME_TARGET/snapMiniFrameThickness()), aplicado aquí por
   primera vez. Motivo: Gus reportó, con dos capturas propias marcando en círculo rojo la
   franja superior del marco, que se veía más delgada que los costados (confirmado también
   en video y en vivo, en Edge y en Chrome). La medición con getBoundingClientRect() seguía
   dando los 4 lados perfectamente iguales (6px/3px exactos, en cualquier densidad de
   pantalla) -- eso solo prueba que la matemática del CSS es correcta, no que lo pintado en
   pantalla también lo sea. Renderizando la propia página con GPU real (no solo headless) a
   densidades de pantalla fraccionarias (1.25x/1.5x -- las más comunes en Windows con
   escalado al 125%/150%) SÍ se reprodujo una diferencia real de hasta 1px físico entre
   lados, exactamente el mismo tipo de defecto ya encontrado y corregido para el marco del
   catálogo el 05/09/2026 ("Catálogo: asimetría de grosor de marco"), pero que nunca se
   había aplicado aquí, en el marco de la ficha de producto. La causa es la misma: 6px/3px
   son números enteros de píxeles CSS, pero se convierten en un número fraccionario de
   píxeles FÍSICOS en cualquier pantalla con densidad no entera, y ese sobrante se redondea
   de forma independiente en cada lado. La solución es la misma ya usada dos veces: en vez
   de un número fijo, calcular en JS el valor en píxeles CSS más cercano al grosor deseado
   que dé SIEMPRE un número entero de píxeles físicos en la pantalla real de quien esté
   viendo la página. */
/* Corrección 09/09/2026, primera versión (SUPERADA el mismo día, ver la corrección
   siguiente más abajo): se había puesto aquí un fondo oscuro FIJO (background:rgb(40,28,18),
   el mismo tono que ya se usaba en "img-edge-shadow" más abajo) detrás de la imagen del
   diseño, dentro del marco, para tapar una línea/orla blanca reportada por Gus (ver detalle
   de la causa real en la corrección siguiente). Gus notó un efecto secundario nuevo:
   "se ve como si se moviese/ensanchase un poco el marco" al pasar de la foto de ambiente
   de vuelta al diseño -- confirmado (ver capturas de esa investigación): con un acabado de
   marco CLARO (humo, roble, nogal -- los 5 acabados menos "castaña" son más claros que
   rgb(40,28,18)), ese fondo fijo oscuro quedaba más oscuro que el propio marco de al lado,
   y al difuminarse el borde del diseño hacia ese fondo se generaba una franja oscura extra,
   pegada al marco pero de un tono distinto a él -- se leía como si el marco hubiera
   "ganado" un borde interior oscuro (un ensanchamiento aparente), sobre todo mientras el
   filtro de desenfoque todavía estaba a medio transicionar (0.6s) al agrandar el diseño.
   Reemplazado por la corrección siguiente (fondo dinámico, no fijo). */
/* Corrección 09/09/2026 (versión final, vigente): el fondo detrás de la imagen del diseño,
   dentro del marco, ya NO es un color fijo -- se aplica como estilo en línea sobre el propio
   ".art-frame" en renderModal() (variable "baseColor", la misma que ya se usaba para pintar
   "frame-strip"/"frame-corner", es decir, el color real del acabado de marco QUE ESTÉ
   SELECCIONADO en ese momento -- humo/roble/nogal/castaña/negro). Motivo original (reportado
   por Gus el 09/09/2026): "líneas/bordes blancos" alrededor del diseño, en los cuadros
   azules y verdes, tanto en Edge como en Chrome, en la web ya publicada -- no era el archivo
   ni el navegador. Causa real: "filter:blur(1.6px)" de abajo (el desenfoque que se le aplica
   a esta foto cuando se achica, al mostrar la foto de ambiente) difumina también el borde
   MISMO de la imagen hacia adentro, mezclando, en una franja de 1-2px, el color real del
   diseño con lo que haya detrás de la imagen en ese punto -- que antes era el crema de fondo
   de la página (".modal-media"), y por eso se veía como una línea u orla blanca pegada al
   marco, mucho más visible en diseños de tono oscuro (azules, verdes) por el contraste. No es
   un problema de tamaño/posición (medido con precisión de sub-píxel, la imagen y su caja
   calzan exactas) sino de qué color queda "revelado" en ese difuminado. Al usar SIEMPRE el
   mismo color que YA tiene el marco de al lado (en vez de un color fijo), ese difuminado se
   funde limpiamente con el propio marco sin importar qué acabado esté elegido ni qué tan
   oscuro/claro sea el diseño en su borde -- ya no puede quedar un tono ajeno (ni blanco de
   la página, ni un oscuro fijo más oscuro que el marco claro), porque el color revelado y el
   color del marco son siempre uno mismo. Corrige también, de paso, una versión mucho más
   sutil (menos de 1px) del mismo efecto que ya existía en la foto SIN achicar, por redondeo
   de sub-píxel del navegador -- mismo origen, mismo arreglo. Para revertir: quitar el
   "style=\"background:...\"" del div ".art-frame" en renderModal(). */
.art-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  filter: blur(1.6px);
  transition:
    filter 0.6s ease,
    transform 0.3s ease;
}
.modal-media-inner:not(.shrink) .art-frame img {
  filter: none;
}
/* 02/10/2026, pedido de Gus: "cuando colocas el cursor sobre alguna de las fotos, la
   flecha se transforma en una lupa con el signo '+' en el centro" -- "cursor:zoom-in"
   es exactamente ese ícono de lupa-con-mas en todos los navegadores/sistemas
   principales, sin necesidad de un ícono propio. Se aplica a la foto GRANDE (la que
   se está mirando en detalle, ".modal-media-inner img" -- cubre tanto ".art-frame img"
   como el <img> simple que se usa cuando la foto grande es una de las miniaturas, ver
   renderModal()), no a la fila de miniaturas de abajo (esas ya tienen su propio click
   para elegir cuál foto mostrar grande -- agregarle zoom ahí chocaría con eso). El
   zoom en sí (acercar/alejar, initImageZoom()) va en el JS. */
.modal-media-inner img {
  cursor: zoom-in;
}
.modal-media-inner img.zoomed {
  cursor: zoom-out;
}
/* 02/10/2026, pedido de Gus: "que la lupa del zoom me permita ver la imagen 20% mas
   grande que ahora" -- de 2x a 2.4x (2 * 1.2). Debe coincidir exacto con ZOOM_SCALE
   en initImageZoom() (JS): ese valor gobierna el recorrido (pan) de la foto ya
   acercada arrastrando el mouse, y tiene que ser el MISMO numero que el zoom
   inicial de aca (CSS, lo que se ve al primer click, antes de arrastrar nada) --
   si no coinciden, la foto "saltaria" de tamaño en cuanto se empieza a arrastrar. */
/* 03/10/2026, pedido de Gus: "que el zoom de la lupa sea 30% mas grande de lo
   que es ahora" -- de 2.4x a 3.12x (2.4 * 1.3). */
.art-frame img.zoomed {
  transform: scale(3.12);
}
.modal-media-inner > div > img.zoomed {
  transform: scale(3.12);
}
/* 20/09/2026, reportado por Gus sobre "Moka" ("al pasar de la imagen 1 a la imagen 2 en
   Moka, se desenfoca, se ve extremadamente mal"): el difuminado de arriba (filter:blur(1.6px),
   pensado para que la miniatura chica se vea "medio blurry" sin molestar) se probó en su
   momento con diseños de manchas/colores planos (ej. Plum) y ahí se ve bien -- pero el diseño
   de Moka es un estampado de puntitos muy finos, y ese mismo desenfoque, sumado a que la
   miniatura ya es chica de por sí, borra el patrón por completo (queda una mancha gris sin
   forma, no puntos). Ver THUMB_NO_BLUR_PRODUCT_IDS en el JS (cerca de EDGE_SHADOW_PRODUCT_IDS):
   solo a esos diseños (por ahora, Moka) se les quita el desenfoque de la miniatura, agregando
   la clase "no-thumb-blur" al ".art-frame" correspondiente en renderModal(). El resto de los
   diseños (Plum incluido) sigue exactamente igual, con su mismo difuminado de siempre. Para
   revertir: quitar esta regla y la clase "no-thumb-blur" de renderModal(). */
.art-frame.no-thumb-blur img {
  filter: none;
}
/* Corrección 08/09/2026 (ver EDGE_SHADOW_PRODUCT_IDS más arriba en el JS, cerca de
   RECT_SIZES): sombra interior sutil, solo para los diseños de esa lista, que separa el
   diseño del marco de madera con una línea oscura y fina -- así el color propio del diseño
   (que en esos casos llega claro hasta el borde) ya no se puede confundir con el fondo de
   la página. Deliberadamente oscura (no clara) para que nunca se mezcle con el crema de
   fondo. Para revertir: borrar esta regla completa. */
.art-frame img.img-edge-shadow {
  box-shadow: inset 0 0 0 1px rgba(40, 28, 18, 0.32);
}
/* 02/10/2026, pedido de Gus: "la primera imagen del preview 15% más pequeña" --
   600px * 0.85 = 510px. Sólo esta regla de escritorio (sin media query) se toca;
   la versión de 400px para pantallas angostas (ver "@media (max-width: 800px)"
   más abajo) se deja igual, es la que está ajustando Maria para movil. */
/* 03/10/2026, pedido de Gus: "la foto deberia tener mas o menos el mismo tamaño
   que el rectangulo rojo [de su captura], pero respetando las proporciones que
   actualmente tiene el diseño" -- SOLO CSS, sin tocar script.js (pedido explicito
   de Gus). Esta regla (".modal-media-inner{height:...}") es la unica que de
   verdad fija el tamaño de la foto grande: ".art-frame{height:100%}" hereda
   este alto tal cual, y su ancho sale solo, sin ningun calculo en JS, de la
   proporcion real del archivo de imagen (object-fit:contain + width:100% sobre
   un alto ya fijo -- ver ".art-frame img" mas abajo) -- por eso subir SOLO este
   numero agranda la foto completa (alto y ancho) sin deformarla ni recortarla,
   en cualquier producto, sin importar su proporcion. 510 -> 690px (mismo factor,
   ~1.35x, en las dos variantes de abajo) calcula, a ojo sobre la captura de Gus,
   un tamaño final parecido al del rectangulo rojo. */
.modal-media-inner {
  justify-content: center;
  align-items: center;
  height: 690px;
}

@media (max-height: 800px) {
  .modal-media-inner {
    height: 540px;
  }
}

/* 03/10/2026, bug real encontrado y confirmado por Gus (captura desde su celular, "se
   alarga, se hace mas alta la foto" al pasar a ver la 2da/3ra miniatura en grande): esta
   regla de Maria le da a la foto grande, SOLO cuando es una miniatura (no ".art-frame",
   que es la foto 1) "height:100%" pero nunca "width" ni "object-fit" -- sin object-fit,
   el valor por defecto del navegador es "fill" (estirar para llenar la caja exacta, sin
   respetar la proporcion real). Mientras esa caja tuviera la proporcion correcta no se
   notaba, pero al ser hijo de un contenedor flex (".modal-media-inner"), si la foto no
   entra a lo ancho a su alto real, flexbox la encoge de ANCHO para que quepa -- y como
   el alto queda fijo en 100% y no hay "object-fit:contain", el navegador ESTIRA el
   contenido para llenar esa caja ya mas angosta, deformando la foto (se ve mas alta y
   angosta de lo real). Se agrega "width:100%" (mismo criterio que ".art-frame img" mas
   arriba) y "object-fit:contain" para que, pase lo que pase con el ancho de la caja, la
   foto SIEMPRE se vea completa y sin deformar (con margen vacio a los lados si hace
   falta, nunca estirada) -- exactamente el mismo comportamiento que ya tiene la foto 1. */
.modal-media-inner > div,
.modal-media-inner > div img {
  height: 100%;
}
.modal-media-inner > div img {
  width: 100%;
  object-fit: contain;
}
.frame-strip {
  position: absolute;
  background-repeat: repeat;
  background-position: center;
  transition:
    width 0.6s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.frame-strip.fs-top {
  top: 0;
  left: 0;
  right: 0;
  height: var(--art-frame-thick, 6px);
  background-size: 110px auto;
}
.frame-strip.fs-bottom {
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--art-frame-thick, 6px);
  background-size: 110px auto;
}
.frame-strip.fs-left {
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--art-frame-thick, 6px);
  background-size: auto 110px;
}
.frame-strip.fs-right {
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--art-frame-thick, 6px);
  background-size: auto 110px;
}
.frame-corner {
  position: absolute;
  width: var(--art-frame-thick, 6px);
  height: var(--art-frame-thick, 6px);
  z-index: 1;
  overflow: hidden;
  transition:
    width 0.6s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Cuando la foto se achica al 45%, el marco (y el padding que lo aloja) debe achicarse
   en esa misma proporción para que se vea con el mismo grosor real, no proporcionalmente
   más grueso. */
.modal-media-inner.shrink .art-frame {
  padding: var(--art-frame-thick-shrink, 3px);
}
/* Corrección 15/09/2026, pedido de Gus: reportó un "marco blanco" alrededor de la
   imagen minimizada al pasar de la foto principal a la de ambiente -- en piezas con
   marco ya incluido en la foto (p.fixedFrame, ".art-frame.no-frame", que debe quedarse
   siempre en padding:0, ver esa regla más arriba) la regla de arriba
   (".modal-media-inner.shrink .art-frame") tiene más especificidad CSS (3 clases contra
   2) y le ganaba a ".art-frame.no-frame{padding:0}", así que al achicarse SÍ le entraban
   3px de padding que no debían estar ahí. Como ".art-frame" no tiene fondo propio en
   estas piezas (no hay "style=\"background:...\"" en el div, ver renderModal), esos 3px
   de más quedaban transparentes -- se veía el crema de fondo de ".modal-media" detrás,
   un poco más claro que la propia foto, formando el "marco blanco" que reportó. Esta
   regla, más específica todavía (4 clases), devuelve el padding a 0 también en el
   estado achicado, para que la imagen ocupe la caja completa sin ningún margen -- igual
   que ya pasa en el estado grande. No afecta a las piezas con marco dinámico (sin
   ".no-frame"), que siguen usando el grosor de marco correspondiente al achicarse. */
.modal-media-inner.shrink .art-frame.no-frame {
  padding: 0;
}
.modal-media-inner.shrink .frame-strip.fs-top,
.modal-media-inner.shrink .frame-strip.fs-bottom {
  height: var(--art-frame-thick-shrink, 3px);
  background-size: 50px auto;
}
.modal-media-inner.shrink .frame-strip.fs-left,
.modal-media-inner.shrink .frame-strip.fs-right {
  width: var(--art-frame-thick-shrink, 3px);
  background-size: auto 50px;
}
.modal-media-inner.shrink .frame-corner {
  width: var(--art-frame-thick-shrink, 3px);
  height: var(--art-frame-thick-shrink, 3px);
}
.frame-corner.fc-tl {
  top: 0;
  left: 0;
}
.frame-corner.fc-tr {
  top: 0;
  right: 0;
}
.frame-corner.fc-bl {
  bottom: 0;
  left: 0;
}
.frame-corner.fc-br {
  bottom: 0;
  right: 0;
}
/* 16/09/2026 (correccion), pedido de Gus: un margin-top fijo (71px) solo
   coincidia con la burbuja de "Cantidad" en UNA resolucion -- en otras se
   desalineaba mucho, porque el tamaño de esta foto (calculado por
   computeImageTargets segun el alto de ventana) y la posicion de "Cantidad"
   (repartida por flexbox segun ese mismo alto) no crecen igual. Se vuelve al
   margin-top original (26px, el que usa ROOM_MARGIN en computeImageTargets).
   16/09/2026 (tercera vuelta), pedido de Gus: aclaró que esta foto NO debe
   moverse -- ambas fotos (principal y de ambiente) se quedan exactamente
   donde estaban (26px). Lo único que se mueve es "Cantidad", que ahora se
   acomoda para centrarse sobre el eje vertical de esta miniatura (ver
   centerCantidadOnRoomPreview() mas abajo). */
.room-preview {
  width: 170px;
  margin: 61px auto 0;
  cursor: pointer;
  flex-shrink: 0;
}
/* 16/09/2026, pedido de Gus: relleno invisible que compensa la diferencia de alto total
   entre los dos estados (ver comentario junto al div "modalMediaSpacer" en renderModal()
   y syncMediaSpacer() en JS) -- su altura la pone JS, aqui solo se fija que nunca se
   achique (flex-shrink:0) y que no tenga ancho/fondo propio (invisible). */
.modal-media-spacer {
  flex-shrink: 0;
  width: 100%;
  height: 0px;
}
.room-preview.expanded {
  width: 100%;
}
/* 23/09/2026, pedido de Gus: en piezas con pareja, ".room-preview" (la foto de
   ambiente de siempre) y ".room-preview-pair" (nueva, la foto de ambiente de la
   pieza hermana) van dentro de este wrapper -- un flex row que las centra a las
   DOS juntas como un solo bloque (en vez de centrar cada una por separado), con
   un espacio chico entre ambas (mismo efecto visual que "la segunda foto corre a
   la izquierda y la 3ra a la derecha, centradas respecto a la primera foto",
   pedido de Gus). El margin-top de 61px que antes tenía ".room-preview" se mueve
   acá (ver la regla siguiente, que se lo resta a ".room-preview" solo cuando está
   dentro de este wrapper) -- el espacio libre respecto a la foto principal es
   igual de siempre, tanto en piezas con pareja como sin ella. width:fit-content
   para que el wrapper mida solo lo que ocupan las dos miniaturas (y quede
   centrado como bloque compacto vía margin:auto), no las 100% del ancho de la
   columna -- excepto en el estado expandido (ver más abajo). */
/* 02/10/2026, pedido de Gus: "las fotos que están abajo 10% más chicas, mantenlas
   centradas respecto a la primera imagen tal y como está ahora" -- 150px * 0.9 =
   135px. "justify-content:center" (ya estaba) sigue centrando el bloque igual que
   antes, sólo que más chico.
   02/10/2026 (segunda vuelta, mismo día): "reduce otro 15% las fotos de abajo, la
   primera queda con esa primera reducción" -- la foto principal (.modal-media-inner)
   no se toca de nuevo, sólo esta fila: 135px * 0.85 = 114.75 -> 115px. */
.room-preview-row {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 20px;
  width: fit-content;
  flex-shrink: 0;
  margin-top: 16px;
  height: 115px;
}

.room-preview-row .room-preview {
  margin: 0;
}
/* 24/09/2026 (corrección, pedido de Gus -- "no debe desaparecer ninguna foto... en todo
   momento tienen que verse las tres" + "las tres fotos estén como en una misma fila, una
   debajo de la otra, [más] cerca de lo que están ahorita"): en piezas CON pareja, las
   3 fotos quedan SIEMPRE en columna (diseño arriba -- fuera de este wrapper, ver
   ".art-frame" -- 2da y 3ra debajo, una sobre otra) en los DOS estados por igual, no
   solo cuando la de ambiente está grande -- por eso esta regla ya no depende de la
   clase "expanded" como antes. El espacio contra el diseño y el espacio entre 2da/3ra
   usan la MISMA distancia, reducida un 20% (61px -> 48.8px, ver PAIR_ROW_GAP en el JS,
   que debe coincidir exacto con este número para que el relleno invisible funcione
   bien). La 3ra foto (".room-preview-pair") ya NUNCA se oculta (antes:
   ".room-preview-row.expanded .room-preview-pair{display:none}") -- se ve siempre como
   miniatura chica, se puede clickear en cualquier momento para intercambiar DIRECTO su
   contenido con ".room-preview" (ver swapAndExpandPair() en el JS), sin pasar primero
   por el diseño principal, y sin que ninguna de las tres desaparezca jamás. */
/* 24/09/2026 (segunda corrección del mismo día, pedido de Gus: "reduce otro 25% el
   espacio entre cada foto, pégalas más"): 48.8px -> 36.6px (48.8 * 0.75) -- mismo
   valor para los dos espacios (diseño->2da Y 2da->3ra), igual que antes. Debe
   coincidir exacto con PAIR_ROW_GAP en el JS. */
/* 24/09/2026 (décima corrección, pedido de Gus -- ver comentario grande junto a
   applyImageBoxes() en el JS): "position:relative" para que ".room-preview-pair"
   (ahora "position:absolute", ver esa regla más abajo) se posicione respecto a ESTA
   fila, no respecto a un ancestro más lejano. "justify-content:flex-start" (antes
   heredaba "center" de la regla base de ".room-preview-row" más arriba): al volverse
   ".room-preview-pair" absoluta, deja de contar para el alto natural de la fila -- sin
   este cambio, el alto explícito que le fija el JS a la fila (para seguir reservando
   el mismo espacio total de siempre) sobraría por encima/debajo de ".room-preview" (el
   único hijo que sigue en flujo normal) y "center" la desplazaría verticalmente de su
   sitio de siempre (arriba de todo). Con "flex-start" se queda siempre pegada arriba,
   igual que antes. */
.room-preview-row:has(.room-preview-pair) {
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 29.28px;
  margin: 29.28px auto 0;
  position: relative;
}
/* Al expandir la foto de ambiente (expandRoomPreview() agrega esta misma clase acá, ver
   JS), el wrapper pasa a ocupar el 100% de la columna -- igual que hacía
   ".room-preview.expanded" sola en piezas sin pareja. En piezas con pareja, el
   flex-direction/gap/margin ya vienen de la regla de arriba (siempre columna, en los
   dos estados) -- acá sólo cambia el ancho. */
.room-preview-row.expanded {
  width: 100%;
}
/* 23/09/2026 (corrección urgente): Gus pidió que la 3ra foto también se pueda
   agrandar con la misma transición que la 2da -- ver swapAndExpandPair() en el JS,
   que intercambia el CONTENIDO de las dos imágenes y deja que el mecanismo de
   expandir de siempre (expandRoomPreview(), ya usado por ".room-preview") haga
   el resto, sin tocar ninguna de las funciones de alineación que ya dependen de
   ".room-preview" como identidad fija. cursor:pointer para que se note que
   también es clickeable, igual que ".room-preview". */
/* 24/09/2026 (décima corrección): "position:absolute" -- ver el comentario grande
   junto a applyImageBoxes() en el JS (arriba se explica por qué). "top" lo pone el JS
   en cada momento (ahí también se explica con qué valor). "left:50%" +
   "transform:translateX(-50%)" centra esta foto horizontalmente sin que el JS necesite
   saber su ancho -- coincide con el centro real de ".room-preview" (que sigue en flujo
   normal, centrada por ".modal-media{align-items:center}" de siempre) porque, al ser
   ".room-preview-pair" el único hijo NO absoluto que queda además de ".room-preview"
   dentro de esta fila, el ancho "fit-content" de la fila (y por lo tanto su propio
   centro) sigue determinado únicamente por ".room-preview" -- exactamente igual que
   como ya se centraba antes. */
.room-preview-pair {
  width: 170px;
  flex-shrink: 0;
  cursor: pointer;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}
/* 24/09/2026 (corrección, reportado por Gus -- "una se ve cuadrada y la otra
   rectangular, deben verse igual, sin hacer zoom"): esta regla venía de una
   versión anterior de la 3ra foto (cuando mostraba la foto LIMPIA/enmarcada del
   cuadro hermano, sin ambiente alrededor) -- "object-fit:contain" tenía sentido
   ahí porque esa foto casi nunca compartía la proporción del recuadro. Desde que
   la 3ra foto pasó a ser la foto REAL de ambiente con ambas piezas juntas (misma
   naturaleza que la 2da foto, ".room-preview-frame img"), "contain" dejaba un
   "letterbox" (relleno de color) a los costados o arriba/abajo cada vez que la
   proporción real de esa foto no calzaba exacto con la del recuadro (que mide lo
   mismo, en píxeles, que el de la 2da foto -- ver applyImageBoxes() en el JS,
   copia roomW/roomH sin cambios) -- ese relleno era justo lo que hacía "verse
   cuadrada" en vez de rectangular como la 2da. Corregido a "object-fit:cover"
   (mismo criterio que ya usa ".room-preview-frame img" para toda foto de
   ambiente/lifestyle, ver el criterio de diseño documentado para estas fotos):
   llena el recuadro completo sin dejar relleno, recortando solo el sobrante
   -- ningún "zoom" adicional, es la misma técnica ya aprobada para la 2da foto,
   aplicada ahora también a la 3ra para que ambas se vean con el mismo tamaño y
   proporción. object-position centrado (50% 50%, a diferencia del 0% 50% de la
   2da foto) porque acá lo importante es que ambas piezas del dúo queden
   centradas en el recorte, no ancladas a un costado. */
.room-preview-pair-frame {
  position: relative;
  border-radius: 2px;
  overflow: hidden;
  background: #dddcd4;
}
/* 24/09/2026 (décima corrección, SEXTA vuelta): "object-fit:cover" (de la corrección de
   arriba) pasa a "contain" siempre, sin esperar a ".settled" -- ver el comentario grande
   junto a "roomBigOuterW"/"pairBigOuterW" en computeImageTargets() (JS): la caja chica Y
   la caja grande de esta foto ahora comparten siempre la misma proporción (la real de la
   foto, ya no la del diseño), así que nunca hace falta recortar nada -- "cover" y
   "contain" pintarían exactamente lo mismo en cualquier cuadro de la animación, pero
   "contain" es más seguro (nunca puede recortar contenido por error de redondeo, a lo
   sumo un margen de menos de 1px, nunca visible). La regla ".room-preview-pair.expanded.
   settled" de más abajo queda con los mismos valores -- ya no hace falta que "settled"
   dispare ningún cambio, pero se deja sin tocar para minimizar el cambio. */
.room-preview-pair-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 50%;
  display: block;
}
/* 17/09/2026: se quita "box-shadow" (ver el comentario junto a ".art-frame" más arriba,
   mismo motivo -- la "franja blanca" que reportó Gus entre las dos fotos). */
.room-preview-frame {
  position: relative;
  border-radius: 2px;
  overflow: hidden;
  height: 100%;
}
/* 24/09/2026 (décima corrección, SEXTA vuelta -- reemplaza el criterio del 15/09/2026
   descrito antes aquí, "miniatura forzada al ancho del diseño + cover anclado a la
   izquierda"): Gus identificó que esa caja angosta (proporción del diseño, ~0.81) forzada
   sobre esta foto (proporción real ~0.95, casi cuadrada) era la causa de "se agranda y
   luego se reacomoda" -- mientras crecía se veía RECORTADA (cover) y al terminar se veía
   la foto COMPLETA de golpe (contain, ver la regla ".settled" más abajo), un cambio de
   composición visible en el instante exacto en que terminaba de crecer. Ver el
   comentario grande junto a "smallAmbientW"/"roomBigOuterW" en computeImageTargets()
   (JS): ahora la caja chica Y la caja grande de esta foto comparten siempre su propia
   proporción real (nunca la del diseño), así que "object-fit:contain" puede quedar fijo
   siempre, sin recorte en ningún momento ni switch a mitad de camino. */
.room-preview-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 50%;
  display: block;
  filter: none;
  transition: filter 0.6s ease;
}
/* 24/09/2026 (décima corrección, SEXTA vuelta): estas dos reglas ".settled" quedan
   redundantes -- ya piden exactamente los mismos valores ("contain"/"50% 50%") que la
   regla base de arriba pasó a tener siempre, sin esperar a ".settled" (ver el comentario
   grande junto a ".room-preview-frame img"). Se dejan sin tocar (no hacen daño, sólo
   repiten el mismo valor) para minimizar el cambio -- el historial de por qué existían
   (bordes beige del 15/09/2026, luego el "reacomodo" de hoy) queda documentado ahí. */
.room-preview.expanded.settled .room-preview-frame img {
  filter: none;
  object-fit: contain;
  object-position: 50% 50%;
}
.room-preview-pair.expanded.settled .room-preview-pair-frame img {
  filter: none;
  object-fit: contain;
  object-position: 50% 50%;
}
/* "Detalles" (06/09/2026): acordeón debajo de la imagen del diseño, dentro de la misma
   columna de la ficha de producto (.modal-media). Como esa columna ahora puede llevar más
   contenido que antes (ya no es solo la imagen), pasa de overflow:hidden a overflow-y:auto
   arriba, para que un acordeón abierto pueda hacer scroll dentro de esa columna si hiciera
   falta, sin afectar la columna de precio/tallas/carrito (.modal-details), que ya tenía su
   propio scroll independiente desde antes. */
/* 16/09/2026, pedido de Gus: segunda correccion -- "Detalles" baja un poco mas
   (margin-top 26px -> 96px) para dejar mas aire despues de la foto de ambiente.
   NOTA: esto NO significa que las 6 filas queden siempre visibles por separado --
   ese comportamiento (un solo "+" en "Detalles" que abre/cierra las 6 secciones
   juntas, "Sobre la pieza" incluida como la primera) ya estaba vigente y se
   mantiene; lo unico que cambia aqui es la posicion vertical del bloque.
   IMPORTANTE: el "+26" en computeImageTargets (mas abajo) representa este mismo
   margin-top y se actualiza junto con este valor. */
/* 16/09/2026, pedido de Gus: volver "Total" a su posicion natural (revertido
   arriba) y en vez de eso subir/alinear "Detalles" a esa altura -- se ajusta
   el margin-top (96px -> 109px) para que el titulo "Detalles" quede a la
   misma altura que "Total" en la columna de al lado. El "+26" de
   computeImageTargets se deja igual (no se sincroniza con este valor a
   proposito, ver comentario ahi) para que las fotos sigan en su tamaño
   original.
   17/09/2026, pedido de Gus: ese enganche con "Total" se quita (ver
   pullTotalCloseToCantidad() en JS -- "Total" ahora se mueve hacia ARRIBA para
   acercarse a "Cantidad", así que seguir igualando "Detalles" a esa altura lo
   habría subido también, justo lo contrario de lo pedido: "Detalles" tiene que
   bajar un poco respecto a como estaba). Como ".modal-media" es flex-column y sus
   hijos no colapsan márgenes entre sí, este margin-top ES directamente el aire
   visible entre el final de la foto de ambiente (o su "spacer" de igualación, ver
   syncMediaSpacer()) y "Detalles" -- se baja de 109px (que con fotos altas caía
   fuera de la pantalla y quedaba topeado a un mínimo de 8px, ver
   alignDetallesToTotal() más abajo, ya no existe) a un valor bajo pero fijo, unos
   px más que ese mínimo anterior. */
/* 02/10/2026, pedido de Gus (captura con recuadro rojo): "Detalles" debe ir pegado
   al fondo de la columna de fotos (".modal-media"), a la misma altura donde terminan
   "Total"/"Añadir al carrito" en la columna de al lado -- con "margin-top:28px" fijo
   se quedaba naciendo justo debajo de las miniaturas, mucho más arriba de ahí,
   dejando vacío todo el resto de la columna (exactamente el hueco que marcó el
   recuadro rojo). Como ".modal-media" es "display:flex;flex-direction:column", un
   "margin-top:auto" en el último hijo consume TODO el espacio libre de arriba y lo
   empuja al fondo -- sin "position:fixed" (no vuelve a quedar "pinneada" al
   scrollear, sigue siendo parte normal del flujo) y respetando igual el
   "padding-bottom:28px" de ".modal-media" (no queda pegado al borde exacto). Si el
   contenido de arriba ya llena toda la columna (fotos muy altas), este margen
   simplemente se reduce a 0 por sí solo y "Detalles" queda donde siempre, inmediato
   después de las miniaturas -- no hace falta ningún caso especial aparte. */
/* 02/10/2026 (segunda correccion, mismo dia), pedido de Gus (captura con linea de
   referencia): el extremo izquierdo de "Detalles" (donde empiezan las letras) debe
   coincidir exacto con el extremo izquierdo de la burbuja "Volver" (arriba a la
   izquierda, ".modal-back-link", en left:16px) -- medido, "Detalles" nacia en 28px
   (hereda el padding-left de ".modal-media", que tambien aplica a las fotos de
   arriba, asi que no se toca ese padding para no moverlas). Se compensa SOLO en
   este elemento con "margin-left:-12px" (28-12=16, igual que "Volver"); el resto del
   ancho (100%) no se ve afectado, solo se corre el bloque entero 12px a la
   izquierda. "align-self:flex-start" es necesario aparte: ".modal-media" tiene
   "align-items:center" (de siempre, para centrar las fotos), y ese centrado
   reparte un margen negativo de un solo lado a la mitad (quedaba a 22px, no a
   16px, un corrimiento de solo 6px en vez de 12) -- "align-self:flex-start" saca a
   este elemento puntual de ese centrado (no afecta a nada mas, las fotos de arriba
   siguen centradas igual que siempre) para que el margen se aplique completo. */
/* 02/10/2026 (tercera correccion, mismo dia), pedido de Gus (captura con recuadro
   rojo sobre la verificacion anterior): "Detalles" seguia sin llegar al fondo real
   -- quedaba a la altura de "Total"/"Añadir al carrito", pero no ocupaba los 28px
   de "padding-bottom" de ".modal-media" (el mismo respiro que tienen los otros tres
   lados de esa columna), que es justo el hueco que marco el recuadro rojo.
   "margin-top:auto" solo reparte el espacio libre DENTRO del área de contenido
   (sin contar el padding, que queda afuera por definicion) -- para que este
   elemento se meta tambien en ese padding y quede pegado al borde real, se le
   resta ese mismo valor con "margin-bottom:-28px" (no se toca el padding de
   ".modal-media", que sigue igual para los otros lados/casos). */
.modal-extra-details {
  width: 100%;
  max-width: 100%;
  margin-left: -12px;
  margin-bottom: -28px;
  align-self: flex-start;
  text-align: left;
  flex-shrink: 0;
}
/* 02/10/2026: "margin-top:auto" (lo que antes empujaba este bloque hasta el fondo
   SOLO cuando sobraba espacio libre -- la causa raiz del bug de posicion
   inconsistente, ver comentario grande en el HTML) ya no hace falta: este bloque
   ahora es hermano de ".modal-media-scroll" (arriba), que es "flex:1 1 auto" y
   absorbe TODO el espacio libre o faltante por si solo -- ".modal-extra-details",
   con "flex-shrink:0", siempre queda exactamente del tamaño de su contenido
   (el boton "Detalles"), empujado de forma natural al fondo real de la columna
   sin necesidad de ningun margen especial. "margin-bottom:-28px" se mantiene
   igual que antes (ver comentario original mas abajo) -- sigue haciendo la misma
   funcion (meter el boton en el padding-bottom de ".modal-media" para que llegue
   al borde real), sin relacion con el bug que se corrige aca. */
/* 16/09/2026, pedido de Gus: "Detalles" es ahora el UNICO disparador del acordeon --
   este boton envuelve el titulo y el "+"/"-" (antes cada encabezado tenia su propio
   boton/icono, ver ".accordion-header-label" mas abajo, que ya no es clickeable). */
/* 17/09/2026, pedido de Gus: el "+"/"-" estaba pegado al extremo derecho del boton
   (justify-content:space-between separaba "Detalles" del icono casi 800px en pantallas
   anchas) -- pidió que el icono quede junto al texto, "casi al costado". Se cambia a
   flex-start; el "gap" es lo único que separa texto e icono. */
/* 17/09/2026 (séptima corrección), pedido de Gus: acercar un poco más el icono "+"/"-"
   al texto "Detalles" -- probó dos opciones (reducir el hueco un 50% o un 30%) y se
   quedó con el 30% ("un 30% mejor"). De 14px de hueco (gap) a 10px (14 * 0.7 = 9.8,
   redondeado). */
/* 24/09/2026, pedido de Gus (sólo en esta "Opción 2", para ver cómo se ve): "que la
   parte que dice Detalles esté como fija en la pantalla... lo mismo que aparece en
   la página web, en la parte principal, la barra que dice catálogo, tú vas hacia
   abajo y se queda como fija" -- primer intento: mismo criterio que ".site-header"
   (arriba, barra del catálogo): "position:sticky; top:0" dentro de su propio
   contenedor con scroll (".modal-media"). DESCARTADO: medido con Playwright
   (getBoundingClientRect() cuadro a cuadro mientras se fuerza ".modal-media.scrollTop"),
   el botón NO quedaba fijo en absoluto -- se movía en exacto paralelo al scroll,
   0 corrimiento "pegado". No se identificó la causa exacta (una reproducción mínima
   con la misma estructura -- flex-column + overflow-y:scroll + hijo sticky -- sí
   funciona en el mismo Chromium), así que en vez de seguir cazando el bug se optó
   por otro enfoque más acorde al pedido SIGUIENTE de Gus (ver abajo).
   24/09/2026 (corrección sobre lo anterior, mismo día, pedido de Gus): "la parte de
   detalles también tiene que anclarse en la parte de abajo para que la persona...
   se dé cuenta que hay una opción ahí" -- ya no "se queda fija mientras se scrollea"
   (sticky), sino SIEMPRE visible desde el instante en que se abre la ficha, sin
   necesidad de scrollear primero para descubrirla. Un sticky (arriba o abajo) no
   logra esto: sólo se "pega" una vez que, al scrollear, pasaría de largo su
   posición natural -- si esa posición natural ya está fuera de la pantalla al
   abrir (como acá, después de 3 fotos), sticky no la trae a la vista antes de
   scrollear. Se usa en cambio "position:fixed", con "left"/"width"/"bottom"
   calculados por JS (pinDetailsBarToMediaBottom(), ver ese comentario) para calzar
   exacto con el borde inferior de ".modal-media" (la columna de fotos) -- esa
   columna no se mueve nunca (sólo su CONTENIDO scrollea puertas adentro), así que
   una vez calculado, el botón queda anclado ahí sin importar cuánto se scrollee.
   Fondo sólido (mismo color de ".modal-media") para que las fotos no se
   transparenten por debajo cuando el scroll las hace pasar bajo esta barra. */
.modal-extra-details-title-btn {
  width: 100%;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: 10px;
  border: none;
  cursor: pointer;
  padding: 10px 0;
  margin-bottom: 0;
  text-align: left;
  z-index: 6;
  position: static;
  /* 02/10/2026, pedido de Gus: "si bien se siga ubicando en la parte inferior
     izquierda, no se mantenga como pinneada" -- antes "position:fixed; bottom:0"
     dejaba la barra "Detalles" clavada en pantalla aunque se scrollee (ver el
     comentario grande de arriba, que describe ese anclaje como diseño anterior).
     Se saca el anclaje ("position:fixed"+"bottom") para que vuelva a ser un elemento
     normal del documento: sigue naciendo abajo de las fotos (al final del contenido
     de ".modal-media", mismo lugar de siempre) pero ahora scrollea junto con ellas en
     vez de quedarse fijo en la pantalla. */
}
.modal-extra-details-title {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.44rem;
  margin: 0;
}
.modal-extra-details-title-btn .accordion-icon {
  font-size: 1.26rem;
}
/* 17/09/2026, pedido de Gus: quitada la línea horizontal que quedaba pegada debajo del
   título "Detalles" (este "border-top" se veía SIEMPRE, incluso con el acordeón
   cerrado, ya que sólo su alto -- no el borde -- dependía de estar abierto/cerrado). */
.accordion {
  /* 02/10/2026, pedido de Gus (captura anotada): el texto del acordeon ("SOBRE LA
     PIEZA", etc.) nacia pegado contra las miniaturas de arriba (0px de hueco,
     room-preview-row terminaba exactamente donde este empezaba). Se le da un
     respiro fijo. Para no agrandar el bloque total (ya hay riesgo de que "Detalles"
     se salga de pantalla en ventanas bajas, ver comentarios de
     ".accordion.open .accordion-panel" mas abajo), este aire sale de REDUCIR a la
     mitad el margen que ya existia DESPUES del texto (30/09 -> 15px -> 8px, ver esa
     regla), no de sumar aire nuevo -- el total ocupado no cambia casi nada. */
  margin-top: 8px;
  /* 02/10/2026: ".accordion" ahora es hijo directo de ".modal-media-scroll" (flex
     column, "align-items:center") en vez de hijo de un div normal -- sin esto, al
     ser un item de flex sin ancho propio quedaria encogido/centrado al ancho de su
     contenido (texto) en vez de ocupar todo el ancho de la columna, como siempre
     tuvo. "align-self:stretch" restaura el ancho completo (el comportamiento que
     ya tenia "gratis" antes, por ser un bloque normal). */
  align-self: stretch;
  width: 100%;
  /* 03/10/2026, pedido de Gus: al reducir un 25% la distancia entre cada opcion
     del acordeon (ver ".accordion-header-label"/".accordion-panel p" mas abajo),
     el bloque completo mide menos de alto -- pidio que esa reduccion se note
     "alejando" el bloque de las fotos (mas aire arriba), no acortando el calce
     de abajo con "Detalles" (ese calce queda garantizado aparte, de forma
     puramente geometrica, por ".accordion.open .accordion-panel{margin-bottom:0}"
     -- ver ese comentario). Se suma aca exactamente lo que se ahorra abajo: 6
     encabezados x 2.5px (10px -> 7.5px) + 6 parrafos x 4px (16px -> 12px) = 39px.
     8px (el margen de siempre) + 39px = 47px. */
  margin-top: 47px;
}
.accordion-item {
  border-bottom: 1px solid var(--line);
}
.accordion-item:last-child {
  border-bottom: none;
}
/* 16/09/2026: reemplaza ".accordion-trigger" (boton individual por encabezado, ya no
   existe) -- el encabezado de cada seccion queda como texto simple, no clickeable. */
.accordion-header-label {
  width: 100%;
  /* 02/10/2026, pedido de Gus: reducir un 30% el hueco entre cada encabezado
     ("CUIDADOS", etc.) y su parrafo de abajo -- de "padding:10px 2px" (10px
     arriba Y abajo) a padding-bottom:7px (10 * 0.7 = 7), dejando padding-top
     y los costados exactamente igual que antes (10px arriba, 2px a los lados). */
  /* 03/10/2026, pedido de Gus: reducir un 25% la distancia entre cada opcion de
     la lista y la siguiente -- esa distancia sale de sumar el padding-bottom
     del parrafo anterior (16px) con este padding-top (10px). Se reduce cada uno
     un 25% por igual: 10 * 0.75 = 7.5. */
  padding: 7.5px 2px 7px;
  text-align: left;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink);
}
.accordion-icon {
  font-size: 1.08rem;
  font-weight: 400;
  flex-shrink: 0;
  width: 14px;
  text-align: center;
}
/* 30/09/2026 (segunda corrección, mismo día), pedido de Gus: "se abre de forma menos
   fluida y al cerrarse... no tiene la misma forma que al aperturarse... busco que se
   cierre igual que como se abre". La primera corrección (animar "height" cuadro a
   cuadro a mano, con JS) sí abría y cerraba con la misma matemática, pero notarse
   "menos fluido" que antes: mover un estilo a mano en cada cuadro obliga al navegador a
   recalcular el layout en cada uno (más pesado que una transición nativa). Se cambia a
   que el propio CSS anime "height" con "transition" (motor nativo del navegador, igual
   de fluido en los dos sentidos porque es la MISMA transición para abrir y cerrar) --
   toggleDetailsAccordion() ahora sólo mide el alto ACTUAL, lo fija como número
   (reemplazando "auto" si estaba abierto) y en el siguiente cuadro pone el alto
   DESTINO (0 al cerrar, el alto real del contenido al abrir) -- la transición de acá
   abajo hace todo el movimiento. Al terminar de abrir, un timeout (misma duración)
   libera el alto a "auto" para que se adapte si el contenido cambia después (ver
   comentario junto a toggleDetailsAccordion()). */
.accordion-panel {
  height: 0px;
  overflow: hidden;
  transition: height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 16/09/2026: antes era ".accordion-item.open" (cada seccion se abria por separado) --
   ahora las 6 comparten un solo estado, ".accordion.open" en el contenedor. */
/* 02/10/2026, pedido de Gus: el espacio entre el ultimo texto desplegado
   ("Devoluciones") y el boton "Detalles" (que ahora vive al fondo de la columna,
   ver ".modal-extra-details{margin-top:auto}") quedaba "muy separado" y a veces
   empujaba "Detalles" fuera de pantalla en ventanas mas bajas -- se reduce a la
   mitad, 30px -> 15px. Esta regla es solo del acordeon de escritorio
   (".accordion.open", el que togglea "Detalles"): el panel "siempre abierto" de
   movil (".accordion-panel.accordion-force-open", fuera de ".accordion") no
   coincide con este selector y no se ve afectado -- confirmado con
   ".matches()" antes de tocar nada. */
.accordion.open .accordion-panel {
  /* 03/10/2026, pedido de Gus (captura anotada, flecha verde): el texto
     desplegado debe llegar hasta tocar "Detalles" sin ningun hueco de por
     medio -- se quita este margen por completo (era 32px, pensado para una
     version anterior en la que "Detalles" vivia DENTRO de este mismo flujo,
     ver historial debajo). Desde la restructuracion del 02/10
     (".modal-media-scroll", ver CSS/HTML), ".modal-media-scroll" (el que
     contiene este acordeon) y "Detalles" son hermanos de flex que SIEMPRE se
     tocan entre si (uno termina exactamente donde el otro empieza, sea cual
     sea el contenido) -- al llegar al final del scroll, el ultimo pixel
     visible del acordeon cae justo en el borde de ".modal-media-scroll", que a
     su vez ya esta pegado a "Detalles". Con este margen en 0, ya no queda
     ningun hueco de sobra DESPUES del texto, asi que el calce con "Detalles"
     es automatico, geometrico, sin depender de ningun numero fijo. */
  margin-bottom: 0;
}
.accordion-panel-inner {
  overflow: hidden;
  min-height: 0;
}
.accordion-panel p {
  color: var(--muted);
  font-size: 0.936rem;
  line-height: 1.6;
  /* 03/10/2026, pedido de Gus: reducir un 25% la distancia entre cada opcion de
     la lista y la siguiente -- la otra mitad de ese hueco (ver
     ".accordion-header-label{padding-top}" de arriba): 16 * 0.75 = 12. */
  padding: 0 2px 12px;
  margin: 0;
  text-align: justify;
}
/* 16/09/2026, pedido de Gus (segunda correccion): el titulo del diseño baja junto con
   la foto grande (mismo valor, 108px) -- ver comentario en ".modal-media" de arriba:
   las dos partes de arriba (marco de la foto y titulo) deben coincidir en altura entre
   si, ya NO con "Volver" (que se queda en 16px, independiente). */
/* 17/09/2026, pedido de Gus: "quita la barra de scrolleo de la parte derecha... no es
   necesaria esa barra, no tienes que bajar, se ve todo con claridad". Primer intento:
   "overflow-y:visible" -- deshecho de inmediato (ver getBoundingClientRect() de prueba):
   sin "auto"/"hidden" que la recorte, esta columna dejaba de contener su contenido, y
   ".modal-inner" (el grid que reparte esta columna y la de las fotos) crecía para
   acomodar ese contenido sin recortar -- agrandando de paso TODA la columna de fotos
   (".modal-media") más allá del alto del modal, lo que a su vez hacía que las fotos se
   recalcularan más grandes (violaría "no agrandes ni achiques las fotos"). Se usa
   "hidden" en su lugar: sigue recortando (conteniendo) esta columna a su alto real, así
   que el modal no se mueve ni las fotos cambian de tamaño, pero ya no dibuja NINGUNA
   barra (ni siquiera al pasar el mouse, como sí podía pasar con "auto" en algunos
   sistemas). Ya no hace falta poder scrollear esta columna de todos modos:
   pinTotalToRoomBottom() (ver JS) frena el corrimiento de ".modal-footer" antes de que
   el botón "Añadir al carrito" llegue a salirse del borde de abajo (con 8px de aire),
   así que en la práctica el contenido siempre entra sin necesitar scroll propio. Esto
   también evita que aparezca/desaparezca una barra nativa entre un estado y otro --
   angostaba o ensanchaba el ancho disponible de esta columna de golpe, un efecto
   secundario que se sentía como si "todo el lado derecho" temblara o se corriera al
   cambiar de foto. */
.modal-details {
  /* 03/10/2026, pedido de Gus (linea roja desde "Detalles" hasta "Añadir al
     carrito"): seguian sin calzar exacto (quedaba ~20px de diferencia). Esta
     columna usa "margin-top:auto" en ".modal-footer" (mas abajo) para empujarlo
     al fondo -- con "box-sizing:border-box" y "height:100%", ese fondo es el
     borde de ABAJO DEL PADDING, no el borde real de la columna; con
     "padding-bottom:20px" el boton quedaba pegado 20px ANTES del borde real.
     ".modal-media" (columna de las fotos, misma altura exacta que esta) no tiene
     ese problema: "Detalles" vive en un contenedor hermano con
     "margin-bottom:-28px" que lo mete DENTRO del padding a proposito (ver CSS de
     ".modal-extra-details") para llegar al borde real. Se aplica el mismo
     criterio aca de la forma mas directa: "padding-bottom:0" (de 20px a 0) hace
     que el borde del padding Y el borde real coincidan, asi "Añadir al carrito"
     (ultimo elemento, empujado por "margin-top:auto") llega solo, sin ningun
     calculo en JS, exactamente al mismo borde de abajo que "Detalles" -- las dos
     columnas comparten el mismo alto exacto (grid de ".modal-inner"), asi que
     "borde real de abajo" es el mismo numero para ambas. */
  /* 03/10/2026, pedido de Gus (ver comentario grande junto a ".modal-media" sobre
     el mismo cambio, 73px -> 28px): esta columna y la de las fotos comparten
     SIEMPRE el mismo padding-top (es como el titulo queda a la misma altura que
     el marco, sin JS, por construccion) -- se mueve el mismo numero aca para
     mantener esa relacion. */
  padding: 28px 44px 0 36px;
  display: flex;
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
  overflow-y: hidden;
  /* 02/10/2026, pedido de Gus: "Total"/"Añadir al carrito" debian bajar 24px fijos
     para coincidir con la nueva posicion de "Detalles" (ver
     DETAILS_ALIGN_EXTRA_SHIFT en pinTotalToRoomBottom(), JS) -- pero el tope de
     seguridad que ya existia (BOTTOM_SAFE_GAP, misma funcion: nunca dejar que
     "Añadir al carrito" pase del borde visible de esta columna) frenaba ese
     corrimiento antes de que llegara a aplicarse, porque el boton ya vivia pegado
     a ese limite. Gus pidio explicitamente mantener ese tope de seguridad intacto
     ("esa parte no deberia bajar mas en ningun caso") y resolver el corrimiento de
     otra forma. Como esta columna usa "box-sizing:border-box" y "height:100%", su
     borde exterior (lo que mide "detailsRect.bottom", el limite real) NO depende
     de cuanto padding tenga adentro -- reducir el padding-bottom de 44px a 20px
     (24px menos) le da a "Total"/el boton 24px mas de espacio utilizable SIN
     mover ni un pixel el borde real de la columna ni el alto del modal (que
     tambien determina el tamaño de las fotos, ver el comentario grande de arriba
     sobre por que "overflow-y:hidden" y no "visible" -- esto no lo toca). El tope
     de seguridad (BOTTOM_SAFE_GAP:8px desde el borde real) sigue exactamente
     igual que antes, solo que ahora hay margen de sobra para que el corrimiento
     de 24px entre sin chocar contra el. */
}
.modal-details .eyebrow {
  margin-bottom: 10px;
}
/* 16/09/2026, pedido de Gus: aunque la caja de "Nora" (h2) ya empieza a la
   misma altura que el marco de la foto (44px ambos), la letra visible queda
   ~10px mas abajo por el interlineado propio de la tipografia (medido pixel a
   pixel con Playwright: el borde del marco empieza en y=44, la "N" recien en
   y=54). Un margin-top negativo sube el texto visible esos mismos 10px para
   que coincida de verdad con el marco. */
.modal-details h2 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 2.28rem;
  margin-top: -10px;
  margin-bottom: 14px;
}
.modal-details .desc {
  color: var(--muted);
  font-size: 1.104rem;
  line-height: 1.6;
  margin-bottom: 26px;
}
.pair-note {
  font-size: 0.984rem;
  color: #000;
  background: rgba(80, 26, 25, 0.06);
  border-radius: 2px;
  padding: 10px 12px;
  margin: -14px 0 22px;
  line-height: 1.5;
}
.pair-note strong {
  font-weight: 700;
}
.option-group {
  margin-bottom: 30px;
}
/* 14/09/2026, pedido de Gus: "Tamaño", "Acabado de marco —" y "Cantidad" ya
   vienen en mayúscula-minúscula correcta desde los datos (t('label_tamano')
   etc. y el nombre del acabado, ej. "castaña"); lo que las mostraba TODO EN
   MAYÚSCULAS era este text-transform:uppercase, ahora quitado. */
/* 14/09/2026 (2do ajuste), pedido de Gus: la negrita de estas etiquetas está
   bien, pero deben verse del mismo tamaño que el texto señalado en naranja
   (.desc / el aviso de variación de color, ambos a 1.104rem) -- antes
   0.936rem, un paso más chico en la escala del sitio. */
.option-label {
  font-size: 1.104rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--ink);
  margin-bottom: 10px;
  display: block;
}
.pill-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.pill {
  border: 1.5px solid var(--line);
  border-radius: 3px;
  padding: 8px 6px;
  font-weight: 600;
  transition: all 0.15s;
  text-align: center;
  /* 14/09/2026, pedido de Gus: EB Garamond solo para los números de tamaño
     (cm), mismo font-size que el precio (.price-line .big) para
     uniformizar.
     14/09/2026 (2do ajuste), pedido de Gus: reducir un paso el tamaño de
     estos números -- de 1.104rem a 1.08rem (el paso inmediato de abajo en
     la escala que ya usa el sitio, ej. .cart-btn, .link-ghost, .pillar p).
     14/09/2026 (3er ajuste), pedido de Gus: un paso más abajo -- de 1.08rem
     a 1.056rem (siguiente paso de la misma escala, ej. .ci-price, .os-line,
     el input de búsqueda del header).
     14/09/2026 (4to ajuste), pedido de Gus: un paso más abajo -- de 1.056rem
     a 1.032rem (siguiente paso de la misma escala, ej. .card-price,
     .trust-strip).
     14/09/2026 (5to ajuste), pedido de Gus: esta vez SOLO los tamaños en cm
     (no el precio, que se queda en 1.032rem -- ver .price-line .big) bajan
     dos pasos más -- de 1.032rem a 0.984rem y luego a 0.96rem (siguientes
     pasos de la misma escala, ej. .swatch-name/.cart-btn en mobile, y
     .lang-toggle/.field label). */
  font-family: "EB Garamond", serif;
  font-size: 0.96rem;
}
.pill.active {
  border-color: var(--burgundy);
  background: var(--burgundy);
  color: #fff;
}
.swatches {
  display: flex;
  gap: 12px;
  align-items: center;
}
.swatch {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  position: relative;
}
.swatch.active {
  border-color: var(--burgundy);
  box-shadow:
    0 0 0 2px var(--paper),
    0 0 0 3px var(--burgundy);
}
.swatch-name {
  font-size: 0.984rem;
  color: var(--muted);
  margin-left: 4px;
}
/* 16/09/2026, pedido de Gus: "Tamaño"/"Acabado de marco"/"Cantidad" (el primer
   grupo de opciones de esta columna) se centraban verticalmente entre el
   titulo/descripcion de arriba y el boton "Añadir al carrito" de abajo --
   margin-top:auto en el primer .option-group + el margin-top:auto que ya
   tenia .modal-footer repartian el espacio libre en dos mitades iguales.
   03/10/2026, pedido de Gus (con captura): ya no es "centrado" -- ahora
   "Cantidad" debe coincidir EXACTO con la parte de abajo de la foto grande, y
   el hueco titulo->Tamaño, Tamaño->Acabado y Acabado->Cantidad debe ser
   EXACTAMENTE el mismo. SOLO CSS (pedido explicito de Gus, sin tocar
   script.js) -- las funciones que antes median esto en vivo con JS
   (alignAcabadoToArtFrame(), equalizeCantidadGap(), etc.) ya no se llaman
   desde ningun lado (se puede confirmar: no hay un solo "nombreFuncion()" en
   todo el archivo fuera de su propia definicion y de comentarios) -- quedaron
   como codigo muerto de una version anterior de esta ficha, as que el ajuste
   de hoy tiene que calcularse a mano, en numeros fijos, igual que hacia esa
   JS pero sin ella.
   Los numeros de abajo se calcularon así: alto disponible = la misma parte de
   abajo de ".art-frame" (28px de padding + 690px de alto, ver
   ".modal-media-inner{height}" mas arriba = 718px) menos donde termina de
   verdad el bloque de arriba (la nota "Pieza hermana" en piezas con pareja,
   o la descripcion en piezas sin pareja -- distinto punto de partida, por
   eso ":has(.pair-note)" separa los dos casos) menos la suma de alto de
   Tamaño+Acabado+Cantidad (sin cambios, 111+61+60=232px) -- lo que sobra se
   reparte en 3 huecos iguales, y a cada "margin-top" se le resta el margen
   que ya traía el elemento de arriba (pair-note/.desc/.option-group) para
   que el hueco FINAL (ya sumados los dos márgenes, que no se funden entre sí
   en flexbox) de exactamente ese tercio. Mismo criterio, con los numeros
   propios de esa altura, para la variante de pantallas bajas
   (@media max-height:800px, mas abajo, donde ".art-frame" mide 540px en vez
   de 690px). Medido y verificado con Playwright, en los dos casos (con/sin
   pareja) el resultado da exactamente los 3 huecos iguales y "Cantidad"
   coincidiendo con la foto (0-0.1px de diferencia, redondeo). */
.modal-details:has(.pair-note) > .option-group:first-of-type {
  margin-top: 96.458px;
  margin-bottom: 30px;
}
.modal-details:not(:has(.pair-note)) > .option-group:first-of-type {
  margin-top: 111px;
  margin-bottom: 30px;
}
.modal-details:has(.pair-note) #acabadoGroup {
  margin-top: 88.458px;
}
.modal-details:not(:has(.pair-note)) #acabadoGroup {
  margin-top: 107px;
}
.modal-details:has(.pair-note) #cantidadGroup {
  margin-top: 88.458px;
}
.modal-details:not(:has(.pair-note)) #cantidadGroup {
  margin-top: 107px;
}
@media (max-height: 800px) {
  .modal-details:has(.pair-note) > .option-group:first-of-type {
    margin-top: 46.458px;
  }
  .modal-details:not(:has(.pair-note)) > .option-group:first-of-type {
    margin-top: 61px;
  }
  .modal-details:has(.pair-note) #acabadoGroup {
    margin-top: 38.458px;
  }
  .modal-details:not(:has(.pair-note)) #acabadoGroup {
    margin-top: 57px;
  }
  .modal-details:has(.pair-note) #cantidadGroup {
    margin-top: 38.458px;
  }
  .modal-details:not(:has(.pair-note)) #cantidadGroup {
    margin-top: 57px;
  }
}
.qty-row {
  display: flex;
  align-items: center;
  gap: 16px;
}
.modal-footer {
  margin-top: auto;
  padding-top: 20px;
}
/* 17/09/2026 (segunda corrección), pedido de Gus, marcado con una línea celeste bajo
   "Total" en su captura: el botón "Añadir al carrito" debe subir hasta que su borde de
   ARRIBA toque esa línea -- es decir, prácticamente sin aire entre "Total" y el botón.
   Bajado de 16px (que era el aire real, medido, entre esta línea y el botón) a 2px. */
/* 24/09/2026 (séptima corrección, pedido de Gus, sólo "Opción 2"): "la parte de total
   tiene que estar ubicada entre la segunda y la tercera foto, alineada con el espacio
   que hay entre las dos" -- se agrega "position:relative" para poder moverlo con "top"
   (ver pinTotalToPairGap() en JS) sin afectar el resto del layout (a diferencia de un
   margin, un corrimiento con "top" no empuja a "Añadir al carrito", que sigue
   exactamente donde estaba -- pedido explícito de Gus en varias correcciones
   anteriores: "Añadir al carrito" nunca se mueve). */
.price-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 2px;
  width: 50%;
}
.price-line span:first-child {
  font-size: 1.2rem;
  font-weight: 600;
}
/* 14/09/2026, pedido de Gus: EB Garamond solo para el número del precio,
   mismo font-size que los números de tamaño en .pill -- antes 1.6rem con
   Cormorant Garamond.
   14/09/2026 (2do ajuste), pedido de Gus: reducir un paso el tamaño de
   estos números -- de 1.104rem a 1.08rem, igual que .pill.
   14/09/2026 (3er ajuste), pedido de Gus: un paso más abajo -- de 1.08rem
   a 1.056rem, igual que .pill.
   14/09/2026 (4to ajuste), pedido de Gus: un paso más abajo -- de 1.056rem
   a 1.032rem, igual que .pill. */
.price-line .big {
  font-family: "EB Garamond", serif;
  font-weight: 700;
  font-size: 1.032rem;
}
.micro-note {
  font-size: 0.936rem;
  color: var(--muted);
  margin-top: 12px;
  text-align: center;
}
.modal-footer .micro-note {
  font-size: 1.104rem;
}
@media (max-width: 800px) {
  .modal-inner {
    grid-template-columns: 1fr;
    margin: 0;
    border-radius: 0;
    min-height: 100%;
  }
  .modal-details {
    padding: 32px 24px 40px;
  }

  .modal-media-inner {
    height: 400px;
  }

  .modal-media {
    height: unset;
    padding-top: 80px;
    padding-bottom: 0;
  }
}

/* ---------- Checkout ---------- */
.checkout-overlay {
  position: fixed;
  inset: 0;
  background: var(--cream);
  z-index: 80;
  overflow-y: auto;
  transform: translateY(100%);
  /* 25/09/2026, primer intento (INSUFICIENTE, revertido): se había alargado la duración
     de .4s a .5s manteniendo la curva de siempre ("cubic-bezier(.2,.7,.2,1)"), pero Gus
     reportó que seguía viéndose "la misma estupidez". Se midió con Playwright, frame a
     frame, cuánto se desplazaba el panel en cada tramo de tiempo con esa curva: en los
     primeros ~107ms de los 500ms totales, el panel ya recorría 534 de los 835px totales
     (más de la mitad del recorrido "de un salto"), y los ~400ms restantes apenas movían
     los 301px que quedaban -- es decir, la curva concentraba casi todo el movimiento
     visible al principio, como un salto brusco, y el resto era una cola casi imperceptible.
     Por eso alargar la duración no arreglaba nada: solo alargaba esa cola invisible. Se
     cambia a la curva "cubic-bezier(.4,0,.2,1)", la misma que ya se usa en el resto del
     sitio para los movimientos "elegantes" (el desplegable del catálogo, el acordeón de
     detalle de producto, MODAL_TRANSITION_EASE) -- esta curva arranca despacio, acelera
     en el tramo medio y frena suave al final, repartiendo el movimiento de forma pareja
     en vez de concentrarlo al inicio. También se alarga un poco más la duración (.5s a
     .55s) ahora que la curva sí reparte el movimiento de forma pareja. */
  transition: transform 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}
.checkout-overlay.open {
  transform: translateY(0);
}
/* "Sobre Morello" debe abrirse debajo de la barra superior real (misma que en el
   inicio), no taparla — a diferencia del checkout, que sí cubre toda la pantalla. */
#aboutOverlay {
  top: 64.6px;
  z-index: 35;
  background: var(--bone);
}
#aboutOverlay .checkout-head {
  background: var(--bone);
  border-bottom: none;
}
/* Transicion rapida para "Contacto" (05/09/2026, revertido a scroll): en vez del
   scroll suave nativo (que hace ver bajar toda la web) o un salto seco sin transicion,
   se hace un fundido corto de opacidad, se salta la posicion del scroll de forma
   instantanea mientras la pagina esta invisible, y se vuelve a mostrar -- se ve como
   una transicion, pero no obliga a ver el recorrido completo. */
/* 25/09/2026, primer intento (INSUFICIENTE): se alargó el fundido de ".18s" a ".28s"
   con la curva "ease" (rápida al inicio, lenta al final), pero Gus reportó que seguía
   sintiéndose igual de brusco. La curva "ease" también concentra buena parte del cambio
   de opacidad al principio del tramo -- el mismo problema de fondo que se detectó y
   corrigió en ".checkout-overlay" (ver ese comentario, con las mediciones frame a frame).
   Se cambia a "cubic-bezier(.4,0,.2,1)", la curva "elegante" ya usada en el resto del
   sitio, y se alarga un poco más la duración (.28s a .35s) para que el fundido se note
   como una transición deliberada y no como un parpadeo. El "setTimeout" de
   "handleContactoNavClick()" en el JS se ajusta a la misma duración, para que el salto
   de scroll siga ocurriendo justo cuando la pantalla ya está completamente invisible, ni
   antes ni después. Sigue siendo mucho más corto que un scroll suave largo (lo que Gus
   pidió evitar el 05/09/2026), solo ya no se ve como un parpadeo brusco. */
body {
  transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
body.fast-scroll-fade {
  opacity: 0;
}
.checkout-head {
  position: sticky;
  top: 0;
  background: var(--cream);
  border-bottom: 1px solid var(--line);
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 32px;
}
.checkout-head-left {
  display: flex;
  align-items: center;
  gap: 22px;
}
.back-link {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1.104rem;
  font-weight: 700;
  color: #000;
  padding: 8px 10px 8px 4px;
  border-radius: 20px;
  transition: background 0.15s;
}
.back-link:hover {
  background: rgba(80, 26, 25, 0.06);
}
.checkout-wrap {
  max-width: 920px;
  margin: 0 auto;
  padding: 48px 32px 100px;
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 56px;
}
.checkout-wrap h2 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.68rem;
  margin-bottom: 24px;
}
.field {
  margin-bottom: 16px;
}
.field label {
  font-size: 0.96rem;
  font-weight: 700;
  margin-bottom: 6px;
  display: block;
}
.field input,
.field select {
  width: 100%;
  padding: 12px 14px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1.104rem;
  background: var(--paper);
}
.field input:focus,
.field select:focus {
  outline: 2px solid var(--burgundy);
  outline-offset: 1px;
  border-color: var(--burgundy);
}
@media (max-width: 768px) {
  .field input,
  .field select {
    font-size: 16px;
  }
}
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.pay-methods {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 18px 0 26px;
}
.pay-method {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  font-size: 1.08rem;
  font-weight: 600;
}
.pay-method.active {
  border-color: var(--burgundy);
  background: rgba(80, 26, 25, 0.05);
}
.pay-icon {
  width: 40px;
  flex-shrink: 0;
  text-align: center;
  font-size: 1.38rem;
}
.pay-icon-yape {
  font-weight: 700;
  font-size: 0.984rem;
  color: #7b2d8e;
}
.pay-details {
  background: var(--cream);
  border-radius: var(--radius);
  padding: 12px 16px;
  font-size: 1.02rem;
  line-height: 1.7;
  margin: -10px 0 18px;
}
.secure-payments {
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.secure-payments-label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.936rem;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 12px;
}
.card-badges-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.card-badge {
  font-size: 0.816rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 6px 10px;
  border-radius: 4px;
  color: #fff;
}
.card-badge-visa {
  background: #1a1f71;
}
.card-badge-mc {
  background: #242424;
}
.card-badge-amex {
  background: #016fd0;
}
.card-badge-diners {
  background: #0079be;
}
.card-badge-applepay {
  background: #000;
  font-family: -apple-system, sans-serif;
}
.card-badge-gpay {
  background: #f1f3f4;
  color: #3c4043;
}
.secure-payments-note {
  font-size: 0.888rem;
  color: var(--muted);
  margin-top: 10px;
}
.footer-payment-badges .card-badge {
  font-size: 0.41rem;
  padding: 3.6px 6px;
  border-radius: 2.5px;
}
.pay-details-yape {
  display: flex;
  align-items: center;
  gap: 14px;
}
.pay-qr-placeholder {
  width: 64px;
  height: 64px;
  flex-shrink: 0;
  border: 1.5px dashed var(--muted);
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 0.62rem;
  color: var(--muted);
  padding: 4px;
  line-height: 1.3;
}
.delivery-note {
  background: rgba(80, 26, 25, 0.05);
  padding: 10px 14px !important;
  border-radius: var(--radius);
  margin-top: 6px;
}
.order-summary {
  background: var(--paper);
  border-radius: var(--radius);
  padding: 26px;
  height: fit-content;
  border: 1px solid var(--line);
}
.order-summary h3 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  margin-bottom: 18px;
  font-size: 1.26rem;
}
.os-item {
  display: flex;
  align-items: center;
  font-size: 1.02rem;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
  gap: 10px;
}
.os-thumb-wrap {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 2px;
  padding: 3px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.os-item-text {
  flex: 1;
  color: var(--muted);
}
.os-line {
  display: flex;
  justify-content: space-between;
  font-size: 1.056rem;
  padding: 8px 0;
  color: var(--muted);
}
.os-total {
  display: flex;
  justify-content: space-between;
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.38rem;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  margin-top: 4px;
}
@media (max-width: 760px) {
  .checkout-wrap {
    grid-template-columns: 1fr;
  }
}

/* ---------- Confirmation ---------- */
.confirm-view {
  text-align: center;
  max-width: 520px;
  margin: 80px auto;
  padding: 0 24px;
}
.confirm-view .check-badge {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--burgundy);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 24px;
  font-size: 1.92rem;
}
.confirm-view h2 {
  font-family: "Cormorant Garamond", serif;
  font-weight: 700;
  font-size: 1.92rem;
  margin-bottom: 14px;
}
.confirm-view p {
  color: var(--muted);
  line-height: 1.6;
  margin-bottom: 28px;
}

/* ---------- WhatsApp floating button ---------- */
.whatsapp-fab {
  position: fixed;
  bottom: 28px;
  right: 28px;
  z-index: 45;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: #25d366;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.28);
  transition:
    transform 0.2s ease,
    background 0.2s ease;
}
.whatsapp-fab:hover {
  transform: translateY(-3px);
  background: #1ebe5a;
}
.whatsapp-fab svg {
  width: 30px;
  height: 30px;
}
@media (max-width: 640px) {
  .whatsapp-fab {
    width: 50px;
    height: 50px;
    bottom: 18px;
    right: 18px;
  }
  .whatsapp-fab svg {
    width: 24px;
    height: 24px;
  }
}
.hidden {
  display: none !important;
}

@media (max-width: 800px) {
  .hidden-mobile {
    display: none !important;
  }

  .modal-inner {
    display: block;
    overflow: scroll;
  }

  .modal-details {
    overflow-y: unset;
  }

  .modal-footer .btn-primary.cart-btn-compact {
    width: 100%;
  }
}

@media (min-width: 801px) {
  .hidden-desktop {
    display: none !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

.mini-preview {
  cursor: pointer;
  height: 100%;
}

.mini-preview img {
  opacity: 0.3;
}

.mini-preview.selected img {
  opacity: 1;
}

.accordion-force-open {
  margin-top: 30px;
  height: unset !important;
  overflow: unset !important;
}

@media (max-width: 480px) {
  .site-header .container {
    padding: 0 14px;
  }
  .header-right {
    gap: 8px;
  }
  .lang-toggle {
    font-size: 0.9rem;
  }
  .cart-btn {
    font-size: 0.984rem;
    gap: 5px;
  }
  .header-left {
    gap: 12px !important;
    margin-right: 12px;
  }
  .logo-img {
    height: auto;
  }
}
