:root{--bg:#050505;--surface:#0b0b0b;--surface-2:#121212;--text:#fff;--muted:#b7b7b7;--line:#292929;--pink:#FF007A;--pink2:#FF4FA3;--max:1180px;--radius:18px;
  /* Fase 42.3 — el Hero (abajo, .hero-grid) usa deliberadamente un
     contenedor más ancho que --max (1440px vs 1180px, ver nota de la
     Fase 35.3: la pieza del carrusel necesitaba más espacio) — eso deja
     su borde izquierdo más pegado al viewport que el de .wrap, que es
     lo que usa el logo del Header. En vez de un padding fijo (que se
     desalinearía en otros anchos), esto calcula la mitad exacta de la
     diferencia entre ambos contenedores en CUALQUIER viewport, y se
     aplica como padding a .hero-copy — el logo y el texto del Hero
     quedan en el mismo sistema de grid sin tocar el ancho/proporción
     de la imagen del carrusel. Se resuelve a 0 automáticamente en
     cuanto cualquiera de los dos contenedores cae por debajo de su
     tope (≤1220px), que es también donde el Hero pasa a una columna. */
  --hero-align-pad:calc((min(1440px,100vw - 40px) - min(var(--max),100vw - 40px)) / 2)
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--text);font-family:"Inter",system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
button,input,textarea{font:inherit}
button,a{cursor:pointer}
a{color:inherit;text-decoration:none}
.wrap{width:min(var(--max),calc(100% - 40px));margin:auto}
.hidden{display:none!important}
/* Fase 36.1 — pedido explícito: "cada bloque debe sentirse como una
   sección editorial independiente". Mismo dash de acento que ya usaba
   .value-mark, ahora delante de CUALQUIER eyebrow del sitio — marca de
   apertura consistente para cada bloque, en vez de solo un texto chico.
   inline-flex (no position:absolute) para que el dash quede PEGADO al
   texto y el conjunto se alinee como una sola unidad — funciona igual
   en contextos alineados a la izquierda que centrados (ej.
   .discover-empty), sin asumir la alineación del contenedor. */
.eyebrow{display:inline-flex;align-items:center;gap:10px;font-size:11px;letter-spacing:.2em;font-weight:800;color:var(--muted);margin:0 0 14px}
.eyebrow::before{content:"";display:block;flex-shrink:0;width:26px;height:2px;background:var(--pink)}
.accent{color:var(--pink)}

