:root {
  --black: #030102;
  --near-black: #0a0406;
  --wine: #5c0e18;
  --wine-bright: #c22a39;
  --bone: #f4efec;
  --bone-dim: #cabcb8;
  --font-serif: "Cormorant Garamond", Georgia, serif;
  --font-sans: "Jost", system-ui, sans-serif;
  /* Góticas (ver memoria web-banash-fuentes-goticas): jerarquía acordada
     -- Grenze Gotisch para títulos de sección, Metamorphous para
     nav/nombres de producto, Germania One solo para badges/etiquetas
     cortas. 2026-08-25: reemplazan a Pirata One/MedievalSharp/Uncial
     Antiqua -- esas tres leían "medieval de fantasía" (caligráficas,
     redondeadas) y no encajaban con el wordmark ENTROPÍA, que es un
     logo estilo black-metal (anguloso, con goteos). Este trío es más
     sólido/anguloso, mismo registro que el logo. Cuerpo de texto largo
     se queda en --font-serif (Cormorant), ninguna gótica es cómoda
     para párrafos. */
  --font-display: "Grenze Gotisch", Georgia, serif;
  --font-display-alt: "Metamorphous", Georgia, serif;
  --font-accent: "Germania One", Georgia, serif;
}

/* 2026-08-27, bug real reportado (celular real del usuario): CUALQUIER
   elemento interactuable mostraba el recuadro azul nativo de "toque"
   de Chrome/Safari móvil al tocarlo -- universal en `*` a propósito,
   no solo en botones, porque el sitio también usa tarjetas/divs
   clickeables (`.shop-card`, `.coleccion__card`, etc.) que no son
   `<button>` pero sí reciben ese resaltado igual. */
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  background: var(--black);
  color: var(--bone);
  font-family: var(--font-sans);
  overflow-x: hidden;
}

/* 2026-08-26, a pedido del usuario: arrastrar el mouse por encima de
   una imagen (o de un ícono) la seleccionaba con el resaltado azul
   nativo del navegador -- se ve poco profesional, sobre todo en el
   logo/lockup de marca. `user-select:none` quita ese resaltado,
   `-webkit-user-drag:none` evita el "fantasma" de arrastre que
   aparece al intentar sacar la imagen del navegador, y
   `-webkit-touch-callout:none` apaga el menú "Guardar imagen" que
   aparece en iOS con una pulsación larga. Ojo: esto es UX, no
   seguridad real -- no impide guardar por clic derecho > Guardar
   imagen como, ni por las herramientas de desarrollador; no existe
   una forma 100% efectiva de evitar eso solo con CSS/HTML en el
   cliente. Si más adelante se quiere ir más lejos (bloquear también
   el clic derecho), es un cambio aparte -- no se activó acá porque no
   se pidió y es un patrón que a muchos usuarios les resulta molesto. */
img {
  max-width: 100%;
  display: block;
  -webkit-user-drag: none;
  user-select: none;
  -webkit-touch-callout: none;
}
svg { user-select: none; }

/* ============ HERO ============ */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  isolation: isolate;
  overflow: hidden;
  /* Nada acá es contenido para copiar (logo, cuervos/nubes
     decorativos, nav, tagline) -- un arrastre del mouse por encima ya
     no deja el resaltado azul nativo del navegador. Los hijos lo
     heredan salvo que se re-habilite explícito, no hace falta
     repetirlo en cada pieza. */
  user-select: none;
}

.hero__sky {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -6;
}

/* Nubes: site/assets/hero-clouds.png (web-banash/nubes.png) tal cual,
   sin recortar ni re-teñir -- se usa dos/tres veces para enmarcar la
   base del hero. La imagen trae una franja ancha de alfa muy bajo (no
   0%) en vez de transparencia limpia en los bordes; sobre el degradado
   oscuro del cielo eso se veía como un recuadro tenue ("cuadrado
   invisible" reportado) -- la máscara de abajo funde esos bordes a
   transparente real sin tocar el archivo. */
.hero__clouds {
  position: absolute;
  inset: 0;
  z-index: -5;
  pointer-events: none;
  overflow: hidden;
}
.cloud {
  position: absolute;
  /* bottom:0 (no colgar % del contenedor) -- la nube ya tiene su propio
     desvanecido radial que no llega a 0% de alfa justo en su borde
     inferior real (el gradiente no cubre tanto), así que "colgar" la
     imagen por debajo del contenedor (como antes, bottom:-10%) cortaba
     el borde ANTES de esa zona ya difuminada, en la parte todavía
     opaca -- se veía como una línea recta en vez de un desvanecido.
     Además ese % era relativo al alto del contenedor (.hero, que puede
     crecer más que la pantalla si el contenido no entra), no al tamaño
     de la propia imagen (que crece con el ancho de ventana, vw) -- en
     pantallas anchas el corte quedaba aún más adentro de la zona
     opaca. bottom:0 deja ver el propio desvanecido de la imagen, que sí
     alcanza a disimular el borde. */
  bottom: 0;
  width: 40vw;
  min-width: 320px;
  opacity: 0.9;
  -webkit-mask-image: radial-gradient(ellipse 68% 66% at 50% 48%, #000 55%, transparent 96%);
          mask-image: radial-gradient(ellipse 68% 66% at 50% 48%, #000 55%, transparent 96%);
}
.cloud--bl { left: -10%; }
.cloud--br { right: -10%; transform: scaleX(-1); }
.cloud--bc { display: none; }

/* Nubes de arriba (tl/tr, .hero__clouds--top) quitadas a pedido del
   usuario (2026-08-24): "no esta bien puesta" -- nunca se logró una
   posición/tamaño que se viera bien enmarcando el logo BANASH sin
   verse desalineada o (al agrandarla) sacar a la luz el halo del
   raven--e centrado. Solo quedan las nubes de abajo. */

/* La tercera copia centrada (.cloud--bc, mismo archivo) cierra el hueco
   de cielo liso que queda entre las dos de esquina. Antes vivía en un
   @media (min-width:900px) propio; ahora se define junto al resto del
   sistema proporcional (ver bloque "HERO EN PROPORCIÓN FIJA"), para que
   las nubes no cambien de composición en ningún breakpoint. */

/* .cloud--bc partida en dos mitades (2026-08-24, a pedido del usuario)
   -- ataca la causa real de por qué la costura derecha se veía cortada
   y la izquierda no.

   Medido en hero-clouds.png (columnas de alfa desde cada borde):
   izquierdo ~54-65 de 255 (tenue), derecho ~93-97 de 255 (denso). bl y
   br SÍ son mirror exacto una de otra (mismo archivo, una normal y otra
   con scaleX(-1)) y por eso ya son simétricas entre sí -- el borde de
   cada una que mira al centro siempre termina siendo el borde derecho
   original (denso), no importa cuál sea. El problema real es .cloud--bc:
   al no espejarse nunca, su lado izquierdo (tenue) y su lado derecho
   (denso) son fijos. Contra bl (denso) queda denso+tenue -> blend
   suave. Contra br (denso) queda denso+denso -> choque, se ve corte.

   Fix: en vez de una imagen completa, .cloud--bc son dos mitades de
   50% cada una, y LAS DOS muestran la mitad IZQUIERDA (tenue) del
   archivo -- la derecha, espejada con scaleX(-1). Así el lado que
   toca a bl y el lado que toca a br son igual de tenues, igual que ya
   pasaba a la izquierda. El costado interior de ambas mitades (donde
   se juntan en el centro de bc) queda en la columna central del
   archivo (alfa intermedio), simétrico consigo mismo.

   Técnica: cada mitad es un wrapper de 50% con overflow:hidden, con la
   imagen adentro al 200% (mismo ancho que tenía el .cloud--bc entero)
   -- el wrapper solo dibuja la porción que le corresponde. Para la
   mitad derecha, además de espejar con scaleX(-1), se corre la imagen
   -100% para que el wrapper muestre la mitad que, después del espejo,
   corresponde a la mitad IZQUIERDA original (verificado con la
   aritmética del flip: origen del transform en el centro de la imagen,
   así que un punto en x pasa a -x; con el corrimiento de -100% el
   punto que queda visible en el borde exterior del wrapper es
   exactamente el borde izquierdo original del archivo).

   .cloud--bc conserva su tamaño/posición/máscara de siempre (clases
   "cloud cloud--bc", nada de eso cambió) -- solo pasó de ser un <img>
   a ser un contenedor con dos hijos. aspect-ratio:1/1 reemplaza el
   alto automático que antes daba el propio <img> (un div no tiene
   proporción intrínseca). */
.cloud--bc { aspect-ratio: 1 / 1; }

.cloud--bc-half {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100%;
  overflow: hidden;
}
.cloud--bc-half--l { left: 0; }
.cloud--bc-half--r { right: 0; }

.cloud--bc-half img {
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  max-width: none; /* el reset global pone img{max-width:100%}, hay que anularlo o el recorte no funciona */
  height: auto;
}
.cloud--bc-half--r img {
  left: -100%;
  transform: scaleX(-1);
}

/* ============ FONDO "SE RECONSTRUYE DE CENIZAS" (solo Home) ============
   2026-08-27, agrupa cielo/nubes/grano/viñeta/bandada (todos siguen
   exactamente igual, ver sus propias reglas) en un contenedor que se
   revela de ABAJO hacia ARRIBA al entrar a la página.

   v3 (misma tarde, a pedido del usuario): v2 tenía un borde
   `clip-path:polygon()` dentado simulando ceniza desmoronándose, más
   un canvas de partículas -- el usuario lo vio y pidió explícito
   "quita las cenizas, no parecen cenizas... esas puntas dañan la
   estética... quiero algo fluido y limpio". Se sacaron las dos piezas
   -- el dentado se veía como un defecto/glitch, no como ceniza real,
   y las partículas del canvas (puntos chicos con gradiente radial) no
   leían como brasas a ese tamaño. v3 vuelve a algo simple: la tapa
   sigue existiendo (`.hero__ash-cover`) pero con un borde LISO,
   suavizado con `mask-image` (degradado, no una línea dura ni
   dientes) -- fluido de verdad. `.hero__ash-glow` (resplandor cálido,
   sin partículas ni bordes) es lo único que queda para la sensación
   de "algo se enciende". Sigue cumpliendo la regla de oro de
   `web-animation-design` (solo `transform`/`opacity` animados,
   `mask-image` es un valor fijo -- gratis).

   1) `.hero__backdrop` (cielo/nubes/bandada) no se anima -- normal
      todo el tiempo.
   2) `.hero__ash-cover`: tapa opaca por encima del fondo. Solo
      `transform:scaleY()` animado, `transform-origin:top` -- el
      borde superior queda fijo, el inferior sube hacia él, revelando
      de abajo hacia arriba. El borde inferior se desvanece los
      últimos ~14% de la propia altura de la tapa vía `mask-image`
      (degradado suave, no polígono) -- se siente como una bruma que
      se disipa, no un corte.
   3) `.hero__ash-glow`: resplandor cálido (radial) que solo anima
      `opacity`.

   Va oculto por defecto -- no hay flash porque `#page-veil` (negro
   opaco, z-index altísimo) ya cubre todo antes de que
   page-transition.js agregue `.hero--reveal`. Vuelta por bfcache: la
   clase ya quedó puesta y la animación (`forwards`) ya terminó en su
   estado final -- no hace falta re-disparar nada. */
.hero__backdrop {
  position: absolute;
  inset: 0;
}
/* v4 (2026-08-27, otra ronda): el usuario vio la transición entre la
   tapa y el rojo revelado "muy sólida" y pidió (a) que la parte
   TODAVÍA tapada sea negro puro (no el degradado cálido -#050202 a
   #120705- que traía antes) y (b) un desvanecido bastante más largo
   en el borde. Cambios: `background` pasa a `#000` liso; el
   `mask-image` pasa de un solo tramo angosto (86%→100%, ~14% de la
   propia altura de la tapa) a un desvanecido LARGO Y CON CURVA
   (empieza recién a los 45%, con varias paradas de alfa intermedias
   en vez de una rampa lineal sola) -- una rampa lineal larga igual se
   percibe "a rayas"/dura al ojo; varias paradas con alfa decreciente
   (0.9→0.6→0.28→0.08→0) imitan mejor una neblina real disipándose.
   Sigue siendo un valor ESTÁTICO del `mask-image` (nunca animado) --
   la regla de oro de `web-animation-design` sigue intacta, lo único
   que se anima es `transform:scaleY()`. */
.hero__ash-cover {
  position: absolute;
  inset: 0;
  background: #000;
  transform-origin: 50% 0%;
  transform: scaleY(1);
  pointer-events: none;
  mask-image: linear-gradient(
    180deg,
    #000 0%, #000 45%,
    rgba(0,0,0,0.9) 58%,
    rgba(0,0,0,0.6) 70%,
    rgba(0,0,0,0.28) 84%,
    rgba(0,0,0,0.08) 94%,
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    180deg,
    #000 0%, #000 45%,
    rgba(0,0,0,0.9) 58%,
    rgba(0,0,0,0.6) 70%,
    rgba(0,0,0,0.28) 84%,
    rgba(0,0,0,0.08) 94%,
    transparent 100%
  );
}
.hero.hero--reveal .hero__ash-cover {
  animation: heroAshCoverShrink 1.9s cubic-bezier(.19, 1, .22, 1) forwards;
  will-change: transform;
}
@keyframes heroAshCoverShrink {
  to { transform: scaleY(0); }
}

/* .hero__ash-glow (resplandor cálido/naranja anclado abajo) SACADO
   2026-08-27, otra ronda: el usuario lo vio en su navegador real
   como una "nube amarilla" pegada a la costura entre el hero y la
   sección Colección (justo donde estaba anclado, `50% 100%`) y pidió
   quitarlo -- no se reemplazó por nada, mismo criterio que el aro del
   logo y las partículas (ver más arriba: si el usuario rechaza un
   efecto puntual, se saca en vez de intentar afinarlo a ciegas). El
   `<div class="hero__ash-glow">` correspondiente también se sacó de
   index.html. */

.hero__grain {
  position: absolute;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  opacity: 0.42;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}

.hero__vignette {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 50%, transparent 45%, rgba(0,0,0,0.5) 80%, rgba(0,0,0,0.9) 100%),
    linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 20%, transparent 74%, rgba(0,0,0,0.8) 100%);
}

/* Bandada: por encima del cielo, por detrás del texto. Referencias
   reales del usuario (web-banash/cuervos*.png), ya con el fondo
   removido -- se colocan tal cual, sin recortar ni redibujar. */
.hero__birds {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.raven {
  position: absolute;
  width: auto;
}
/* Tamaños mobile +22% (2026-08-26, a pedido del usuario) -- vh Y
   min-height suben por el mismo factor para que la bandada se vea más
   grande a cualquier alto de pantalla, no solo en una referencia fija.
   Valores originales: 60/330, 26/180, 28/180, 20/150, 42/240. */
.raven--a { top: 2%;   right: 0%;   height: 73.2vh; min-height: 403px; }
/* left corrido hacia la derecha, dos veces (2026-08-26, SOLO mobile) --
   primera vuelta: -3%→2% en `raven--b` (el único que colgaba del
   borde izquierdo). El usuario señaló con un círculo la MISMA zona (a
   la izquierda del logo Entropía, entre el eyebrow y el subtítulo) y
   pidió correrla más -- esa zona en pantalla la ocupan a la vez
   `raven--b` (arriba) Y `raven--d` (más abajo, se superponen ahí), así
   que esta vuelta se corrieron los DOS. `raven--a` (fondo de ancho
   completo) y `raven--e` (centrada A PROPÓSITO detrás del logo, ver
   más abajo) no se tocaron -- si la queja era sobre alguna de esas
   dos, avisar. Versión desktop (`@media min-width:600px`) sin
   cambios, no se pidió ahí. */
.raven--b { top: 8%;   left: 25%;   height: 31.72vh; min-height: 220px; opacity: 0.85; }
.raven--c { bottom: 4%; right: 4%;  height: 34.16vh; min-height: 220px; opacity: 0.75; }
.raven--d { top: 44%;  left: 42%;   height: 24.4vh; min-height: 183px; opacity: 0.85; transform: scaleX(-1); }
/* raven--e +14% adicional (2026-08-26, ronda aparte, SOLO mobile) --
   es la ilustración de tinta/goteo ("los cuervos que tienen tinta"),
   la única de las 5 con esa técnica -- a/b/c/d son fotos de bandada
   real, sin tocar. Sobre el valor que ya traía (51.24vh/293px, que a
   su vez ya era +22%): 51.24*1.14=58.41vh, 293*1.14=334px. Versión
   desktop (`max(244px,32vw)` en el media query de abajo) sin cambios,
   no se pidió ahí. */
.raven--e { top: 24%;  left: 50%;   height: 58.41vh; min-height: 334px; opacity: 0.6; transform: translateX(-50%); }
/* b2/c2/d2 (contrapartes espejadas de las fotos chicas) solo existen
   en desktop -- ver `raven--a2` justo abajo para por qué esa SÍ se
   activa en mobile. */
.raven--b2, .raven--c2, .raven--d2 { display: none; }
/* raven--a2 en mobile (2026-08-27, a pedido del usuario, con
   referencia visual): la causa real de "el lado derecho se ve vacío"
   comparado con la referencia no es la posición de b/d (ya se corrieron
   dos veces) -- es que `raven--a` es una FOTO REAL de bandada, y esa
   foto en sí misma es asimétrica (cuervos densos en su mitad
   izquierda, sueltos/dispersos en la derecha, ver hero-raven-a.png).
   Como `raven--a` ya ocupa el ancho completo del hero en mobile, ese
   desbalance del archivo fuente se nota directo en pantalla. Mismo
   diagnóstico y misma solución que ya se aprobó para desktop hace unos
   días (comentario "en pantallas grandes el hero se sentía vacío de un
   lado", más abajo en este archivo): sumar la contraparte espejada
   (scaleX(-1)) del lado opuesto -- al espejarse, el racimo denso que
   estaba a la izquierda del archivo cae del lado derecho de la
   pantalla, llenando justo el hueco. Solo `a2` (la capa grande) se
   activa acá -- b2/c2/d2 son refuerzos menores pensados para pantallas
   anchas con espacio de sobra, y sumarlas en un celular saturaría la
   composición en vez de equilibrarla. */
.raven--a2 {
  display: block;
  top: 2%; left: 0%; height: 73.2vh; min-height: 403px;
  transform: scaleX(-1);
}

/* En mobile las referencias ya se ven bien a su tamaño base (aprobado
   por el usuario) -- en pantallas grandes el hero se sentía vacío de un
   lado porque las 5 fotos no eran simétricas (ej. raven--a, la más
   grande, solo estaba a la derecha). Solución: cada foto (salvo la e,
   que queda centrada como eje) suma su contraparte espejada
   (scaleX(-1)) del lado opuesto, con el mismo tamaño -- simetría real
   en vez de solo "agrandar y repartir". Arranca en 600px para que
   cualquier ventana de escritorio angosta (no solo tablet/desktop
   grande) ya se vea simétrica -- solo celulares reales (max ~480px de
   ancho) se quedan con el layout asimétrico de abajo.

   Tamaño con clamp() atado a vw (no vh): las fotos son verticales
   (más altas que anchas), así que atar el tamaño al ALTO de ventana
   (como antes, con 2 escalones fijos en 700/1024px) las dejaba chicas
   en monitores anchos-pero-no-altos, con huecos enormes a los lados.
   Con clamp(min, Xvw, max) el ancho de la foto escala directo con el
   ancho de ventana, en una curva continua (sin saltos entre tablet y
   desktop) y sigue creciendo en pantallas ultra anchas en vez de
   quedar plana en el tamaño de 1024px. height:auto mantiene la
   proporción real de cada foto. */
@media (min-width: 600px) {
  /* "Una sola imagen" (2026-08-24, a pedido del usuario) -- la bandada
     entera se comporta como UN lienzo de proporción fija que se adapta
     al ancho de la pestaña, en vez de 5 fotos con su propia regla.

     Dos piezas hacen falta para eso:

     1) .hero__birds deja de ser "todo el alto del hero" (que varía
        según cuánto mida el contenido) y pasa a ser un lienzo de
        proporción FIJA 1600x1000 (aspect-ratio 1.6) anclado arriba.
        Así el eje vertical escala junto con el horizontal: un top:20%
        siempre cae en el mismo punto de la composición, no en un punto
        que se corre según el alto de la ventana.

     2) Los tamaños pasan a % del lienzo, SIN clamp -- nada de pisos ni
        techos en px. Ese era el problema real que se veía: al angostar
        la pestaña los cuervos tocaban su piso mínimo y dejaban de
        achicarse, así que ocupaban proporcionalmente MUCHO más ancho
        que en pantalla completa (se veían "agrandados"). Con % puros
        la composición es idéntica a cualquier ancho: solo cambia la
        escala, nunca las proporciones ni las posiciones relativas.

     Los % salen de los tamaños de referencia a 1600px de ancho:
     576->36%, 280->17.5%, 300->18.75%, 210->13.125%, 512->32%. */
  .hero__birds {
    /* El propio .hero ya tiene la proporción 1600x1000 (ver bloque
       "PROPORCIÓN FIJA" más abajo), así que el lienzo de la bandada es
       simplemente todo el hero: inset:0. overflow visible para que el
       goteo de raven--e no se corte antes de tiempo -- el recorte final
       lo hace .hero, que ya tiene overflow:hidden. */
    inset: 0;
    height: 100%;
    overflow: visible;
  }

  /* Las reglas base (mobile) traen min-height en px. Acá HAY que
     anularlo: con width en % y height:auto, un min-height fijo fuerza
     la altura sin tocar el ancho -- o sea DEFORMA la foto (la estira a
     lo alto) y además le corre la posición vertical, justo en las
     ventanas angostas. Con esto la composición queda idéntica en todos
     los anchos, que es el objetivo. */
  .raven { min-height: 0; }

  /* Piso agregado (2026-08-26, a pedido del usuario: "que no se
     reduzcan tanto en desktop, que se vea lo más idéntica posible a
     pantalla completa"). Causa real: el resto del hero (logo, nav,
     eyebrow, textos) usa `max(piso, Nvw)` desde la corrección del
     salto de 600px (ver más arriba) -- con piso, esos elementos dejan
     de achicarse bajo el ancho de cruce (~762px, el mismo que usa
     `.brand img`/`.collection-logo`). Los cuervos NO tenían piso (%
     puro) -- así que bajo ese ancho seguían achicándose sin límite
     mientras el resto de la composición ya se había "frenado", y
     terminaban viéndose cada vez más chicos/vacíos en relación al
     resto. Mismo patrón `max(px, vw)` que ya usa todo lo demás, NO el
     patrón viejo de dos @media con valores fijos/vw sin relación (eso
     fue lo que rompió el breakpoint de 600px la primera vez). Pisos
     calculados desde el mismo ancho de cruce (~762px) sobre el % de
     referencia de cada cuervo: 36%->274px, 17.5%->133px, 18.75%->143px,
     13.125%->100px, 32%->244px. */
  .raven--a  { top: 0%;   right: -4%; width: max(274px, 36vw); height: auto; }
  .raven--a2 { top: 0%;   left: -4%;  width: max(274px, 36vw); height: auto; transform: scaleX(-1); }

  .raven--b  { top: 4%;   left: -6%;  width: max(133px, 17.5vw); height: auto; }
  .raven--b2 { top: 4%;   right: -6%; width: max(133px, 17.5vw); height: auto; opacity: 0.85; transform: scaleX(-1); }

  .raven--c  { bottom: 2%; right: -2%; width: max(143px, 18.75vw); height: auto; }
  .raven--c2 { bottom: 2%; left: -2%;  width: max(143px, 18.75vw); height: auto; opacity: 0.75; transform: scaleX(-1); }

  .raven--d  { top: 40%;  left: 4%;   width: max(100px, 13.125vw); height: auto; }
  .raven--d2 { top: 40%;  right: 4%;  width: max(100px, 13.125vw); height: auto; opacity: 0.85; }

  /* Agrandada (2026-08-24) para que el goteo de tinta llegue más abajo
     y cubra el hueco entre el logo Entropía y el subtítulo -- chequeado
     antes que su alfa es limpia (sin neblina uniforme), así que
     crecerla no debería sacar a la luz el artefacto rectangular que sí
     pasó cuando se reforzaron las nubes (ver memoria del proyecto). */
  .raven--e  { top: 20%;  left: 50%;  width: max(244px, 32vw); height: auto; transform: translateX(-50%); }

  .raven--a2, .raven--b2, .raven--c2, .raven--d2 { display: block; }
}

/* ============ MARCA ARRIBA ============ */

.topbar {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Bug real reportado (2026-08-26): "cuando la pestaña se encoge
     hasta cierto punto se pierde la estética" -- el sitio tenía DOS
     sistemas de tamaño para el hero, uno fijo en px/rem bajo 599px y
     otro proporcional en vw sin techo desde 600px (ver el bloque
     @media más abajo, "599px, no 700px"). Los dos NUNCA se diseñaron
     para dar el mismo valor justo en el límite -- ej. el logo pasaba
     de 158px (600px de ancho) a 280px (599px) por un solo pixel de
     diferencia. Arreglo estructural (no ajustar el corte de nuevo):
     unificar cada propiedad en una sola fórmula continua con
     `max(piso, Nvw)` -- por debajo del punto de cruce se queda en el
     piso fijo (mismo comportamiento que el sistema mobile de antes),
     por encima crece proporcional sin techo (mismo comportamiento que
     el sistema desktop de antes) -- sin ningún salto en el medio,
     para cualquier ancho de pestaña. Los dos bloques @media de abajo
     quedan solo para lo que SÍ es un cambio de layout real (columna
     vs fila en el footer), no de tamaño. */
  gap: max(10px, 0.875vw);
  padding: max(20px, 1.625vw) max(12px, 1.25vw) 0;
}

.brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
}
.brand img {
  width: max(200px, 26.25vw);
  /* 2026-08-27, bug real reportado (captura del usuario, ventana muy
     angosta): el piso de 200px puede terminar más ancho que el área
     REALMENTE visible. Causa raíz -- en Windows (sin scrollbar
     "overlay"), la barra de scroll le come ancho real a la ventana,
     pero la unidad `vw` (y por lo tanto `window.innerWidth`) sigue
     calculando contra el ancho COMPLETO, sin restarla -- bug conocido
     de CSS, no algo que se pueda arreglar ajustando el propio `vw`.
     `max-width:100%` no depende de `vw` -- se mide contra la caja real
     del contenedor (que si respeta el ancho visible), así que actúa
     como techo de seguridad sin tocar la fórmula proporcional de
     arriba. */
  max-width: 100%;
}
.brand__tagline {
  margin-top: -6px;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: max(12px, 1.05vw);
  letter-spacing: 0.04em;
  color: var(--bone);
  text-shadow: 0 2px 10px rgba(0,0,0,0.95), 0 0 26px rgba(0,0,0,0.8);
}

