/* Componentes compartidos entre varias páginas. Layout específico de cada
   página va en su <style> inline, no acá. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 48px;
  padding: 0 var(--space-6);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background-color 150ms, border-color 150ms, color 150ms;
}

.btn-primary {
  background: var(--jg-onyx);
  color: #FFFFFF;
  border-color: var(--jg-onyx);
}
.btn-primary:hover { background: var(--ink-800); }

.btn-secondary {
  background: transparent;
  color: var(--jg-onyx);
  border-color: var(--ink-300);
}
.btn-secondary:hover { border-color: var(--gold-600); }

.btn-onDark {
  background: transparent;
  color: var(--gold-400);
  border-color: var(--gold-400);
}
.btn-onDark:hover { background: var(--gold-400); color: var(--jg-onyx); }

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-block { width: 100%; }

.link {
  color: var(--jg-onyx);
  text-decoration: underline;
  text-decoration-color: var(--gold-600);
  text-underline-offset: 3px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.field label {
  font-size: 14px;
  color: var(--text-muted);
}

.field input,
.field textarea,
.field select {
  height: 48px;
  padding: 0 var(--space-4);
  background: var(--jg-white);
  border: 1px solid var(--ink-300);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--jg-onyx);
}

.field textarea {
  height: auto;
  min-height: 96px;
  padding-block: var(--space-3);
  resize: vertical;
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: 2px solid var(--gold-600);
  outline-offset: 1px;
  border-color: var(--gold-600);
}

.field-error {
  font-size: 13px;
  color: var(--danger);
}

.card {
  background: var(--jg-white);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius);
  transition: border-color 150ms;
}

.card:hover { border-color: var(--gold-200); }

.badge {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.03em;
}

.badge-warning { color: var(--warning); }
.badge-danger { color: var(--danger); }
.badge-success { color: var(--success); }

/* barra superior — sólida arriba del todo; apenas se hace scroll pasa a un
   vidrio esmerilado (semitransparente + blur) en vez de quedarse como una
   franja plana pegada arriba. El alpha se queda alto a propósito: abajo del
   header suele pasar la sección oscura del hero, y el texto/íconos son
   siempre oscuros — con menos opacidad se volverían ilegibles ahí. */
.nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--jg-paper);
  border-bottom: 1px solid var(--ink-100);
  transition: background-color 250ms ease, box-shadow 250ms ease, border-color 250ms ease, transform 250ms ease;
}

.nav.is-scrolled {
  background: rgba(247, 245, 241, 0.62);
  border-bottom-color: rgba(201, 196, 191, 0.35);
  box-shadow: var(--shadow-float);
}

/* header que se esconde al bajar y reaparece apenas se sube (ver
   initNavHideOnScroll en site-settings.js) — en todas las páginas menos
   home, que tiene su propio comportamiento de header flotante/transparente
   sobre el hero (ver body.home-transparent-nav más abajo) y no participa de
   esto. */
body:not(.home-transparent-nav) .nav.nav-hidden {
  transform: translateY(-100%);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .nav.is-scrolled {
    background: rgba(247, 245, 241, 0.4);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
  }
}

/* home: header transparente flotando sobre el banner en vez de la franja
   sólida de siempre entre la barra de anuncios y el banner — ver
   body.home-transparent-nav en index.html (solo ahí, es la única página con
   una sección oscura pegada debajo del header). Se saca del flujo normal
   (position:absolute, con el offset de la barra de anuncios calculado por
   JS en --header-top-offset) para que el banner suba y quede detrás; un
   degradado oscuro sutil (::before) hace de "sombrita" para que el logo e
   íconos —acá en blanco— se lean sobre cualquier foto. Apenas se hace
   scroll pasa a position:fixed y recupera el look sólido/vidrio de
   .is-scrolled de siempre, con el logo e íconos oscuros otra vez. */
body.home-transparent-nav .nav {
  position: absolute;
  top: var(--header-top-offset, 0px);
  left: 0;
  right: 0;
  background: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
}

body.home-transparent-nav .nav::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 160px;
  background: linear-gradient(to bottom, rgba(0, 0, 0, .4), rgba(0, 0, 0, 0));
  pointer-events: none;
  z-index: -1;
}

body.home-transparent-nav .nav .nav-icon-btn,
body.home-transparent-nav .nav .nav-mobile-toggle,
body.home-transparent-nav .nav .nav-classes-toggle {
  color: #FFFFFF;
}
body.home-transparent-nav .nav .nav-classes-toggle:hover { color: #FFFFFF; }
body.home-transparent-nav .nav .nav-search {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .4);
}
body.home-transparent-nav .nav .nav-search input { color: #FFFFFF; }
body.home-transparent-nav .nav .nav-search input::placeholder { color: rgba(255, 255, 255, .7); }
body.home-transparent-nav .nav .nav-search button { color: #FFFFFF; }

/* el menú móvil (y ahora el buscador, ver .nav-search-panel) abiertos
   necesitan un fondo sólido propio para leerse, sin importar que el
   header de atrás siga transparente. */
body.home-transparent-nav .nav-mobile-panel.is-open { background: var(--jg-paper); }
body.home-transparent-nav .nav-search-panel:not([hidden]) { background: var(--jg-paper); }

/* El buscador de escritorio, ahí sí sobre la foto transparente, usa texto
   blanco (ver arriba) — pero este mismo .nav-search vive también adentro
   de .nav-search-panel, ya con fondo sólido claro propio: mismo texto
   oscuro de siempre, blanco quedaría invisible. Misma especificidad que
   la regla de arriba (3 clases + input/button), gana por venir después. */
body.home-transparent-nav .nav .nav-search-panel input { color: var(--jg-onyx); }
body.home-transparent-nav .nav .nav-search-panel input::placeholder { color: var(--text-muted); }
body.home-transparent-nav .nav .nav-search-panel button { color: var(--text-muted); }

body.home-transparent-nav .nav.is-scrolled {
  position: fixed;
  top: 0;
}
body.home-transparent-nav .nav.is-scrolled::before { display: none; }
body.home-transparent-nav .nav.is-scrolled .nav-icon-btn,
body.home-transparent-nav .nav.is-scrolled .nav-mobile-toggle,
body.home-transparent-nav .nav.is-scrolled .nav-classes-toggle {
  color: var(--jg-onyx);
}
body.home-transparent-nav .nav.is-scrolled .nav-classes-toggle:hover { color: var(--jg-onyx); }
body.home-transparent-nav .nav.is-scrolled .nav-search {
  background: var(--jg-white);
  border-color: var(--ink-300);
}
body.home-transparent-nav .nav.is-scrolled .nav-search input { color: var(--jg-onyx); }
body.home-transparent-nav .nav.is-scrolled .nav-search input::placeholder { color: var(--text-muted); }
body.home-transparent-nav .nav.is-scrolled .nav-search button { color: var(--text-muted); }

.nav-logo { height: 28px; width: auto; }
.nav-logo-light { display: none; }
body.home-transparent-nav .nav:not(.is-scrolled) .nav-logo-dark { display: none; }
body.home-transparent-nav .nav:not(.is-scrolled) .nav-logo-light { display: block; }

.nav-icon-btn {
  position: relative;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--jg-onyx);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
}

.nav-cart-count {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 8px;
  background: var(--gold-800);
  color: #FFFFFF;
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
}

.nav-mobile-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--jg-onyx);
  width: 44px;
  height: 44px;
}