header{position:sticky;top:0;z-index:20;background:#050505ee;border-bottom:1px solid var(--line);backdrop-filter:blur(14px)}
.nav{height:88px;display:flex;align-items:center;justify-content:space-between;transition:height .25s ease}
header.scrolled .nav{height:68px}
.logo{display:flex;flex-direction:column;gap:1px}
/* Fase 42.2 — logo oficial (assets/brand/logo/vibe-logo-light.png,
   504x207px reales) reemplaza el SVG reconstruido. aspect-ratio usa la
   proporción NATIVA real del PNG en vez de forzar el 154x62 heredado
   del SVG (proporción distinta, 2.48:1 vs 2.43:1 real) — así
   object-fit:contain nunca necesita compensar con letterboxing.
   Fase 42.6 — pedido explícito: el logo se sentía demasiado grande en
   su estado máximo. Reducido ~18% (170px→140px) manteniendo la misma
   proporción de reducción al hacer scroll (~17%, 138px→114px) — más
   aire alrededor sin tocar el asset ni la altura del Header. */
.logo-image{display:block;width:140px;aspect-ratio:504/207;overflow:hidden;transition:width .25s ease}
header.scrolled .logo-image{width:114px}
.logo-image img{width:100%;height:100%;object-fit:contain;display:block}
.logo small{font-size:6px;letter-spacing:.31em;color:#fff;white-space:nowrap}
.nav-links{display:flex;gap:30px;color:#b7b7b7;font-size:13px;font-weight:700;letter-spacing:.02em}
/* Fase 36 — antes solo cambiaba de color en hover; un subrayado que
   crece desde el centro (en vez de un salto de color plano) es el
   mismo tipo de microinteracción "premium = control + suavidad" ya
   usada en otras partes del sitio (.category-card, .hero-arrow). */
.nav-links a{position:relative;padding-bottom:2px;transition:color .2s ease}
.nav-links a::after{content:"";position:absolute;left:50%;right:50%;bottom:-2px;height:1px;background:var(--pink);transition:left .2s ease,right .2s ease}
.nav-links a:hover{color:#fff}
.nav-links a:hover::after{left:0;right:0}
/* Fase 36.1 — antes el único indicador de sección activa era el
   subrayado en HOVER (desaparecía al alejar el mouse). .nav-active se
   aplica desde JS (ver updateHeaderNavActive) mientras ese filtro siga
   vigente — mismo lenguaje visual que el hover, pero persistente. */
.nav-links a.nav-active{color:#fff}
.nav-links a.nav-active::after{left:0;right:0}
@media(prefers-reduced-motion:reduce){
  .nav-links a{transition:none}
  .nav-links a::after{transition:none}
}
.nav-actions{display:flex;align-items:center;gap:14px}
.cart-btn{border:1px solid #fff;background:transparent;color:#fff;border-radius:999px;padding:9px 13px 9px 16px}
.cart-btn b{display:inline-grid;place-items:center;min-width:22px;height:22px;margin-left:7px;border-radius:50%;background:var(--pink);color:#fff;font-size:12px}

.menu-btn{display:none;flex-direction:column;justify-content:center;align-items:center;gap:5px;width:38px;height:38px;border:1px solid #333;border-radius:12px;background:transparent;padding:0}
.menu-btn span{display:block;width:16px;height:1.5px;background:#fff;transition:transform .2s ease,opacity .2s ease}
.menu-btn[aria-expanded="true"] span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
.menu-btn[aria-expanded="true"] span:nth-child(2){opacity:0}
.menu-btn[aria-expanded="true"] span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}

.mobile-nav{display:none;flex-direction:column;background:#070707;border-top:1px solid var(--line);overflow:hidden;max-height:0;transition:max-height .25s ease}
/* Fase 41 — el submenú Rostro/Ojos/Labios sumó 3 filas al menú mobile;
   220px (el alto de 4 filas) ya no alcanza para las 9 filas actuales —
   con overflow:hidden, el exceso simplemente quedaba invisible. */
.mobile-nav.open{max-height:520px}
.mobile-nav a{padding:16px 20px;border-bottom:1px solid var(--line);font-size:14px;font-weight:700;letter-spacing:.02em;color:#ddd;transition:color .2s ease,background .2s ease}
.mobile-nav a:active{color:#fff;background:#111}
.mobile-nav a.nav-active{color:var(--pink);border-left:3px solid var(--pink);padding-left:17px}
@media(prefers-reduced-motion:reduce){.mobile-nav a{transition:none}}
.mobile-nav a:last-child{border-bottom:0}
/* Fase 41 — submenú Maquillaje → Rostro/Ojos/Labios: mismo link de
   mobile-nav, con indentación + tipografía más chica para leerse como
   subcategoría anidada, no como un mundo de nivel 1. */
.mobile-nav a.mobile-nav-sub{padding-left:36px;font-size:13px;font-weight:600;color:#aaa}
.mobile-nav a.mobile-nav-sub.nav-active{padding-left:33px}

.hero{padding:56px 0 72px;overflow:hidden;background:radial-gradient(circle at 80% 45%,#430020 0,#050505 46%)}
/* Fase 35.3 — bug/pedido reportado: la pieza ya se mostraba completa
   (sin crop) pero se sentía chica dentro del Hero, con demasiado
   espacio vacío alrededor. Dos cambios, ninguno toca object-fit ni la
   proporción real (--hero-desktop-ratio sigue intacta en .hero-carousel,
   ver más abajo): (1) grid-template-columns 2fr/3fr en vez de 1fr/1fr —
   la columna de imagen pasa de 50% a 60% del ancho disponible; (2)
   width:min(1440px,...) en vez de heredar el --max de .wrap (1180px,
   compartido con el resto del sitio) — solo el Hero se permite un
   contenedor más ancho. Con más ancho Y más proporción relativa, la
   imagen crece en ambas dimensiones (aspect-ratio la mantiene
   completa, nunca la recorta) — no es "más alto con más aire", es la
   pieza misma más grande. min() seguido degrada igual que antes en
   mobile/tablet (el cap de 1440px nunca compite ahí), así que esto no
   toca esos breakpoints. */
.hero-grid{display:grid;grid-template-columns:2fr 3fr;gap:48px;align-items:center;width:min(1440px,calc(100% - 40px));margin:0 auto}
/* Fase 42.3 — desplaza el bloque de bienvenida hacia el centro de su
   columna (antes pegado al borde izquierdo real del viewport) y lo
   alinea con el logo del Header — ver --hero-align-pad arriba. Sigue
   siendo una composición alineada a la izquierda, solo con aire real
   antes del primer carácter. */
.hero-copy{padding-left:var(--hero-align-pad)}
.hero h1{font-size:clamp(58px,8vw,98px);line-height:.88;letter-spacing:-.055em;margin:0;font-weight:800}
.hero h1 span{color:var(--pink)}
.lead{max-width:450px;color:#bdbdbd;font-size:17px;line-height:1.55;margin:30px 0}
.button{border:1px solid var(--pink);border-radius:999px;min-height:48px;padding:0 22px;display:inline-flex;align-items:center;justify-content:center;font-weight:800}
.dark{background:var(--pink);color:#fff;box-shadow:0 0 28px #FF007A44}
.dark:hover{background:var(--pink2)}
.button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
.outline{background:transparent;color:var(--pink);transition:background .2s ease,color .2s ease}
.outline:hover{background:var(--pink);color:#fff}
@media(prefers-reduced-motion:reduce){.outline{transition:none}}
.full{width:100%}

/* Hero banner carousel (Fase Visual 2) — pieza gráfica a pantalla
   completa, sin composición editorial generada por código: el catálogo
   solo exhibe la imagen que llegue en cada slide.
   Fase estabilización — bug real: era min-height, no height. .hero-slides
   (hijo directo, height:100%) y en cascada .hero-slide/picture/img
   (position:absolute;inset:0 o herencia de 100%) necesitan que ESTE
   contenedor tenga una altura explícita para que un porcentaje se
   resuelva — min-height dejaba el "height" computado en auto, así que
   %100 nunca resolvía a nada y colapsaba a 0px.
   Fase 35.2 — bug real reportado después: con height fija (500px) y
   object-fit:cover, cualquier imagen que no fuera EXACTAMENTE esa
   proporción se veía recortada/con zoom (cover rellena el cuadro
   recortando lo que sobre). aspect-ratio:var(--hero-desktop-ratio,16/9)
   (medida en vivo desde la imagen real por hero-carousel.js, igual que
   ya se hacía para --hero-mobile-ratio) le da al contenedor la altura
   REAL de la pieza en vez de una fija — con object-fit:contain (ver
   abajo) esto muestra la composición completa, sin recortar producto,
   modelo ni texto. min-height:260px es solo un piso de seguridad para
   antes de que la medición llegue o si falla. */
.hero-carousel{position:relative;aspect-ratio:var(--hero-desktop-ratio, 16/9);height:auto;min-height:260px;border-radius:28px;overflow:hidden;background:#0c0c0c}

.hero-slides{position:relative;width:100%;height:100%}
.hero-slide{position:absolute;inset:0;display:block;opacity:0;transition:opacity 1s ease}
.hero-slide.active{opacity:1;z-index:1}
/* Fase 35.2 — object-fit:contain en desktop también (antes era cover,
   que recortaba/hacía zoom en cualquier pieza que no calzara exacto con
   el height fijo de entonces): la composición completa es la prioridad,
   nunca recortarla, igual que ya regía en mobile. Con el contenedor
   ahora dimensionado según la proporción real de la imagen (arriba),
   "contain" no genera letterboxing perceptible salvo que un slide
   posterior tenga una proporción distinta a la del primero (la que fija
   el contenedor) — en ese caso se ve completo pero con márgenes, nunca
   recortado: la alternativa (recalcular el contenedor por cada slide)
   rompería la transición de crossfade entre slides superpuestos. */
.hero-slide picture{display:block;width:100%;height:100%}
.hero-slide img{width:100%;height:100%;object-fit:contain;display:block}

@media(prefers-reduced-motion:reduce){
  .hero-slide{transition:none}
}

/* Fase 33 — overlay de texto opcional por slide (eyebrow/título/
   subtítulo/CTA), administrable desde /admin. Ausente por completo en
   cualquier slide que no traiga ninguno de estos campos — el
   comportamiento "solo imagen" de siempre no cambia. */
.hero-slide-copy{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:28px 32px 34px;background:linear-gradient(0deg,#000000b3 0%,#00000000 70%);color:#fff;pointer-events:none}
.hero-slide-eyebrow{margin:0 0 8px;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--pink);font-weight:800}
.hero-slide-title{margin:0 0 8px;font-size:clamp(22px,3vw,34px);line-height:1.1;font-weight:800;max-width:520px}
.hero-slide-subtitle{margin:0 0 14px;font-size:14px;color:#eee;max-width:440px}
.hero-slide-cta{display:inline-flex;align-items:center;border:1px solid #fff;border-radius:999px;padding:9px 18px;font-size:12px;font-weight:800;letter-spacing:.02em}

.hero-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:38px;height:38px;border-radius:50%;border:1px solid #ffffff33;background:#00000055;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:#fff;font-size:19px;line-height:1;display:grid;place-items:center;opacity:.65;transition:opacity .2s ease,background .2s ease}
.hero-arrow:hover{opacity:1;background:#00000088}
.hero-arrow-prev{left:14px}
.hero-arrow-next{right:14px}

.hero-dots{position:absolute;left:0;right:0;bottom:16px;z-index:2;display:flex;justify-content:center;gap:2px}
.hero-dot{width:22px;height:22px;padding:0;border:0;background:transparent;display:grid;place-items:center}
.hero-dot:before{content:"";width:6px;height:6px;border-radius:50%;background:#ffffff66;transition:background .2s ease,width .2s ease,border-radius .2s ease}
.hero-dot.active:before{background:#fff;width:16px;border-radius:3px}

.heading{display:flex;justify-content:space-between;align-items:end;gap:20px;margin-bottom:40px}
.heading h2{font-size:58px;letter-spacing:-.045em;margin:0}
.heading>p{color:#6e6e6e;font-size:13px}

/* Fase 42.7 — "Descubre tu VIBE": cuarta iteración, pedido explícito de
   reemplazar la grilla (2 tiles grandes + 3 compactas) por una única
   fila horizontal editorial — referencia conceptual "Shop by Category"
   de retail de belleza (solo el patrón de layout, ningún asset/texto/
   código ajeno). display:flex + overflow-x:auto en vez de grid: las 5
   categorías viven en la misma banda, varias visibles a la vez, sigue
   horizontalmente si el contenido excede el contenedor — nunca una
   segunda fila. scroll-snap para que el swipe se sienta premium/
   intencional. Scrollbar nativa oculta (mismo patrón ya usado en
   .tabs) — la tarjeta siguiente asomándose es la señal de interacción,
   pedida explícitamente en vez de dejar la scrollbar visible. Sin foto
   real disponible para una categoría, la tarjeta cae al mismo
   gradiente oscuro que trae .discover-tile por defecto — pedido
   explícito de esta fase: nunca repetir el wordmark "VIBE" encima de
   cada tile (ver renderDiscoverVibe() en app.js). */
.discover-vibe-visual{background:#fff;color:#111;padding:56px 0}
.discover-vibe-visual .eyebrow{color:var(--pink)}
.discover-tile-row{position:relative}
.discover-tile-grid{display:flex;gap:20px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;padding-bottom:2px}
.discover-tile-grid::-webkit-scrollbar{display:none}
/* Fase 42.8 — pedido explícito: EXACTAMENTE 4 tiles completos visibles
   en 1440px (contenedor .wrap ahí = --max = 1180px), la 5ta se
   descubre con scroll/flechas. Ancho calculado, no un número fijo:
   (100% del contenedor - 3 gaps de 20px entre 4 tiles) / 4 — así
   siempre entran exactamente 4 completos sin importar el ancho real
   del contenedor en este breakpoint, nunca una tarjeta cortada a la
   mitad. */
.discover-tile{position:relative;flex:0 0 auto;scroll-snap-align:start;width:calc((100% - 60px) / 4);height:420px;border:0;border-radius:var(--radius);overflow:hidden;padding:0;text-align:left;font:inherit;background:linear-gradient(160deg,#1c1c1c,#0b0b0b);transition:transform .25s ease,box-shadow .25s ease}
.discover-tile:hover{transform:translateY(-4px);box-shadow:0 22px 44px -20px rgba(255,0,122,.5)}
@media(prefers-reduced-motion:reduce){.discover-tile{transition:none}.discover-tile:hover{transform:none}}
.discover-tile-photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;transition:transform .35s ease}
.discover-tile:hover .discover-tile-photo{transform:scale(1.04)}
@media(prefers-reduced-motion:reduce){.discover-tile-photo{transition:none}.discover-tile:hover .discover-tile-photo{transform:none}}
.discover-tile-scrim{position:absolute;inset:0;background:linear-gradient(0deg,#000000d9 0%,#00000000 55%)}
.discover-tile-body{position:absolute;left:0;right:0;bottom:0;padding:20px 22px;color:#fff}
.discover-tile-label{display:block;font-weight:800;letter-spacing:-.01em;font-size:24px;margin-bottom:8px}
.discover-tile-cta{font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:#fff}
/* Fase 42.8 — flechas de navegación de la fila: mismo lenguaje visual
   que .hero-arrow/.pdp-arrow (círculo oscuro translúcido + blur),
   nunca un componente nuevo. Son hermanas de #discoverVibeGrid (no
   hijas de ninguna tile), así que nunca interceptan el click de una
   tarjeta — solo se superponen visualmente sobre el borde de la
   primera/última tile visible, igual que las flechas del Hero se
   superponen a la pieza. disabled (ver JS) las oculta por completo
   cuando no hay contenido en esa dirección. */
.discover-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:44px;height:44px;border-radius:50%;border:1px solid #ffffff33;background:#00000088;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:#fff;font-size:20px;line-height:1;display:grid;place-items:center;opacity:.85;transition:opacity .2s ease,background .2s ease}
.discover-arrow:hover{opacity:1;background:#000000aa}
.discover-arrow[disabled]{opacity:0;pointer-events:none}
@media(prefers-reduced-motion:reduce){.discover-arrow{transition:none}}
.discover-arrow-prev{left:10px}
.discover-arrow-next{right:10px}

/* Fase 41 — Instagram: bloque estático oscuro, sin feed embebido. */
.instagram-vibe{background:#0b0b0b;color:#fff;padding:96px 0;position:relative;overflow:hidden}
.instagram-vibe:after{content:"";position:absolute;width:320px;height:320px;border-radius:50%;background:#FF007A22;filter:blur(90px);right:8%;top:20%}
.instagram-vibe-inner{position:relative;text-align:center;max-width:560px;margin:0 auto}
.instagram-vibe-inner h2{font-size:clamp(30px,4vw,44px);line-height:1.05;letter-spacing:-.02em;color:#fff;margin:14px 0 26px}
.button.outline.light{color:#fff;border-color:#ffffff77}
.button.outline.light:hover{background:#fff;color:#111}

/* Fase 42 — pedido explícito: reducir la sensación de "bloques
   gigantes" en el Home — 88px de aire superior se sentía pesado para
   una sección de vitrina curada, nunca una PLP. */
.featured{background:#fff;color:#111;padding:64px 0 24px}
.featured .eyebrow{color:var(--pink)}
.featured .wrap>.button{margin-top:8px}
/* Fase 36.1 — pedido explícito: "cada bloque debe sentirse como una
   sección editorial independiente" — Novedades/Destacados/Promociones
   compartían literalmente la misma clase .featured (nombre histórico,
   no el flag `featured` de JS) y se veían idénticas entre sí. Un acento
   de color propio por sección (nunca inventando datos, solo tratamiento
   visual) para que cada una se lea como su propio momento comercial:
   Novedades = frescura, Promociones = urgencia de compra. */
#ofertasHome{background:linear-gradient(180deg,#fff0f6,#fff)}
#ofertasHome .eyebrow::before{background:#111}

.catalog{background:#fff;color:#111;padding:88px 0 110px}
.catalog .eyebrow{color:var(--pink)}
/* Fase 27 — PLP: breadcrumb, contador + Filter & Sort, chips de filtros
   activos y aviso de "todo agotado" (compacto, sin ocupar demasiado
   espacio en mobile, sin scroll horizontal del documento). */
.breadcrumb{margin:0 0 14px;font-size:12px;color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.catalog-meta{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:-8px 0 18px}
.catalog-meta .result-count{font-size:12px;color:#777}
.filter-sort-btn{min-height:40px;padding:0 18px;font-size:12px}
.active-filters{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px}
.filter-chip{display:inline-flex;align-items:center;border:1px solid #eee;background:#fafafa;border-radius:999px;padding:6px 12px;font-size:12px;color:#333;cursor:pointer}
.filter-chip:hover{border-color:var(--pink);color:var(--pink)}
.filter-chip-clear{border-color:#111;color:#111;font-weight:700}
.notice{background:#fff7ea;color:#8a5a00;border-radius:10px;padding:12px 16px;font-size:13px;margin-bottom:18px}
/* dialog{} ya define fondo oscuro (#0b0b0b) y texto blanco por defecto
   (igual que #checkoutForm) — este panel hereda ese mismo tema oscuro,
   nunca texto oscuro sobre fondo oscuro. */
.filter-sort-form{padding:24px 28px;display:grid;gap:16px}
.filter-sort-form label{display:grid;gap:6px;font-size:12px;font-weight:700}
.filter-sort-form select{border:1px solid #333;border-radius:10px;padding:10px 12px;font-size:13px;background:#151515;color:#fff;outline:0}
.filter-sort-form .checkbox-field{display:flex;align-items:center;gap:8px;flex-direction:row;font-size:13px;font-weight:400}
.filter-sort-form .checkbox-field input{width:16px;height:16px}
.filter-sort-form .save-row{display:flex;justify-content:space-between;gap:10px;padding-top:6px;border-top:1px solid #292929}
.tools{display:flex;justify-content:space-between;gap:20px;margin-bottom:30px}
.tabs{display:flex;flex-wrap:wrap;gap:8px}
.tab{display:inline-flex;align-items:center;min-height:44px;border:1px solid #ddd;background:#fff;border-radius:999px;padding:10px 16px;color:#555;font-size:13px}
.tab.active{background:var(--pink);color:#fff;border-color:var(--pink)}
.tab:hover{border-color:var(--pink)}
.tabs-sub{margin:-12px 0 26px}
.tabs-sub .tab{min-height:38px;padding:7px 14px;font-size:12px;color:#888;border-color:#eee}
.tabs-sub .tab.active{background:#111;color:#fff;border-color:#111}
#searchInput{width:260px;border:1px solid #ddd;border-radius:999px;padding:11px 16px;background:#fafafa;outline:0}

/* Fase 41 — pedido explícito, revierte la decisión de Fase 36.1: vuelve
   a 4 columnas en desktop (retail premium, más productos visibles por
   fila) — mobile se mantiene en 2 columnas (@media max-width:900px,
   sin cambios). */
.grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}
.card{min-width:0}
/* Fase 36 — la card ganó una elevación sutil en hover (foto completa
   levantándose con una sombra suave), el mismo lenguaje que ya usaba
   .category-card, para que se sienta una pieza de merchandising y no
   una fila de base de datos. Guard de prefers-reduced-motion porque es
   una animación nueva (antes .card-img no tenía transition propio, solo
   la foto interior). */
.card-img{aspect-ratio:4/5;border:0;border-radius:var(--radius);background:#f2f2f2;display:grid;place-items:center;width:100%;position:relative;overflow:hidden;padding:0;transition:transform .25s ease,box-shadow .25s ease}
.card:hover .card-img{transform:translateY(-4px);box-shadow:0 20px 40px -16px rgba(0,0,0,.35)}
.product-photo{width:100%;height:100%;object-fit:cover;display:block;transition:transform .35s ease,opacity .25s ease}
.card-img:hover .product-photo{transform:scale(1.035)}
/* Fase 36.1 — la mayoría del catálogo real todavía no tiene foto
   cargada (flujo aparte, no se inventa ninguna aquí): antes esto era
   texto plano sobre gris #f2f2f2, se leía como una imagen rota. Mismo
   lenguaje oscuro+wordmark rosa que .pdp-no-image/.discover-empty en
   vez de un vacío técnico — presencia editorial incluso sin foto. */
.card-img:has(.card-img-empty){background:linear-gradient(160deg,#151515,#0b0b0b)}
.card-img-empty{color:var(--pink);font-weight:800;letter-spacing:.16em;font-size:13px;text-transform:uppercase}
@media(prefers-reduced-motion:reduce){
  .card-img{transition:none}
  .card:hover .card-img{transform:none}
}
.card-badge{position:absolute;top:10px;left:10px;z-index:2;background:var(--pink);color:#fff;font-size:10px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:4px 10px;border-radius:999px}
.card-body{padding:18px 4px}
.card-brand{margin:0 0 6px;font-size:12px;color:#888;font-weight:700;text-transform:uppercase;letter-spacing:.04em}
.card h3{margin:0 0 4px;font-size:19px;letter-spacing:-.01em}
.price{font-size:17px;font-weight:800}
.old{text-decoration:line-through;color:#777;font-weight:400;margin-left:7px;font-size:13px}
.availability-badge{display:inline-block;margin-left:8px;font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#b91c4a}
/* Fase 36.1 — bug/pedido explícito: el outline de Fase 36 se percibió
   "demasiado discreto", casi invisible. Pill sólida negra (lenguaje de
   beauty premium — alto contraste, nunca la barra rosa plana de antes)
   que se llena de rosa en hover/focus: se nota de inmediato, sin volver
   a ser el bloque de máximo peso que ya se había descartado. */
.card .button{width:100%;margin-top:14px;min-height:44px;font-size:13px}
.card-cta{background:#111;color:#fff;border-color:#111;letter-spacing:.06em;text-transform:uppercase;font-size:12px;transition:background .2s ease,border-color .2s ease,transform .2s ease}
.card-cta:hover,.card-cta:focus-visible{background:var(--pink);border-color:var(--pink);color:#fff;transform:translateY(-1px)}
@media(prefers-reduced-motion:reduce){
  .card-cta{transition:background .2s ease,border-color .2s ease}
  .card-cta:hover,.card-cta:focus-visible{transform:none}
}
.empty{text-align:center;padding:60px;color:#777}

.cta-final{background:var(--surface);padding:70px 0;border-top:1px solid var(--line)}
.cta-final-inner{display:flex;justify-content:space-between;align-items:center;gap:20px;flex-wrap:wrap}
.cta-final-inner h2{margin:0;font-size:28px;letter-spacing:-.02em}

.overlay{position:fixed;inset:0;background:#000b;z-index:29}
.drawer{position:fixed;right:0;top:0;z-index:30;width:min(440px,100%);height:100dvh;background:#0b0b0b;color:#fff;transform:translateX(100%);transition:.25s;display:flex;flex-direction:column;box-shadow:0 20px 60px #0008}
.drawer.open{transform:none}
.drawer-head,.dialog-head{padding:25px;display:flex;justify-content:space-between;border-bottom:1px solid #292929}
.drawer h2,.dialog-head h2{margin:0;font-size:28px}
.x{border:0;background:transparent;color:inherit;font-size:28px;line-height:1;padding:2px 6px}
.cart-items{flex:1;overflow:auto;padding:20px 25px}
.cart-item{display:grid;grid-template-columns:64px 1fr auto;gap:13px;padding-bottom:18px;margin-bottom:18px;border-bottom:1px solid #292929}
.thumb{width:64px;aspect-ratio:1;border-radius:12px;background:#171717;display:grid;place-items:center;font-size:9px;color:var(--pink);overflow:hidden}
.thumb img{width:100%;height:100%;object-fit:cover;display:block}
.cart-item h3{font-size:14px;margin:0 0 4px}
.meta{font-size:11px;color:#aaa;margin-bottom:8px}
.qty{display:inline-flex;border:1px solid #333;border-radius:999px;overflow:hidden}
.qty button{border:0;background:transparent;color:#fff;width:28px}
.qty span{min-width:25px;display:grid;place-items:center;font-size:12px}
.remove{display:block;border:0;background:none;color:#aaa;font-size:11px;padding:5px 0}
.cart-price{font-weight:800;font-size:13px}
.cart-foot{padding:20px 25px 25px;border-top:1px solid #292929}
.total{display:flex;justify-content:space-between;margin-bottom:18px}
.cart-foot small,dialog small{display:block;text-align:center;color:#999;font-size:10px;margin-top:10px}

dialog{border:1px solid #333;border-radius:22px;padding:0;width:min(860px,calc(100% - 28px));max-height:calc(100dvh - 28px);overflow:auto;background:#0b0b0b;color:#fff;box-shadow:0 20px 60px #000b}
dialog::backdrop{background:#000d}
.dialog-x{position:absolute;right:15px;top:15px;z-index:2}
/* Fase 35.1 — align-items:start evita que la columna de imagen se
   estire para igualar el alto de .detail-copy (que ahora puede ser muy
   larga con toda la ficha editorial): esa estirada era la causa real de
   las franjas enormes arriba/abajo de la foto (el contenedor de imagen
   terminaba mucho más alto que ancho, y object-fit:contain dejaba la
   foto pequeña en el medio). Con align-items:start, la columna de
   imagen conserva su propia altura (definida por aspect-ratio abajo). */
#productDetail{display:grid;grid-template-columns:1fr 1fr;align-items:start}
/* aspect-ratio:4/5 (el estándar recomendado de foto de producto, ver
   docs/fase35-estandar-imagenes.md) le da a la columna de imagen una
   altura predecible basada en su propio ancho, en vez de min-height
   fijo (que ignoraba la proporción real y era la otra mitad del origen
   de las franjas).
   Fase 35.2 — bug real encontrado con un navegador headless real: sin
   width:100%/min-width:0 explícitos, este ítem de grid con aspect-ratio
   y sin ancho definido terminaba calculando su ancho A PARTIR de una
   altura enorme e indefinida (heredada de .detail-copy, ahora muy larga
   con toda la ficha editorial), en vez de al revés — se medió
   1199×1499px reales en producción, desbordando el diálogo y la
   ventana. width:100% + min-width:0 fuerzan que el ancho SIEMPRE venga
   de la columna del grid (429px reales), y aspect-ratio entonces deriva
   la altura a partir de ESE ancho, nunca al revés.
   Fase 35.2 (solo desktop — mobile abajo sigue exactamente igual, sin
   tocar) — bug real reportado dos veces: poner aspect-ratio aquí, en el
   contenedor OUTER que incluye el strip de thumbnails, siempre dejaba un
   residual de espacio — el strip le resta alto al total sin restarle
   proporción a la foto, así que el ÁREA VISIBLE de la foto
   (.pdp-gallery-main) terminaba con una proporción distinta a la real
   de la imagen aunque el contenedor completo ya usara --pdp-photo-ratio
   (confirmado con Playwright: quedaba un ~5% de banda a los lados,
   pequeño pero real). La proporción real ahora se aplica DIRECTAMENTE a
   .pdp-gallery-main (ver más abajo) — el contenedor total mide
   exactamente foto + thumbnails, sin sobra ni redondeo. */
.detail-img{width:100%;min-width:0;max-width:100%;background:#111;display:grid;place-items:center;text-align:center;color:#aaa;padding:0;overflow:hidden}
/* Fase 35 — la foto de producto debe verse completa, nunca recortada:
   object-fit:contain (no cover). .detail-img conserva su fondo oscuro
   (#111) para el letterboxing cuando la proporción real de la foto no
   llena la caja exactamente. */
.detail-photo{width:100%;height:100%;object-fit:contain;display:block}
.detail-copy{padding:45px 38px}
.detail-copy h2{font-size:42px;line-height:1.02;letter-spacing:-.01em;margin:0 0 10px}
.variants{display:flex;gap:8px;flex-wrap:wrap;margin:20px 0}
.variant{border:1px solid #333;background:transparent;color:#fff;border-radius:999px;padding:9px 13px;font-size:12px}
.variant.selected{background:var(--pink);border-color:var(--pink)}
/* Fase 36.1 — pedido explícito: "el CTA principal debe ser visualmente
   inequívoco". Antes el botón compartía fila con el selector de
   cantidad (grid 120px 1fr), limitado a lo que sobraba de esa columna.
   Apilado: la cantidad queda arriba con su ancho natural (compacto), el
   botón de compra ocupa TODO el ancho de la columna debajo — el cambio
   de tamaño más directo posible sin tocar la galería/ratio del PDP. */
.detail-actions{display:flex;flex-direction:column;gap:14px;margin-top:24px}
/* Fase 36 — pedido explícito: "el CTA de compra debe tener mucha más
   presencia". Antes usaba exactamente .button.dark, la misma clase que
   cualquier botón del sitio (nav, "Explorar catálogo", etc.) — el
   momento de decisión de compra no se distinguía visualmente de nada
   más. Se mantiene la forma de pastilla y la paleta de marca (nada
   nuevo, solo más peso): más alto, texto más grande, sombra más
   marcada, con una elevación sutil en hover/focus. */
.pdp-buy-button{width:100%;min-height:60px;font-size:16px;letter-spacing:.02em;box-shadow:0 10px 32px #ff007a55;transition:transform .15s ease,box-shadow .15s ease,background .15s ease}
.pdp-buy-button:hover:not(:disabled),.pdp-buy-button:focus-visible:not(:disabled){transform:translateY(-2px);box-shadow:0 14px 38px #ff007a70}
@media(prefers-reduced-motion:reduce){
  .pdp-buy-button{transition:none}
  .pdp-buy-button:hover:not(:disabled),.pdp-buy-button:focus-visible:not(:disabled){transform:none}
}
.quantity{display:flex;border:1px solid #333;border-radius:999px;overflow:hidden}
.quantity button{border:0;background:transparent;color:#fff;width:35px}
.quantity button:disabled{opacity:.4}
.quantity input{width:45px;border:0;text-align:center;outline:0;background:transparent;color:#fff}

/* Fase 28 — PDP: la galería reemplaza el centrado simple de .detail-img
   por una columna (imagen principal + thumbnails), reutilizando .detail-
   img/.detail-photo tal cual para el tamaño/recorte de la imagen
   principal (misma regla mobile que ya existía en @media(max-width:640px)). */
/* align-items:stretch explícito — bug real encontrado con Playwright:
   .detail-img trae place-items:center (pensado para cuando centraba una
   sola foto, antes de esta galería); ese "center" se traduce en
   align-items:center, y aunque .pdp-gallery cambia display a flex, NADA
   volvía a fijar align-items, así que el center seguía aplicando sobre
   este contenedor flex. Con align-items:center, .pdp-gallery-main/
   .pdp-thumbs se encogían a su ancho de contenido (medido: 383px/268px
   dentro de una columna de 429px) en vez de ocupar el ancho completo. */
.pdp-gallery{display:flex;flex-direction:column;height:100%;align-items:stretch}
/* Fase 35.2 (solo desktop — la regla mobile de abajo restaura flex:1 /
   min-height:0 sin tocar el comportamiento que ya funcionaba ahí) — el
   área de la foto ahora dimensiona SU PROPIA altura según la proporción
   real de la imagen (misma variable --pdp-photo-ratio que .detail-img
   ya no necesita cargar), en vez de flex:1 estirándose dentro de un
   total ya fijado por el contenedor exterior. */
.pdp-gallery-main{width:100%;aspect-ratio:var(--pdp-photo-ratio, 4/5);position:relative;overflow:hidden}
/* Fase 35.1 — flechas de navegación manual del PDP (sección 4): mismo
   lenguaje visual que .hero-arrow, pero con área táctil de 44px (mínimo
   recomendado para mobile) en vez de 38px. Nunca autoplay, nunca hover
   para cambiar de imagen — solo click/tap/teclado. */
.pdp-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:44px;height:44px;border-radius:50%;border:1px solid #ffffff33;background:#00000055;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:#fff;font-size:20px;line-height:1;display:grid;place-items:center;opacity:.7;transition:opacity .2s ease,background .2s ease}
.pdp-arrow:hover,.pdp-arrow:focus-visible{opacity:1;background:#00000088}
.pdp-arrow-prev{left:10px}
.pdp-arrow-next{right:10px}
/* Sin ninguna foto real, no hay proporción que medir — se mantiene un
   4/5 fijo razonable solo para este estado (nunca para una foto real,
   ver .pdp-gallery-main arriba). height:auto porque ya no depende de un
   .detail-img con aspect-ratio propio (solo desktop; mobile lo restaura
   con height:100% dentro de su propio .detail-img de alto fijo). */
.pdp-no-image{width:100%;aspect-ratio:4/5;height:auto;display:grid;place-items:center;background:linear-gradient(160deg,#151515,#0b0b0b);color:var(--pink);font-weight:800;letter-spacing:.16em;font-size:14px}
.pdp-thumbs{display:flex;gap:8px;padding:10px;overflow-x:auto;background:#0d0d0d;scrollbar-width:none}
.pdp-thumbs::-webkit-scrollbar{display:none}
.pdp-thumb{flex:0 0 56px;width:56px;height:56px;border-radius:10px;overflow:hidden;border:2px solid transparent;padding:0;background:#111;cursor:pointer}
.pdp-thumb.active{border-color:var(--pink)}
.pdp-thumb img{width:100%;height:100%;object-fit:contain;background:#111;display:block}

.pdp-breadcrumb{margin-bottom:16px}
.pdp-badge{display:inline-block;position:static;background:var(--pink);color:#fff;font-size:10px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:4px 10px;border-radius:999px;margin-bottom:10px}
.pdp-brand{margin:0 0 6px;font-size:13px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--pink)}
.pdp-commercial-name{margin:-4px 0 8px;font-size:13px;color:#ccc;font-style:italic}
.pdp-short-desc{color:#ccc;font-size:14px;margin:0 0 16px}
.pdp-price{display:flex;align-items:baseline;flex-wrap:wrap;gap:10px;margin:4px 0}
.pdp-price strong{font-size:30px;font-weight:800}
.pdp-discount{background:var(--pink);color:#fff;font-size:11px;font-weight:800;padding:3px 9px;border-radius:999px}
.pdp-promo-text{color:var(--pink);font-size:13px;font-weight:700;margin:0 0 12px}

/* Fase 34 — ficha técnica compacta (tono, acabado, cobertura, etc.):
   etiqueta/valor, siempre visible (no acordeón, valores demasiado cortos
   para justificar el clic). Cruelty-free/vegano/certificaciones/claims
   comparten el mismo lenguaje visual de "pill". */
.pdp-specs{margin:18px 0;padding:16px 0;border-top:1px solid #292929;border-bottom:1px solid #292929;display:grid;gap:10px}
.pdp-spec-row{display:flex;justify-content:space-between;gap:16px;font-size:13px}
.pdp-spec-row dt{margin:0;color:#999}
.pdp-spec-row dd{margin:0;color:#fff;font-weight:600;text-align:right}
.pdp-traits{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
.pdp-trait{display:inline-block;border:1px solid #333;border-radius:999px;padding:5px 12px;font-size:11px;color:#ccc}
.pdp-tag-list{margin-bottom:14px}
.pdp-tag-list-label{margin:0 0 8px;font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--pink);font-weight:800}
.pdp-tag{display:inline-block;background:#1a1a1a;border:1px solid #333;border-radius:999px;padding:5px 12px;font-size:11px;color:#eee;margin:0 6px 6px 0}

.pdp-sections{margin-top:22px}
.pdp-accordion{border-top:1px solid #292929;padding:16px 0}
.pdp-accordion summary{cursor:pointer;font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--pink);font-weight:800}
.pdp-accordion-body{font-size:13px;color:#aaa;padding-top:12px;line-height:1.6}
.pdp-accordion-body ul{margin:0;padding-left:18px}
.pdp-accordion-body li{margin-bottom:4px}

.pdp-related{margin-top:32px;border-top:1px solid #292929;padding-top:24px}
.pdp-related h3{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--pink);margin:0 0 16px}
.pdp-related .grid{grid-template-columns:repeat(2,1fr);gap:14px}

.pdp-not-found{grid-column:1/-1;padding:70px 38px;text-align:center}
.pdp-not-found .eyebrow{color:var(--pink)}
.pdp-not-found h2{font-size:26px;margin:8px 0 10px}
.pdp-not-found p{color:#aaa;margin:0 0 22px}


#checkoutDialog{width:min(560px,calc(100% - 28px))}
#checkoutForm{padding:28px;display:grid;gap:16px}
#checkoutForm label{display:grid;gap:7px;font-size:12px;font-weight:700}
#checkoutForm input,#checkoutForm textarea{width:100%;border:1px solid #333;border-radius:12px;padding:12px;background:#151515;color:#fff;outline:0}
#checkoutForm input[aria-invalid="true"]{border-color:#e0527a}
#checkoutForm .field-error{color:#ff6b93;font-size:11px;font-weight:600;min-height:14px}
#checkoutSubmit:disabled{opacity:.45;cursor:not-allowed}
/* Fase 39 — copy de ayuda bajo el campo de WhatsApp (sección 2 del
   brief: deja claro que ese número es el canal real de contacto) y el
   checkbox de autorización de contacto (sección 3 — nunca mezclado con
   marketing/newsletter). */
#checkoutForm .field-help{font-size:11px;color:#999;line-height:1.5;margin:-8px 0 0;font-weight:400}
#checkoutForm .checkbox-field{display:flex;align-items:flex-start;gap:8px;font-size:12px;font-weight:400;color:#ccc}
#checkoutForm .checkbox-field input{width:16px;height:16px;margin-top:2px;flex-shrink:0}

/* Fase 29 — carrito: marca (opcional) y precio unitario con el
   promocional tachado cuando aplica, encima del stepper de cantidad. */
.cart-item-brand{font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#aaa;margin:0 0 2px}
.cart-item-unit-price{display:flex;gap:8px;align-items:baseline;font-size:12px;color:#ccc;margin-bottom:8px}

/* Fase 29, sección 11 — estado vacío del carrito, con CTA real de
   regreso al catálogo (reutilizado tal cual dentro del checkout cuando
   el carrito llega vacío ahí, ver checkoutSummaryHtml). */
.cart-empty{padding:50px 10px;text-align:center}
.cart-empty p{margin:0 0 6px}
.cart-empty-sub{color:#999;font-size:12px;margin-bottom:20px}

/* Fase 29 — resumen del pedido dentro del diálogo de checkout. Reutiliza
   .thumb/.cart-price/.remove/.total ya definidos arriba para el carrito,
   y agrega solo lo que es propio del resumen (línea marcada como no
   disponible, con su aviso). */
.checkout-summary{padding:24px 28px 0}
.checkout-summary-title{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--pink);margin:0 0 16px}
.checkout-line{display:grid;grid-template-columns:56px 1fr auto;gap:12px;padding-bottom:16px;margin-bottom:16px;border-bottom:1px solid #292929;align-items:start}
.checkout-line .thumb{width:56px}
.checkout-line-info h3{font-size:13px;margin:0 0 4px}
.checkout-line-warning{color:#ff6b93;font-size:11px;font-weight:700;margin:4px 0 0}
.checkout-line.unavailable{opacity:.85}
.checkout-line-right{display:flex;flex-direction:column;align-items:flex-end;gap:6px}
.checkout-summary .total{padding-top:4px}

/* Fase 39 — confirmación: el pedido queda REGISTRADO en VIBE (no solo
   "listo para enviar por WhatsApp"); nunca afirma pago/confirmación,
   solo que fue recibido. */
.checkout-confirmation{padding:40px 28px 32px;text-align:center}
.checkout-confirmation h3{font-size:22px;margin:6px 0 12px}
.checkout-confirmation p{color:#aaa;font-size:13px}
.checkout-confirmation-number{color:#fff !important;font-weight:700;font-size:14px !important}
.checkout-confirmation-actions{display:flex;gap:10px;justify-content:center;margin-top:22px;flex-wrap:wrap}

footer{padding:40px 0;border-top:1px solid #292929;color:#aaa;font-size:11px;background:var(--bg)}
footer .wrap{display:flex;justify-content:space-between;align-items:end;gap:20px;flex-wrap:wrap}
.footer-brand{display:flex;flex-direction:column;gap:8px}
.footer-logo-image{width:150px;aspect-ratio:504/207;overflow:hidden}
.footer-logo-image img{width:100%;height:100%;object-fit:contain;display:block}
footer small{letter-spacing:.25em;font-size:7px}
.footer-nav{display:flex;gap:18px}
.footer-nav a{color:#aaa}
.footer-nav a:hover{color:#fff}
.footer-note{color:#777;font-size:11px}

@media(max-width:900px){
  .grid{grid-template-columns:repeat(2,1fr)}
  .hero-grid{grid-template-columns:1fr}
  /* Fase 35.2 — height:340px fijo ya no hace falta: .hero-carousel ahora
     deriva su alto de aspect-ratio (ver regla base), que ya responde
     solo al ancho disponible en este breakpoint (columna completa). */
  .heading{align-items:flex-start;flex-direction:column}
  /* Fase 42.7 — tiles un poco más angostas por debajo de 900px, mismo
     concepto de fila horizontal con scroll (nunca grid). */
  .discover-tile{width:260px;height:380px}
  .instagram-vibe{padding:64px 0}
}

@media(max-width:640px){
  .wrap{width:calc(100% - 28px)}
  .nav{height:78px}
  /* Fase 42.3 — bug real encontrado: header.scrolled .nav ya achicaba
     la barra (78→64px), pero header.scrolled .logo-image seguía
     resolviendo a la regla de escritorio (especificidad 0,2,0) porque
     .logo-image{width} de acá arriba tiene menos especificidad
     (0,1,0) y nunca le ganaba. Esta regla (misma especificidad 0,2,0
     que la de escritorio, pero definida después en el archivo) sí
     gana — mantiene el efecto de scroll perceptible en mobile.
     Fase 42.6 — misma reducción ~18% que en escritorio (152px→125px,
     scrolled 112px→92px, mismo ratio de achique ~26% al hacer scroll). */
  header.scrolled .nav{height:58px}
  .logo-image{width:125px}
  header.scrolled .logo-image{width:92px}
  .logo small{font-size:5px}
  .nav-links{display:none}
  .menu-btn{display:flex}
  .mobile-nav{display:flex}
  /* Fase Diseño Mobile-First 1 — el Hero mobile deja de ser
     texto+imagen y pasa a ser un banner editorial protagonista (NO
     pantalla completa). El copy actual (eyebrow/H1/lead/CTA) se oculta
     solo aquí — sigue intacto en el código y en desktop, no se eliminó
     (no se reutiliza en ningún otro lugar del sitio, se verificó antes
     de tocar nada).
     Fase 42.6/42.7 — pedido explícito, SOLO mobile: con padding:0 el
     carrusel arrancaba pegado al Header sticky, se sentía "montado"
     encima. margin-top (no padding-top del Header, que no se toca)
     agrega un respiro externo sin cambiar la altura interna del Header
     ni el tamaño/proporción del carrusel. 10px (Fase 42.6) resultó
     insuficiente en la revisión visual — subido a 18px, dentro del
     rango 16-20px pedido. Desktop no tiene esta regla — el spacing ahí
     ya era correcto y no se toca. */
  .hero{padding:0;margin-top:18px}
  .hero-copy{display:none}
  .hero-grid{gap:0}

  /* Fase 1.2 — el alto del Hero YA NO depende del viewport (se
     abandonó height:calc(100svh - ...), que en la Fase 1.1 hacía el
     Hero casi de pantalla completa: demasiado grande según la
     visualización real). Ahora el alto se deriva de la proporción real
     de la creatividad, sobre el ancho completo del viewport (el
     carrusel sigue rompiendo el gutter de .wrap para llegar borde a
     borde) — y como no depende de ninguna unidad de viewport, tampoco
     puede sufrir el problema de reflow-durante-scroll que sí tenía
     100dvh.
     Fase estabilización — bug real: 4/5 fijo asumía que la pieza
     siempre sería exactamente 1080x1350; cualquier asset con otra
     proporción real se veía con bandas (el contenedor no coincidía con
     la imagen, y object-fit:contain deja huecos cuando no coinciden).
     hero-carousel.js mide la proporción REAL de la primera pieza al
     cargar y la expone como --hero-mobile-ratio; 4/5 queda solo como
     respaldo mientras esa medición no ha llegado o falla. */
  .hero-carousel{
    aspect-ratio:var(--hero-mobile-ratio, 4/5);
    height:auto;
    min-height:0;
    margin-left:-14px;
    margin-right:-14px;
    width:calc(100% + 28px);
    border-radius:0;
  }
  /* La pieza creativa se ve completa, nunca recortada — ver nota junto
     a .hero-slide img más arriba. Los placeholders ahora son 4:5
     (1080x1350), el formato editorial oficial de estas piezas — con el
     contenedor en la misma proporción, "contain" no genera
     letterboxing perceptible. */
  .hero-slide img{object-fit:contain}
  .hero-slide-copy{padding:20px 18px 26px}
  .hero-slide-subtitle{display:none}
  .hero-arrow{width:34px;height:34px;font-size:17px}
  .hero-arrow-prev{left:8px}
  .hero-arrow-next{right:8px}
  /* Fase 42.7 — mobile: tile angosta lo suficiente para que la
     siguiente se asome (~85px visibles de la tile #2 dentro del ancho
     de .wrap en 390px) — esa asomada es la señal de "hay más
     categorías", pedida explícitamente en vez de dejar la scrollbar
     visible. Sigue siendo scroll horizontal nativo, nunca grid. */
  .discover-vibe-visual{padding:40px 0}
  .discover-tile-grid{gap:12px}
  .discover-tile{width:265px;height:350px}
  .discover-tile-label{font-size:21px}
  /* Fase 42.8 — pedido explícito: flechas discretas en mobile, sin
     estorbar el swipe nativo (que sigue siendo la interacción
     principal) — más chicas y más translúcidas que en desktop. */
  .discover-arrow{width:34px;height:34px;font-size:16px;opacity:.55}
  .discover-arrow-prev{left:4px}
  .discover-arrow-next{right:4px}
  .featured{padding:40px 0 10px}
  .catalog{padding:40px 0 80px}
  .heading h2{font-size:46px}
  .tools{flex-direction:column}
  .tools input,#searchInput{width:100%}
  .tabs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .tabs::-webkit-scrollbar{display:none}
  .tabs-sub{margin:-6px 0 20px}
  .grid{gap:16px}
  .cta-final{padding:50px 0}
  .detail-copy{padding:30px 24px}
  #productDetail{grid-template-columns:1fr}
  /* Restaura exactamente el patrón mobile de siempre (sin tocarlo): el
     contenedor OUTER vuelve a fijar la proporción (4/5) y el área de la
     foto vuelve a flex:1/min-height:0 estirándose dentro de ese total —
     el rediseño de arriba (proporción real en .pdp-gallery-main) es
     solo para desktop. */
  .detail-img{aspect-ratio:4/5;min-height:0}
  .pdp-gallery-main{aspect-ratio:auto;flex:1;min-height:0}
  .pdp-no-image{aspect-ratio:auto;height:100%}
  .pdp-price strong{font-size:24px}
  .pdp-related .grid{grid-template-columns:1fr 1fr;gap:12px}
  .checkout-summary{padding:20px 20px 0}
  .checkout-line{grid-template-columns:48px 1fr;grid-template-areas:"thumb info" "right right"}
  .checkout-line .thumb{grid-area:thumb;width:48px}
  .checkout-line-info{grid-area:info}
  .checkout-line-right{grid-area:right;flex-direction:row;justify-content:space-between;align-items:center;margin-top:10px}
  #checkoutForm{padding:20px}
  .checkout-confirmation{padding:32px 20px 26px}
  .checkout-confirmation-actions{flex-direction:column}
  .checkout-confirmation-actions .button{width:100%}
  footer .wrap{flex-direction:column;align-items:flex-start;gap:16px}
}