/* Línea logo/nav (2026-08-26, a pedido del usuario). Proporcional
   (`max(piso,vw)`), mismo criterio que todo lo demás del topbar -- sin
   margin propio, el `gap` de `.topbar` ya la separa arriba/abajo.

   Se probó una versión distinta para mobile (degradado + rombo
   ornamental) en una ronda intermedia -- el usuario pidió volver a
   dejarla IGUAL que en desktop, una sola línea lisa en todos los
   anchos. No reintroducir el `::before`/degradado sin que lo pidan de
   nuevo -- el override de `≥600px` que quedaba más abajo ya no hace
   falta, se quitó (ver ese media query). */
.topbar__divider {
  width: max(180px, 15vw); /* 2026-08-28: +50% a pedido del usuario (antes max(120px,10vw), que a su vez ya venía del doble de max(60px,5vw) original) */
  height: 1px;
  background: rgba(202, 188, 184, 0.3);
  flex-shrink: 0;
  opacity: 0;
}
/* 2026-08-28, a pedido del usuario: mismo tratamiento que
   `.eyebrow__rule` de más abajo -- crece desde el centro
   (`lineGrowFromCenter`). Delay propio (0.6s, después de que `.topbar`
   -- `tx-from-top`, 0.7s de transición -- ya casi terminó de asentarse)
   -- no hay ningún otro hijo de `.topbar` con animación propia de la
   que heredar un delay ya establecido, a diferencia de `.eyebrow__rule`
   (que sí copia el de `.eyebrow`). */
.tx-block.tx-in .topbar__divider {
  animation: lineGrowFromCenter 0.8s cubic-bezier(.19, 1, .22, 1) 0.6s forwards;
  will-change: opacity, transform;
}

.nav {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: max(14px, 3.25vw);
}

.nav__link {
  color: var(--bone-dim);
  text-decoration: none;
  font-family: var(--font-display-alt);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 400;
  transition: color 0.3s ease;
}
.topbar .nav__link { font-size: max(9.6px, 0.78vw); }
.nav__link:hover { color: var(--bone); }
/* Barras verticales minimalistas entre secciones del nav del home
   (mismo criterio y mismo pedido del usuario que ya se aplicó al nav
   de las páginas "de lectura", ver `.site-topbar__nav .nav__link`
   más abajo) -- centrada en el hueco del `gap` de `.nav`
   (`left: -(gap/2)`), mismo tono `rgba(202,188,184,.25)`. El `gap` acá
   es en `vw` (no un px fijo como en site-topbar), por eso el cálculo
   usa la misma fórmula `max(14px, 3.25vw)` de la regla de `.nav`
   dividida entre 2 dentro de `calc()`, en vez de un valor fijo. La
   ocultación en mobile la maneja `topbar-actions.js` igual que en
   site-topbar (mide `offsetTop` real, no un breakpoint adivinado) --
   en la práctica el nav del home no envuelve a los anchos probados,
   así que hoy siempre se ven las 3, pero el mecanismo ya cubre el caso
   si algún día envuelve. */
.topbar .nav .nav__link { position: relative; }
.topbar .nav .nav__link:not(:first-child)::before {
  content: ''; position: absolute; left: calc(max(14px, 3.25vw) / -2); top: 50%;
  transform: translateY(-50%); width: 1px; height: 0.8em;
  background: rgba(202, 188, 184, 0.25); pointer-events: none;
}
.topbar .nav .nav__link.nav__link--no-divider::before { display: none; }

/* ============ CONTENIDO ============ */

.hero__content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: max(24px, 1.5vw) max(20px, 1.25vw);
}

/* 2026-08-27, a pedido del usuario: "SEGUNDA COLECCIÓN · MEDELLÍN" ya
   no viaja con el resto de `.hero__content` (que sigue deslizándose
   desde la izquierda) -- se crea en su lugar con el MISMO lenguaje
   que el logo Entropía (opacity+blur+scale, ver `.collection-logo`/
   `logoFadeIn` más abajo), no un slide. `opacity:0` acá reemplaza el
   `0.9` que tenía antes como estado de reposo -- ese 0.9 sigue siendo
   el final real, solo se movió al keyframe (ver `eyebrowFadeIn`).

   **Bug real corregido (ronda siguiente, el usuario lo vio y avisó
   "sigue viniendo de la izquierda")**: aunque `.eyebrow` ya no tenía
   SU PROPIO slide, seguía siendo hijo de `.hero__content`
   (`tx-from-left`), y el `transform:translateX()` del PADRE mueve
   igual a TODO lo de adentro sin importar qué animación propia tenga
   cada hijo -- no hay forma de que un hijo "cancele" el transform de
   un ancestro por CSS. Con el primer delay (0.4s) el eyebrow se
   volvía visible mientras el padre TODAVÍA estaba terminando de
   deslizarse (el padre tarda ~0.79s en total: 0.09s de
   `transition-delay` propio + 0.7s de `transition`) -- se veía
   arrastrado hacia la izquierda mientras aparecía. Fix: el delay del
   eyebrow sube a 0.85s (después de que el padre ya se asentó del
   todo, sin superposición) Y la escala inicial baja de 0.96 a 0.75
   -- más notorio, para que el efecto "crece desde el centro hacia
   afuera" (pedido explícito) se lea claro una vez que ya no compite
   con el arrastre del padre. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: max(12px, 0.75vw);
  font-family: var(--font-accent);
  font-size: max(10px, 0.72vw);
  font-weight: 400;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--bone-dim);
  opacity: 0;
  margin-bottom: max(14px, 0.875vw);
}
.tx-block.tx-in .eyebrow {
  animation: eyebrowFadeIn 0.8s cubic-bezier(.19, 1, .22, 1) 0.85s forwards;
  will-change: opacity, filter, transform;
}
@keyframes eyebrowFadeIn {
  0%   { opacity: 0; filter: blur(6px); transform: scale(0.75); }
  100% { opacity: 0.9; filter: blur(0); transform: scale(1); }
}
/* 2026-08-28, a pedido del usuario: mismo lenguaje que `eyebrowFadeIn`
   (opacity+scale, misma curva/duración), pero `scaleX` en vez de
   `scale` uniforme -- en una línea horizontal fina, escalar los dos
   ejes por igual apenas se nota (1px de alto no cambia visualmente),
   `scaleX` sola sí lee como "crece desde el centro hacia afuera"
   (transform-origin por defecto ya es 50% 50%, el centro). Reusada por
   `.topbar__divider` y `.eyebrow__rule` -- ver cada regla para su
   propio delay. */
@keyframes lineGrowFromCenter {
  0%   { opacity: 0; transform: scaleX(0); }
  100% { opacity: 1; transform: scaleX(1); }
}
.eyebrow__dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--wine); /* 2026-08-28: más oscuro a pedido del usuario (antes --wine-bright) */
}
/* 2026-08-28: línea horizontal aparte (distinta de la barrita vertical
   de arriba) -- separa "Segunda colección · Medellín" del logo
   Entropía que sigue debajo. 50% más corta que `.topbar__divider`
   (spec textual del usuario): `max(180px,15vw)` / 2 = `max(90px,
   7.5vw)`. Mismo criterio de espaciado que `.eyebrow` (que ya trae su
   propio margin-bottom arriba de esta línea). */
.eyebrow__rule {
  width: max(90px, 7.5vw);
  height: 1px;
  background: rgba(202, 188, 184, 0.3);
  margin-top: max(-14px, -0.875vw); /* 2026-08-28: sube un poco a pedido del usuario -- quedaba pegada a "Segunda colección · Medellín" (margin-bottom) y lejos del nav de arriba (el hueco real lo reparte `justify-content:space-between` de .hero, no un margin fijo); un negativo simétrico al margin-bottom la centra mejor en ese hueco */
  margin-bottom: max(14px, 0.875vw);
  opacity: 0;
}
/* 2026-08-28, a pedido del usuario: aparece igual que `.eyebrow`
   (mismo delay 0.85s, ambos hijos directos de `.hero__content` se
   "crean" juntos una vez que el bloque ya se deslizó a su lugar) pero
   creciendo desde el centro (`lineGrowFromCenter`, ver arriba). */
.tx-block.tx-in .eyebrow__rule {
  animation: lineGrowFromCenter 0.8s cubic-bezier(.19, 1, .22, 1) 0.85s forwards;
  will-change: opacity, transform;
}

/* Logo de la colección Entropía: reemplaza el titular de texto como
   pieza central del hero. site/assets/entropia-logo.png ya trae alfa
   real (ver scripts/prep_brand_images.py), solo se re-tiñe a --bone. */
.collection-logo {
  width: max(260px, 33.75vw);
  margin: max(6px, 0.375vw) 0;
  filter: drop-shadow(0 4px 34px rgba(0,0,0,0.75));
  /* Subido levemente (2026-08-24, a pedido del usuario) -- transform en
     vez de tocar el margin para no correr el resto del bloque
     (eyebrow/subtítulo/botón), que sigue centrado igual dentro de
     .hero__content. */
  transform: translateY(-16px);
}

/* ============ PULSO DEL LOGO ENTROPÍA (al entrar) ============
   2026-08-27, a pedido del usuario: el logo ya no se limita a viajar
   junto con el resto de `.hero__content` (que sigue deslizándose desde
   la izquierda como siempre, sin tocar) -- "se crea" con un pulso
   propio DESPUÉS de que el bloque ya llegó a su lugar. El logo del
   topbar (BANASH, `.brand img`) no tiene ninguna de estas reglas,
   sigue exactamente igual que antes.

   v2 (misma tarde): la v1 sumaba dos aros vinotinto (`::before`/
   `::after`) que colapsaban de afuera hacia adentro -- el usuario lo
   vio y pidió sacarlos ("no me gusta como se ve"). Se sacaron del
   todo, sin reemplazarlos por un efecto similar -- el pedido fue
   "algo fluido y limpio", así que el logo se queda con lo mínimo:
   aparece (`opacity`) mientras se enfoca (`filter:blur()→0`) y se
   asienta con un `scale()` sutil, sin ningún elemento extra
   superpuesto.

   `.hero__logo-wrap` sigue existiendo como ancla del tamaño exacto
   del logo (por si se necesita en el futuro), pero ya no tiene
   pseudo-elementos propios.

   El disparo real usa `.tx-block.tx-in` (la misma clase que ya usa el
   resto de la transición de entrada, agregada por page-transition.js)
   -- con `animation-delay` puesto a mano para que el pulso arranque
   recién cuando `.hero__content` ya terminó de deslizarse (~0.7s de
   transition) y no a mitad de camino. Cumple la regla de oro de
   `web-animation-design` (transform/opacity); el `filter:blur()` se
   queda porque el mismo skill lo marca como aceptable bajo 20px (acá
   9px) para un solo elemento. */
.hero__logo-wrap {
  position: relative;
  display: inline-block;
}
.collection-logo {
  opacity: 0;
}
.tx-block.tx-in .collection-logo {
  animation: logoFadeIn 1s cubic-bezier(.19, 1, .22, 1) 0.55s forwards;
  will-change: opacity, filter, transform;
}
/* `translateY(-16px)` se repite en las dos puntas del keyframe a
   propósito -- `.collection-logo` ya trae ese offset fijo en su regla
   base (ver arriba) para subir el logo un poco dentro del bloque; un
   `transform` de `@keyframes` REEMPLAZA el de la regla base entera
   (no se combinan), así que si acá solo hubiera `scale()`, el offset
   se perdería apenas termina la animación (`forwards` deja pegado el
   `transform` del último keyframe). Mismo tipo de bug que ya pasó con
   `.coleccion__nav`/`.auth__eye`, ver
   [[web-banash-transicion-paginas-y-footer]]. */
@keyframes logoFadeIn {
  0%   { opacity: 0; filter: blur(9px); transform: translateY(-16px) scale(0.97); }
  100% { opacity: 1; filter: blur(0); transform: translateY(-16px) scale(1); }
}

.hero__sub {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 300;
  font-size: max(15px, 1.15vw);
  line-height: 1.65;
  color: var(--bone-dim);
  margin: max(22px, 1.375vw) 0 max(32px, 2vw);
  text-shadow: 0 2px 18px rgba(0,0,0,0.7);
}

.btn-pill {
  display: inline-block;
  padding: max(15px, 0.9375vw) max(42px, 2.625vw);
  border: 1px solid rgba(244,239,236,0.4);
  border-radius: 999px;
  color: var(--bone);
  text-decoration: none;
  font-size: max(10.88px, 0.68vw);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  background: rgba(0,0,0,0.25);
  backdrop-filter: blur(3px);
  transition: background 0.35s ease, border-color 0.35s ease;
}
.btn-pill:hover {
  background: var(--wine);
  border-color: var(--wine-bright);
}

/* ============ FOOTER DEL HERO ============ */

.hero__footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: max(18px, 1.125vw) max(30px, 1.875vw) max(28px, 1.75vw);
  font-size: max(9.6px, 0.6vw);
  letter-spacing: 0.18em;
  color: var(--bone-dim);
  opacity: 0.75;
}

.hero__footer-left {
  display: flex;
  align-items: center;
  gap: max(15px, 0.9375vw);
}
.hero__footer-left a {
  color: var(--bone-dim);
  text-decoration: none;
  display: inline-flex;
  transition: color 0.3s ease;
}
.hero__footer-left a:hover { color: var(--bone); }
/* 2026-08-26: "Aviso de privacidad"/"Términos y políticas" pasaron de
   texto suelto (`<span>`) a links reales -- sin esto tomaban el azul
   subrayado por defecto del navegador (nada en el sitio resetea `a`
   de forma global). */
.hero__footer-right a { color: var(--bone-dim); text-decoration: none; }
.hero__footer-right a:hover { color: var(--bone); }
.hero__footer-left svg { width: max(15px, 0.9375vw); height: auto; }
/* Ícono real de Instagram, mismo criterio que `.site-footer__ig` del
   footer sitewide (ver styles.css, sección FOOTER) -- círculo bone
   detrás para que el blanco del PNG se funda en vez de verse como un
   parche suelto sobre negro. Proporcional (max piso/vw), mismo sistema
   que el resto del hero. */
.hero__footer-ig {
  width: max(20px, 1.25vw); height: max(20px, 1.25vw); border-radius: 50%;
  background: var(--bone); align-items: center; justify-content: center;
  flex-shrink: 0; transition: filter 0.25s ease;
}
.hero__footer-ig:hover { filter: brightness(1.15); }
.hero__footer-ig img { width: 60%; height: 60%; display: block; }

.hero__footer-text { text-transform: uppercase; }

/* ============ HERO EN PROPORCIÓN FIJA ============
   (2026-08-24) El pedido del usuario, repetido varias veces: que el hero
   se vea EXACTAMENTE igual a cualquier ancho de pestaña, "como si todo
   fuera una sola imagen". Para eso no alcanza con que escalen los
   cuervos -- tiene que escalar TODO junto, y nada puede tener un mínimo
   ni un máximo en px.

   Por qué fallaban los intentos anteriores:
   - Los `clamp(Npx, Xvw, Mpx)` tienen piso: al angostar la pestaña el
     elemento deja de achicarse y pasa a ocupar proporcionalmente MUCHO
     más ancho que en pantalla completa. Le pasaba a los cuervos y
     también al logo BANASH, al de Entropía, al nav y a los textos.
   - El alto del hero era variable (`min-height:100svh` + lo que
     ocupara el contenido), así que aunque los cuervos escalaran bien a
     lo ancho, un `top:20%` caía en distinto punto de la composición
     según el alto de la ventana -- por eso la bandada se "comprimía"
     arriba y quedaba un vacío rojo abajo en ventanas angostas.

   Solución: todos los tamaños del hero se expresan en vw (fracción del
   ancho de ventana), derivados de su valor de diseño a 1600px, y se
   quita el `min-height:100svh`. Al ser TODO proporcional al ancho, el
   alto natural del hero pasa a ser también una fracción fija del ancho
   (medido: ratio 1.34 tanto a 700px como a 1500px), así que la
   composición entera queda idéntica a cualquier tamaño sin necesidad de
   forzar una proporción a mano.

   Ojo con `min-height:100svh`: era justamente lo que rompía esto. En
   una ventana alta estiraba el hero más allá de su alto proporcional,
   y como los cuervos se posicionan en % de ese alto, la bandada se
   "comprimía" arriba y dejaba un vacío rojo abajo.

   Consecuencia a tener en cuenta: en pantallas anchas y bajitas el hero
   queda más alto que la ventana y hay que scrollear un poco para ver el
   botón. Es el precio de no recortar ni deformar nada; la alternativa
   (recortar los lados) es justo lo que hacía que los cuervos se vieran
   gigantes en ventanas angostas. */