@media (min-width: 1024px) {
  .nav-mobile-toggle { display: none; }
}

.nav-mobile-panel {
  display: none;
  flex-direction: column;
  gap: var(--space-4);
  padding-top: var(--space-4);
  padding-bottom: var(--space-6);
  border-top: 1px solid var(--ink-100);
}

.nav-mobile-panel.is-open { display: flex; }

@media (min-width: 1024px) {
  .nav-mobile-panel { display: none !important; }
}

/* Buscador de móvil: antes vivía adentro del panel del menú hamburguesa
   (había que abrir "Clases"/"Mi cuenta" y todo lo demás solo para buscar);
   ahora tiene su propio ícono al lado de "Abrir menú" y abre una franja
   angosta solo con el campo, sin arrastrar el resto de los links del
   menú. Mismo .nav-search de siempre adentro — initNavSearch (más abajo)
   no le importa dónde viva el form. */
.nav-search-panel {
  display: none;
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
  border-top: 1px solid var(--ink-100);
}

.nav-search-panel:not([hidden]) { display: block; }

.nav-search-panel .nav-search-mobile { margin-bottom: 0; }

@media (min-width: 1024px) {
  .nav-search-panel { display: none !important; }
}

/* header compacto de una sola fila: en móvil, logo centrado con el menú a
   la izquierda y el carrito a la derecha (clases, cuenta y buscador se
   recogen en el panel del menú); en escritorio (≥1024px) se abre a un
   grid de 3 columnas — clases a la izquierda, logo perfectamente centrado,
   buscador+cuenta+carrito a la derecha — todo en la misma línea. */
.nav-bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 64px;
}

.nav-logo-link { display: inline-flex; }

.nav-bar .nav-mobile-left { position: absolute; left: 0; display: flex; align-items: center; }

.nav-bar-actions {
  position: absolute;
  right: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.nav-bar-actions .nav-search,
.nav-bar-actions .nav-account-icon {
  display: none;
}

/* clases del catálogo (Mujer/Hombre/Ocasiones...) — antes una fila de
   links en línea, pero con más de 3-4 clases esa fila se ve apretada y
   compite con el logo/buscador en la misma línea. Ahora un botón (icono +
   "Clases") abre un menú flotante con la lista, distinto del panel de
   menú móvil de abajo — solo escritorio: en móvil las clases siguen
   viviendo en el panel del menú (.nav-mobile-classes) como siempre. */
.nav-classes-menu {
  position: relative;
  display: none;
}

.nav-classes-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--text-muted);
  transition: color 150ms;
}

.nav-classes-toggle svg { flex-shrink: 0; transition: color 150ms, transform 150ms; }

.nav-classes-toggle:hover,
.nav-classes-toggle[aria-expanded="true"] { color: var(--jg-onyx); }

.nav-classes-toggle[aria-expanded="true"] svg { color: var(--gold-600); transform: rotate(90deg); }

/* Panel flotante — sombra reservada a menús/modales (ver DESIGN.md,
   Elevation & Depth): siempre sólido en Marfil/Blanco, sin importar que el
   header de atrás esté transparente u oscurecido (home). */
.nav-classes {
  position: absolute;
  top: calc(100% + var(--space-3));
  left: 0;
  z-index: 30;
  width: max-content;
  max-width: 360px;
  margin: 0;
  padding: var(--space-4);
  list-style: none;
  background: var(--jg-white);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--space-1) var(--space-6);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 180ms ease, transform 180ms ease, visibility 180ms;
}

.nav-classes.is-open { opacity: 1; visibility: visible; transform: translateY(0); }

.nav-classes a {
  display: inline-block;
  padding-bottom: 3px;
  border-bottom: 1px solid transparent;
  text-decoration: none;
  font-size: 14px;
  color: var(--text-muted);
  transition: color 150ms, border-color 150ms;
}

.nav-classes a:hover { color: var(--jg-onyx); border-bottom-color: var(--gold-400); }

@media (min-width: 1024px) {
  .nav-bar {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    height: 64px;
  }
  .nav-classes-menu { display: block; justify-self: start; }
  .nav-logo-link { justify-self: center; }
  .nav-bar-actions { position: static; justify-self: end; gap: var(--space-4); }
  .nav-bar-actions .nav-search,
  .nav-bar-actions .nav-account-icon {
    display: flex;
  }
}

.nav-search {
  display: flex;
  align-items: center;
  height: 40px;
  border: 1px solid var(--ink-300);
  border-radius: var(--radius);
  background: var(--jg-white);
  transition: border-color 150ms;
}

.nav-search:focus-within { border-color: var(--gold-600); }

.nav-search input {
  border: none;
  background: none;
  height: 100%;
  width: 180px;
  padding: 0 var(--space-3);
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--jg-onyx);
}

.nav-search input:focus { outline: none; }