@media (min-width: 600px) {
  .hero {
    min-height: 0;
  }

  /* 2026-08-26: todo lo que era tamaño/espaciado puro (logo, nav,
     topbar, eyebrow, collection-logo, hero__sub, btn-pill,
     hero__footer) se unificó en una sola fórmula `max(piso, Nvw)` en
     la regla base de cada uno (ver más arriba) -- ya no hace falta
     repetirlo acá, y así se elimina el salto en el límite de 600px de
     raíz en vez de sincronizar dos bloques a mano. Esta sección ahora
     solo trae lo que SÍ depende del ancho de ventana de otra forma
     (min-height del hero, tamaño de nubes) o necesita el escopado
     `.topbar` para no filtrarse a Tienda/Diario (ver `.topbar
     .nav__link` en la regla base). */
  .eyebrow__dot  { width: 0.1875vw; height: 0.1875vw; }

  /* Nubes: también tenían min-width en px (mismo problema del piso) y
     además cambiaban de tamaño en un breakpoint a 900px, con la nube
     central apareciendo solo a partir de ahí -- debajo de 900px quedaba
     un hueco visible entre las dos nubes de esquina (una costura
     vertical en mitad del hero). Acá quedan los tres tamaños fijos en vw
     desde 600px, sin pisos y sin escalón. */
  .cloud         { min-width: 0; width: 46vw; }
  .cloud--bc     { display: block; min-width: 0; width: 40vw; bottom: 0; left: 50%; opacity: 0.55; transform: translateX(-50%); }

  /* left/right:0 -- SOLO desde 600px (2026-08-24). Mismo motivo que
     bottom:0 arriba: con -10% la nube colgaba fuera de .hero__clouds
     (overflow:hidden), y el recorte del contenedor caía ANTES de que
     el degradado radial de abajo (radio 50%, solo desktop) terminara
     de desvanecerla -- verificado con overflow:visible temporal: sin
     el recorte, el borde se desvanece perfecto; con -10% se corta
     mientras todavía está ~100% opaca. Con la máscara de radio 50% el
     degradado ya llega a alfa 0 justo en el borde propio de la imagen,
     así que colgarla fuera del contenedor ya no hace falta.

     OJO: no tocar la regla base (arriba de este media query) -- esa
     sigue en -10% para mobile, que usa la máscara ORIGINAL (no la de
     radio 50%) y sigue necesitando el hang. Poner esto en la regla
     base por error afectó mobile sin querer la primera vez; quedó
     acotado a min-width:600px para que no vuelva a pasar. */
  .cloud--bl { left: 0; }
  .cloud--br { right: 0; }

  /* CORTE RECTO EN EL BORDE DE LAS NUBES (2026-08-24) -- SOLO nubes,
     los cuervos no se tocan.

     Causa medida: la mascara original usa `ellipse 68% 66%` y recien se
     apaga al 96% de ese radio, o sea que en el borde izq/der de la
     imagen todavia queda ~55% de alfa. Con esa opacidad la nube se
     corta de golpe contra su propio rectangulo y se lee como una linea
     vertical. Verificado pintando el hero de gris plano: aparecian
     bordes rectos en los limites de caja de cada nube.

     Fix: elipse de radio 50% (= medio ancho / medio alto), asi el
     degradado toca alfa 0 justo en los bordes y no queda nada que
     cortar; las tres nubes se cruzan y se funden entre si en vez de
     pisarse con un escalon. El stop opaco sube a 74% para que el nucleo
     denso quede donde estaba: 74%*50% = 37% del ancho desde el centro,
     contra 55%*68% = 37,4% antes.

     Se aplica a las tres por igual para no romper la simetria
     izquierda/derecha. En mobile se deja la mascara original (la nube
     es chica y el corte no se nota). */
  .cloud {
    -webkit-mask-image: radial-gradient(ellipse 50% 50% at 50% 50%, #000 74%, transparent 100%);
            mask-image: radial-gradient(ellipse 50% 50% at 50% 50%, #000 74%, transparent 100%);
  }
}

/* ============ PLACEHOLDER / COLECCIÓN (arranque) ============
   Primer intento (nubes grises del hero reutilizadas, volteadas) fue
   reemplazado -- el usuario dio una imagen de referencia exacta (ver
   comentario en index.html sobre coleccion-nube.jpg). Fondo blanco
   (var(--bone)) porque la imagen ya termina en blanco sólido; el humo
   negro/vinotinto/dorado de la imagen es la pieza puntual de rojo que
   permite la paleta de marca (ver CLAUDE.md), no un accidente.

   .coleccion__nube es un <img> normal EN FLUJO (no position:absolute)
   a width:100%/height:auto -- por diseño, no por descuido: al no ser
   absoluta no necesita ningún contenedor con overflow:hidden que
   pueda cortarla antes de tiempo, así que evita de raíz toda la
   categoría de bug de corte que se resolvió ayer en las nubes del
   hero (ver [[arreglos-estructurales-no-parches]]) en vez de tener que
   volver a lidiar con ella acá. También es por lo que no hace falta
   ningún breakpoint mobile/desktop: al ser una sola imagen con
   height:auto, escala como una sola pieza a cualquier ancho. */
.placeholder-section {
  position: relative;
  min-height: 40vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* justify-content:center (original, de cuando esto solo tenía una
     nota corta) centraba el contenido dentro del min-height:40vh --
     ahora que hay una imagen real que debe quedar pegada al hero sin
     salto, ese centrado dejaba un hueco blanco ARRIBA de la imagen en
     cualquier viewport donde imagen+nota miden menos que 40vh (pasaba
     en mobile: 54px de hueco medidos). flex-start la pega arriba;
     si sobra alto, el hueco ahora queda abajo (invisible, sin costura
     que lo delate). */
  justify-content: flex-start;
  /* Blanco PURO (#fff), no var(--bone) -- var(--bone) es un blanco
     cálido/crema (#f4efec) que no combina con el blanco real de
     coleccion-nube.jpg (medido con PIL: #ffffff en su parte baja).
     Con --bone se notaba el salto justo donde termina la imagen. */
  background: #fff;
}

/* Transición hero -> imagen: el hero termina en un plano negro liso y
   la imagen arranca con textura de humo detallada -- ese salto de
   "vacío" a "detalle" en una línea recta es lo que se leía como corte
   brusco (no un desajuste de color: el negro de ambos ya combina, por
   eso el primer intento -- desvanecer la imagen con mask-image sobre
   un fondo negro a juego -- no se notaba: estaba revelando negro
   sobre negro, sin ningún cambio de brillo real).

   Fix: un degradado negro superpuesto (::before, no mask) que
   OSCURECE la imagen desde negro sólido en su borde superior hasta
   transparente hacia abajo -- como tinta que recién se está
   asentando y se aclara para revelar el detalle. Esto sí cambia el
   brillo real (pinta negro encima y lo desvanece), a diferencia de
   revelar transparencia contra un fondo que ya era del mismo color.
   Sin margin negativo/superposición sobre el hero a propósito: el
   footer del hero queda a solo 1.75vw de su borde inferior, no hay
   margen seguro para montarse encima sin arriesgar taparlo. */
.coleccion__nube-wrap {
  position: relative;
}

/* Capas puente (2026-08-25) -- el oscurecido de abajo (::before) por sí
   solo seguía leyéndose como "un diseño termina, otro empieza": mismo
   color, pero texturas distintas (pincelada del hero vs. tinta
   fotografiada de la imagen nueva) chocando en una línea recta.
   Reutiliza hero-clouds.png -- mismo mecanismo ya probado del hero
   (máscara elipse radio 50%, alfa 0 real en el propio borde, sin nada
   que recortarla) -- como un par de nubes semitransparentes que
   arrancan justo en la costura y se funden con la textura de la
   imagen. overflow:visible a propósito: el desvanecido de la máscara
   ya llega a 0 solo, no hace falta ningún contenedor que corte (esa es
   justo la categoría de bug que NO se quiere repetir). top:0, SIN
   invadir el hero -- se queda dentro de la caja de esta sección. */
.coleccion__seam {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
}
/* width en vw + min-width en px (igual que las nubes del hero) se
   probó primero acá y rompió: en el hero ese piso existe para un
   lienzo casi tan alto como la ventana, pero acá el "lienzo" es la
   imagen banner (.coleccion__nube-wrap), mucho más baja -- en mobile
   el piso de 320px quedaba más alto que el wrap entero (145px) y la
   nube se metía encima del texto de abajo. Causa compartida con el
   bug de las nubes del hero: un tamaño en unidad independiente de la
   proporción real del contenedor. Fix de raíz: height en % del propio
   wrap (que ya escala proporcional con la ventana vía el height:auto
   de la imagen) en vez de un ancho en vw con piso en px -- el alto de
   la nube queda SIEMPRE atado al alto real del banner, a cualquier
   tamaño de ventana. width en vw se mantiene (controla el alcance
   horizontal) -- el radial-gradient de la máscara es relativo a la
   propia caja en cada eje por separado, así que igual llega a alfa 0
   justo en el borde aunque la caja no sea cuadrada como el PNG
   original (el navegador estira la imagen para llenarla; en una capa
   traslúcida decorativa como esta no se nota). */
.coleccion__seam-cloud {
  position: absolute;
  top: 0;
  width: 46vw;
  height: 60%;
  opacity: 0.65;
  -webkit-mask-image: radial-gradient(ellipse 50% 50% at 50% 50%, #000 74%, transparent 100%);
          mask-image: radial-gradient(ellipse 50% 50% at 50% 50%, #000 74%, transparent 100%);
}
.coleccion__seam-cloud--l { left: 0; transform: scaleY(-1); }
.coleccion__seam-cloud--r { right: 0; transform: scale(-1, -1); }

/* Desvanecimiento largo (2026-08-25) -- intentos anteriores en esta
   sesión (ver git history / memoria): 1) degradado CSS lineal, muy
   corto; 2) mask-image con el jpg de grano -- descartado, este motor
   de vista previa NO renderiza mask-image con archivos raster (jpg ni
   png), confirmado con una prueba aislada; 3) mix-blend-mode
   (multiply+screen) -- funcionaba, pero TEÑÍA/lavaba el color real de
   la imagen (se veía todo gris, tapaba el rojo/dorado). El usuario
   pidió explícitamente que no quedara "sobre la nube superior"
   mezclándose con ella.

   Fix real: el usuario actualizó el archivo con transparencia ALFA
   real (coleccion-degradado.png, RGBA -- negro opaco 0-40% de su
   propio alto, se desvanece 40-90%, transparente 90-100%, medido con
   PIL). Con alfa real no hace falta mask NI blend: es una imagen
   normal superpuesta (position:absolute, z-index arriba de la nube),
   sin mix-blend-mode -- donde es opaca tapa con negro sólido (continúa
   el negro del hero sin salto), donde se desvanece se ve la imagen de
   abajo TAL CUAL, sin tinte ni lavado. height:80% se sentía demasiado
   metido sobre la nube (tapaba mucho de ella) -- bajado a 52% para que
   suelte la imagen antes, conservando el mismo mecanismo. */
.coleccion__degradado {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  width: 100%;
  height: 52%;
  pointer-events: none;
}

.coleccion__nube {
  display: block;
  width: 100%;
  height: auto;
}

/* ============ SLIDER COLECCIÓN (cover flow 3D) ============
   Spec exacta del usuario + medidas tomadas del video de referencia
   (Envato Elements) reproducido en el Browser pane, pausado fotograma
   a fotograma vía `video.currentTime` -- no se descargó ni se
   reprodujo el video en la página, solo se midió ahí (ver memoria
   web-banash-seccion-coleccion.md): cadencia de transición ~0.7s,
   tarjeta central 100%/nítida/0°, vecinas ~escala reducida/rotadas
   sobre Y/con blur.

   La física (drag + snap magnético, cálculo de transform por
   tarjeta) vive en site/slider-coleccion.js -- acá solo el aspecto
   visual de cada tarjeta y el contenedor 3D. `perspective` va en
   `.coleccion__slider-track` (el padre directo de las tarjetas), no
   en `.coleccion__slider`, para que el rotateY de cada una se vea
   con la profundidad correcta.

   `.coleccion__card` NO tiene tamaño en `vw` con techo/piso en `px`
   -- todo en `vw`/`%`, mismo principio que el resto del sitio (ver
   [[web-banash-hero-cuervos-nubes]]). El "paso" entre tarjetas (cuánto
   se corre cada vecina) lo mide el JS del ancho real de la tarjeta en
   cada resize, no un valor fijo -- así el carrusel escala como una
   sola pieza a cualquier ancho, igual que el hero. */
.coleccion__slider {
  position: relative;
  width: 100%;
  padding: 10vw 0 12vw;
  overflow: hidden;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
}
/* 2026-08-26: el cursor "grab" (y el propio arrastre, ver
   slider-coleccion.js) se movió del `.coleccion__slider` completo
   (que incluye el padding vacío arriba/abajo) a `.coleccion__slider-
   track` -- el cursor ya no promete que se puede arrastrar en una
   zona donde en realidad no hay ninguna tarjeta. */
.coleccion__slider-track { cursor: grab; }
.coleccion__slider[data-dragging] .coleccion__slider-track { cursor: grabbing; }

/* Flechas de navegación (2026-08-25) -- círculo negro puro + flecha
   blanca de trazo con puntas redondeadas (round linecap/linejoin en
   el SVG, no un triángulo sólido), misma estética blanco/negro del
   slider. Sin vinotinto a propósito: ese color se reserva como único
   acento (el botón de cada tarjeta), agregarlo acá también le
   quitaría fuerza a ese acento único. */
.coleccion__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  /* Las tarjetas van hasta z-index:100 (JS, según cercanía al centro)
     -- con 50 acá, las laterales quedaban ENCIMA del botón en la zona
     donde se superponen y se robaban el click. 200 asegura que las
     flechas siempre queden clickeables, sin importar qué tarjeta
     esté debajo. */
  z-index: 200;
  width: 11vw;
  height: 11vw;
  border-radius: 50%;
  border: none;
  background: #000;
  color: var(--bone);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 0.6vw 1.4vw rgba(0, 0, 0, 0.35);
  transition: opacity 0.15s ease;
}
/* "se atenúa levemente" al hacer click -- :active cubre mouse y touch,
   sin necesitar JS para el propio feedback visual del click. */
.coleccion__nav:active { opacity: 0.6; }
.coleccion__nav--prev { left: 2%; }
.coleccion__nav--next { right: 2%; }

.coleccion__slider-track {
  position: relative;
  width: 100%;
  height: 84vw;
  perspective: 1200px;
  /* Bug real encontrado (2026-08-26, "el slider se cancela a mitad de
     arrastre"): `touch-action:pan-y` vivía solo en `.coleccion__slider`
     (el padre) -- `touch-action` NO es una propiedad heredada en CSS,
     así que `.coleccion__slider-track` (donde de verdad viven los
     listeners de pointerdown/pointermove desde que se movieron acá,
     ver slider-coleccion.js) se quedaba con el valor por defecto
     `auto`. Con eso, el navegador queda libre de interpretar un
     arrastre en curso como scroll nativo en cualquier momento y
     dispara `pointercancel` a mitad de gesto -- invisible en este
     Browser pane (emula mouse, no gestos táctiles reales), pero
     reproducible en cualquier navegador real con pantalla táctil. */
  touch-action: pan-y;
}

.coleccion__card {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 62vw;
  aspect-ratio: 4 / 5;
  /* Fondo de las tarjetas: negro puro (no var(--near-black), que
     tiene un tinte cálido) -- el usuario pidió explícitamente "negro
     puro" para un contraste crudo contra el blanco del contenedor. */
  background: #000;
  border-radius: 16px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 6.5vw 5.5vw;
  box-shadow: 0 1.5vw 3.5vw rgba(0, 0, 0, 0.4);
  will-change: transform, filter, opacity;
  transition: transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1),
              filter 0.7s ease, opacity 0.7s ease;
  cursor: pointer;
}
/* Durante el arrastre, sin transición -- la tarjeta debe seguir el
   dedo/mouse 1:1, sin retraso. La transición vuelve al soltar (para
   el snap magnético) porque JS deja de tocar `data-dragging`. */
.coleccion__slider[data-dragging] .coleccion__card {
  transition: none;
}

.coleccion__card-top {
  /* Zona superior izquierda: título + categoría (spec). */
}
.coleccion__card-title {
  font-family: var(--font-sans);
  font-weight: 700;
  color: var(--bone);
  font-size: 5.4vw;
  line-height: 1.05;
  letter-spacing: -0.01em;
}
.coleccion__card-cat {
  font-family: var(--font-sans);
  font-weight: 400;
  color: var(--bone-dim);
  font-size: 2.7vw;
  margin-top: 1vw;
}

.coleccion__card-art {
  position: relative; /* ancla del badge de inventario (Preventa/Agotado/Últimas), ver .shop-card__badge */
  flex: 1;
  margin: 4vw 0;
  border-radius: 10px;
  /* Sin prenda real todavía (a pedido del usuario) -- placeholder
     sutil en vez de vacío total, listo para la foto real + su propio
     drop-shadow "flotando" cuando exista. */
  background: radial-gradient(ellipse at 50% 62%, rgba(244, 239, 236, 0.08), transparent 70%);
}
/* El badge se reusa tal cual de .shop-card__badge (Tienda) -- acá el
   `top/left:10px` en px queda un poco chico contra el resto del
   sistema vw del hero, pero es intencional: el badge es una etiqueta
   de estado, no una pieza que deba escalar con la composición. */

.coleccion__card-bottom {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
}
.coleccion__card-price {
  font-family: var(--font-sans);
  font-weight: 700;
  color: var(--bone);
  font-size: 6.2vw;
  letter-spacing: -0.01em;
}
.coleccion__card-btn {
  flex-shrink: 0;
  width: 9.5vw;
  height: 9.5vw;
  border-radius: 50%;
  border: none;
  /* Único acento de color de la tarjeta, a propósito (spec: "el único
     punto de color estratégico para guiar el ojo hacia la acción"). */
  background: var(--wine);
  color: var(--bone);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

@media (min-width: 600px) {
  /* Historial: 21vw -> 27vw (se sentía chico) -> 24vw (a pedido del
     usuario, "en pantalla completa se ocupa casi toda la pantalla").
     Todo escalado en conjunto (tarjeta, alto del track, tipografía,
     botón) para que siga siendo la misma tarjeta, solo el tamaño
     ajustado -- no solo el ancho. */
  .coleccion__slider-track { height: 34vw; }
  /* Subido más (a pedido del usuario, dos rondas): menos padding
     arriba acerca el conjunto a la nube de transición sin tocar el
     tamaño de las tarjetas. */
  .coleccion__slider { padding: 3vw 0 10vw; }
  .coleccion__card { width: 24vw; padding: 2.1vw 2vw; }
  .coleccion__card-title { font-size: 1.9vw; }
  .coleccion__card-cat { font-size: 0.9vw; margin-top: 0.35vw; }
  .coleccion__card-price { font-size: 2.15vw; }
  .coleccion__card-btn { width: 3.3vw; height: 3.3vw; }
  .coleccion__nav { width: 3.1vw; height: 3.1vw; }
}

/* ============ FICHA DE PRODUCTO (PDP flotante) ============
   2026-08-25, a pedido del usuario -- se abre desde el botón vinotinto
   de la tarjeta activa del slider (JS en producto-modal.js). Sigue al
   pie de la letra la identidad de [[web-banash-estetica-interactiva]]:
   panel negro puro #000 (no --near-black), texto bone/bone-dim, y el
   vinotinto reservado EXCLUSIVAMENTE para `.pdp__cta` -- ningún otro
   control acá (talla, envío, mangas, miniaturas) lleva ese color.

   Mismo patrón de breakpoint único que el resto de la sección
   (@media min-width:600px) y todo en vw/% -- nada de pisos en px. */
.pdp {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vw;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.pdp.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.25s ease;
}
.pdp__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.82);
  cursor: pointer;
}
.pdp__panel {
  position: relative;
  z-index: 1;
  width: 92vw;
  max-height: 88vh;
  overflow-y: auto;
  background: #000;
  border-radius: 20px;
  padding: 9vw 6vw 8vw;
  /* 2026-08-27, bug real reportado (captura del usuario): con `scale()`
     de por medio, el botón cerrar (`position:absolute; right:4vw`)
     visiblemente "se corría" hacia la derecha durante la apertura --
     escalar un contenedor lo hace desde su CENTRO por defecto, así que
     un hijo cerca del borde derecho recorre una distancia real
     (a 0.98 queda más cerca del centro, a 1 vuelve a su sitio). Fix
     estructural (mismo criterio en los otros 3 paneles con el mismo
     patrón): se saca el `scale()`, se deja solo el `translateY()` --
     sigue siendo una animación real (entra deslizando hacia arriba),
     pero un translate uniforme no mueve para nada a los hijos
     posicionados en absoluto, así que el botón ya no puede "correrse"
     sin importar qué tan cerca del borde esté. */
  transform: translateY(3vw);
  transition: transform 0.3s cubic-bezier(.22, .61, .36, 1);
  /* El panel recibe foco por script al abrir (accesibilidad) -- sin
     esto el navegador dibuja su anillo de foco por defecto (amarillo
     en este entorno), un color fuera de la paleta. */
  outline: none;
}
.pdp.is-open .pdp__panel { transform: translateY(0); }

.pdp__close {
  position: absolute;
  top: 4vw;
  right: 4vw;
  width: 9vw;
  height: 9vw;
  border-radius: 50%;
  border: 1px solid var(--bone-dim);
  background: transparent;
  color: var(--bone);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.pdp__close:active { opacity: 0.6; }

.pdp__crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.6vw;
  font-family: var(--font-sans);
  font-size: 3.2vw;
  color: var(--bone-dim);
  margin: 0 10vw 6vw 0;
}
.pdp__crumbs a { color: var(--bone-dim); text-decoration: none; }
.pdp__crumbs a:hover, .pdp__crumbs a:active { color: var(--bone); }
.pdp__crumb-current { color: var(--bone); }

.pdp__body { display: flex; flex-direction: column; gap: 7vw; }

.pdp__gallery-main {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 16px;
  /* #141414 (gris neutro, no --near-black) para diferenciarse del
     panel #000 sin salirse de la paleta acromática. */
  background: #141414 radial-gradient(ellipse at 50% 55%, rgba(244, 239, 236, 0.07), transparent 70%);
  display: flex;
  align-items: flex-end;
  padding: 4vw;
}
.pdp__gallery-tag {
  font-family: var(--font-sans);
  font-size: 3vw;
  color: var(--bone-dim);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.pdp__thumbs { display: flex; gap: 2.4vw; margin-top: 3vw; }
.pdp__thumb {
  flex: 1;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  border: 1px solid rgba(202, 188, 184, 0.3);
  background: #141414;
  color: var(--bone-dim);
  font-family: var(--font-sans);
  font-size: 2.4vw;
  cursor: pointer;
  transition: opacity 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.pdp__thumb.is-active { border-color: var(--bone); color: var(--bone); }
.pdp__thumb:active { opacity: 0.6; }

.pdp__cat {
  font-family: var(--font-sans);
  color: var(--bone-dim);
  font-size: 3vw;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 1.5vw;
}
.pdp__title {
  font-family: var(--font-sans);
  font-weight: 700;
  color: var(--bone);
  font-size: 7.5vw;
  line-height: 1.08;
  margin: 0 0 5vw;
}

.pdp__label { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3vw; margin: 0 0 2.5vw; }
.pdp__sizes-block { margin-bottom: 6vw; }
.pdp__sizes { display: flex; flex-wrap: wrap; gap: 2.5vw; }
.pdp__size {
  min-width: 11vw;
  height: 11vw;
  padding: 0 2vw;
  border-radius: 50%;
  background: #000;
  border: 1px solid var(--bone);
  color: var(--bone);
  font-family: var(--font-sans);
  font-size: 3.4vw;
  cursor: pointer;
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease;
}
/* "en negro con borde blanco cuando no ha seleccionado, al
   seleccionar se vuelve blanco" -- pedido explícito del usuario. */
.pdp__size.is-selected { background: var(--bone); color: #000; }
.pdp__size:active { opacity: 0.6; }
/* 2026-08-26, a pedido del usuario: talla sin stock AHORA -- el botón
   se queda (nunca se borra), pero se ve claramente inutilizable:
   atenuado + tachado + cursor "no permitido". Especificidad doble
   (`.pdp__size.pdp__size--unavailable`) a propósito -- le gana al
   hover compartido `.pdp__size:hover` de más abajo sin importar el
   orden en el archivo, para que no "brille" como si fuera clickeable. */
.pdp__size.pdp__size--unavailable {
  opacity: 0.35;
  cursor: not-allowed;
  text-decoration: line-through;
  border-color: rgba(244, 239, 236, 0.35);
  color: var(--bone-dim);
  background: #000;
}
.pdp__size.pdp__size--unavailable:hover,
.pdp__size.pdp__size--unavailable:active {
  filter: none;
  transform: none;
  opacity: 0.35;
}
.pdp__sizes-block--attn { animation: pdpShake 0.4s ease; }
@keyframes pdpShake {
  25% { transform: translateX(-1.2vw); }
  75% { transform: translateX(1.2vw); }
}

/* 2026-08-26, a pedido del usuario: selector de Corte (Boxy fit /
   Oversized) -- mismo criterio visual que `.pdp__ship-toggle` (pastilla
   segmentada) en vez del círculo de `.pdp__size`, porque acá el
   contenido es texto/palabra, no un número corto. */
.pdp__cut-block { margin-bottom: 6vw; }
.pdp__cut {
  display: inline-flex;
  border: 1px solid rgba(202, 188, 184, 0.35);
  border-radius: 999px;
  overflow: hidden;
}
.pdp__cut-btn {
  background: transparent;
  border: none;
  color: var(--bone-dim);
  font-family: var(--font-sans);
  font-size: 3vw;
  padding: 2vw 4vw;
  cursor: pointer;
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease;
}
/* 2026-08-26, a pedido del usuario: barra separadora entre las dos
   opciones -- sin esto, "Boxy fit" y "Oversized" se sentían como un
   solo bloque de texto en vez de dos opciones distintas. */
.pdp__cut-btn:not(:first-child) { border-left: 1px solid rgba(202, 188, 184, 0.35); }
.pdp__cut-btn.is-selected { background: var(--bone); color: #000; }
.pdp__cut-btn:active { opacity: 0.6; }
.pdp__cut-block--attn { animation: pdpShake 0.4s ease; }

.pdp__financial {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 3vw;
  margin-bottom: 5vw;
}
.pdp__price { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 8vw; }
.pdp__shipping { text-align: right; }
.pdp__ship-toggle {
  display: inline-flex;
  border: 1px solid rgba(202, 188, 184, 0.35);
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 1.5vw;
}
.pdp__ship-btn {
  background: transparent;
  border: none;
  color: var(--bone-dim);
  font-family: var(--font-sans);
  font-size: 2.6vw;
  padding: 1.6vw 3.2vw;
  cursor: pointer;
}
.pdp__ship-btn.is-active { background: var(--bone); color: #000; }
.pdp__ship-note { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.6vw; margin: 0; max-width: 46vw; }

.pdp__unavailable {
  font-family: var(--font-sans);
  color: var(--bone-dim);
  font-size: 3.2vw;
  line-height: 1.5;
  border: 1px solid rgba(202, 188, 184, 0.3);
  border-radius: 12px;
  padding: 4vw;
  margin: 0 0 5vw;
}

/* Interruptor de mangas desmontables -- upsell de un clic, spec
   textual del usuario. Sin color: es un control secundario, el
   vinotinto queda reservado para `.pdp__cta`. */
.pdp__addon {
  display: flex;
  align-items: center;
  gap: 3vw;
  margin-bottom: 5vw;
  cursor: pointer;
}
/* Sin esta línea, `hidden` no ocultaba el interruptor: una regla de
   autor (`display:flex` arriba) le gana en el cascade a la regla
   `[hidden]{display:none}` del user-agent sin importar especificidad
   -- las reglas de autor siempre superan a las del navegador. Este
   selector, más específico, es autor contra autor: sí gana. */
.pdp__addon[hidden] { display: none; }
.pdp__addon-switch {
  position: relative;
  flex-shrink: 0;
  width: 12vw;
  height: 6.6vw;
  border-radius: 999px;
  background: #000;
  border: 1px solid var(--bone-dim);
  transition: border-color 0.15s ease;
}
.pdp__addon-switch::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0.8vw;
  width: 5vw;
  height: 5vw;
  border-radius: 50%;
  background: var(--bone-dim);
  transform: translateY(-50%);
  transition: transform 0.2s cubic-bezier(.22, .61, .36, 1), background 0.15s ease;
}
.pdp__addon.is-on .pdp__addon-switch { border-color: var(--bone); }
.pdp__addon.is-on .pdp__addon-switch::after { transform: translateY(-50%) translateX(5.4vw); background: var(--bone); }
.pdp__addon-text { font-family: var(--font-sans); color: var(--bone); font-size: 3vw; }

/* Único elemento vinotinto de toda la interfaz -- "túnel visual
   directo hacia la transacción" (spec textual del usuario). Sin
   backend real todavía: el cambio "Añadir al carrito" -> "Comprar
   ahora" es solo estado visual (ver producto-modal.js). */
.pdp__cta {
  position: relative;
  width: 100%;
  height: 13vw;
  border-radius: 999px;
  border: none;
  background: var(--wine);
  color: var(--bone);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 3.6vw;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: opacity 0.15s ease, background 0.15s ease;
}
.pdp__cta:active { opacity: 0.7; }
.pdp__cta.is-bought { background: var(--wine-bright); }
.pdp__cta[disabled] {
  background: transparent;
  border: 1px solid rgba(202, 188, 184, 0.35);
  color: var(--bone-dim);
  cursor: default;
}
/* Estado de carga (2026-08-25, a pedido del usuario): 1.5s de spinner
   al presionar "Añadir al carrito" antes del aviso de éxito. El label
   se apaga con `visibility` (no `display`) para que el botón no
   cambie de ancho -- el spinner queda centrado encima. */
.pdp__cta.is-loading { cursor: default; }
.pdp__cta.is-loading .pdp__cta-label { visibility: hidden; }
.pdp__cta.is-loading .pdp__cta-spinner { display: block; }

/* 2026-08-26: "ver catálogo completo" -- solo en la ficha abierta
   desde el slider de Colección (index.html). Mismo ancho/forma que
   `.pdp__cta` (pill, 100% de ancho) para que se sienta parte del
   mismo grupo de acciones, pero en outline/transparente -- es la
   acción SECUNDARIA (el vinotinto de `.pdp__cta` sigue siendo el
   único acento reservado para "Añadir al carrito", ver
   web-banash-estetica-interactiva). */
.pdp__catalog-link {
  display: block;
  width: 100%;
  margin-top: 2.4vw;
  padding: 3vw 0;
  border-radius: 999px;
  border: 1px solid rgba(244, 239, 236, 0.35);
  color: var(--bone);
  text-decoration: none;
  text-align: center;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 3.2vw;
  letter-spacing: 0.02em;
  background: rgba(244, 239, 236, 0.04);
  transition: border-color 0.2s ease, background 0.2s ease, filter 0.15s ease, transform 0.15s ease;
}
.pdp__catalog-link:hover { border-color: var(--bone); background: rgba(244, 239, 236, 0.08); }
.pdp__catalog-link:active { transform: scale(0.97); }
.pdp__cta-spinner {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5vw;
  height: 5vw;
  margin: -2.5vw 0 0 -2.5vw;
  border-radius: 50%;
  /* Anillo con degradado (no un borde de dos colores): conic-gradient
     de transparente a bone, recortado a anillo con un mask radial --
     mismo mecanismo ya probado en .coleccion__seam-cloud (los masks
     de gradiente SÍ renderizan en este entorno; un mask-image:url()
     de archivo no -- ver [[web-banash-hero-cuervos-nubes]]). */
  background: conic-gradient(from 0deg, transparent 0%, var(--bone) 100%);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2.5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2.5px));
  animation: pdpSpin 0.8s linear infinite;
}
@keyframes pdpSpin { to { transform: rotate(360deg); } }

/* Aviso flotante compartido (2026-08-25, generalizado -- ver
   banash-ui.js) -- fixed a la ventana, por encima de TODOS los paneles
   (z-index 1200: PDP/auth/carrito/cuenta usan 1000, la confirmación de
   cerrar sesión 1050), independiente de si el panel que lo disparó
   sigue abierto. Sin vinotinto: es una confirmación, no la acción
   principal. */
.toast {
  position: fixed;
  top: 5vw;
  left: 50%;
  z-index: 1200;
  display: flex;
  align-items: center;
  gap: 2.6vw;
  max-width: 88vw;
  background: #000;
  border: 1px solid rgba(202, 188, 184, 0.35);
  border-radius: 999px;
  padding: 2.6vw 5vw 2.6vw 2.6vw;
  box-shadow: 0 1.5vw 4vw rgba(0, 0, 0, 0.5);
  transform: translateX(-50%) translateY(-10vw);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.35s cubic-bezier(.22, .61, .36, 1), opacity 0.3s ease;
}
.toast.is-visible { transform: translateX(-50%) translateY(0); opacity: 1; }
.toast__check {
  flex-shrink: 0;
  width: 6.5vw;
  height: 6.5vw;
  border-radius: 50%;
  background: var(--bone);
  color: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.toast__text { font-family: var(--font-sans); color: var(--bone); font-size: 3.2vw; }

.pdp__specs { margin-top: 6vw; border-top: 1px solid rgba(202, 188, 184, 0.2); padding-top: 4vw; }
.pdp__specs summary {
  font-family: var(--font-sans);
  color: var(--bone);
  font-size: 3.4vw;
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.pdp__specs summary::-webkit-details-marker { display: none; }
.pdp__specs summary::after { content: '+'; color: var(--bone-dim); font-size: 5vw; }
.pdp__specs[open] summary::after { content: '\2013'; }
.pdp__specs dl { margin: 4vw 0 0; display: grid; grid-template-columns: auto 1fr; gap: 2vw 4vw; }
.pdp__specs dt { color: var(--bone-dim); font-family: var(--font-sans); font-size: 2.8vw; }
.pdp__specs dd { color: var(--bone); font-family: var(--font-sans); font-size: 2.8vw; margin: 0; }

body.modal-open { overflow: hidden; }

@media (min-width: 600px) {
  /* Galería a la izquierda, bloque de conversión a la derecha --
     "pantalla dividida" pedida explícitamente por el usuario. En
     mobile se apilan (galería arriba) por ser la adaptación natural
     de una pantalla angosta. */
  .pdp { padding: 3vw; }
  .pdp__panel { width: 76vw; max-height: 86vh; padding: 3.2vw 3vw; border-radius: 24px; }
  .pdp__close { top: 2vw; right: 2vw; width: 2.6vw; height: 2.6vw; }
  .pdp__crumbs { font-size: 0.85vw; gap: 0.8vw; margin-bottom: 2vw; }
  .pdp__body { flex-direction: row; gap: 3.2vw; align-items: flex-start; }
  .pdp__gallery { width: 50%; flex-shrink: 0; }
  .pdp__info { width: 50%; }
  .pdp__gallery-tag { font-size: 0.85vw; }
  .pdp__thumbs { gap: 1vw; margin-top: 1.2vw; }
  .pdp__thumb { font-size: 0.75vw; border-radius: 8px; }
  .pdp__cat { font-size: 0.85vw; margin-bottom: 0.6vw; }
  .pdp__title { font-size: 2.3vw; margin-bottom: 1.6vw; }
  .pdp__label { font-size: 0.85vw; margin-bottom: 0.9vw; }
  .pdp__sizes-block { margin-bottom: 1.8vw; }
  .pdp__sizes { gap: 0.8vw; }
  .pdp__cut-block { margin-bottom: 1.8vw; }
  .pdp__cut-btn { font-size: 0.8vw; padding: 0.55vw 1.1vw; }
  .pdp__size { min-width: 3vw; height: 3vw; font-size: 0.95vw; padding: 0 0.8vw; }
  .pdp__financial { margin-bottom: 1.6vw; }
  .pdp__price { font-size: 2.4vw; }
  .pdp__ship-btn { font-size: 0.78vw; padding: 0.5vw 1vw; }
  .pdp__ship-note { font-size: 0.78vw; max-width: 15vw; }
  .pdp__unavailable { font-size: 0.9vw; padding: 1.4vw; margin-bottom: 1.6vw; }
  .pdp__addon { gap: 0.9vw; margin-bottom: 1.6vw; }
  .pdp__addon-switch { width: 3.4vw; height: 1.9vw; }
  .pdp__addon-switch::after { width: 1.4vw; height: 1.4vw; left: 0.25vw; }
  .pdp__addon.is-on .pdp__addon-switch::after { transform: translateY(-50%) translateX(1.5vw); }
  .pdp__addon-text { font-size: 0.85vw; }
  .pdp__cta { height: 3.6vw; font-size: 1vw; }
  .pdp__cta-spinner { width: 1.4vw; height: 1.4vw; margin: -0.7vw 0 0 -0.7vw; }
  .pdp__catalog-link { margin-top: 0.7vw; padding: 0.85vw 0; font-size: 0.85vw; }
  .toast { top: 2vw; gap: 0.9vw; padding: 0.7vw 1.6vw 0.7vw 0.7vw; max-width: 40vw; }
  .toast__check { width: 2.1vw; height: 2.1vw; }
  .toast__text { font-size: 0.9vw; }
  .pdp__specs { margin-top: 1.8vw; padding-top: 1.2vw; }
  .pdp__specs summary { font-size: 0.95vw; }
  .pdp__specs summary::after { font-size: 1.3vw; }
  .pdp__specs dl { margin-top: 1.2vw; gap: 0.6vw 1.2vw; }
  .pdp__specs dt, .pdp__specs dd { font-size: 0.8vw; }
}

/* Red de seguridad global (2026-08-25): cualquier clase de autor con
   un `display` explícito le gana a `[hidden]{display:none}` del
   user-agent por ORIGEN del cascade, sin importar especificidad -- ya
   nos mordió una vez con `.pdp__addon` (ver memoria
   web-banash-seccion-coleccion.md). En vez de parchear selector por
   selector cada vez que un componente nuevo usa `hidden`, esta regla
   universal se asegura de que `hidden` SIEMPRE oculte. */
[hidden] { display: none !important; }

/* ============ ACCIONES DE TOPBAR (carrito / cuenta) ============ */
.topbar-actions {
  position: absolute;
  top: 26px;
  right: 20px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
}
.icon-btn {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(244, 239, 236, 0.25);
  background: rgba(3, 1, 2, 0.35);
  color: var(--bone);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}
.icon-btn:active { opacity: 0.6; }
.icon-btn svg { width: 46%; height: 46%; }
.icon-btn img { width: 62%; height: 62%; object-fit: contain; }
/* El cuervo (topbar-actions.js lo inyecta dentro de un <span
   data-account-icon> al iniciar sesión) se veía descentrado/gigante y
   cortado -- causa real: ese <span> es inline y sin tamaño propio, así
   que el `width:62%` del <img> de arriba no tiene contra qué
   resolverse (contenedor de ancho indeterminado) y el navegador cae a
   su tamaño intrínseco real (512px), desbordando el círculo de 40px
   sin recortarse -- se veía solo una esquina al azar del cuervo. Fix:
   el span pasa a ser el propio contenedor flex que llena el botón, así
   el `%` del img sí tiene un 100% real contra qué calcularse.

   Escopado a `[data-account-icon]` (2026-08-27, bug real): el selector
   genérico `.icon-btn > span` también hacía match con el OTRO span
   hijo directo de `.icon-btn`, el badge del contador del carrito
   (`.icon-btn__badge`, `data-cart-badge`) -- por especificidad
   (0,1,1 contra 0,1,0 del propio `.icon-btn__badge`) el `width:100%;
   height:100%` de acá le ganaba a `height:16px` de la regla del badge
   sin importar el orden, así que el badge crecía hasta tapar todo el
   círculo del ícono en vez de quedar como una pastilla chica en la
   esquina. Acotar el selector al atributo real que identifica ESTE
   span (no "cualquier span hijo de .icon-btn") elimina la categoría
   completa del bug -- cualquier otro span que se agregue ahí a futuro
   no hereda esto por accidente. */
.icon-btn > span[data-account-icon] {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
.icon-btn__badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--bone);
  color: #000;
  font-family: var(--font-sans);
  font-size: 0.62rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ============ ACCESO (login / crear cuenta) ============
   Mismo esqueleto de overlay que .pdp (ver web-banash-seccion-
   coleccion.md): backdrop + panel negro puro centrado, foco al abrir
   con `outline:none` (el navegador dibuja un anillo fuera de paleta si
   no se apaga). */
.auth {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vw;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.auth.is-open { opacity: 1; visibility: visible; transition: opacity 0.25s ease; }
.auth__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.82); cursor: pointer; }
.auth__panel {
  position: relative;
  z-index: 1;
  width: 92vw;
  max-height: 90vh;
  overflow-y: auto;
  background: #000;
  border-radius: 20px;
  padding: 10vw 7vw 8vw;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  transform: translateY(3vw); /* ver comentario en .pdp__panel -- mismo fix, sin scale() para no correr el botón cerrar */
  transition: transform 0.3s cubic-bezier(.22, .61, .36, 1);
  outline: none;
}
.auth.is-open .auth__panel { transform: translateY(0); }
.auth__close {
  position: absolute; top: 4vw; right: 4vw; width: 9vw; height: 9vw; border-radius: 50%;
  border: 1px solid var(--bone-dim); background: transparent; color: var(--bone);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.auth__close:active { opacity: 0.6; }

/* "el logo del cuervo coronando el formulario" -- spec textual. */
.auth__raven { width: 16vw; margin-bottom: 4vw; }

.auth__tabs {
  --tabs-pad: 1vw; /* fuente única de verdad del padding -- ver comentario abajo */
  display: flex; width: 100%; border: 1px solid rgba(202, 188, 184, 0.3);
  border-radius: 999px; padding: var(--tabs-pad); margin-bottom: 6vw;
  position: relative; /* ancla al indicador que desliza, ver abajo */
}
/* 2026-08-26, a pedido del usuario: el cambio de pestaña era solo un
   fundido de color en el propio botón -- pidió "una especie de tirón,
   se desliza hacia la izquierda y derecha". Se agrega un indicador
   propio (`<span>`, un hijo más de `.auth__tabs`) que es el que de
   verdad lleva el fondo blanco y se DESLIZA de un lado al otro, en vez
   de que cada botón prenda/apague su propio fondo en el mismo lugar.
   `left/width` calculados sobre el padding real del contenedor (no
   asumidos) -- con `flex:1` en los botones, cada uno ocupa exactamente
   la mitad del área interior (100% menos el padding de los dos lados),
   así que el indicador con ese mismo ancho + `translateX(100%)` cae
   justo sobre el segundo botón. `cubic-bezier(.34,1.56,.64,1)` es un
   "ease-out-back" -- pasa un poco de la posición final y vuelve, el
   "tirón" pedido, en vez de un deslizamiento plano.

   Bug real corregido (2026-08-26, ronda siguiente): el indicador tenía
   `1vw` HARDCODEADO acá abajo para `top/bottom/left/width`, pero
   `.auth__tabs` cambia su `padding` a `0.3vw` desde 600px (ver media
   query más abajo, "MARCA ARRIBA"/auth en desktop) -- el indicador
   nunca se enteraba de ese cambio, así que en desktop quedaba
   desalineado y más chico de lo que debía (no llegaba a los bordes
   reales del contenedor, "se veía roto"). Fix estructural: `--tabs-pad`
   como variable única, leída tanto por el `padding` real de arriba
   como por este indicador -- si el padding vuelve a cambiar en algún
   breakpoint, alcanza con actualizar la variable ahí, no hay un
   segundo lugar que sincronizar a mano. */
.auth__tab-indicator {
  position: absolute;
  top: var(--tabs-pad); bottom: var(--tabs-pad); left: var(--tabs-pad);
  width: calc(50% - var(--tabs-pad));
  background: var(--bone);
  border-radius: 999px;
  z-index: 0;
  transition: transform 0.4s cubic-bezier(.34, 1.56, .64, 1);
}
.auth__tabs[data-active="signup"] .auth__tab-indicator { transform: translateX(100%); }
.auth__tab {
  position: relative; z-index: 1; /* por encima del indicador */
  flex: 1; background: transparent; border: none; color: var(--bone-dim);
  font-family: var(--font-sans); font-size: 3vw; padding: 2.4vw 0;
  border-radius: 999px; cursor: pointer; transition: color 0.25s ease;
}
.auth__tab.is-active { color: #000; }

.auth__title { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 6vw; margin-bottom: 6vw; }

.auth__form { width: 100%; display: flex; flex-direction: column; gap: 4vw; text-align: left; }
.auth__field { display: flex; flex-direction: column; gap: 1.6vw; }
.auth__label { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.8vw; }
.auth__input {
  width: 100%; height: 12vw; border-radius: 12px; border: 1px solid transparent;
  /* Cajas gris oscuro con texto blanco -- spec textual. #1a1a1a, no un
     token del sitio: es una superficie interactiva (ver
     web-banash-estetica-interactiva.md). */
  background: #1a1a1a; color: var(--bone); font-family: var(--font-sans); font-size: 3.4vw;
  padding: 0 4vw;
}
.auth__input::placeholder { color: var(--bone-dim); }
.auth__input:focus { outline: none; border-color: var(--bone-dim); }
/* Bug real corregido (2026-08-26): el autocompletado del navegador
   (usuario/correo/contraseña guardados) pinta el campo de un amarillo
   sólido por su cuenta -- es un estilo de user-agent con prioridad muy
   alta, `background` normal no le gana. El truco real (no hay otro):
   un `box-shadow` inset del mismo color que el fondo, tan grande que
   tapa toda la caja por dentro, más `-webkit-text-fill-color` porque
   autofill también fuerza el color de texto aparte del fondo. La
   transición larga en `background-color` evita el parpadeo amarillo
   de un frame antes de que el box-shadow se pinte. Mismo fix en
   `.account__input` más abajo -- mismo problema, mismo mecanismo. */
.auth__input:-webkit-autofill,
.auth__input:-webkit-autofill:hover,
.auth__input:-webkit-autofill:focus,
.auth__input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--bone);
  -webkit-box-shadow: 0 0 0 1000px #1a1a1a inset;
  box-shadow: 0 0 0 1000px #1a1a1a inset;
  caret-color: var(--bone);
  transition: background-color 9999s ease-in-out 0s;
}
.auth__pw-wrap { position: relative; }
.auth__pw-wrap .auth__input { padding-right: 12vw; }
.auth__eye {
  position: absolute; top: 50%; right: 2.2vw; transform: translateY(-50%);
  width: 8vw; height: 8vw; border: none; background: transparent; color: var(--bone-dim);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
/* Bug real corregido (2026-08-26) -- mismo patrón que ya pasó con las
   flechas del slider de Colección (`.coleccion__nav`, ver esa
   sección): `.auth__eye` usa `transform:translateY(-50%)` en su regla
   base para centrarse verticalmente, pero el tratamiento compartido de
   hover/click de más abajo (y el `button:not(:disabled):active`
   genérico) le pisaban ese `transform` con `scale()`, así que el ojo
   perdía su centrado y "saltaba" de posición al pasar el mouse o
   hacer click. Se saca `.auth__eye` de esa lista compartida (ver más
   abajo) y acá se reafirma `translateY(-50%)` sin escala en su propio
   `:active` -- mismo criterio que `.coleccion__nav`, cero movimiento. */
.auth__eye:active { opacity: 0.6; }
.auth__eye:hover { filter: brightness(1.18); }

.auth__forgot {
  align-self: flex-end; font-family: var(--font-sans); color: var(--bone-dim);
  font-size: 2.8vw; text-decoration: underline;
}
.auth__forgot:active { opacity: 0.6; }

.auth__error { color: var(--wine-bright); font-family: var(--font-sans); font-size: 2.6vw; }
.auth__error--form { text-align: center; }
.auth__info { color: var(--bone-dim); font-family: var(--font-sans); font-size: 2.6vw; line-height: 1.5; margin-top: -1.5vw; }

/* CTA de mayor peso visual de toda la pantalla -- spec textual. Único
   bloque vinotinto sólido. */
.auth__cta {
  width: 100%; height: 13.5vw; border-radius: 999px; border: none;
  background: var(--wine); color: var(--bone); font-family: var(--font-sans);
  font-weight: 700; font-size: 3.8vw; cursor: pointer; margin-top: 1vw;
  transition: opacity 0.15s ease;
}
.auth__cta:active { opacity: 0.7; }

/* ============ CARRITO ============
   Panel lateral (no centrado como .auth/.pdp): entra desde la derecha,
   coherente con que su ícono vive en la esquina superior derecha. */
.cart {
  position: fixed; inset: 0; z-index: 1000; display: flex; justify-content: flex-end;
  opacity: 0; visibility: hidden; transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.cart.is-open { opacity: 1; visibility: visible; transition: opacity 0.25s ease; }
.cart__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.82); cursor: pointer; }
.cart__panel {
  position: relative; z-index: 1; width: 88vw; max-width: 92vw; height: 100%; overflow-y: auto;
  background: #000; padding: 14vw 6vw 8vw;
  transform: translateX(6vw); transition: transform 0.35s cubic-bezier(.22, .61, .36, 1);
  outline: none;
}
.cart.is-open .cart__panel { transform: translateX(0); }
.cart__close {
  position: absolute; top: 4vw; right: 4vw; width: 9vw; height: 9vw; border-radius: 50%;
  border: 1px solid var(--bone-dim); background: transparent; color: var(--bone);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.cart__close:active { opacity: 0.6; }
.cart__title { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 6vw; margin-bottom: 6vw; }
.cart__empty { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3.4vw; }

.cart__list { display: flex; flex-direction: column; gap: 6vw; margin-bottom: 5vw; }
/* 2026-08-27, a pedido del usuario: revisando la animación del ícono
   de basura, la causa real era que NO HABÍA ninguna -- `render()` en
   cart-panel.js hace `listEl.innerHTML = ''` y reconstruye toda la
   lista de una, así que un ítem borrado desaparecía de golpe (y con
   más de un ítem en el carrito, TODA la lista parpadeaba al
   reconstruirse). Fix: `.cart__row--removing` (agregada por JS antes
   de sacar el ítem del store, ver cart-panel.js) hace que la fila se
   desvanezca/achique en su lugar ANTES de que el re-render la borre
   de verdad -- solo `opacity`+`transform`, cumple la regla de oro de
   `web-animation-design`. No se anima `max-height`/`margin` para que
   las filas de abajo "suban" suave -- eso animaría layout (caro) por
   una mejora chica; el salto instantáneo de las filas restantes al
   reconstruirse, con el ítem ya invisible, se siente aceptable. */
.cart__row {
  display: flex; gap: 3.5vw; align-items: flex-start;
  transition: opacity 0.22s ease, transform 0.22s ease;
}
.cart__row--removing {
  opacity: 0;
  /* 2026-08-27, bug real reportado (captura del usuario): el
     `translateX(2vw)` corría la fila (con el ícono de basura adentro)
     hacia la derecha a propósito -- se veía como un glitch, no como un
     gesto intencional. Se saca ese desplazamiento lateral y se deja
     solo el achique -- la fila se desvanece/encoge en su propio sitio,
     sigue siendo una animación real, ya no "viaja" hacia ningún lado. */
  transform: scale(0.96);
  pointer-events: none;
}
.cart__row-thumb {
  flex-shrink: 0; width: 17vw; aspect-ratio: 4 / 5; border-radius: 10px;
  background: #141414 radial-gradient(ellipse at 50% 55%, rgba(244, 239, 236, 0.07), transparent 70%);
}
.cart__row-info { flex: 1; min-width: 0; }
.cart__row-name { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 3.6vw; line-height: 1.2; }
.cart__row-variant { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.7vw; margin-top: 0.8vw; }
.cart__row-price { font-family: var(--font-sans); color: var(--bone); font-size: 3.3vw; margin-top: 1.8vw; font-weight: 600; }
.cart__row-qty { display: flex; align-items: center; gap: 2.2vw; margin-top: 2.6vw; }
.cart__qty-btn {
  width: 7vw; height: 7vw; border-radius: 50%; border: 1px solid var(--bone-dim); background: transparent;
  color: var(--bone); font-size: 4vw; line-height: 1; display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.cart__qty-btn:active { opacity: 0.6; }
.cart__qty-val { font-family: var(--font-sans); color: var(--bone); font-size: 3.1vw; min-width: 5vw; text-align: center; }
.cart__row-remove {
  flex-shrink: 0; width: 8vw; height: 8vw; border: none; background: transparent; color: var(--bone-dim);
  display: flex; align-items: center; justify-content: center; cursor: pointer; margin-top: -1vw;
}
.cart__row-remove:active { opacity: 0.6; }

.cart__crosssell {
  display: flex; align-items: center; justify-content: space-between; gap: 3vw; padding: 4vw;
  border: 1px solid rgba(202, 188, 184, 0.25); border-radius: 14px; margin-bottom: 5vw;
}
.cart__crosssell-text { font-family: var(--font-sans); color: var(--bone); font-size: 2.9vw; }
.cart__crosssell-btn {
  flex-shrink: 0; border: 1px solid var(--bone); background: transparent; color: var(--bone);
  font-family: var(--font-sans); font-size: 2.7vw; padding: 2vw 3.6vw; border-radius: 999px; cursor: pointer;
}
.cart__crosssell-btn:active { opacity: 0.6; }

.cart__totals { border-top: 1px solid rgba(202, 188, 184, 0.25); padding-top: 4vw; margin-bottom: 5vw; display: flex; flex-direction: column; gap: 2vw; }
.cart__totals-row { display: flex; justify-content: space-between; font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.9vw; }
.cart__totals-row--total { color: var(--bone); font-size: 4.4vw; font-weight: 700; margin-top: 1vw; }

/* CTA de checkout -- "única salida lógica y visual" (spec textual). */
.cart__checkout {
  width: 100%; height: 13.5vw; border-radius: 999px; border: none;
  background: var(--wine); color: var(--bone); font-family: var(--font-sans); font-weight: 700;
  font-size: 3.8vw; cursor: pointer;
}
.cart__checkout:active { opacity: 0.7; }

/* ============ CUENTA (panel de usuario) ============ */
.account {
  position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  padding: 4vw; opacity: 0; visibility: hidden; transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.account.is-open { opacity: 1; visibility: visible; transition: opacity 0.25s ease; }
.account__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.85); cursor: pointer; }
/* 2026-08-27, a pedido del usuario ("la X tiene un error" -- lo que en
   realidad pasaba es que dejaba de verse donde debía): `.account__panel`
   tenía el scroll interno Y el botón cerrar (`position:absolute`) los
   dos juntos -- un `position:absolute` dentro de un contenedor con
   `overflow-y:auto` se desplaza CON el contenido, no se queda fijo en
   la esquina. Al scrollear la pestaña "Cuenta" (dos formularios +
   switch, no siempre entra completo en pantalla) la X se iba de su
   lugar. Fix estructural: el scroll se movió a un envoltorio nuevo
   (`.account__scroll`, un solo `<div>` nuevo en el HTML, sin tocar
   nada de adentro) -- `.account__panel` en sí ya NO scrollea, solo
   `.account__scroll` lo hace, así que el botón cerrar (que sigue
   siendo `position:absolute`, pero ahora respecto del panel que NO se
   mueve) queda fijo en la esquina sin importar cuánto se baje adentro. */
.account__panel {
  position: relative; z-index: 1; width: 92vw; max-height: 90vh; overflow: hidden;
  background: #000; border-radius: 20px; display: flex; flex-direction: column;
  transform: translateY(3vw); transition: transform 0.3s cubic-bezier(.22, .61, .36, 1); /* ver .pdp__panel -- sin scale() para no correr el botón cerrar */
  outline: none;
}
.account__scroll { overflow-y: auto; flex: 1; min-height: 0; padding: 10vw 6vw 8vw; }
.account.is-open .account__panel { transform: translateY(0); }
.account__close {
  position: absolute; top: 4vw; right: 4vw; width: 9vw; height: 9vw; border-radius: 50%;
  border: 1px solid var(--bone-dim); background: transparent; color: var(--bone);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  z-index: 2; /* por encima de .account__scroll */
}
.account__close:active { opacity: 0.6; }

/* "corona la cabecera de la cuenta con el logo del cuervo" en vez de
   un avatar genérico -- spec textual. */
.account__header {
  display: flex; align-items: center; gap: 4vw; margin-bottom: 8vw; padding-bottom: 6vw;
  border-bottom: 1px solid rgba(202, 188, 184, 0.2);
}
.account__raven { width: 16vw; flex-shrink: 0; }
.account__name { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 5vw; }
.account__email { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3vw; margin-top: 1vw; }

/* 2026-08-27, a pedido del usuario: Pedidos/Direcciones/Cuenta vivían
   todos apilados en un solo scroll largo -- "confusa, no se ve
   profesional". Mismo esqueleto que `.auth__tabs` (indicador que se
   desliza con un "tirón", ver esa regla para el porqué de cada
   detalle) generalizado a 3 pestañas en vez de 2 -- el indicador mide
   un tercio del área interior y se traslada 100%/200% de su propio
   ancho para caer sobre la 2ª/3ª pestaña, así que el mecanismo no
   necesita ningún ajuste especial por tener una pestaña más. */
.account__tabs {
  --tabs-pad: 1vw;
  display: flex; width: 100%; border: 1px solid rgba(202, 188, 184, 0.3);
  border-radius: 999px; padding: var(--tabs-pad); margin-bottom: 6vw;
  position: relative;
}
.account__tab-indicator {
  position: absolute;
  top: var(--tabs-pad); bottom: var(--tabs-pad); left: var(--tabs-pad);
  width: calc((100% - var(--tabs-pad) * 2) / 3);
  background: var(--bone);
  border-radius: 999px;
  z-index: 0;
  transition: transform 0.4s cubic-bezier(.34, 1.56, .64, 1);
}
.account__tabs[data-active="addresses"] .account__tab-indicator { transform: translateX(100%); }
.account__tabs[data-active="account"] .account__tab-indicator { transform: translateX(200%); }
.account__tab {
  position: relative; z-index: 1; flex: 1; background: transparent; border: none; color: var(--bone-dim);
  font-family: var(--font-sans); font-size: 2.6vw; padding: 2.2vw 0;
  border-radius: 999px; cursor: pointer; transition: color 0.25s ease;
}
.account__tab.is-active { color: #000; }

.account__section { margin-bottom: 9vw; }
.account__section-title {
  font-family: var(--font-sans); font-weight: 700; color: var(--bone); text-transform: uppercase;
  letter-spacing: 0.06em; font-size: 3.2vw; margin-bottom: 4vw;
}
/* El historial de pedidos es "el protagonista indiscutible" (spec
   textual) -- se le da el trato tipográfico más fuerte del panel,
   reutilizando .section-title (Grenze Gotisch, ver memoria
   web-banash-fuentes-goticas) en vez de un h3 más del montón. */
.account__section--orders .account__section-title { font-size: 7vw; margin-bottom: 5vw; }

.account__orders { display: flex; flex-direction: column; gap: 5vw; }
.order { border: 1px solid rgba(202, 188, 184, 0.25); border-radius: 14px; padding: 5vw; }
.order__head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2vw; flex-wrap: wrap; gap: 1vw; }
.order__code { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 3.6vw; }
.order__date { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.6vw; }
.order__items { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.8vw; line-height: 1.6; margin-bottom: 2vw; }
/* Faltaba el total del pedido -- solo se veía el precio de cada línea
   suelta (.order__items), nunca la suma. Mismo lenguaje visual que
   .cart__totals-row--total (blanco sólido + peso 700 = "esto es lo que
   importa"), pero en una sola línea porque acá no hace falta el
   desglose completo (subtotal/envío), solo el número final. */
.order__total { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.8vw; margin-bottom: 4.5vw; }
.order__total b { color: var(--bone); font-weight: 700; }
.order__progress { display: flex; align-items: flex-start; margin-bottom: 3vw; }
.order__step { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 1.6vw; position: relative; }
.order__step:not(:last-child)::after {
  content: ''; position: absolute; top: 2vw; left: 50%; width: 100%; height: 2px;
  background: rgba(202, 188, 184, 0.25); z-index: 0;
}
.order__step.is-done:not(:last-child)::after { background: var(--bone); }
.order__dot { position: relative; z-index: 1; width: 4vw; height: 4vw; border-radius: 50%; background: #000; border: 2px solid rgba(202, 188, 184, 0.4); }
.order__step.is-done .order__dot { background: var(--bone); border-color: var(--bone); }
.order__step.is-current .order__dot { border-color: var(--bone); width: 5vw; height: 5vw; margin-top: -0.5vw; }
.order__step-label {
  font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.3vw; text-transform: uppercase; letter-spacing: 0.04em;
  text-align: center;
}
.order__step.is-done .order__step-label, .order__step.is-current .order__step-label { color: var(--bone); font-weight: 700; }
.order__tracking {
  font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.6vw;
  border-top: 1px dashed rgba(202, 188, 184, 0.25); padding-top: 3vw;
}

/* Banner de checkout (2026-08-25) -- solo visible cuando "Ir a pagar"
   trae al usuario a este panel a elegir dirección (ver
   account-panel.js, checkoutMode). Franja vinotinto a la izquierda en
   vez de un bloque sólido -- el acento sigue reservado para LA acción
   principal (acá, completar la compra), no se convierte en decoración
   suelta. */
.checkout-banner {
  display: flex; align-items: center; gap: 2.5vw;
  background: rgba(244, 239, 236, 0.05);
  border-left: 3px solid var(--wine-bright);
  border-radius: 8px;
  padding: 3vw 3.5vw;
  margin-bottom: 4vw;
  font-family: var(--font-sans); color: var(--bone); font-size: 2.8vw; line-height: 1.5;
}
.checkout-banner__icon { flex-shrink: 0; width: 6vw; height: 6vw; color: var(--wine-bright); }

.account__addresses { display: flex; flex-direction: column; gap: 3vw; margin-bottom: 4vw; }
.account__address {
  display: flex; justify-content: space-between; align-items: center; gap: 3vw; padding: 3.5vw;
  border: 1px solid rgba(202, 188, 184, 0.25); border-radius: 12px;
}
/* Modo checkout: la tarjeta entera es el "botón" de seleccionar (spec
   textual: "seleccionarla directamente con un click") -- sin agregar
   un botón de texto aparte, solo un hover que confirma que responde. */
.account__address--pickable { cursor: pointer; transition: border-color 0.15s ease, background 0.15s ease; }
.account__address--pickable:hover, .account__address--pickable:focus-visible {
  border-color: var(--bone); background: rgba(244, 239, 236, 0.05);
}
.account__address-text { font-family: var(--font-sans); color: var(--bone); font-size: 2.8vw; line-height: 1.6; }
.account__address-text b { display: block; margin-bottom: 0.5vw; }
.account__address-remove {
  flex-shrink: 0; width: 7vw; height: 7vw; border: none; background: transparent; color: var(--bone-dim);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.account__address-remove:active { opacity: 0.6; }

.account__form { display: flex; flex-direction: column; gap: 2.6vw; margin-top: 3vw; }
.account__input {
  width: 100%; height: 11vw; border-radius: 10px; border: none;
  background: #1a1a1a; color: var(--bone); font-family: var(--font-sans); font-size: 3vw; padding: 0 3.5vw;
}
.account__input::placeholder { color: var(--bone-dim); }
.account__input:focus { outline: 1px solid var(--bone-dim); }
/* Mismo bug/fix que `.auth__input` -- ver ese comentario. */
.account__input:-webkit-autofill,
.account__input:-webkit-autofill:hover,
.account__input:-webkit-autofill:focus,
.account__input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--bone);
  -webkit-box-shadow: 0 0 0 1000px #1a1a1a inset;
  box-shadow: 0 0 0 1000px #1a1a1a inset;
  caret-color: var(--bone);
  transition: background-color 9999s ease-in-out 0s;
}
.account__form-btn {
  align-self: flex-start; border: 1px solid var(--bone); background: transparent; color: var(--bone);
  font-family: var(--font-sans); font-size: 2.8vw; padding: 2.2vw 5vw; border-radius: 999px; cursor: pointer;
}
.account__form-btn:active { opacity: 0.6; }
.account__error { color: var(--wine-bright); font-family: var(--font-sans); font-size: 2.6vw; margin-top: -1vw; }

/* ============ CAMPOS DEL FORMULARIO DE DIRECCIÓN ============
   `.addr-field` agrupa cada input con su propio mensaje debajo (así el
   `gap` del `.account__form` separa CAMPOS, no un input de su propio
   error). `.field-msg` es el patrón compartido de "ícono + texto" --
   error en rojo (obligatorio/teléfono a medias) o tip en gris (ayuda),
   mismo componente, solo cambia el color. Reusado también por
   `phone-input.js`. */
.addr-field { display: flex; flex-direction: column; gap: 1.4vw; }
.addr-field__head { display: flex; align-items: baseline; justify-content: space-between; }
.addr-field__label { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.6vw; }
.addr-field__optional { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.4vw; font-style: italic; opacity: 0.75; }

.field-msg { display: flex; align-items: flex-start; gap: 1.4vw; font-family: var(--font-sans); font-size: 2.4vw; line-height: 1.4; }
.field-msg__icon { flex-shrink: 0; width: 3.4vw; height: 3.4vw; margin-top: 0.1vw; }
.field-msg--error { color: var(--wine-bright); }
.field-msg--hint { color: var(--bone-dim); }

/* ============ TELÉFONO: solo números + conteo ============
   Lógica en `phone-input.js`. `.is-invalid` reusa `var(--wine-bright)`
   (mismo rojo de `.account__error`) y `.is-shake` reusa
   `@keyframes pdpShake`, ya definido para el selector de talla del PDP
   -- mismo lenguaje de error en todo el sitio, no uno nuevo para este
   campo. `.account__input` normalmente no tiene borde (`border:none`)
   -- agregar uno acá no mueve nada porque `box-sizing:border-box` es
   global. */
.account__input.is-invalid { border: 1px solid var(--wine-bright); }
.account__input.is-shake { animation: pdpShake 0.4s ease; }
/* Bug real (2026-08-25): espaciado del formulario de dirección se
   veía disparejo -- causa real, nada que ver con el `gap` (ese ya
   salía idéntico entre los cuatro campos). `.phonefield`/`.citypick`
   son simples `<div>` de bloque con un `<input>` (inline-block por
   default del navegador) adentro, sin ningún `display` propio -- eso
   deja el hueco fantasma clásico que el navegador reserva para
   descendentes de texto bajo contenido inline-block dentro de una
   caja de bloque (la misma familia del típico "imagen con 4px de más
   debajo"). Medido: el `<input>` mide 22.4px en los tres campos por
   igual, pero el DIV que lo envuelve (`.phonefield`/`.citypick`)
   medía 25.7px -- ~3.3px de más que "Dirección" (un `<input>` suelto,
   sin wrapper, sin ese hueco). `display:flex` en el wrapper quita el
   hueco de raíz (los flex items no tienen esa reserva de línea) sin
   afectar a los hijos `position:absolute` (lista/contador), que
   igual salen del flujo sin importar el `display` del padre. */
.phonefield { position: relative; display: flex; }
.phonefield__count {
  position: absolute;
  top: 50%;
  right: 3.5vw;
  transform: translateY(-50%);
  font-family: var(--font-sans);
  font-size: 2.4vw;
  color: var(--bone-dim);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.phonefield__count.is-visible { opacity: 1; }
.phonefield__count.is-complete { color: var(--bone); }

/* ============ SELECTOR DE CIUDAD (combobox buscable) ============
   Reemplaza el campo de texto libre de "Ciudad" del formulario de
   direcciones -- pedido explícito: el usuario elige de una lista en
   vez de escribir a mano. Lógica en `city-picker.js`. Lista negra pura
   #000 (no --near-black) flotando sobre el input, mismo criterio de
   [[web-banash-estetica-interactiva]] para partes interactivas
   nuevas. Medellín encabeza la lista con `.citypick__badge` gris
   "Gratis" -- mismo envío gratuito que ya existe en el toggle
   Medellín/otra ciudad del PDP (ver producto-modal.js). */
/* Mismo bug/fix que `.phonefield` de arriba -- ver ese comentario. */
.citypick { position: relative; display: flex; }
.citypick__list {
  position: absolute;
  top: calc(100% + 1.5vw);
  left: 0;
  right: 0;
  z-index: 30;
  max-height: 48vw;
  overflow-y: auto;
  background: #000;
  border: 1px solid rgba(202, 188, 184, 0.3);
  border-radius: 10px;
  padding: 1.5vw 0;
  list-style: none;
  box-shadow: 0 1.5vw 3vw rgba(0, 0, 0, 0.5);
}
.citypick__option {
  display: flex; align-items: center; justify-content: space-between; gap: 2.5vw;
  padding: 2.6vw 3.5vw;
  font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.8vw;
  cursor: pointer;
}
/* Blanco liso en hover/activo -- no vinotinto: ese acento se reserva
   para LA acción principal de cada pantalla (acá no hay una, es solo
   selección), mismo criterio que la talla del PDP. */
.citypick__option:hover, .citypick__option.is-active { background: rgba(244, 239, 236, 0.08); color: var(--bone); }
.citypick__badge {
  flex-shrink: 0;
  background: rgba(202, 188, 184, 0.18);
  color: var(--bone-dim);
  font-size: 2.3vw;
  padding: 0.8vw 2.2vw;
  border-radius: 6px;
}
.citypick__option:hover .citypick__badge, .citypick__option.is-active .citypick__badge { color: var(--bone); }
.citypick__empty { padding: 2.6vw 3.5vw; font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.8vw; }

.account__switch-row { display: flex; align-items: center; gap: 3vw; margin-top: 4.5vw; cursor: pointer; }
.account__switch-text { font-family: var(--font-sans); color: var(--bone); font-size: 2.8vw; }
.account__switch-row.is-on .pdp__addon-switch { border-color: var(--bone); }
.account__switch-row.is-on .pdp__addon-switch::after { transform: translateY(-50%) translateX(5.4vw); background: var(--bone); }

/* "Cerrar Sesión" al fondo, texto apagado -- SIN vinotinto a
   propósito: spec textual explícita para no arrastrar el ojo hacia la
   salida. */
.account__logout {
  display: block; width: 100%; text-align: center; margin-top: 8vw;
  background: transparent; border: none; color: var(--bone-dim);
  font-family: var(--font-sans); font-size: 3vw; text-decoration: underline; cursor: pointer; padding: 2vw;
}
.account__logout:active { opacity: 0.6; }

/* ============ CONFIRMACIÓN DE CIERRE DE SESIÓN ============
   z-index 1050: por encima de .account (1000), por debajo del toast
   (1200) -- ver comentario en .toast. */
.logout-confirm {
  position: fixed; inset: 0; z-index: 1050; display: flex; align-items: center; justify-content: center;
  padding: 6vw; opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}
.logout-confirm.is-open { opacity: 1; visibility: visible; transition: opacity 0.2s ease; }
.logout-confirm__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.85); cursor: pointer; }
.logout-confirm__panel {
  position: relative; z-index: 1; width: 84vw; background: #000; border: 1px solid rgba(202, 188, 184, 0.25);
  border-radius: 16px; padding: 7vw 6vw; text-align: center;
  transform: scale(0.96); transition: transform 0.2s ease; outline: none;
}
.logout-confirm.is-open .logout-confirm__panel { transform: scale(1); }
.logout-confirm__title { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 4.6vw; margin-bottom: 2vw; }
.logout-confirm__text { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3vw; margin-bottom: 6vw; }
.logout-confirm__actions { display: flex; gap: 3vw; }
.logout-confirm__cancel, .logout-confirm__confirm {
  flex: 1; height: 11vw; border-radius: 999px; font-family: var(--font-sans); font-size: 3.1vw; cursor: pointer; border: none;
}
.logout-confirm__cancel { background: transparent; border: 1px solid var(--bone-dim); color: var(--bone); }
/* Ni acá usamos vinotinto -- mismo principio que el link de arriba,
   esta confirmación tampoco debe "tirar" del ojo. Bloque blanco sólido
   en vez de rojo/vinotinto (mismo lenguaje que una talla seleccionada). */
.logout-confirm__confirm { background: var(--bone); color: #000; font-weight: 700; }
.logout-confirm__cancel:active, .logout-confirm__confirm:active { opacity: 0.7; }

@media (min-width: 600px) {
  .topbar-actions { top: 1.625vw; right: 1.25vw; gap: 0.6vw; }
  /* Mismo bug de fuga que `.nav__link` arriba -- `.icon-btn` también
     es compartida con `.site-topbar__actions` (Diario/Tienda).
     Escopado a `.topbar-actions` -- el tamaño base de 40px (regla sin
     media query, arriba en el archivo) queda intacto para el resto. */
  .topbar-actions .icon-btn { width: 2.6vw; height: 2.6vw; }
  .topbar-actions .icon-btn__badge { min-width: 1.1vw; height: 1.1vw; font-size: 0.55vw; top: -0.2vw; right: -0.2vw; }

  .auth__panel { width: 30vw; padding: 3vw 3vw 3.4vw; border-radius: 24px; }
  .auth__close { top: 1.6vw; right: 1.6vw; width: 2.4vw; height: 2.4vw; }
  .auth__raven { width: 5vw; margin-bottom: 1.4vw; }
  .auth__tabs { --tabs-pad: 0.3vw; padding: var(--tabs-pad); margin-bottom: 2.2vw; }
  .auth__tab { font-size: 0.85vw; padding: 0.8vw 0; }
  .auth__title { font-size: 1.7vw; margin-bottom: 2vw; }
  .auth__form { gap: 1.4vw; }
  .auth__field { gap: 0.5vw; }
  .auth__label { font-size: 0.8vw; }
  .auth__input { height: 3.2vw; border-radius: 8px; font-size: 0.95vw; padding: 0 1.2vw; }
  .auth__pw-wrap .auth__input { padding-right: 3.2vw; }
  .auth__eye { right: 0.6vw; width: 2.2vw; height: 2.2vw; }
  .auth__forgot { font-size: 0.8vw; }
  .auth__info { font-size: 0.78vw; margin-top: -0.4vw; }
  .auth__error { font-size: 0.78vw; }
  .auth__cta { height: 3.4vw; font-size: 1vw; margin-top: 0.3vw; }

  .cart__panel { width: 34vw; max-width: 40vw; padding: 4.5vw 2.6vw 3vw; }
  .cart__close { top: 1.6vw; right: 1.6vw; width: 2.4vw; height: 2.4vw; }
  .cart__title { font-size: 1.7vw; margin-bottom: 2vw; }
  .cart__empty { font-size: 1vw; }
  .cart__list { gap: 2vw; margin-bottom: 2vw; }
  .cart__row { gap: 1.2vw; }
  .cart__row-thumb { width: 5.6vw; border-radius: 8px; }
  .cart__row-name { font-size: 1vw; }
  .cart__row-variant { font-size: 0.8vw; margin-top: 0.3vw; }
  .cart__row-price { font-size: 0.95vw; margin-top: 0.6vw; }
  .cart__row-qty { gap: 0.7vw; margin-top: 0.9vw; }
  .cart__qty-btn { width: 1.9vw; height: 1.9vw; font-size: 1.1vw; }
  .cart__qty-val { font-size: 0.85vw; min-width: 1.4vw; }
  .cart__row-remove { width: 2.2vw; height: 2.2vw; }
  .cart__crosssell { padding: 1.2vw; margin-bottom: 1.6vw; border-radius: 10px; }
  .cart__crosssell-text { font-size: 0.82vw; }
  .cart__crosssell-btn { font-size: 0.78vw; padding: 0.5vw 1vw; }
  .cart__totals { padding-top: 1.2vw; margin-bottom: 1.6vw; gap: 0.6vw; }
  .cart__totals-row { font-size: 0.82vw; }
  .cart__totals-row--total { font-size: 1.3vw; }
  .cart__checkout { height: 3.4vw; font-size: 1vw; }

  .account__panel { width: 62vw; border-radius: 24px; }
  .account__scroll { padding: 3vw 3vw 3.4vw; }
  .account__close { top: 1.6vw; right: 1.6vw; width: 2.4vw; height: 2.4vw; }
  .account__header { gap: 1.4vw; margin-bottom: 2.4vw; padding-bottom: 1.8vw; }
  .account__raven { width: 4.6vw; }
  .account__name { font-size: 1.5vw; }
  .account__email { font-size: 0.9vw; margin-top: 0.3vw; }
  .account__tabs { --tabs-pad: 0.3vw; padding: var(--tabs-pad); margin-bottom: 2.2vw; }
  .account__tab { font-size: 0.85vw; padding: 0.8vw 0; }
  .account__section { margin-bottom: 2.6vw; }
  .account__section-title { font-size: 0.9vw; margin-bottom: 1.2vw; }
  .account__section--orders .account__section-title { font-size: 2.1vw; margin-bottom: 1.5vw; }
  .account__orders { gap: 1.6vw; }
  .order { padding: 1.6vw; border-radius: 10px; }
  .order__code { font-size: 1vw; }
  .order__date { font-size: 0.78vw; }
  .order__items { font-size: 0.82vw; margin-bottom: 0.6vw; }
  .order__total { font-size: 0.82vw; margin-bottom: 1.4vw; }
  .order__progress { margin-bottom: 1vw; }
  .order__step { gap: 0.5vw; }
  .order__step:not(:last-child)::after { top: 0.6vw; }
  .order__dot { width: 1.2vw; height: 1.2vw; }
  .order__step.is-current .order__dot { width: 1.5vw; height: 1.5vw; margin-top: -0.15vw; }
  .order__step-label { font-size: 0.68vw; }
  .order__tracking { font-size: 0.78vw; padding-top: 1vw; }
  .checkout-banner { gap: 0.9vw; padding: 1vw 1.2vw; margin-bottom: 1.4vw; font-size: 0.85vw; border-radius: 6px; }
  .checkout-banner__icon { width: 1.6vw; height: 1.6vw; }
  .account__addresses { gap: 1vw; margin-bottom: 1.4vw; }
  .account__address { padding: 1.2vw; border-radius: 10px; }
  .account__address-text { font-size: 0.82vw; }
  .account__address-remove { width: 2vw; height: 2vw; }
  .account__form { gap: 0.8vw; margin-top: 1vw; }
  .addr-field { gap: 0.4vw; }
  .addr-field__label { font-size: 0.8vw; }
  .addr-field__optional { font-size: 0.75vw; }
  .field-msg { gap: 0.5vw; font-size: 0.75vw; }
  .field-msg__icon { width: 1vw; height: 1vw; }
  .account__input { height: 3vw; border-radius: 8px; font-size: 0.85vw; padding: 0 1vw; }
  .phonefield__count { right: 1vw; font-size: 0.72vw; }
  .citypick__list { top: calc(100% + 0.4vw); border-radius: 8px; padding: 0.4vw 0; max-height: 14vw; }
  .citypick__option { padding: 0.8vw 1vw; font-size: 0.85vw; gap: 0.8vw; }
  .citypick__badge { font-size: 0.7vw; padding: 0.25vw 0.7vw; border-radius: 4px; }
  .citypick__empty { padding: 0.8vw 1vw; font-size: 0.85vw; }
  .account__form-btn { font-size: 0.8vw; padding: 0.6vw 1.6vw; }
  .account__error { font-size: 0.78vw; }
  .account__switch-row { gap: 0.9vw; margin-top: 1.4vw; }
  .account__switch-text { font-size: 0.82vw; }
  /* Bug real (2026-08-25): el interruptor de "Recibir novedades por
     correo" reusa `.pdp__addon-switch` (mismo componente visual que el
     de mangas desmontables en la ficha de producto), pero el ajuste de
     escritorio para la posición "encendida" del círculo solo existía
     para `.pdp__addon.is-on` (ver ~línea 1284) -- acá en desktop la
     pista se encoge a 3.4vw pero el círculo seguía saltando al
     `translateX(5.4vw)` de mobile (definido en la regla base de este
     switch, más abajo en el archivo que ese otro ajuste, así que le
     ganaba en el cascade por orden de aparición pese a estar fuera de
     cualquier media query) -- se salía por completo de la pista y
     quedaba flotando sobre el texto de al lado. Se agrega acá (después
     de la regla base de `.account__switch-row.is-on`, no antes) para
     que sí gane. Ojo: por eso mismo, un tercer switch que reuse este
     componente en el futuro va a necesitar su PROPIA línea como esta,
     puesta después de su propia regla base -- no una compartida. */
  .account__switch-row.is-on .pdp__addon-switch::after { transform: translateY(-50%) translateX(1.5vw); }
  .account__logout { font-size: 0.85vw; margin-top: 2.4vw; }

  .logout-confirm__panel { width: 28vw; padding: 2.2vw 2vw; border-radius: 16px; }
  .logout-confirm__title { font-size: 1.3vw; margin-bottom: 0.6vw; }
  .logout-confirm__text { font-size: 0.88vw; margin-bottom: 2vw; }
  .logout-confirm__actions { gap: 1vw; }
  .logout-confirm__cancel, .logout-confirm__confirm { height: 3vw; font-size: 0.88vw; }
}

/* ============ TIPOGRAFÍA GÓTICA ============
   Utilidades semánticas (ver memoria web-banash-fuentes-goticas). El hero
   actual no usa .section-title todavía porque su titular ("Entropía") es
   un logo-imagen, no texto -- se activa solo cuando existan títulos de
   sección reales (Colección, Proceso, Diario) o tarjetas de producto. */
/* 2026-08-26: `.section-title` pasa de Grenze Gotisch a Cinzel
   Decorative -- la misma elegida para `.article-header__title` del
   Diario (ver esa sección en memoria). Único uso real hoy en el sitio:
   "Historial de pedidos" (`.account__section-title`) -- el usuario la
   vio ahí y pidió aplicar "la fuente nueva que habíamos acordado".
   `--font-display` (Grenze Gotisch) NO cambia -- sigue siendo la de
   nav/eyebrow/logo, esto es solo esta utilidad puntual. */
.section-title {
  font-family: "Cinzel Decorative", var(--font-display), Georgia, serif;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.15;
  text-transform: uppercase;
}

.product-name {
  font-family: var(--font-display-alt);
  font-weight: 400;
  letter-spacing: 0.01em;
}

.badge {
  display: inline-block;
  font-family: var(--font-accent);
  font-weight: 400;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* ============ DIARIO (blog de marca) ============ 2026-08-25.
   A diferencia del hero (que escala como "una sola imagen" en vw
   puros, ver [[web-banash-hero-cuervos-nubes]]), el Diario es
   contenido de LECTURA -- tipografía en rem/px con un par de
   media queries convencionales, no vw. Texto escalado a vw se vuelve
   ilegible en los extremos (demasiado chico en pantallas angostas,
   gigante en ultra anchas) para párrafos largos; el hero es una
   composición decorativa de tamaño fijo, esto no.

   Vive en páginas nuevas (`diario.html` + `diario-*.html`, archivos
   planos en la raíz de site/, no en una subcarpeta -- este servidor
   estático no reescribe rutas, así que todos los `src`/`href`
   relativos (styles.css, assets/, *.js) tienen que seguir apuntando
   igual que en index.html sin `../`). Cada artículo/hub duplica el
   head + topbar + modales (auth/carrito/cuenta/toast) de index.html
   -- no hay sistema de includes en este stack estático. Si se toca
   el carrito/cuenta/auth en index.html, replicar el cambio acá
   también (ver comentario en cada archivo *.html nuevo). */

.media-placeholder {
  background: #0c0c0c radial-gradient(ellipse at 50% 60%, rgba(244, 239, 236, 0.09), transparent 70%);
}

/* -------- Topbar liviano (páginas sin el hero completo) --------
   2026-08-26, rediseñado a pedido del usuario: antes era logo-
   izquierda/nav-centro/íconos-derecha (navbar convencional). Ahora
   sigue la misma identidad "centrada" que `.topbar` del home (logo +
   nav apilados y centrados como bloque único) -- los íconos de
   carrito/cuenta quedan clavados en la esquina superior derecha vía
   `position:absolute` (el propio `.site-topbar` ya es `position:
   sticky`, que también sirve de contenedor de posicionamiento para
   ellos, no hace falta `position:relative` aparte). */
.site-topbar {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10.5px 20px;
  background: var(--black);
  border-bottom: 1px solid rgba(244, 239, 236, 0.08);
  position: sticky;
  top: 0;
  z-index: 60;
  user-select: none; /* logo + nav, mismo criterio que .hero -- ver ahí */
}
/* 2026-08-26: primer intento fue angostar el ANCHO 25% -- el usuario
   corrigió, era el ALTO. Revertido el ancho (100%, borde a borde como
   siempre) y en su lugar se bajó el alto ~25% recortando el padding
   vertical (14px -> 10.5px), el gap logo/nav y el tamaño del logo --
   el alto de la barra lo definen esos tres, no hay una propiedad de
   "altura" propia que tocar directamente. */
.site-topbar__center { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.site-topbar__brand { display: block; }
/* 2026-08-26: REGLA ESTRUCTURAL, no volver a romperla -- ya pasó 3
   veces en la misma sesión que "agrandar el logo" terminó agrandando
   `.site-topbar` (fijar por WIDTH, +40% directo, y clonar el ancho
   del home) porque el logo es un bloque normal dentro de
   `.site-topbar__center` (columna flex): cualquier alto que tome el
   <img> se suma al alto del topbar, sin excepción. La única forma de
   que el logo se vea grande SIN que la barra crezca es fijar el
   tamaño por ALTO (nunca por ancho/vw/clamp-de-ancho) usando el
   archivo YA RECORTADO al bbox de tinta (`banash-logo-crop.png` --
   `banash-logo.png` tiene ~2/3 de margen transparente, así que a
   igual alto de caja el recorte se ve mucho más grande). El pedido de
   "mismo tamaño que la página principal" no puede cumplirse al pixel
   sin repetir el bug (el logo del home vive en un hero completo, sin
   límite de alto) -- se prioriza la barra angosta ya aprobada. Si en
   el futuro piden "más grande" de nuevo, subir el techo del clamp de
   ALTO de abajo, nunca agregar una regla de `width`. */
.site-topbar__brand img { height: clamp(80px, 7.5vw, 140px); width: auto; display: block; }
.site-topbar__nav { display: flex; justify-content: center; gap: 22px; flex-wrap: wrap; }
/* Bug real reportado (2026-08-26): `.nav__link` heredaba el tamaño en
   vw pensado para el hero (ver fix más arriba, "Escopado a .topbar")
   -- acá va su propio tamaño fijo, mismo criterio rem/px que el resto
   del contenido "de lectura" (Diario/Tienda), no depende del ancho de
   ventana así que nunca se vuelve ilegible en pantallas intermedias. */
.site-topbar__nav .nav__link { font-size: 0.78rem; letter-spacing: 0.06em; }
.site-topbar__nav .nav__link.is-active { color: var(--bone); }
/* Barras verticales minimalistas entre secciones (a pedido del
   usuario) -- una por cada hueco del `gap` del flex, centrada dentro
   de ese mismo hueco (`left: -(gap/2)`) para no sumar espacio extra:
   el link ya tiene `position:relative` implícito por necesitar `left`
   con `::before` absoluto. Mismo tono sutil que ya usan los bordes de
   `.pay-method` (`rgba(202,188,184,.25)`, --bone-dim al 25%). En
   mobile (ver @media abajo) se oculta la del medio -- el nav pasa a 2
   líneas ahí, una barra a mitad de una línea no tiene sentido. */
.site-topbar__nav .nav__link { position: relative; }
.site-topbar__nav .nav__link:not(:first-child)::before {
  content: ''; position: absolute; left: -11px; top: 50%;
  transform: translateY(-50%); width: 1px; height: 0.8em;
  background: rgba(202, 188, 184, 0.25); pointer-events: none;
}
/* Ocultada por JS (`topbar-actions.js`) en cualquier link que ya no
   comparta línea con el anterior tras el `flex-wrap` -- ver el
   comentario largo de ese archivo, reemplaza un intento anterior con
   un breakpoint fijo que resultó frágil. */
.site-topbar__nav .nav__link.nav__link--no-divider::before { display: none; }
.site-topbar__actions {
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
}
@media (max-width: 640px) {
  /* Único caso donde el logo SÍ se fija por ANCHO en vez de alto: acá
     el límite real es el espacio horizontal libre antes de chocar con
     `.site-topbar__actions` (carrito/cuenta, fijos a la derecha), no
     el alto de la barra. Techo verificado sin overlap en 375px y en
     el borde del breakpoint (640px) -- ver Ronda de "mismo tamaño que
     el home", que sí choca con los íconos porque usa un ancho mucho
     mayor sin este límite. */
  .site-topbar__brand img { width: clamp(120px, 38vw, 165px); height: auto; }
  .site-topbar__nav { gap: 14px; }
  .site-topbar__nav .nav__link { font-size: 0.68rem; }
  .site-topbar__nav .nav__link:not(:first-child)::before { left: -7px; }
  .site-topbar__actions { right: 12px; gap: 8px; }
  /* 2026-08-27, bug real reportado (captura del usuario): por debajo
     de ~310px "DIARIO"/el logo quedaban tapados detrás de los íconos
     de carrito/cuenta. Causa raíz -- `.site-topbar__actions` es
     `position:absolute` (fuera del flujo), así que `.site-topbar__center`
     nunca sabía que debía dejarles espacio: se centraba usando el
     ANCHO COMPLETO de la barra sin importar cuánto ocupaban los
     íconos. Fix estructural (no un tamaño mágico más): reservarle a
     `.site-topbar__center` un margen fijo a cada lado, del mismo
     ancho que la zona de íconos (dos `.icon-btn` de 40px + gap 8px +
     el `right:12px` de más arriba = 100px), para que el logo/nav
     jamás puedan crecer hasta invadirla, sin importar qué tan angosta
     se ponga la ventana. */
  .site-topbar__center { max-width: calc(100% - 200px); margin: 0 auto; }
}

@media (max-width: 360px) {
  /* Con el margen de arriba ya reservado, acá el problema pasa a ser
     el propio contenido DENTRO de esa columna angosta: el piso del
     logo (120px) y el texto del nav todavía no bajan más. Un segundo
     escalón, verificado sin overlap hasta 230px de ancho. */
  .site-topbar__center { max-width: calc(100% - 152px); }
  .site-topbar__brand img { width: clamp(84px, 30vw, 120px); }
  .site-topbar__nav { gap: 9px; }
  .site-topbar__nav .nav__link { font-size: 0.6rem; }
  .site-topbar__nav .nav__link:not(:first-child)::before { left: -4.5px; }
  .site-topbar__actions { right: 8px; gap: 6px; }
  .site-topbar__actions .icon-btn { width: 32px; height: 32px; }
}

/* -------- Cabecera editorial del hub -------- */
.diario-masthead { text-align: center; padding: 56px 20px 36px; }
/* 2026-08-27, a pedido del usuario: pasa a Cinzel Decorative -- la
   misma que ya se había elegido para `.article-header__title` y
   `.section-title` (ver comentario largo en `.article-header__title`
   más abajo) -- "DIARIO" seguía en Grenze Gotisch, quedando afuera del
   acuerdo. `line-height` sube de 0.92 a 1.05 por el mismo motivo que
   el título de artículo: los remates de Cinzel Decorative se cortaban
   con un line-height tan angosto, pensado para Grenze Gotisch. */
.diario-masthead__title {
  font-family: "Cinzel Decorative", var(--font-display), Georgia, serif; font-weight: 700; color: var(--bone);
  font-size: clamp(2.6rem, 9vw, 5.5rem); text-transform: uppercase; line-height: 1.05; letter-spacing: 0.01em;
}
.diario-masthead__manifesto {
  font-family: var(--font-serif); font-style: italic; color: var(--bone-dim);
  font-size: clamp(0.85rem, 2vw, 1.05rem); margin-top: 10px;
}

/* -------- Hero post (60% superior, spec textual) -------- */
.diario-hero { max-width: 1300px; margin: 0 auto 40px; padding: 0 20px; }
.diario-hero__media {
  position: relative; aspect-ratio: 16 / 9; min-height: 55vh; border-radius: 16px; overflow: hidden;
  display: flex; align-items: flex-end; padding: clamp(20px, 4vw, 48px);
  text-decoration: none; /* es un <a> -- si no, el navegador subraya el
  título con el color de link por defecto aunque el texto ya esté en
  blanco (el color del subrayado sigue al del <a>, no al override del
  hijo). */
}
.diario-hero__media::after {
  /* Oscurece la base para que el título blanco siempre sea legible
     encima, sin depender del contraste real de la foto -- mismo
     criterio que .hero__vignette. */
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(0deg, rgba(0,0,0,0.85) 0%, transparent 55%);
}
.diario-hero__body { position: relative; z-index: 1; max-width: 640px; }
.diario-hero__tag {
  display: inline-block; font-family: var(--font-accent); color: var(--wine-bright);
  font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: 10px;
}
/* 2026-08-27: mismo cambio que `.diario-masthead__title` -- este
   título ("El Drop de Entropía" en la tarjeta grande del hub) es el
   MISMO texto que `.article-header__title` del artículo, así que debe
   verse igual. line-height sube por el mismo motivo (remates de Cinzel
   Decorative). */
.diario-hero__title {
  font-family: "Cinzel Decorative", var(--font-display), Georgia, serif; font-weight: 700; color: #fff;
  font-size: clamp(1.9rem, 5vw, 3.4rem); line-height: 1.2; margin-bottom: 18px;
}
.diario-hero__cta {
  display: inline-block; background: var(--wine); color: var(--bone); text-decoration: none;
  font-family: var(--font-sans); font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 13px 28px; border-radius: 999px;
}
.diario-hero__cta:active { opacity: 0.75; }

/* -------- Navegación táctica (tags) -------- */
.diario-tags { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; padding: 0 20px 36px; }
.diario-tag {
  border: 1px solid rgba(244, 239, 236, 0.3); color: var(--bone-dim); background: transparent;
  font-family: var(--font-sans); font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 8px 18px; border-radius: 999px; text-decoration: none; cursor: pointer;
}
.diario-tag.is-active { border-color: var(--bone); color: var(--bone); }

/* -------- Grilla del archivo (asimétrica, foco en imagen) -------- */
.diario-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 26px;
  max-width: 1300px; margin: 0 auto; padding: 0 20px 90px;
}
/* La primera tarjeta del archivo ocupa 2 columnas en pantallas anchas
   -- "layout asimétrico", no una grilla pareja de cajitas iguales. */
.diario-card:first-child { grid-column: span 2; }
@media (max-width: 720px) { .diario-card:first-child { grid-column: span 1; } }
.diario-card { color: inherit; text-decoration: none; display: block; }
.diario-card__media {
  aspect-ratio: 4 / 5; border-radius: 12px; margin-bottom: 14px;
  filter: grayscale(1) contrast(1.08); /* portadas alto contraste, spec textual */
}
.diario-card:first-child .diario-card__media { aspect-ratio: 16 / 9; }
.diario-card__tag { font-family: var(--font-accent); color: var(--wine-bright); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; }
.diario-card__title {
  font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 1.15rem; line-height: 1.25; margin: 6px 0 4px;
}
.diario-card:first-child .diario-card__title { font-size: 1.5rem; }
.diario-card__date { font-family: var(--font-sans); color: var(--bone-dim); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; }

/* -------- Artículo individual -------- */
.article-header { max-width: 720px; margin: 0 auto; padding: 48px 20px 28px; text-align: center; }
.article-header__tag { font-family: var(--font-accent); color: var(--wine-bright); font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase; }
/* 2026-08-26: el usuario no quedó convencido con Grenze Gotisch
   (`--font-display`, la gótica del resto del sitio) específicamente
   para el titular de artículo del Diario -- ver `font-preview.html`,
   sección "Alternativas para el TÍTULO DE ARTÍCULO", donde se comparó
   con el texto real del titular en vez de la palabra BANASH. Eligió
   Cinzel Decorative. Queda solo acá (no reemplaza `--font-display` en
   ningún otro lado del sitio -- títulos de sección, etc. siguen en
   Grenze Gotisch). Line-height sube de 1.05 a 1.2: Cinzel Decorative
   tiene remates/florituras más altos que Grenze Gotisch y se cortaban
   arriba/abajo con el valor angosto anterior. */
.article-header__title {
  font-family: "Cinzel Decorative", var(--font-display), Georgia, serif; font-weight: 700; color: var(--bone);
  font-size: clamp(1.9rem, 6vw, 3.2rem); line-height: 1.2; margin: 12px 0 16px;
}
.article-header__meta { color: var(--bone-dim); font-size: 0.78rem; display: flex; gap: 14px; justify-content: center; }

.article-hero-media { max-width: 1100px; margin: 0 auto 8px; aspect-ratio: 16 / 9; border-radius: 14px; }

/* ============ BOTÓN "VOLVER" (arriba de página) ============
   2026-08-27, a pedido del usuario: Diario (hub + artículos), Términos
   y políticas y Aviso de privacidad son páginas "de lectura" sin la
   jerarquía visual del home -- para salir antes había que bajar hasta
   el pie (`.article-footer__back`, que sigue existiendo, no se quita).
   Minimalista a propósito: mismo lenguaje visual que ese link de pie
   (bone-dim, sans, sin fondo/borde) -- no es la acción principal de la
   página, así que no lleva vinotinto (ver
   web-banash-estetica-interactiva.md). Vive como primer hijo de
   `<main>` en vez de dentro de `.site-topbar` para no tocar esa barra
   (regla ya documentada ahí: cualquier hijo nuevo le suma alto). */
.page-back {
  display: inline-flex; align-items: center; gap: 5px;
  margin: 18px 0 0 20px;
  color: var(--bone-dim); font-family: var(--font-sans); font-size: 0.78rem; letter-spacing: 0.02em;
  text-decoration: none; opacity: 0.7; transition: opacity 0.2s ease;
}
.page-back:hover { opacity: 1; }
.page-back svg { width: 12px; height: 12px; flex-shrink: 0; }

.article-body {
  max-width: 680px; margin: 40px auto 0; padding: 0 20px 30px;
  font-family: var(--font-serif); font-size: 1.15rem; line-height: 1.85; color: var(--bone-dim);
}
.article-body p { margin-bottom: 26px; }
.article-body p:first-of-type { color: var(--bone); font-size: 1.25rem; } /* primer párrafo con más peso -- ancla la lectura */
.article-body h2 {
  font-family: var(--font-display-alt); color: var(--bone); font-size: 1.5rem; font-weight: 400;
  margin: 44px 0 18px;
}

/* Bloque de fotos "tipo carrete" -- el archivo real todavía no existe
   (mismo criterio que .coleccion__card-art: placeholder honesto, no
   una foto inventada) -- tres celdas para simular el carrete que
   describe la spec, listas para reemplazar por <img> reales. */
.article-reel { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 8px 0 40px; }
.article-reel__cell { aspect-ratio: 4 / 5; border-radius: 8px; }

/* -------- Inyección comercial (cross-sell) --------
   "El bloque más importante" (spec textual) -- reusa el lenguaje del
   slider de Colección: fondo negro puro, único acento vinotinto en el
   botón. `data-cross-*` lo lee `diario-crosssell.js`. */
.crosssell {
  display: flex; gap: 20px; align-items: center; background: #000;
  border: 1px solid rgba(244, 239, 236, 0.15); border-radius: 16px;
  padding: 20px; margin: 44px 0;
}
.crosssell__thumb { width: 100px; aspect-ratio: 4 / 5; border-radius: 10px; flex-shrink: 0; }
.crosssell__info { flex: 1; min-width: 0; }
.crosssell__eyebrow { font-family: var(--font-sans); color: var(--bone-dim); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em; }
.crosssell__name { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 1.05rem; margin: 3px 0; }
.crosssell__price { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 0.95rem; }
.crosssell__btn {
  flex-shrink: 0; background: var(--wine); color: var(--bone); border: none; border-radius: 999px;
  font-family: var(--font-sans); font-size: 0.78rem; letter-spacing: 0.04em; padding: 11px 20px; cursor: pointer;
}
.crosssell__btn:active { opacity: 0.75; }
@media (max-width: 480px) {
  .crosssell { flex-direction: column; align-items: stretch; text-align: center; }
  .crosssell__thumb { width: 100%; aspect-ratio: 16/9; }
}

.article-footer { max-width: 680px; margin: 0 auto; padding: 10px 20px 90px; text-align: center; }
.article-footer__back {
  display: inline-block; color: var(--bone-dim); font-family: var(--font-sans); font-size: 0.85rem;
  text-decoration: none; border-bottom: 1px solid rgba(244, 239, 236, 0.3); padding-bottom: 2px;
}

/* ============ FOOTER DEL SITIO (Tienda/Diario) ============
   2026-08-26, a pedido del usuario: estas 7 páginas no tenían NINGÚN
   footer -- el contenido terminaba en seco. Mismo criterio visual que
   `.site-topbar` (negro puro, no los tokens de fondo de cada página),
   sin vinotinto -- acá no hay ninguna acción "principal" que destacar,
   es navegación secundaria/legal, así que se queda en bone-dim/bone
   como el resto de estos links (ver [[web-banash-estetica-interactiva]]:
   el vinotinto es un acento que se gasta, no un color de link genérico). */
/* `position:relative;z-index:2` (2026-08-26, bug real): en Tienda,
   `.shop-bg-crisp` es `position:fixed;z-index:1` -- eso lo mete en el
   "bucket" de apilamiento de positivos, que pinta ENCIMA de cualquier
   contenido normal sin posicionar sin importar el orden en el DOM.
   `main` ya tenía su propio `z-index:2` por este mismo motivo (ver
   sección TIENDA más abajo) pero `.site-footer` no lo tenía -- quedaba
   completamente tapado por el fondo (que es un JPG opaco, sin alfa) en
   toda la página, reportado por el usuario como "no lo veo en ningún
   lado". Sin efecto en las demás páginas que usan `.site-footer`
   (Diario/artículos/legales) -- no tienen ningún fondo fijo con el que
   competir. */
.site-footer {
  position: relative;
  z-index: 2;
  background: var(--black);
  border-top: 1px solid rgba(244, 239, 236, 0.08);
  padding: 40px 20px 24px;
  margin-top: 60px;
}
/* 2026-08-26, rediseño a pedido del usuario: antes era una fila
   logo-izquierda/nav-centro/redes-derecha (`justify-content:space-
   between`) -- "no la tires a un costado", quería todo centrado como
   un solo bloque, mismo criterio que `.topbar`/`.site-topbar` del
   resto del sitio. También el logo pasó del wordmark completo
   (`banash-logo-crop.png`) al cuervo blanco (`raven-white.png`) --
   acá funciona como sello/firma, no como titular, mismo criterio de
   "espacios reducidos" del isotipo (ver CLAUDE.md). */
.site-footer__top {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 20px;
  padding-bottom: 28px;
  border-bottom: 1px solid rgba(244, 239, 236, 0.08);
}
.site-footer__brand { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.site-footer__logo { width: 34px; display: block; }
.site-footer__tagline { font-family: var(--font-serif); font-style: italic; color: var(--bone-dim); font-size: 0.8rem; margin: 0; }
.site-footer__nav { display: flex; flex-wrap: wrap; justify-content: center; gap: 20px; }
.site-footer__nav a {
  color: var(--bone-dim); text-decoration: none; font-family: var(--font-display-alt);
  font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; transition: color 0.25s ease;
}
.site-footer__nav a:hover { color: var(--bone); }
.site-footer__social { display: flex; align-items: center; justify-content: center; gap: 14px; }
.site-footer__social a { color: var(--bone-dim); display: inline-flex; transition: color 0.25s ease; }
.site-footer__social a:hover { color: var(--bone); }
/* Ícono real de Instagram (`instagram-logo.png`, glifo negro sobre
   blanco, sin transparencia) -- mismo tratamiento que
   `.shop-channel__icon` de Tienda: circulo bone sólido detrás para
   que el blanco del archivo se funda con el círculo en vez de verse
   como un parche blanco suelto sobre el negro del footer. */
.site-footer__ig {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bone); color: #000; transition: filter 0.25s ease;
}
.site-footer__ig:hover { filter: brightness(1.15); }
.site-footer__ig img { width: 18px; height: 18px; display: block; }
.site-footer__bottom {
  max-width: 1100px;
  margin: 0 auto;
  padding-top: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
}
.site-footer__copy { color: var(--bone-dim); font-family: var(--font-sans); font-size: 0.72rem; margin: 0; opacity: 0.7; }
.site-footer__legal { display: flex; align-items: center; gap: 10px; }
.site-footer__legal a { color: var(--bone-dim); text-decoration: none; font-family: var(--font-sans); font-size: 0.72rem; transition: color 0.25s ease; }
.site-footer__legal a:hover { color: var(--bone); }
.site-footer__legal span { color: var(--bone-dim); opacity: 0.5; font-size: 0.7rem; }
@media (max-width: 640px) {
  .site-footer__top, .site-footer__bottom { flex-direction: column; align-items: center; }
}

/* ============ RESPONSIVE ============ */

/* 2026-08-26: este bloque tenía un piso fijo en px/rem para CADA una
   de estas propiedades, mientras que `@media(min-width:600px)` (ver
   arriba) las escalaba en vw sin techo -- los dos sistemas nunca se
   diseñaron para coincidir justo en el límite, así que cualquier cosa
   acá abajo (logo, nav, eyebrow, collection-logo) pegaba un salto
   visible al cruzar 599->600px. Todo eso se unificó en las reglas
   base de cada uno con `max(piso, Nvw)` -- ver arriba -- así que ya
   no hace falta un bloque aparte para eso. Lo único que queda acá es
   el cambio de LAYOUT real (fila -> columna del footer), que no es un
   tamaño interpolable, es un modo de layout distinto a propósito en
   pantallas angostas. */
@media (max-width: 599px) {
  .eyebrow { letter-spacing: 0.16em; }

  .hero__footer {
    flex-direction: column;
    gap: 8px;
    align-items: flex-start;
    padding-bottom: 24px;
  }
}

/* ============ TIENDA (catálogo completo) ============ 2026-08-25.
   Vitrina completa de BANASH + punto de venta multicanal (a pedido
   del usuario). Página nueva (tienda.html), archivo plano en la raíz
   de site/ -- mismo criterio que Diario (ver esa sección más arriba):
   sin sistema de includes en este stack estático, el head/topbar/
   modales se duplican a mano en cada página nueva.

   Tipografía en rem/px + un par de media queries, igual que Diario
   (NO el sistema vw puro del hero) -- esto es una grilla de contenido
   que crece/decrece según cuántos productos haya, no una composición
   de tamaño fijo.

   Estética de tarjeta = la del slider de Colección (fondo negro puro,
   bordes redondeados, vinotinto como ÚNICO acento reservado para
   Preventa + Añadir rápido -- ver [[web-banash-estetica-interactiva]]),
   no la estética "editorial" del Diario. */

/* -------- Fondo de página: tríptico en espejo + difuminado --------
   2026-08-26, sexta vuelta: el archivo fuente (`fondo-tienda.jpg`) se
   reemplazó por uno nuevo que dio el usuario -- más resolución
   (4724x4068 vs 1000x1722 el anterior) y más ancho/panorámico. A
   diferencia del anterior (`nuevo fondo.png`, blanco-dominante, hubo
   que invertirlo), este archivo YA viene negro-dominante -- mismo
   duotono negro-de-marca/bone de siempre, pero SIN invertir. Misma
   técnica de tríptico+difuminado de la quinta vuelta (sin cambios en
   la estructura, solo el archivo) -- documentada completa abajo.
   Historial completo de rondas anteriores (resumen, ver
   [[web-banash-tienda]] para el detalle completo de cada una):
     1-3. `fixed`+`cover` iba y venía de/a `scroll`+`100%auto` según
        qué se rompiera (imagen no cubre toda la página vs. se ve
        borrosa/recortada).
     4. `fixed`+`contain`+fondo BORROSO de respaldo detrás: se ve
        completa y nítida, la pantalla nunca queda con negro liso --
        pero el usuario prefirió otra solución para esas franjas
        laterales: en vez de desenfocar, ESPEJAR la misma imagen hacia
        los lados (más contenido real, no un blur atmosférico) y
        difuminar SOLO el borde exterior de esos espejos hacia negro,
        reusando el mismo archivo de desvanecimiento que ya se usaba
        en la costura hero->colección (`coleccion-degradado.png`).

   Estructura nueva (ver tienda.html): `.shop-bg-crisp` ya no es un
   solo `<img>` de fondo CSS -- es un contenedor con:
     - `.shop-bg-crisp__row`: fila centrada de 3 copias de la MISMA
       imagen nítida (`fondo-tienda.jpg`), cada una a `height:100%`
       (mismo alto que daría `contain`) -- la del medio tal cual, las
       de los costados con `transform:scaleX(-1)` (espejo real, no
       una imagen distinta). El espejo hace que la costura entre
       copias sea invisible por definición: el pixel del borde de la
       original y el de su espejo son el mismo pixel.
     - `.shop-bg-fade--left`/`--right`: dos capas de desvanecimiento
       superpuestas en el borde exterior de cada copia lateral, con
       `assets/coleccion-degradado-h.png` -- el MISMO archivo de
       `coleccion-degradado.png` (la costura hero->colección),
       rotado 90° una sola vez con Python/PIL (no con CSS transform:
       rotate(), que complica el tamaño de la caja al girar -- ver
       memoria) para que el degradado corra en horizontal en vez de
       vertical. Ya sale con lo oscuro hacia un lado y transparente
       hacia el otro (verificado leyendo el canal alfa por pixel); el
       lado derecho usa el mismo archivo con `scaleX(-1)` para que lo
       oscuro apunte hacia AFUERA en ambos costados, como pidió el
       usuario -- transparente hacia el centro, opaco hacia el borde
       de la pantalla.
   Lo que quede más allá de eso (pantallas muy anchas donde ni el
   tríptico ni el difuminado alcanzan) cae en negro sólido liso, que
   es literalmente `background-color` de `body` -- ya no hace falta
   ninguna imagen de respaldo ahí (se eliminó `fondo-tienda-blur.jpg`,
   ya no se usa).

   En móvil (<640px) el usuario pidió explícito NO poner el espejo
   (con el ancho angosto, una sola copia central ya llena el ancho
   completo vía `contain` -- el espejo no aporta nada ahí) -- los
   paneles espejo y los difuminados se ocultan, solo queda la copia
   central.

   Apilamiento (sin cambios de fondo): `.shop-bg-crisp` sigue con
   z-index:1 -- `position:fixed` la mete en el mismo "bucket" de
   apilamiento que cualquier elemento con z-index positivo, que pinta
   POR ENCIMA del contenido normal sin posicionar. Por eso `main`
   sigue necesitando su propio `z-index:2` acá abajo -- si no, el
   catálogo quedaría tapado por el fondo. */
/* 2026-08-27: CINCO intentos de arreglar un salto/titileo del fondo
   en celular (Android) -- los cinco fallaron y quedaron revertidos.
   Sexto intento (2026-08-28, JS translate3d + absolute) TAMBIÉN
   falló en el celular real del usuario -- revertido el mismo día.
   Ver [[web-banash-tienda]] para el detalle completo de cada uno --
   el usuario pidió explícitamente parar. NO TOCAR `.shop-bg-crisp`
   por este motivo de nuevo sin que el usuario lo pida explícitamente
   -- ya se agotaron variantes de `fixed`, `sticky`,
   `overscroll-behavior`, y tres versiones de "fijo manejado a mano
   por JS" (2D, 3D, y 3D+absolute). Queda `position:fixed` simple,
   como el sitio funcionaba antes de que se reportara el problema por
   primera vez. */
body.shop-body { background-color: var(--black); }
.shop-bg-crisp {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
}
.shop-bg-crisp__row {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}
.shop-bg-crisp__panel {
  height: 100%;
  width: auto;
  max-width: none; /* anula el reset global img{max-width:100%} -- si no, el navegador achica cada copia para caber en el contenedor entero y rompe la proporción */
  flex-shrink: 0;
  display: block;
}
.shop-bg-crisp__panel--mirror { transform: scaleX(-1); }
.shop-bg-fade {
  position: absolute;
  top: 0;
  height: 100%;
  width: clamp(120px, 20vw, 280px);
  background-image: url('assets/coleccion-degradado-h.png');
  background-size: 100% 100%;
  background-repeat: no-repeat;
}
.shop-bg-fade--left { left: 0; }
.shop-bg-fade--right { right: 0; transform: scaleX(-1); }
body.shop-body main { position: relative; z-index: 2; }
@media (max-width: 640px) {
  /* Sin espejo en móvil (spec textual) -- con el ancho angosto, la
     copia central sola ya llena el ancho completo. */
  .shop-bg-crisp__panel--mirror, .shop-bg-fade { display: none; }
}

/* -------- Cabecera --------
   2026-08-26: el título de texto "Tienda" se reemplazó por el logo de
   Entropía (mismo archivo que usa el hero, `entropia-logo.png`) a
   pedido del usuario -- el `<h1>` sigue siendo semánticamente el
   título de la página (accesibilidad/SEO), solo que ahora envuelve la
   imagen en vez de texto. `drop-shadow` (no `text-shadow`, ya no hay
   texto) para la misma legibilidad garantizada contra el fondo. */
.shop-masthead { text-align: center; padding: 56px 20px 28px; user-select: none; }
.shop-masthead__logo {
  width: clamp(220px, 46vw, 420px); height: auto; display: block; margin: 0 auto;
  filter: drop-shadow(0 6px 24px rgba(0, 0, 0, 0.75));
}
.shop-masthead__sub {
  font-family: var(--font-serif); font-style: italic; color: var(--bone-dim);
  font-size: clamp(0.85rem, 2vw, 1.05rem); margin-top: 10px;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.85);
}

/* -------- Venta multicanal --------
   "El punto que centraliza" (spec textual). Mercado Libre y Shopify
   son metas a mediano plazo (ver CLAUDE.md, "Estrategia comercial")
   -- todavía no hay tienda activa en ninguno de los dos, así que sus
   tarjetas son presentacionales (un <div>, no un <a> -- nunca un
   link muerto a "#") con la etiqueta "Próximamente", mismo criterio
   que el Hoodie en preventa más abajo. Instagram SÍ es un canal real
   y activo (@banash.col, confirmado por el usuario) -- esa es la
   única tarjeta que es un <a> de verdad. */
.shop-channels {
  display: flex; gap: 14px; flex-wrap: wrap; justify-content: center;
  max-width: 900px; margin: 0 auto; padding: 0 20px 40px;
}
.shop-channel {
  display: flex; align-items: center; gap: 10px;
  background: #000; border: 1px solid rgba(244, 239, 236, 0.15); border-radius: 999px;
  padding: 10px 18px 10px 14px; text-decoration: none; color: inherit;
}
.shop-channel__icon {
  /* 2026-08-26: círculo más grande y con más contraste (bone sólido
     de fondo + ícono en negro) -- el tinte casi invisible de antes
     (rgba bone al 8%) dejaba el ícono "flotando" sin contenedor
     claro sobre el negro de la píldora, se reportó que "no se veía
     bien". */
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bone); color: #000;
}
/* 2026-08-26: logo real de Instagram que agregó el usuario
   (`logo ig.png`, glifo negro sobre blanco, sin transparencia) en vez
   del ícono de trazo genérico -- el blanco del propio archivo se
   funde con el bone del círculo (son casi el mismo tono), así que no
   hizo falta quitarle el fondo. Un poco más chico que el diámetro del
   círculo para que las esquinas cuadradas del glifo no asomen fuera
   del recorte redondo. */
.shop-channel__icon-img { width: 22px; height: 22px; display: block; }
.shop-channel__label { font-family: var(--font-sans); font-weight: 500; color: var(--bone); font-size: 0.85rem; display: block; }
.shop-channel__tag {
  font-family: var(--font-accent); font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--bone-dim); display: block; margin-top: 1px;
}
.shop-channel--live { transition: border-color 0.15s ease; }
.shop-channel--live:hover, .shop-channel--live:active { border-color: var(--bone); }
.shop-channel--soon { opacity: 0.6; cursor: default; }

/* -------- Filtros + orden -------- */
.shop-controls {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  max-width: 1300px; margin: 0 auto; padding: 0 20px 26px;
}
.shop-filters { display: flex; gap: 10px; flex-wrap: wrap; }
/* 2026-08-27: primer intento (fondo #000 sólido, igual que
   `.shop-sort select`) resolvía la legibilidad pero el usuario no
   quedó conforme -- "había muchas cosas similares" (mismo negro
   sólido que el selector de orden, el header, las tarjetas...) y
   prefiere el look transparente original. Segundo intento, el que
   queda: vidrio esmerilado (`backdrop-filter:blur`) -- el fondo
   sigue viéndose A TRAVÉS de la píldora (transparencia real, no un
   parche opaco), pero desenfocado detrás de ella, así que el texto
   siempre tiene contraste parejo sin importar qué remolino del
   tríptico caiga detrás. Blur estático (nunca animado) sobre 4
   elementos chicos -- no choca con la regla de oro de
   `web-animation-design` (esa es sobre animar `filter`/blur, no
   sobre tenerlo fijo). */
.shop-filter {
  border: 1px solid rgba(244, 239, 236, 0.3); color: var(--bone-dim);
  background: rgba(0, 0, 0, 0.32);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  font-family: var(--font-sans); font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 8px 18px; border-radius: 999px; cursor: pointer;
}
.shop-filter.is-active { border-color: var(--bone); color: var(--bone); background: rgba(0, 0, 0, 0.5); }

.shop-sort { position: relative; }
.shop-sort select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background: #000; border: 1px solid rgba(244, 239, 236, 0.3); color: var(--bone);
  font-family: var(--font-sans); font-size: 0.72rem; letter-spacing: 0.03em;
  padding: 9px 34px 9px 16px; border-radius: 999px; cursor: pointer;
}
.shop-sort select:focus { outline: none; border-color: var(--bone); }
/* 2026-08-27, a pedido del usuario: la LISTA desplegada (no el
   control cerrado, que ya estaba bien) se veía con el tema claro por
   defecto del navegador -- fondo blanco, texto negro, sin ninguna
   relación con la paleta del sitio. El `<select>` nativo no se puede
   rediseñar del todo por CSS (el navegador/sistema operativo dibuja
   el popup), pero `background`/`color` en `option` SÍ lo respetan la
   mayoría de navegadores modernos (Chrome/Edge/Firefox) -- suficiente
   para que se sienta parte del sitio en vez de un elemento ajeno. */
.shop-sort select option {
  background: #0a0a0a;
  color: var(--bone);
}
.shop-sort::after {
  content: ''; position: absolute; top: 50%; right: 15px; width: 7px; height: 7px;
  border-right: 1.5px solid var(--bone-dim); border-bottom: 1.5px solid var(--bone-dim);
  transform: translateY(-70%) rotate(45deg); pointer-events: none;
}

.shop-empty { text-align: center; color: var(--bone-dim); font-family: var(--font-sans); font-size: 0.9rem; padding: 50px 20px; }

/* -------- Grilla --------
   1-2 columnas en móvil, 3 en escritorio (spec: "en móvil 1 o 2, en
   escritorio 3 o 4"). Con 5 productos hoy, 3 columnas deja una última
   fila de 2 en vez del hueco de 1 que dejaría 4 -- mejor balance
   visual; sigue siendo válido subir a 4 más adelante si el catálogo
   crece lo suficiente para justificarlo. */
.shop-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;
  max-width: 1300px; margin: 0 auto; padding: 0 20px 100px;
}
@media (min-width: 720px) {
  .shop-grid { grid-template-columns: repeat(3, 1fr); gap: 24px; }
}

/* -------- Tarjeta de producto --------
   Misma identidad que .coleccion__card: negro puro, radio 16px,
   sombra elevada. Toda la tarjeta es clickeable (abre la ficha
   completa, .pdp -- ver shop.js) EXCEPTO el botón de Añadir rápido y
   su popover de talla, que hacen stopPropagation. */
.shop-card {
  position: relative; background: #000; border-radius: 16px; padding: 12px 12px 16px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45); cursor: pointer;
  border: 1px solid rgba(244, 239, 236, 0.08);
}
.shop-card__media { position: relative; aspect-ratio: 4 / 5; border-radius: 12px; margin-bottom: 14px; overflow: hidden; }
.shop-card__badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
  font-family: var(--font-accent); font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 6px 12px; border-radius: 999px;
}
/* Vinotinto EXCLUSIVO para Preventa + Añadir rápido (spec textual) --
   Sold Out / Últimas unidades quedan listos acá (data-badge en
   tienda.html) para cuando haya inventario real que los justifique,
   pero hoy solo el Hoodie usa "preventa" -- no hay datos reales de
   stock agotado o últimas unidades todavía (ver inventario-pedidos/
   en el repo para cuando exista esa integración). */