.nav-search button {
  width: 36px;
  height: 100%;
  flex-shrink: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.nav-search-mobile { width: 100%; height: 44px; margin-bottom: var(--space-4); }
.nav-search-mobile input { flex: 1; width: auto; font-size: 15px; }

.nav-mobile-classes {
  list-style: none;
  margin: 0 0 var(--space-2);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.nav-mobile-classes a { text-decoration: none; color: var(--jg-onyx); font-size: 15px; }

/* footer */
/* El contenido de #dynamicSections tarda un instante en llegar (fetch a
   /page-sections) — sin esto, el footer se veía pegado al header ese
   instante, antes de que cargara el resto de la página. body.pgs-ready lo
   agrega renderPageSections en sections.js al terminar, salga bien o mal
   el fetch, para no dejar el footer escondido para siempre. */
.site-footer {
  background: var(--jg-onyx);
  color: #FFFFFF;
  padding-top: var(--space-16);
  padding-bottom: var(--space-16);
  opacity: 0;
  transition: opacity 200ms ease;
}
body.pgs-ready .site-footer { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .site-footer { transition: none; }
}

.site-footer a { text-decoration: none; }
.site-footer .text-muted { color: #B9B4B0; }

.footer-grid {
  display: grid;
  gap: var(--space-8);
}

@media (min-width: 768px) {
  .footer-grid { grid-template-columns: repeat(3, 1fr); }
}

.footer-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* franja de confianza (logos de pasarelas) — los SVG ya traen su propio
   color claro (#B9B4B0) porque un <img> con SVG externo no hereda
   currentColor de la página: quedaría negro e ilegible sobre el footer
   oscuro si dependiera del CSS de acá. */
.trust-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
}

.trust-strip img { height: 20px; width: auto; }

/* redes sociales del footer — solo se llenan si el admin configuró el link
   correspondiente en Ajustes > Contacto y redes (ver applyFooterSocial en
   site-settings.js); por eso arrancan vacíos y ocultos. */
.footer-social { display: flex; gap: var(--space-3); margin-top: var(--space-4); }
.footer-social-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid #3A3739;
  border-radius: 50%;
  color: #FFFFFF;
  transition: border-color 150ms, color 150ms;
}
.footer-social-icon:hover { border-color: var(--gold-400); color: var(--gold-400); }

/* estados de carga / vacío */
.empty-state {
  text-align: center;
  padding-top: var(--space-16);
  padding-bottom: var(--space-16);
  color: var(--text-muted);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--jg-onyx);
  color: #FFFFFF;
  padding: var(--space-3) var(--space-4);
  z-index: 100;
}

.skip-link:focus { left: var(--space-4); top: var(--space-4); }

/* aparición progresiva: reveal.js le suma estas clases a cada sección y
   tarjeta cuando entra en pantalla, en vez de que todo aparezca de golpe
   con el resto de la página. Sin JS, el contenido nunca queda invisible
   (reveal.js es quien agrega la clase que lo oculta) — la reducción de
   movimiento global de tokens.css ya vuelve la transición instantánea para
   quien prefiere menos animación. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 600ms cubic-bezier(0.16, 1, 0.3, 1), transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; }
}

/* ============================================================
   Sistema de ficha — vocabulario compartido de todas las páginas.
   Vocabulario de cuaderno de perfumista: filas de ficha técnica
   con regla fina, en vez de tarjetas con ícono.
   ============================================================ */

.spec-list { margin: 0; }
.spec-row {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--space-4);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--ink-100);
}
.spec-row:last-child { border-bottom: none; }
.spec-row dt {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding-top: 2px;
}
.spec-row dd {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 15px;
  line-height: 1.4;
}

/* filas de recibo/ficha — carrito, checkout, pedidos */
.ledger-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--ink-100);
  font-variant-numeric: tabular-nums;
}
.ledger-row:last-child { border-bottom: none; }
.ledger-row-total {
  font-weight: 600;
  font-size: 17px;
  border-top: 1px solid var(--ink-300);
  border-bottom: none;
  padding-top: var(--space-4);
  margin-top: var(--space-2);
}

/* marco del diagrama del hero — reemplaza el ícono decorativo por una
   notación real: la escalera de notas vive adentro (ver .notes-staircase). */
.scent-diagram {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding: var(--space-8);
  border: 1px solid #3A3739;
  clip-path: polygon(0 0, calc(100% - 28px) 0, 100% 28px, 100% 100%, 0 100%);
}
.scent-diagram-heading {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: #B9B4B0;
  margin: 0;
}

/* escalera de notas — nombre y color libres por nota (elegidos por el
   admin), ordenadas de la más suave (arriba, barra corta) a la más fuerte
   (abajo, barra completa). Usada en la ficha de producto y en el hero. */
.notes-staircase { display: flex; flex-direction: column; gap: var(--space-3); }
.notes-step { display: flex; align-items: center; gap: var(--space-4); }
.notes-step-name { font-family: var(--font-display); font-style: italic; font-size: 15px; width: 110px; flex-shrink: 0; }
.notes-step-track { flex: 1; height: 8px; background: var(--border); position: relative; overflow: hidden; }
.notes-step-fill {
  position: absolute;
  inset-block: 0;
  left: 0;
  transform-origin: left;
  transform: scaleX(0);
  animation: notes-fill 700ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes notes-fill { to { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) {
  .notes-step-fill { animation: none; transform: scaleX(1); }
}

/* tira de manifiesto — reemplaza la grilla de tarjetas ícono+texto */
.manifesto-strip { display: grid; gap: var(--space-8); }
@media (min-width: 768px) {
  /* el bloque entero se centra dentro de .container (ver margin-inline:auto
     de esa clase); el margin-left negativo que había acá antes compensaba
     el padding del texto cuando estaba alineado a la izquierda, pero ahora
     que el texto está centrado esa compensación desbalanceaba todo el
     bloque hacia la izquierda — se quita. */
  .manifesto-strip { grid-template-columns: repeat(3, 1fr); gap: 0; }
  .manifesto-item { padding-left: var(--space-8); padding-right: var(--space-8); border-left: 1px solid var(--ink-100); text-align: center; }
  .manifesto-item:first-child { border-left: none; }
  .manifesto-mark { margin-inline: auto; }
}
.manifesto-heading {
  /* Jost, no Bodoni: por debajo de 28px las serifas finas de Bodoni se
     rompen contra fondo claro (Regla de los 28px, DESIGN.md) — mismo
     tratamiento que .h3. */
  font-family: var(--font-body);
  font-size: 20px;
  font-weight: 500;
  margin: 0 0 var(--space-2);
}

.manifesto-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--gold-400);
  border-radius: 50%;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: var(--gold-700);
  margin-bottom: var(--space-3);
}

/* grilla de tarjetas de producto — home, catálogo, relacionados en ficha.
   El catálogo puede pisar --product-grid-cols-mobile por sección (ver
   catalogo_header en sections.js) para mostrar 2, 3 o 4 por fila en móvil;
   el resto de las grillas se queda con el default de 2. */