.shop-card__badge--preventa { background: var(--wine); color: var(--bone); }
.shop-card__badge--soldout { background: rgba(10, 4, 6, 0.9); color: var(--bone-dim); border: 1px solid rgba(244, 239, 236, 0.25); }
.shop-card__badge--low { background: transparent; color: var(--bone); border: 1px solid var(--bone); }

.shop-card__cat {
  font-family: var(--font-accent); color: var(--bone-dim); font-size: 0.65rem;
  letter-spacing: 0.1em; text-transform: uppercase;
}
.shop-card__name {
  font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 1rem;
  margin: 4px 0 12px; line-height: 1.2;
}
.shop-card__foot { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.shop-card__price { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 1.15rem; }

.shop-card__quickadd {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: 50%; border: none;
  background: var(--wine); color: var(--bone); display: flex; align-items: center; justify-content: center;
  cursor: pointer; position: relative;
}
.shop-card__quickadd:active { opacity: 0.7; }
.shop-card__quickadd[disabled] {
  background: transparent; border: 1px solid rgba(244, 239, 236, 0.3); color: var(--bone-dim); cursor: default;
}

/* -------- Popover de talla (Añadir rápido) --------
   Solo aparece para productos con más de una talla (los tres crop
   tees) -- los de talla única (Mangas Desmontables) agregan directo
   al carrito con un solo click, sin abrir nada, que es justo lo que
   pidió el usuario ("comprar sin tener que abrir la ficha de cada
   artículo"). */
.shop-quickpick {
  position: absolute; bottom: calc(100% + 10px); right: 0; z-index: 5;
  background: #000; border: 1px solid rgba(244, 239, 236, 0.2); border-radius: 14px;
  padding: 12px; box-shadow: 0 14px 30px rgba(0, 0, 0, 0.55);
  display: flex; flex-direction: column; gap: 8px; min-width: 168px;
}
.shop-quickpick__label {
  font-family: var(--font-sans); color: var(--bone-dim); font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.06em;
}
.shop-quickpick__sizes { display: flex; gap: 6px; flex-wrap: wrap; }
.shop-quickpick__size {
  min-width: 34px; height: 34px; padding: 0 6px; border-radius: 50%; background: #000;
  border: 1px solid var(--bone); color: var(--bone); font-family: var(--font-sans); font-size: 0.78rem; cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.shop-quickpick__size:hover, .shop-quickpick__size:active { background: var(--bone); color: #000; }
/* 2026-08-27, a pedido del usuario: el popover de "Añadir rápido"
   ahora respeta el stock igual que la ficha completa (.pdp__size--
   unavailable) -- mismo criterio visual (atenuado + tachado + no
   clickeable), doble especificidad para ganarle al hover de arriba. */
.shop-quickpick__size.shop-quickpick__size--unavailable {
  opacity: 0.35; cursor: not-allowed; text-decoration: line-through;
  border-color: rgba(244, 239, 236, 0.35); color: var(--bone-dim); background: #000;
}
.shop-quickpick__size.shop-quickpick__size--unavailable:hover,
.shop-quickpick__size.shop-quickpick__size--unavailable:active { background: #000; color: var(--bone-dim); }
/* Segundo paso del popover -- tras elegir talla, si el producto declara
   más de un corte (Boxy fit/Oversized, ver `data-product-cuts`) el
   popover cambia su contenido a esto en vez de cerrarse, mismo patrón
   segmentado que `.pdp__cut-btn` pero a la escala chica del popover. */
.shop-quickpick__cuts { display: inline-flex; border: 1px solid rgba(202, 188, 184, 0.35); border-radius: 999px; overflow: hidden; }
.shop-quickpick__cut {
  background: transparent; border: none; color: var(--bone); font-family: var(--font-sans);
  font-size: 0.74rem; padding: 8px 14px; cursor: pointer; transition: background 0.15s ease, color 0.15s ease;
}
.shop-quickpick__cut:not(:first-child) { border-left: 1px solid rgba(202, 188, 184, 0.35); }
.shop-quickpick__cut:hover, .shop-quickpick__cut:active { background: var(--bone); color: #000; }
.shop-quickpick__back {
  align-self: flex-start; background: none; border: none; color: var(--bone-dim); font-family: var(--font-sans);
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.05em; cursor: pointer; padding: 0; margin-bottom: 2px;
}
.shop-quickpick__back:hover, .shop-quickpick__back:active { color: var(--bone); }

@media (max-width: 480px) {
  .shop-channel__label { font-size: 0.78rem; }
  .shop-controls { justify-content: center; }
}

/* ============ BOTÓN FLOTANTE DE WHATSAPP ============ 2026-08-26.
   "En todo momento" (spec textual) -- vive en las 8 páginas reales
   del sitio (duplicado a mano, mismo criterio que el resto del chrome
   compartido -- sin sistema de includes en este stack estático).
   `position:fixed`, esquina inferior derecha, por ENCIMA del contenido
   normal (z-index 500, más que `.site-topbar` en 60) pero por DEBAJO
   de cualquier modal (que arrancan en 1000) -- si se abre el carrito/
   cuenta/PDP, el botón queda tapado por ese overlay a propósito, no
   debe competir con un modal abierto.

   Corrección 2026-08-26: estaba en 70 -- por debajo de
   `.coleccion__nav` (z-index:200, las flechas del slider de Colección),
   así que en cualquier punto donde ambos se superponen en pantalla el
   botón de WhatsApp quedaba TAPADO por la flecha ("el logo de wpp pasa
   por debajo de la flecha", reportado por el usuario). Subido a 500 --
   bien por encima de cualquier z-index de contenido normal del sitio
   (el más alto fuera de modales/toast es 200), sigue debajo de los
   modales (1000+) a propósito.

   Dos imágenes superpuestas (negro y verde, los archivos reales que
   dio el usuario -- `logo whatsapp negro.png`/`logo whatsapp
   verde.jpg`, reprocesados con Python/PIL para quitarles el fondo
   blanco/halo que traían y dejarlas con transparencia real) en vez de
   un solo ícono con `filter` de color -- son diseños ligeramente
   distintos (cuadrado redondeado vs. círculo), tal cual los proveyó
   el usuario. Crossfade + una escala/rotación sutil en hover ("breve
   animación", spec textual) en vez de un cambio brusco de opacidad. */
.whatsapp-float {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 58px;
  height: 58px;
  z-index: 500;
  display: block;
}
.whatsapp-float__icon {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* 2026-08-26: la sombra blanca difuminada que se agregó acá (pedido
     de una ronda anterior, "efecto de transparencia con blur") se veía
     en la práctica como una nube blanca indeseada alrededor del ícono
     -- el usuario pidió quitarla. Vuelve a quedar un solo drop-shadow
     oscuro, chico y cerca (apoyo/profundidad real, sin halo). */
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.45));
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.whatsapp-float__icon--green { opacity: 0; transform: scale(0.8) rotate(14deg); }
.whatsapp-float:hover .whatsapp-float__icon--black,
.whatsapp-float:focus-visible .whatsapp-float__icon--black {
  opacity: 0; transform: scale(0.8) rotate(-14deg);
}
.whatsapp-float:hover .whatsapp-float__icon--green,
.whatsapp-float:focus-visible .whatsapp-float__icon--green {
  opacity: 1; transform: scale(1) rotate(0deg);
}
.whatsapp-float:active { transform: scale(0.92); }
@media (max-width: 640px) {
  .whatsapp-float { width: 50px; height: 50px; right: 14px; bottom: 14px; }
}

/* ============ MICRO-ANIMACIONES DE BOTONES/CONTROLES ============
   2026-08-26, a pedido del usuario: "que todo se vea más profesional"
   en hover/click. El sitio tiene ~40 botones distintos repartidos en
   cada modal/sección -- en vez de tocar cada uno a mano (y arriesgar
   dejar alguno afuera), se define un tratamiento COMPARTIDO acá:
     1. Todo <button> real gana una transición base + se hunde un poco
        al hacer click (feedback táctil consistente en todo el sitio).
     2. Los controles chicos/circulares (íconos, flechas, tallas,
        cerrar) además se agrandan un poco y brillan en hover -- se
        ven bien creciendo porque nunca tocan el borde de su
        contenedor.
     3. Los CTA de ancho completo (Añadir al carrito, Ir a pagar, Crear
        cuenta...) SOLO brillan en hover, sin escala -- agrandar un
        botón que ya ocupa el 100% del ancho se ve raro/recortado
        contra el borde del panel.
     4. Los enlaces/píldoras que no son <button> (Instagram, CTA del
        hero, volver al Diario) ganan su propia transición.
   Ninguna de estas reglas cambia DE QUÉ COLOR se pone cada estado
   (eso ya lo definía cada componente, ej. `.pdp__size.is-selected`) --
   solo agrega la transición y el micro-movimiento que faltaban. */
button:not(:disabled) {
  transition: transform 0.15s ease, opacity 0.15s ease, filter 0.15s ease,
              background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
button:not(:disabled):active { transform: scale(0.95); }

.icon-btn:hover,
.coleccion__card-btn:hover,
.shop-card__quickadd:not([disabled]):hover,
.shop-quickpick__size:hover,
.pdp__size:hover,
.pdp__thumb:hover,
.pdp__ship-btn:hover,
.pdp__cut-btn:hover,
.shop-quickpick__cut:hover,
.auth__close:hover, .cart__close:hover, .account__close:hover, .pdp__close:hover,
.cart__qty-btn:hover, .cart__row-remove:hover,
.logout-confirm__cancel:hover, .logout-confirm__confirm:hover,
.shop-filter:hover,
.auth__tab:hover,
.account__tab:hover {
  filter: brightness(1.18);
  transform: scale(1.06);
}
/* Los círculos ya tenían su propio :active con un opacity distinto en
   algunos casos -- acá solo se homogeniza el hundimiento, no se pisa
   ningún color ya definido. */
.icon-btn:active, .coleccion__card-btn:active,
.shop-card__quickadd:active, .pdp__size:active {
  transform: scale(0.94);
}
/* Las flechas del slider de Colección quedan afuera del tratamiento
   compartido de arriba (2026-08-26, a pedido del usuario: "es
   contraintuitivo que el botón se mueva cuando pones el mouse encima")
   -- `.coleccion__nav` ya usa `transform:translateY(-50%)` en su regla
   base para centrarse verticalmente; el `scale()` del hover/active
   compartido PISABA ese transform (mismo `transform`, se reemplaza
   entero) y además la flecha se movía/saltaba al pasar el mouse. Se
   queda solo con brillo en hover (sin escala) y la atenuación de click
   que ya tenía (`:active{opacity:.6}`, arriba en la sección del
   slider) -- cero movimiento. */
.coleccion__nav:hover { filter: brightness(1.18); }
/* Mismo bug, encontrado también en `.auth__eye` (ojo de mostrar/
   ocultar contraseña) -- "se mueve cuando se pasa el mouse", ahora en
   :active. Los dos son <button>, así que además del hover/active
   compartido de arriba, el `button:not(:disabled):active{transform:
   scale(0.95)}` de la MISMA sección (más arriba en este archivo) TAMBIÉN
   les pisaba el `translateY(-50%)` propio -- ese selector tiene más
   especificidad que un `:active` con una sola clase
   (`button`+`:disabled`+`:active` vs `.clase`+`:active`), así que un
   `:active{transform:...}` de una sola clase sola NUNCA le iba a ganar
   sin importar el orden. Acá se iguala la especificidad calificando con
   `button` (mismo nivel) y, al venir DESPUÉS en el archivo, gana el
   empate -- por esto esta regla vive ACÁ y no arriba, junto a cada
   componente. Si se encuentra el mismo salto en otro botón que ya use
   `transform` para posicionarse (no para decorar), agregarlo a esta
   lista, no crear una regla `:active` suelta en otro lado -- no le va a
   ganar al genérico. */
button.coleccion__nav:active,
button.auth__eye:active {
  transform: translateY(-50%);
}

.pdp__cta:not(:disabled):hover,
.cart__checkout:hover,
.auth__cta:hover,
.account__form-btn:hover,
.account__logout:hover,
.diario-hero__cta:hover,
.crosssell__btn:hover,
.cart__crosssell-btn:hover,
.pdp__catalog-link:hover,
.pay-modal__upload-btn:hover,
.pay-modal__done:hover,
.order__pay-btn:hover {
  filter: brightness(1.12);
}

.btn-pill,
.shop-channel--live,
.article-footer__back,
.hero__footer-left a,
.hero__footer-right a {
  transition: opacity 0.18s ease, border-color 0.18s ease, filter 0.18s ease, transform 0.18s ease;
}
.btn-pill:hover { filter: brightness(1.12); transform: translateY(-2px); }
.btn-pill:active { transform: translateY(0) scale(0.97); }
.article-footer__back:hover, .hero__footer-left a:hover, .hero__footer-right a:hover {
  opacity: 0.75;
}

.shop-sort select {
  transition: border-color 0.15s ease;
}
.shop-sort select:hover { border-color: var(--bone); }

/* ============ TRANSICIÓN ENTRE PÁGINAS ============
   2026-08-26, rediseño completo a pedido del usuario -- la primera
   versión (un círculo vinotinto tipo iris) no le gustó. Ver
   page-transition.js (misma carpeta) para la lógica completa. Ahora
   la secuencia es:
     1) SALIDA: los dos ojos rojos reales del logo de la página actual
        parpadean (`.tx-eyelid`, dos óvalos negros que el script
        posiciona por JS exactamente sobre los puntos rojos del arte
        del logo -- no es un logo de mentira dibujado encima, son los
        ojos DE VERDAD del logo que ya está en pantalla).
     2) la pantalla se cubre completamente de negro (`#page-veil`).
     3) recién ahí se navega. La página siguiente arranca ya cubierta
        de negro -- el CSS por defecto (`#page-veil` sin `.is-open`)
        la deja así ANTES de que corra cualquier JS, mismo truco de
        siempre, cero flash.
     4) ENTRADA: el negro se retira y los bloques principales de la
        sección (`.tx-block`, cada uno con su propia `.tx-from-*`)
        se deslizan desde FUERA de la pantalla hasta su lugar, en
        cascada -- la sección "se construye" en vez de aparecer de
        golpe.
   z-index muy alto -- por encima de absolutamente todo (modales
   incluidos), porque una transición de página debe verse SIEMPRE
   completa, nunca tapada a medias por algo que quedó abierto. */
#page-veil {
  position: fixed;
  inset: 0;
  z-index: 99999;
  background: var(--black);
  opacity: 1;
  pointer-events: auto;
  transition: opacity 0.45s ease;
}
#page-veil.is-open {
  opacity: 0;
  pointer-events: none;
}
#page-veil.is-instant { transition: none; }

/* Cuarta corrección del parpadeo (2026-08-26) -- las tres anteriores
   (óvalo con origin al centro, franja compartida, óvalo con origin
   arriba) fallaban por dos motivos de fondo, no por elegir mal la
   dirección de crecimiento:
     1. La geometría medida (ver EYE_GEOM en page-transition.js) era
        ~60% más ancha y casi el doble de alta que el brillo rojo real
        del ojo -- el párpado SIEMPRE fue más grande que el ojo, sin
        importar desde dónde creciera. Re-medido con componentes
        conexos + control visual, ahora es del tamaño real.
     2. Un solo elemento que escala (sea cual sea su origin) no puede
        leer como un párpado real cerrándose sobre un ojo con forma de
        almendra -- o se infla simétrico desde el medio (origin
        centro, parece un obturador) o cae con un borde recto que no
        sigue la curva de las puntas del ojo (origin arriba).
   Fix estructural: `.tx-eyelid` (este elemento) ya NO es el párpado
   en sí -- es un CONTENEDOR con la forma EXACTA del ojo
   (`border-radius:50%` sobre un ancho/alto que YA es el óvalo medido,
   no un cuadrado) y `overflow:hidden`, fijo, sin transform ni
   transición propia. El párpado de verdad es `.tx-eyelid__fill`
   (hijo, ver abajo) -- al crecer desde arriba DENTRO de este
   contenedor, queda recortado a la curva del ojo en todo momento: no
   puede dibujar nada fuera de la almendra por más que crezca. */
.tx-eyelid {
  position: fixed;
  z-index: 100000;
  overflow: hidden;
  border-radius: 50%; /* elipse -- el ancho/alto ya es el óvalo medido del ojo, no hace falta 999px */
  pointer-events: none;
}
/* El párpado real: negro sólido, ocupa todo el contenedor pero nace
   con altura 0 anclado al borde SUPERIOR (`transform-origin:50% 0%`)
   -- `scaleY(0)`→`scaleY(1)` lo hace crecer hacia ABAJO, exactamente
   como un párpado real cerrándose de arriba hacia abajo (pedido
   textual del usuario). Recortado por el `overflow:hidden` del padre
   a la forma de almendra del ojo -- nunca se sale de su contorno.

   2026-08-27 (ronda posterior, a pedido del usuario): el borde de
   abajo (el que se ve "bajar" mientras crece) era una línea recta --
   este `div` era un simple rectángulo (`inset:0`, sin border-radius
   propio), y aunque el CONTENEDOR ya recorta los costados a la forma
   del ojo, un rectángulo escalado en Y siempre tiene el borde inferior
   perfectamente horizontal, sin importar cuánto creció. Fix real:
   `border-radius:50%` acá también -- este `div` pasa a ser una elipse
   IGUAL a la del contenedor (mismo ancho/alto, mismo `inset:0`). Al
   crecer con `scaleY(t)` desde el borde superior, en cualquier punto
   intermedio queda una elipse aplastada en Y (ancho completo, alto
   parcial) -- su borde inferior YA es un arco curvo, no una línea,
   igual que un párpado real cerrándose. En `scaleY(1)` es idéntica a
   la elipse del ojo (se ve igual que antes, ojo completamente
   tapado) -- el cambio solo se nota DURANTE el cierre/apertura. */
.tx-eyelid__fill {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--black);
  transform: scaleY(0);
  transform-origin: 50% 0%;
  /* 2026-08-27, a pedido del usuario ("a veces la animación pasa más
     rápido, como si hubiera una variación"): causa real, no un ajuste
     de curva/duración -- este elemento se crea DE CERO con
     `document.createElement` y se le llama `.animate()` en el mismo
     tick (ver `makeEyelid`/`blink` en page-transition.js), a
     diferencia de `.tx-block` (que sí tiene este hint, línea de
     arriba) que ya existe en el DOM desde que carga la página. Sin
     `will-change`, el navegador decide en caliente si promueve el
     elemento a su propia capa de composición antes de pintar el
     primer frame o recién durante la animación -- si el hilo
     principal está ocupado en ese instante (GC, layout, decode de
     imágenes), esa promoción llega tarde y "come" una porción del
     timeline lógico de 900ms antes del primer frame visible, dejando
     el resto comprimido/más rápido. Mismo blink, misma curva, misma
     duración -- solo se le da a la capa el mismo aviso anticipado que
     ya tienen los demás elementos animados del sitio. */
  will-change: transform;
}

/* Bloques que "se construyen" al entrar -- cada uno lleva además una
   `.tx-from-*` que define desde dónde entra. `.tx-in` (agregada por
   JS, con un pequeño `transition-delay` escalonado por bloque) es lo
   que dispara la animación hacia la posición final. */
.tx-block {
  transition: transform 0.7s cubic-bezier(.16, 1, .3, 1), opacity 0.55s ease;
  will-change: transform;
}
.tx-from-top    { transform: translateY(-100vh); opacity: 0; }
.tx-from-bottom { transform: translateY(100vh); opacity: 0; }
.tx-from-left   { transform: translateX(-100vw); opacity: 0; }
.tx-from-right  { transform: translateX(100vw); opacity: 0; }
.tx-block.tx-in { transform: translate(0, 0); opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  #page-veil, .tx-block { transition: none !important; }
  .tx-from-top, .tx-from-bottom, .tx-from-left, .tx-from-right { transform: none; opacity: 1; }
  /* 2026-08-27: sin esto, alguien con "reducir movimiento" activado
     nunca dispara `.hero--reveal` (page-transition.js lo salta a
     propósito, ver revealHeroAsh) y se quedaría con el fondo del hero
     y el logo Entropía invisibles PARA SIEMPRE -- acá se fuerza el
     estado final de cada animación nueva, sin transición. */
  .hero__ash-cover { animation: none !important; transform: scaleY(0) !important; }
  .collection-logo { animation: none !important; opacity: 1 !important; filter: none !important; transform: translateY(-16px) !important; }
  .eyebrow { animation: none !important; opacity: 0.9 !important; filter: none !important; transform: none !important; }
  .topbar__divider, .eyebrow__rule { animation: none !important; opacity: 1 !important; transform: none !important; }
}

/* ============ PANTALLA DE PAGO (payment-panel.js) ============
   2026-08-27, a pedido del usuario -- mismo esqueleto que
   .logout-confirm (overlay + panel), z-index 1100: por encima de
   .account (1000) porque este panel lo reemplaza al confirmar el
   pedido, por debajo del .toast (1200) para que un aviso siga
   viéndose arriba de todo. Un solo acento vinotinto (el botón de
   subir comprobante), el resto en negro/bone -- misma regla que el
   resto de interfaces interactivas del sitio.

   2026-08-27, segunda pasada ("más amigable y estético", a pedido del
   usuario): más jerarquía visual (encabezado con el isotipo del
   cuervo -- CLAUDE.md lo reserva justo para espacios reducidos como
   este, no como elemento decorativo suelto; caja de total destacada;
   secciones con su propio título) + el `<input type=file>` nativo
   (botón gris del sistema operativo, desentonaba fuerte) reemplazado
   por una zona propia (`.pay-dropzone`) que sigue usando el input real
   por debajo -- accesibilidad/teclado intactos, la única diferencia es
   visual. */
.pay-modal {
  position: fixed; inset: 0; z-index: 1100; display: flex; align-items: center; justify-content: center;
  padding: 6vw; opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}
.pay-modal.is-open { opacity: 1; visibility: visible; transition: opacity 0.2s ease; }
.pay-modal__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.88); cursor: pointer; }
/* Mismo fix estructural que .account__panel/.account__scroll (ver ese
   comentario para el porqué completo): el botón cerrar vivía dentro
   del mismo contenedor con `overflow-y:auto`, así que se desplazaba
   con el contenido en vez de quedarse fijo en la esquina -- este modal
   tiene bastante contenido (formas de pago + zona de subida), fácil
   que no entre completo en pantallas chicas. */