.product-grid {
  display: grid;
  grid-template-columns: repeat(var(--product-grid-cols-mobile, 2), 1fr);
  gap: var(--space-8) var(--space-6);
}
@media (min-width: 640px) { .product-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }

/* Con 3-4 columnas en móvil, el nombre/precio no siempre entra en el ancho
   de columna — por default un grid item no se achica más allá del ancho
   mínimo de su contenido (min-width:auto), así que ese texto empujaba la
   columna a desbordar y la grilla entera se corría hacia la derecha
   (página más ancha que la pantalla, columnas dispares). min-width:0
   deja que 1fr mande de verdad. */
.product-card { text-decoration: none; color: inherit; display: block; min-width: 0; }
.product-card-media {
  aspect-ratio: 4 / 5;
  background: var(--ink-100);
  position: relative;
  overflow: hidden;
}
.product-card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 400ms cubic-bezier(0.16, 1, 0.3, 1); }
.product-card:hover .product-card-media img { transform: scale(1.035); }
.product-card-body { padding-top: var(--space-3); margin-top: var(--space-3); border-top: 1px solid var(--ink-100); }
/* Jost 20px 500, como ya documenta DESIGN.md (H3 = "nombre de producto en
   tarjeta") — antes estaba en Bodoni 16px, por debajo de la Regla de los
   28px, y las serifas finas se perdían contra el fondo blanco de la tarjeta. */
.product-card-name { font-family: var(--font-body); font-weight: 500; font-size: 20px; line-height: 1.3; margin: 0; }
.product-card-meta { color: var(--text-muted); font-size: 13px; margin-top: 2px; }
.product-card-price { font-weight: 600; font-size: 15px; margin-top: var(--space-2); font-variant-numeric: tabular-nums; }

/* Con 3 o 4 columnas en móvil (ver "Productos por fila en móvil" en el
   editor de catalogo_header) la tarjeta a tamaño normal queda apretada —
   texto y separación se achican con la cantidad de columnas, para que 3-4
   se sientan compactas a propósito en vez de forzadas dentro del mismo
   tamaño de tarjeta de 2 columnas. Solo hasta el quiebre de tablet: de
   ahí en adelante manda el grid fijo de 3/4 de siempre, no este dato. */
@media (max-width: 639px) {
  .product-grid[data-cols-mobile="3"] { gap: var(--space-4) var(--space-3); }
  .product-grid[data-cols-mobile="3"] .product-card-name { font-size: 14px; }
  .product-grid[data-cols-mobile="3"] .product-card-meta { font-size: 11px; }
  .product-grid[data-cols-mobile="3"] .product-card-price { font-size: 13px; }

  .product-grid[data-cols-mobile="4"] { gap: var(--space-3) var(--space-2); }
  .product-grid[data-cols-mobile="4"] .product-card-name { font-size: 12px; }
  .product-grid[data-cols-mobile="4"] .product-card-meta { font-size: 10px; }
  .product-grid[data-cols-mobile="4"] .product-card-price { font-size: 12px; }
}

/* "También te puede interesar" (ficha de producto, ver producto_related_heading
   en el admin): mismo .product-card de siempre, en dos formas más además de
   la grilla — lista compacta en fila, o carrusel horizontal con flechas
   (mismo vocabulario de pista que classes_carousel/brands_carousel, ver
   initSnapCarousel en sections.js). */
.product-list { display: flex; flex-direction: column; gap: var(--space-6); }
.product-list .product-card { display: flex; gap: var(--space-4); align-items: flex-start; }
.product-list .product-card-media { width: 88px; flex-shrink: 0; }
.product-list .product-card-body { flex: 1; padding-top: 0; margin-top: 0; border-top: none; }

.pgs-related-carousel .pgs-carousel-track { --pgs-cards: 1.6; }
@media (min-width: 640px) { .pgs-related-carousel .pgs-carousel-track { --pgs-cards: 2.5; } }
@media (min-width: 1024px) { .pgs-related-carousel .pgs-carousel-track { --pgs-cards: 4; } }
.pgs-related-carousel .product-card {
  flex: 0 0 calc((100% - (var(--pgs-cards) - 1) * var(--space-4)) / var(--pgs-cards));
}

/* Sección "products" administrable en modo carrusel (ver renderProducts
   en sections.js) — mismo mecanismo de "cuántas tarjetas por pantalla"
   que classes_carousel, pero acá el mobile también lo elige el admin
   (--pgs-cards-mobile, 1 a 4, ver "Productos por fila en móvil"); tablet
   y escritorio quedan fijos como el resto de los carruseles del sitio. */
.pgs-products-carousel .pgs-carousel-track { --pgs-cards: var(--pgs-cards-mobile, 2); }
@media (min-width: 640px) { .pgs-products-carousel .pgs-carousel-track { --pgs-cards: 3; } }
@media (min-width: 1024px) { .pgs-products-carousel .pgs-carousel-track { --pgs-cards: 4; } }
.pgs-products-carousel .product-card {
  flex: 0 0 calc((100% - (var(--pgs-cards) - 1) * var(--space-4)) / var(--pgs-cards));
}

/* campo compacto — filas de tabla en el panel admin (imágenes, decants) */
.input-sm {
  height: 44px;
  padding: 0 var(--space-2);
  border: 1px solid var(--ink-300);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--jg-onyx);
  background: var(--jg-white);
}
.input-sm:focus {
  outline: 2px solid var(--gold-600);
  outline-offset: 1px;
  border-color: var(--gold-600);
}

/* selector de opciones excluyentes — medio de pago, presentación/tamaño */
.option-list { display: flex; flex-direction: column; gap: var(--space-3); }
.option-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  border: 1px solid var(--ink-300);
  border-radius: var(--radius);
  padding: var(--space-4);
  cursor: pointer;
}
.option-row:has(input:checked) { border-color: var(--gold-600); }
.option-row:has(input:disabled) { opacity: 0.5; cursor: not-allowed; }
.option-row-label { display: flex; align-items: center; gap: var(--space-3); }
.option-row-price { font-weight: 600; font-variant-numeric: tabular-nums; }

/* ficha de acceso — auth (login, registro, recuperar, reset) */
.access-card {
  max-width: 400px;
  margin-inline: auto;
  padding: var(--space-8) var(--space-6);
  border: 1px solid var(--ink-100);
  background: var(--jg-white);
}
@media (min-width: 640px) { .access-card { padding: var(--space-12); } }
.access-mark { width: 22px; height: 22px; margin-bottom: var(--space-6); }
.access-footer { text-align: center; margin-top: var(--space-6); font-size: 14px; padding-top: var(--space-6); border-top: 1px solid var(--ink-100); }

/* ============================================================
   Secciones de página administrables desde "Tienda en vivo" —
   banners, testimonios, contadores. El HTML/CSS libre no necesita
   clases propias, trae las suyas.
   ============================================================ */

/* banner: proporción fija (object-fit:cover) en vez de dejar que la
   imagen mande su propio alto — eso evitaba el recorte pero terminaba en
   marco/letterboxing cuando la imagen no calzaba con el espacio
   disponible, que es justo lo que se quiere evitar siempre que se pueda.
   Con una proporción fija y conocida de antemano (ver dimensiones
   recomendadas en el editor admin: 1600×600 escritorio, 800×1000 móvil)
   el recorte es mínimo si la imagen ya viene en esa proporción, y nunca
   hay marco. <picture> con la fuente de móvil (renderBanner en
   sections.js) hace que el navegador baje una sola imagen, no las dos.
   Admite una o varias fotos ("slides"): con una sola no hay puntos ni
   rotación; con varias, rota sola cada `rotation_interval` segundos (se
   pausa con hover/foco/pestaña oculta — sin botón de pausa visible,
   misma razón que la barra de anuncios) y los puntos de abajo permiten
   saltar a cualquiera a mano. */
.pgs-banner {
  position: relative;
  overflow: hidden;
  background: var(--jg-onyx);
  color: #FFFFFF;
  aspect-ratio: 8 / 3;
  /* pan-y: el navegador sigue haciendo scroll vertical de la página al
     dedo, pero deja el gesto horizontal libre para el arrastre de
     initBanner en vez de interpretarlo como navegación propia. */
  touch-action: pan-y;
  cursor: grab;
}
.pgs-banner:active { cursor: grabbing; }
@media (max-width: 767px) {
  .pgs-banner { aspect-ratio: 4 / 5; }
}
/* transición de "empuje": todas las fotos están apiladas y se reposicionan
   con transform (ver initBanner en sections.js) — la que sale se desliza
   hacia un lado y la que entra desde el otro, a la vez y de forma suave. */