.pay-modal__panel {
  position: relative; z-index: 1; width: 100%; max-width: 92vw; max-height: 86vh; overflow: hidden;
  background: #000; border: 1px solid rgba(202, 188, 184, 0.25); border-radius: 16px;
  display: flex; flex-direction: column;
  transform: translateY(3vw); transition: transform 0.2s ease; outline: none; /* ver .pdp__panel -- sin scale() para no correr el botón cerrar */
}
.pay-modal__scroll { overflow-y: auto; flex: 1; min-height: 0; padding: 9vw 6vw 7vw; }
.pay-modal.is-open .pay-modal__panel { transform: translateY(0); }
.pay-modal__close {
  position: absolute; top: 4vw; right: 4vw; width: 9vw; height: 9vw; border-radius: 50%;
  border: 1px solid var(--bone-dim); background: transparent; color: var(--bone);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  z-index: 2;
}
.pay-modal__close:active { opacity: 0.6; }

.pay-modal__head { display: flex; align-items: center; gap: 2.4vw; margin-bottom: 3vw; }
.pay-modal__mark { width: 6vw; height: 6vw; object-fit: contain; opacity: 0.85; flex-shrink: 0; }
.pay-modal__eyebrow { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3vw; letter-spacing: 0.08em; text-transform: uppercase; }
.pay-modal__eyebrow span { color: var(--bone); }
.pay-modal__title { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 5vw; line-height: 1.25; margin-bottom: 4vw; }