.pgs-banner-slide {
  position: absolute;
  inset: 0;
  transition: transform 550ms cubic-bezier(.4, 0, .2, 1);
}
@media (prefers-reduced-motion: reduce) {
  .pgs-banner-slide { transition: none; }
}
.pgs-banner picture { display: block; width: 100%; height: 100%; }
.pgs-banner-media {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  /* sin esto, arrastrar la foto dispara el drag nativo del navegador
     (fantasma de la imagen siguiendo el cursor) en vez del arrastre propio
     del carrusel. */
  -webkit-user-drag: none;
  user-select: none;
}
.pgs-banner-scrim {
  position: absolute;
  inset: 0;
  background: rgba(32, 30, 31, .45);
  pointer-events: none;
}
.pgs-banner-inner {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-6);
}
@media (min-width: 1024px) {
  .pgs-banner-inner { padding-inline: var(--space-12); }
}
.pgs-banner-copy { max-width: 480px; }
.pgs-banner-title { margin: 0 0 var(--space-3); color: #FFFFFF; }
.pgs-banner-subtitle { color: #B9B4B0; margin: 0 0 var(--space-6); font-size: 15px; }
@media (min-width: 768px) {
  .pgs-banner-subtitle { font-size: 16px; }
}

.pgs-banner-dots {
  position: absolute;
  z-index: 1;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--space-2);
}
.pgs-banner-dot {
  width: 22px;
  height: 2px;
  background: rgba(255, 255, 255, .4);
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background-color 150ms;
}
.pgs-banner-dot.is-active,
.pgs-banner-dot:hover { background: #FFFFFF; }

/* Flechas del banner: mismo círculo translúcido sobre la foto que las de
   la galería en modo carrusel (ver .pgs-gallery-arrow) — acá sí necesitan
   fondo, a diferencia de las del carrusel de clases/marcas que viven sobre
   Marfil, no sobre una foto. */
.pgs-banner-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(32, 30, 31, .5);
  color: #FFFFFF;
  border: none;
  cursor: pointer;
  transition: background-color 150ms;
  z-index: 1;
}
.pgs-banner-arrow:hover { background: rgba(32, 30, 31, .75); }
.pgs-banner-arrow-prev { left: var(--space-4); }
.pgs-banner-arrow-next { right: var(--space-4); }

/* galería de fotos: una imagen a todo el ancho, grilla contenida, tira
   pegada para comparar, o carrusel de navegación manual (sin autoplay, así
   que no hace falta botón de pausa). El texto/botón opcional flota
   centrado sobre las fotos — pointer-events recortado para no tapar los
   controles del carrusel que quedan debajo. */
.pgs-gallery { position: relative; }

.pgs-gallery-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: rgba(32, 30, 31, .35);
  pointer-events: none;
}
.pgs-gallery-overlay-inner { max-width: 560px; padding: var(--space-6); pointer-events: auto; }
.pgs-gallery-heading { color: #FFFFFF; margin: 0 0 var(--space-3); }
.pgs-gallery-subtitle { color: #EDE9E3; margin: 0 0 var(--space-6); }

.pgs-gallery-caption { margin: var(--space-2) 0 0; font-size: 13px; color: var(--text-muted); text-align: center; }

/* texto/botón por foto (distinto del overlay de toda la galería de arriba):
   cada imagen puede traer su propio título/subtítulo/botón, oscurecido,
   desenfoque y posición vertical del texto — mismo criterio que el banner
   (rgba dinámico + filter:blur por foto, ver renderGalleryImageMedia en
   sections.js). align-items acá es solo el valor por defecto: cada imagen
   lo pisa inline según su text_position_vertical. */
.pgs-gallery-img-wrap { position: relative; overflow: hidden; }
.pgs-gallery-img-wrap img {
  display: block;
  /* --pgs-gallery-base-scale la pone inline sections.js solo cuando hay
     desenfoque (1.1, para que el filter:blur no deje un borde claro) — el
     hover de abajo multiplica sobre esa base en vez de pisarla. */
  transform: scale(var(--pgs-gallery-base-scale, 1));
  transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* Excepción acotada (ver DESIGN.md, Cards / Containers): la imagen escala
   levemente en hover/foco, el contenedor no se mueve ni gana sombra. */
.pgs-gallery-img-wrap:hover img,
.pgs-gallery-img-wrap:focus-within img { transform: scale(calc(var(--pgs-gallery-base-scale, 1) * 1.06)); }
@media (prefers-reduced-motion: reduce) {
  .pgs-gallery-img-wrap:hover img,
  .pgs-gallery-img-wrap:focus-within img { transform: scale(var(--pgs-gallery-base-scale, 1)); }
}
.pgs-gallery-img-scrim { position: absolute; inset: 0; pointer-events: none; }
.pgs-gallery-img-inner {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  padding: var(--space-4);
}
.pgs-gallery-img-copy { max-width: 100%; }
.pgs-gallery-img-title { margin: 0 0 var(--space-1); color: #FFFFFF; font-size: 18px; }
.pgs-gallery-img-subtitle { margin: 0 0 var(--space-3); color: #EDE9E3; font-size: 13px; }

/* single: una sola foto, a todo el ancho — mismo tratamiento sin importar
   qué modo haya elegido el admin, si solo cargó una imagen.
   --pgs-gallery-img-ratio (opcional, ver renderGallery en sections.js): si
   el admin puso ancho/alto, fuerza ese recorte; si no, "auto" mantiene el
   alto natural de la foto (con el tope de max-height de siempre). */
.pgs-gallery-single img { width: 100%; aspect-ratio: var(--pgs-gallery-img-ratio, auto); max-height: 640px; object-fit: cover; display: block; }

/* Ancho del bloque "contenido" (ver renderGallery/width en sections.js):
   angosto, con los mismos márgenes que el resto del sitio, en vez de a todo
   el ancho de la pantalla. Por defecto lo trae la grilla (así se adapta
   sola a la cantidad de fotos sin quedar gigante); el admin puede activarlo
   también en carrusel/comparar/una sola foto, o apagarlo acá. */
.pgs-gallery--contained .pgs-gallery-media {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-6);
}
@media (min-width: 1024px) {
  .pgs-gallery--contained .pgs-gallery-media { padding-inline: var(--space-12); }
}
.pgs-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-4);
}
.pgs-gallery-grid-item img { width: 100%; aspect-ratio: var(--pgs-gallery-img-ratio, 4 / 5); object-fit: cover; display: block; }

/* compare: pegadas, sin separación — para poner 2 a 4 fotos una al lado
   de la otra y comparar de un vistazo. Se apilan en móvil, si no quedan
   demasiado angostas para leerse. */
.pgs-gallery-compare { display: flex; flex-direction: column; }
@media (min-width: 640px) {
  .pgs-gallery-compare { flex-direction: row; }
}
.pgs-gallery-compare-item { flex: 1; }
.pgs-gallery-compare-item img { width: 100%; aspect-ratio: var(--pgs-gallery-img-ratio, 4 / 3); object-fit: cover; display: block; }
@media (min-width: 640px) {
  .pgs-gallery-compare-item img { aspect-ratio: var(--pgs-gallery-img-ratio, 4 / 5); }
}

/* carrusel: navegación manual con flechas y guiones (no puntos redondos —
   BRAND.md §11 descarta esquinas redondeadas como vocabulario de SaaS,
   no de esta marca). Sin autoplay: no hay nada que pausar. */
.pgs-gallery-carousel { position: relative; }
.pgs-gallery-slide img { width: 100%; aspect-ratio: var(--pgs-gallery-img-ratio, auto); max-height: 640px; object-fit: cover; display: block; }
.pgs-gallery-slide[hidden] { display: none; }

.pgs-gallery-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(32, 30, 31, .5);
  color: #FFFFFF;
  border: none;
  cursor: pointer;
  transition: background-color 150ms;
}
.pgs-gallery-arrow:hover { background: rgba(32, 30, 31, .75); }
.pgs-gallery-arrow-prev { left: var(--space-4); }
.pgs-gallery-arrow-next { right: var(--space-4); }

.pgs-gallery-dots {
  position: absolute;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--space-2);
}
.pgs-gallery-dot {
  width: 22px;
  height: 2px;
  background: rgba(255, 255, 255, .4);
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background-color 150ms;
}
.pgs-gallery-dot.is-active,
.pgs-gallery-dot:hover { background: #FFFFFF; }

/* --- Carrusel de clases y carrusel de marcas ---
   Vocabulario compartido de pista con scroll-snap (ver initSnapCarousel en
   sections.js): la cantidad de tarjetas/logos visibles por pantalla se fija
   con custom properties (--pgs-cards-* y --pgs-logos-*) que pone cada sección
   inline, según lo que configuró el admin — acá solo se leen. */
.pgs-carousel-wrap { position: relative; }
/* Modo continuo (clases y marcas, ver initContinuousCarousel en
   sections.js): la pista se duplica para el loop y queda el doble de ancha
   que su caja — sin este overflow:hidden esa mitad de más se sale del wrap
   y empuja el ancho de toda la página (aparece scroll horizontal). El
   recorte va acá, no en la pista (que necesita overflow:visible para que
   el JS pueda moverla libremente sin que le aparezca su propia barra de
   scroll encima). */
.pgs-carousel-wrap--continuous { overflow: hidden; }
.pgs-carousel-track {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.pgs-carousel-track::-webkit-scrollbar { display: none; }
.pgs-carousel-track > * { scroll-snap-align: start; flex-shrink: 0; }

/* Sin caja: solo el ícono, sin fondo ni borde en reposo — un círculo
   tenue aparece recién en hover/foco, mismo criterio minimalista que el
   resto de íconos funcionales del sitio (nav-icon-btn). El botón sigue
   midiendo 44px de alto por el mínimo táctil, aunque el ícono visible sea
   más chico. */
.pgs-carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: 50%;
  color: var(--text-muted);
  cursor: pointer;
  transition: color 150ms, background-color 150ms;
  z-index: 1;
}
.pgs-carousel-arrow svg { width: 18px; height: 18px; }
.pgs-carousel-arrow:hover { color: var(--jg-onyx); background: rgba(32, 30, 31, .06); }
.pgs-carousel-arrow:focus-visible { background: rgba(32, 30, 31, .06); }
.pgs-carousel-arrow:disabled { opacity: .3; cursor: not-allowed; background: none; }
.pgs-carousel-arrow-prev { left: calc(-1 * var(--space-3)); }
.pgs-carousel-arrow-next { right: calc(-1 * var(--space-3)); }
@media (max-width: 767px) {
  /* En móvil se desliza al dedo; las flechas quedan pegadas al borde del
     container en vez de salirse de la pantalla. */
  .pgs-carousel-arrow-prev { left: 0; }
  .pgs-carousel-arrow-next { right: 0; }
}

/* Estilo "ancho completo": la pista rompe el container y ocupa todo el
   ancho de la pantalla (el título se queda adentro del container, como
   cualquier otro título de sección) — cada tarjeta queda más grande sola,
   sin tocar --pgs-cards-*. Las flechas quedan sobre la foto de la punta,
   por eso llevan el mismo fondo translúcido que las del banner en vez del
   círculo transparente de siempre (que solo se lee bien sobre Marfil). */
.pgs-classes-carousel--full .pgs-carousel-track { padding-inline: var(--space-6); }
@media (min-width: 1024px) {
  .pgs-classes-carousel--full .pgs-carousel-track { padding-inline: var(--space-12); }
}
.pgs-classes-carousel--full .pgs-carousel-arrow {
  background: rgba(32, 30, 31, .45);
  color: #FFFFFF;
}
.pgs-classes-carousel--full .pgs-carousel-arrow:hover { background: rgba(32, 30, 31, .7); }
.pgs-classes-carousel--full .pgs-carousel-arrow-prev { left: var(--space-2); }
.pgs-classes-carousel--full .pgs-carousel-arrow-next { right: var(--space-2); }

/* Clases: tarjeta con proporción fija, sin marco en la imagen (radio 0) y
   2px en la tarjeta — mismo criterio que el resto de imágenes de producto
   (ver Shapes en DESIGN.md). Cantidad visible por pantalla vía --pgs-cards-*. */
.pgs-classes-carousel .pgs-carousel-track { --pgs-cards: var(--pgs-cards-mobile, 1.3); }
@media (min-width: 768px) {
  .pgs-classes-carousel .pgs-carousel-track { --pgs-cards: var(--pgs-cards-tablet, 3); }
}
@media (min-width: 1024px) {
  .pgs-classes-carousel .pgs-carousel-track { --pgs-cards: var(--pgs-cards-desktop, 4); }
}
.pgs-class-card {
  display: block;
  flex: 0 0 calc((100% - (var(--pgs-cards) - 1) * var(--space-4)) / var(--pgs-cards));
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
}
.pgs-class-card-media {
  position: relative;
  /* --pgs-card-ratio la pone renderClassesCarousel inline en la sección,
     según card_ratio_w/card_ratio_h del admin (default 3/4). */
  aspect-ratio: var(--pgs-card-ratio, 3 / 4);
  overflow: hidden;
  border-radius: var(--radius);
}
.pgs-class-card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 0;
  /* --pgs-class-base-scale la pone inline sections.js solo cuando hay
     desenfoque (1.1, para que el filter:blur no deje un borde claro en el
     límite de la foto — mismo criterio que banner/galería). El hover de
     abajo multiplica sobre esa base en vez de pisarla, así el zoom leve
     sigue funcionando con o sin desenfoque configurado. */
  transform: scale(var(--pgs-class-base-scale, 1));
  transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* Excepción acotada a esta tarjeta (ver DESIGN.md, Cards / Containers): la
   regla general de "nunca transform en hover" es para la tarjeta de
   producto. Acá solo escala la imagen, la tarjeta no se mueve ni gana
   sombra. */
.pgs-class-card:hover .pgs-class-card-media img,
.pgs-class-card:focus-visible .pgs-class-card-media img { transform: scale(calc(var(--pgs-class-base-scale, 1) * 1.06)); }
.pgs-class-card-scrim { position: absolute; inset: 0; pointer-events: none; }
.pgs-class-card-copy {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--space-4);
}
.pgs-class-card-eyebrow {
  margin: 0 0 var(--space-1);
  color: #FFFFFF;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
}
.pgs-class-card-name {
  margin: 0;
  color: #FFFFFF;
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 500;
}
.pgs-class-card-arrow {
  position: absolute;
  bottom: var(--space-4);
  right: var(--space-4);
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--gold-400);
  border-radius: 50%;
  color: var(--gold-400);
  background: transparent;
  transition: background-color 150ms, color 150ms;
}
.pgs-class-card:hover .pgs-class-card-arrow,
.pgs-class-card:focus-visible .pgs-class-card-arrow {
  background: var(--gold-400);
  color: var(--ink-900);
}
@media (prefers-reduced-motion: reduce) {
  /* Sin el *1.06 extra del hover — pero conserva --pgs-class-base-scale si
     hay desenfoque, si no el borde claro del blur queda visible. */
  .pgs-class-card:hover .pgs-class-card-media img,
  .pgs-class-card:focus-visible .pgs-class-card-media img { transform: scale(var(--pgs-class-base-scale, 1)); }
}

/* Modo continuo del carrusel de clases: mismo criterio que el de marcas
   (ver más abajo) — pista duplicada (renderClassesCarousel) que
   initContinuousCarousel (sections.js) desliza sola por JS, se congela en
   hover/foco y se puede arrastrar con el dedo o el cursor en cualquier
   momento. touch-action:pan-y deja que el dedo siga haciendo scroll
   vertical de la página; solo el arrastre horizontal lo toma el carrusel. */
.pgs-class-track--continuous {
  overflow: visible;
  touch-action: pan-y;
  cursor: grab;
}
.pgs-class-track--continuous.is-dragging { cursor: grabbing; }

/* Estilo "ancho completo": mismo criterio que .pgs-classes-carousel--full
   más arriba — la pista rompe el container y ocupa todo el ancho de la
   pantalla (el título se queda adentro del container, como cualquier otro
   título de sección). Las flechas llevan el mismo fondo translúcido que
   las del banner en vez del círculo transparente (que solo se lee bien
   sobre Marfil). */
.pgs-brands-carousel--full .pgs-carousel-track { padding-inline: var(--space-6); }
@media (min-width: 1024px) {
  .pgs-brands-carousel--full .pgs-carousel-track { padding-inline: var(--space-12); }
}
.pgs-brands-carousel--full .pgs-carousel-arrow {
  background: rgba(32, 30, 31, .45);
  color: #FFFFFF;
}
.pgs-brands-carousel--full .pgs-carousel-arrow:hover { background: rgba(32, 30, 31, .7); }
.pgs-brands-carousel--full .pgs-carousel-arrow-prev { left: var(--space-2); }
.pgs-brands-carousel--full .pgs-carousel-arrow-next { right: var(--space-2); }

/* Marcas: altura fija, ancho automático, centrados — escala de grises por
   defecto (franja de logos de pago, ver BRAND.md). */