.pay-modal__total-box {
  display: flex; align-items: baseline; justify-content: space-between; gap: 2vw;
  background: rgba(92, 14, 24, 0.14); border: 1px solid var(--wine); border-radius: 12px;
  padding: 3.4vw 4vw; margin-bottom: 4vw;
}
.pay-modal__total-label { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3vw; text-transform: uppercase; letter-spacing: 0.06em; }
.pay-modal__total-value { font-family: var(--font-sans); font-weight: 700; color: var(--bone); font-size: 5.6vw; }

.pay-modal__hint { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3.2vw; line-height: 1.5; margin-bottom: 6.5vw; }
.pay-modal__hint a { color: var(--wine-bright); }
.pay-modal__loading { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3.2vw; }

.pay-modal__section { margin-bottom: 6.5vw; }
.pay-modal__section-title {
  font-family: var(--font-sans); color: var(--bone); font-size: 3.4vw; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 1vw;
}
.pay-modal__section-hint { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.9vw; margin-bottom: 3.5vw; }

.pay-modal__methods { display: flex; flex-direction: column; gap: 3vw; }
.pay-method {
  display: flex; align-items: center; gap: 3.4vw;
  border: 1px solid rgba(202, 188, 184, 0.25); border-radius: 12px; padding: 3.6vw 4vw;
}
.pay-method__icon {
  width: 9vw; height: 9vw; flex-shrink: 0; border-radius: 50%; background: rgba(244, 239, 236, 0.06);
  color: var(--bone-dim); display: flex; align-items: center; justify-content: center; padding: 2.2vw;
}
.pay-method__icon svg { width: 100%; height: 100%; }
.pay-method__info { display: flex; flex-direction: column; gap: 0.8vw; flex: 1; min-width: 0; }
.pay-method__info b { font-family: var(--font-sans); color: var(--bone); font-size: 3.6vw; }
.pay-method__info span { font-family: var(--font-sans); color: var(--bone); font-size: 3.4vw; letter-spacing: 0.03em; }
.pay-method__info small { font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.9vw; }
/* 2026-08-28, a pedido del usuario: el QR de la tarjeta es chico a
   propósito (tiene que caber en la fila junto al nombre/número) --
   ahora es un botón real (mejor que un div con role="button": foco y
   Enter/Espacio gratis) que abre el mismo QR en grande (`.qr-
   lightbox` abajo) para poder escanearlo de verdad. */