.pgs-brands-carousel .pgs-carousel-track { --pgs-logos: var(--pgs-logos-mobile, 3); }
@media (min-width: 768px) {
  .pgs-brands-carousel .pgs-carousel-track { --pgs-logos: var(--pgs-logos-tablet, 5); }
}
@media (min-width: 1024px) {
  .pgs-brands-carousel .pgs-carousel-track { --pgs-logos: var(--pgs-logos-desktop, 7); }
}
.pgs-brand-item {
  flex: 0 0 calc((100% - (var(--pgs-logos) - 1) * var(--space-6)) / var(--pgs-logos));
  display: flex;
  align-items: center;
  justify-content: center;
}
.pgs-brands-carousel .pgs-carousel-track { gap: var(--space-6); }
.pgs-brand-logo-link { display: block; line-height: 0; }
.pgs-brand-logo {
  height: 48px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  display: block;
  transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1), filter 200ms, opacity 200ms;
}
@media (min-width: 1024px) {
  .pgs-brand-logo { height: 56px; }
}
.pgs-brand-logo--grayscale { filter: grayscale(1); opacity: .6; }
.pgs-brand-item:hover .pgs-brand-logo,
.pgs-brand-logo-link:focus-visible .pgs-brand-logo { transform: scale(1.08); }
.pgs-brand-item:hover .pgs-brand-logo--grayscale,
.pgs-brand-logo-link:focus-visible .pgs-brand-logo--grayscale { filter: grayscale(0); opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .pgs-brand-item:hover .pgs-brand-logo,
  .pgs-brand-logo-link:focus-visible .pgs-brand-logo { transform: none; }
}

/* Modo continuo: pista duplicada (ver renderBrandsCarousel) que
   initContinuousCarousel (sections.js) desliza por JS, igual que el
   carrusel de clases de más arriba — se congela en hover/foco y se puede
   arrastrar. Las flechas quedan en el DOM por accesibilidad pero ocultas
   acá — sections.js ya no marca la sección como "continuous" cuando
   prefers-reduced-motion está activo, así que esta regla nunca convive con
   el movimiento desactivado. */
.pgs-carousel-wrap--continuous .pgs-carousel-arrow { display: none; }
.pgs-brand-track--continuous {
  overflow: visible;
  touch-action: pan-y;
  cursor: grab;
}
.pgs-brand-track--continuous.is-dragging { cursor: grabbing; }

.pgs-testimonials { display: grid; gap: var(--space-8); }
@media (min-width: 768px) { .pgs-testimonials { grid-template-columns: repeat(3, 1fr); } }
.pgs-testimonial-card { padding: var(--space-6); border: 1px solid var(--ink-100); }
.pgs-testimonial-text { font-family: var(--font-display); font-style: italic; margin: 0 0 var(--space-4); }
.pgs-testimonial-author { display: flex; align-items: center; gap: var(--space-3); }
.pgs-testimonial-author img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; }
.pgs-testimonial-name { font-weight: 600; margin: 0; font-size: 14px; }
.pgs-testimonial-detail { color: var(--text-muted); font-size: 13px; margin: 0; }

.pgs-counters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  justify-content: space-around;
  padding-block: var(--space-8);
  border-top: 1px solid var(--ink-100);
  border-bottom: 1px solid var(--ink-100);
}
.pgs-counter { text-align: center; }
.pgs-counter-value { font-family: var(--font-display); font-size: 40px; margin: 0; }
.pgs-counter-label { color: var(--text-muted); font-size: 13px; margin-top: var(--space-1); }

/* barra de anuncios: uno o varios mensajes que rotan con fundido o
   deslizamiento (nunca cinta con scroll infinito — a diferencia del primer
   intento, acá el texto se lee quieto y solo cambia entero de un mensaje al
   siguiente). Con un solo mensaje no hay rotación ni controles. Variantes
   de color cerradas (BRAND.md §3.7): siempre roles/tokens, nunca hex suelto,
   así heredan el acento que regenera Ajustes. */
.announcement-bar--onyx { background: var(--jg-onyx); color: #FFFFFF; }
.announcement-bar--onyx strong { color: var(--gold-400); }
.announcement-bar--paper { background: var(--jg-paper); color: var(--ink-900); }
.announcement-bar--paper strong { color: var(--gold-700); }
.announcement-bar--gold-soft { background: var(--gold-50); color: var(--ink-900); }
.announcement-bar--gold-soft strong { color: var(--gold-800); }

.announcement-bar-inner {
  max-width: var(--container-max);
  margin-inline: auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-6);
  min-height: 36px;
}

@media (min-width: 1024px) {
  .announcement-bar-inner { padding-inline: var(--space-12); }
}

.announcement-bar-viewport { flex: 1; position: relative; overflow: hidden; }

/* todas las diapositivas quedan apiladas (position:absolute) y se
   reposicionan con transform/opacity vía JS (ver initAnnouncementBar en
   sections.js) — mismo mecanismo que el banner, así la que sale y la que
   entra se animan juntas y de forma suave, no una brusca detrás de otra. */
.announcement-bar-slide {
  position: absolute;
  inset: 0;
  padding-block: var(--space-2);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  text-align: center;
  transition: transform 380ms ease, opacity 380ms ease;
}
@media (prefers-reduced-motion: reduce) {
  .announcement-bar-slide { transition: none; }
}

.announcement-bar-slide[hidden] { display: none; }
.announcement-bar-slide strong { font-weight: 600; }

/* mobile-first: hasta 2 líneas, nunca se corta a media palabra — nada de
   marquee ni de recortar con ellipsis a mitad de una palabra. */
.announcement-bar-slide > a,
.announcement-bar-slide > span {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.announcement-bar-slide > a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 3px;
  /* zona tocable de 44px cuando el mensaje es un enlace (BRAND.md §9) */
  min-height: 44px;
  align-items: center;
  justify-content: center;
}

.announcement-bar-controls { display: flex; align-items: center; gap: var(--space-1); flex-shrink: 0; }

.announcement-bar-btn {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  opacity: 0.7;
  transition: opacity 150ms;
}

.announcement-bar-btn:hover { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .announcement-bar-slide.is-entering-fade,
  .announcement-bar-slide.is-entering-slide {
    animation: none;
  }
}

.pgs-categories { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.pgs-category-chip {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
  color: var(--text);
  font-size: 14px;
  transition: border-color 150ms;
}
.pgs-category-chip:hover { border-color: var(--gold-600); }