.pay-method__qr-btn { display: block; padding: 0; border: none; background: none; cursor: pointer; flex-shrink: 0; border-radius: 8px; }
.pay-method__qr { width: 18vw; height: 18vw; object-fit: contain; background: var(--bone); border-radius: 8px; padding: 1vw; flex-shrink: 0; display: block; }

/* Zona de subida -- reemplaza al `<input type=file>` nativo. El input
   real sigue ahí (`hidden`, `<label for=...>` lo activa con un click
   nativo, cero JS hace falta para abrir el selector de archivos) --
   solo cambia lo que se VE. `.has-file` (agregada por JS al elegir una
   imagen) pasa el borde a sólido vinotinto y muestra una miniatura
   real de la foto elegida en vez del ícono, confirmación visual
   inmediata de qué se va a subir. */
.pay-dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2.2vw;
  border: 1.5px dashed rgba(202, 188, 184, 0.35); border-radius: 12px; padding: 6vw 4vw;
  cursor: pointer; text-align: center; transition: border-color 0.15s ease, background-color 0.15s ease;
  margin-bottom: 3.5vw;
}
.pay-dropzone:hover { border-color: var(--bone-dim); background: rgba(244, 239, 236, 0.03); }
.pay-dropzone__icon {
  width: 11vw; height: 11vw; border-radius: 10px; color: var(--bone-dim);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.pay-dropzone__icon svg { width: 60%; height: 60%; }
.pay-dropzone__text { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3.1vw; }
.pay-dropzone.has-file { border-style: solid; border-color: var(--wine-bright); }
.pay-dropzone.has-file .pay-dropzone__text { color: var(--bone); }

.pay-modal__upload-btn {
  width: 100%; height: 11vw; border-radius: 999px; border: none; cursor: pointer;
  background: var(--wine); color: var(--bone); font-family: var(--font-sans); font-weight: 700; font-size: 3.4vw;
}
.pay-modal__upload-btn:not(:disabled):active { opacity: 0.75; }
.pay-modal__upload-btn:disabled { opacity: 0.55; cursor: default; }
.pay-modal__upload-msg {
  display: flex; align-items: center; gap: 1.6vw;
  font-family: var(--font-sans); color: var(--bone-dim); font-size: 2.9vw; margin-top: 2.5vw; min-height: 1.4em;
}
.pay-modal__upload-msg.is-ok { color: #7fd68a; }
.pay-modal__upload-msg svg { width: 4.2vw; height: 4.2vw; flex-shrink: 0; }

.pay-modal__done {
  width: 100%; height: 11vw; border-radius: 999px; border: 1px solid var(--bone-dim); background: transparent;
  color: var(--bone); font-family: var(--font-sans); font-size: 3.4vw; cursor: pointer;
}
.pay-modal__done:active { opacity: 0.7; }

/* -------- Lightbox del QR (2026-08-28) -------- */
/* Por encima de `.pay-modal` (z-index 1100) -- se abre ENCIMA de la
   pantalla de pago, no la reemplaza. Mismo patrón de fade que
   `.pay-modal` (opacity/visibility, sin scale -- ver [[web-banash-
   hero-animacion-cenizas]] sobre por qué los paneles de este sitio no
   escalan en su entrada: descentra hijos `position:absolute`). */
.qr-lightbox {
  position: fixed; inset: 0; z-index: 1150; display: flex; align-items: center; justify-content: center;
  padding: 8vw; opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}
.qr-lightbox.is-open { opacity: 1; visibility: visible; transition: opacity 0.2s ease; }
.qr-lightbox__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.92); cursor: pointer; }
.qr-lightbox__panel {
  position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 3vw;
  outline: none;
}
.qr-lightbox__img {
  width: min(78vw, 78vh); height: min(78vw, 78vh); object-fit: contain;
  background: var(--bone); border-radius: 16px; padding: 5vw;
}
.qr-lightbox__label { font-family: var(--font-sans); color: var(--bone-dim); font-size: 3.2vw; text-align: center; }
.qr-lightbox__close {
  position: absolute; top: -12vw; right: 0; width: 9vw; height: 9vw; border-radius: 50%;
  border: 1px solid var(--bone-dim); background: transparent; color: var(--bone);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.qr-lightbox__close:active { opacity: 0.6; }

/* Aviso de estado dentro de una tarjeta de pedido (account-panel.js) --
   sin vinotinto: es información, no una acción -- mismo criterio que
   los badges de Preventa/Sold Out. */
.order__badge {
  display: inline-block; font-family: var(--font-sans); font-size: 2.9vw; font-weight: 600;
  padding: 1.2vw 3vw; border-radius: 999px; margin: 1.5vw 0 3vw;
}
.order__badge--pending { background: rgba(244, 239, 236, 0.1); color: var(--bone); border: 1px solid var(--bone-dim); }
.order__badge--cancelled { background: rgba(194, 42, 57, 0.12); color: var(--wine-bright); border: 1px solid var(--wine); }
.order__pay-btn {
  margin-top: 3vw; width: 100%; height: 10vw; border-radius: 999px; border: 1px solid var(--bone-dim);
  background: transparent; color: var(--bone); font-family: var(--font-sans); font-size: 3.2vw; cursor: pointer;
}
.order__pay-btn:active { opacity: 0.7; }

@media (min-width: 600px) {
  .pay-modal__panel { width: 34vw; max-width: 38vw; border-radius: 20px; }
  .pay-modal__scroll { padding: 3vw 2.6vw 2.6vw; }
  .pay-modal__close { top: 1.6vw; right: 1.6vw; width: 2.4vw; height: 2.4vw; }
  .pay-modal__head { gap: 0.8vw; margin-bottom: 0.8vw; }
  .pay-modal__mark { width: 1.7vw; height: 1.7vw; }
  .pay-modal__eyebrow { font-size: 0.78vw; }
  .pay-modal__title { font-size: 1.5vw; margin-bottom: 1.3vw; }
  .pay-modal__total-box { padding: 1vw 1.3vw; border-radius: 10px; margin-bottom: 1.3vw; }
  .pay-modal__total-label { font-size: 0.72vw; }
  .pay-modal__total-value { font-size: 1.5vw; }
  .pay-modal__hint { font-size: 0.88vw; margin-bottom: 2vw; }
  .pay-modal__loading { font-size: 0.88vw; }
  .pay-modal__section { margin-bottom: 2vw; }
  .pay-modal__section-title { font-size: 0.85vw; margin-bottom: 0.3vw; }
  .pay-modal__section-hint { font-size: 0.78vw; margin-bottom: 1vw; }
  .pay-modal__methods { gap: 0.9vw; }
  .pay-method { padding: 1vw 1.3vw; border-radius: 10px; gap: 1vw; }
  .pay-method__icon { width: 2.6vw; height: 2.6vw; padding: 0.6vw; }
  .pay-method__info { gap: 0.3vw; }
  .pay-method__info b { font-size: 0.95vw; }
  .pay-method__info span { font-size: 0.88vw; }
  .pay-method__info small { font-size: 0.78vw; }
  .pay-method__qr { width: 4.6vw; height: 4.6vw; border-radius: 6px; padding: 0.3vw; }
  .qr-lightbox__img { width: min(30vw, 60vh); height: min(30vw, 60vh); border-radius: 14px; padding: 1.6vw; }
  .qr-lightbox__label { font-size: 0.85vw; }
  .qr-lightbox__panel { gap: 1vw; }
  .qr-lightbox__close { top: -3.2vw; width: 2.2vw; height: 2.2vw; }
  .pay-dropzone { padding: 1.6vw 1.3vw; gap: 0.6vw; border-radius: 10px; margin-bottom: 1vw; }
  .pay-dropzone__icon { width: 2.8vw; height: 2.8vw; border-radius: 8px; }
  .pay-dropzone__text { font-size: 0.82vw; }
  .pay-modal__upload-btn { height: 3vw; font-size: 0.9vw; border-radius: 999px; }
  .pay-modal__upload-msg { font-size: 0.78vw; margin-top: 0.8vw; gap: 0.5vw; }
  .pay-modal__upload-msg svg { width: 1.1vw; height: 1.1vw; }
  .pay-modal__done { height: 3vw; font-size: 0.9vw; margin-top: 0.4vw; }
  .order__badge { font-size: 0.78vw; padding: 0.4vw 1vw; margin: 0.4vw 0 0.9vw; }
  .order__pay-btn { height: 2.6vw; font-size: 0.85vw; margin-top: 0.9vw; }
}
