/* Estilos de la página de catálogo. Extraído del <style> inline de
   catalog.html (plan CWV A.4) a estático cacheable. */

/* ── Catalog-specific styles (Plum Blossom tokens) ── */
.catalog-layout {
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: flex-start;
  position: relative;
  min-height: 60vh;
}
@media (min-width: 1024px) {
  .catalog-layout { flex-direction: row; }
  /* En desktop el sidebar de filtros (alto, sticky) ya da presencia vertical a
     la fila aun con grid corto. El `min-height:60vh` sólo añadía una banda
     vacía entre el grid y el footer. Lo soltamos a `auto` en desktop; en móvil
     se conserva para que el empty-state no colapse. */
  .catalog-layout { min-height: auto; }
}
/* FAB de WhatsApp (global, bottom-right ~80px) solapaba la columna derecha del
   grid a ≥1280px. Reservamos un gutter derecho para que la última columna de
   cards no quede bajo el botón. No tocamos el FAB (vive en base_landing). */
@media (min-width: 1280px) {
  .catalog-layout { padding-right: 72px; }
}

/* Full-bleed: el catálogo ocupa TODO el ancho de la pantalla (sin el cap de
   1280px de .pl-landing-container). Padding lateral fluido con la misma escala
   que la convención `cat-wide` del home, para mantener aire on-brand en los
   bordes sin centrar el contenido en pantallas anchas. */
.pl-landing-container.cat-wide {
  max-width: none;
  padding-left: clamp(1.25rem, 4vw, 4.5rem);
  padding-right: clamp(1.25rem, 4vw, 4.5rem);
}

/* ── Filter panel: bottom sheet en móvil, columna fija en desktop ──
   Móvil (<1024px): hoja inferior con grabber, cabecera fija y barra de acción
   fija (ver bloque "Bottom-sheet chrome" más abajo). Desktop (≥1024px): sidebar
   sticky de siempre. El motor de filtrado (client-side) no cambia. */
.cat-sidebar {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  width: 100%;
  max-width: 100%;
  max-height: 85vh;
  max-height: 88dvh;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  transform: translateY(100%);
  transition: transform .34s cubic-bezier(0.16,1,0.3,1);
  box-shadow: 0 -12px 48px rgba(45,31,61,.22);
}
.cat-sidebar.open {
  display: block;
  transform: translateY(0);
}
@media (min-width: 1024px) {
  .cat-sidebar {
    display: block;
    position: sticky;
    top: 7rem;
    left: auto;
    right: auto;
    bottom: auto;
    width: 272px;
    max-width: none;
    max-height: none;
    flex-shrink: 0;
    padding: 20px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: none;
    transform: none;
    height: fit-content;
    overflow: visible;
    z-index: auto;
  }
}

.cat-sidebar-overlay {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--overlay);
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s var(--ease), visibility .3s var(--ease);
}
.cat-sidebar-overlay.open { opacity: 1; visibility: visible; }
@media (min-width: 1024px) {
  .cat-sidebar-overlay { display: none !important; }
}

/* ── Bottom-sheet chrome (solo móvil ≤1023px) ──
   Grabber (::before de la cabecera), cabecera fija arriba y barra de acción fija
   abajo con "Limpiar" + "Ver N productos" (conteo en vivo). En desktop la hoja
   es una columna estática y esta cromática se oculta. */
.cat-sheet-footer { display: none; }

@media (max-width: 1023px) {
  .cat-sidebar > .filter-header {
    position: sticky;
    top: 0;
    z-index: 4;
    background: var(--surface-1);
    padding: 22px 20px 12px;
    margin-bottom: 0;
  }
  .cat-sidebar > .filter-header::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    width: 40px;
    height: 4px;
    transform: translateX(-50%);
    border-radius: var(--radius-pill);
    background: var(--border-strong);
  }
  /* La cabecera "Limpiar" se muda a la barra de acción inferior en móvil. */
  #clearFiltersBtn { display: none !important; }
  /* El panel ya no tiene padding lateral (0): lo trasladamos a cada bloque
     scrolleable para que cabecera y footer puedan ir a sangre completa. */
  .cat-sidebar > .sidebar-search,
  .cat-sidebar > .filter-section,
  .cat-sidebar > #filterDynamic,
  .cat-sidebar > #sidebarLoader {
    padding-left: 20px;
    padding-right: 20px;
  }

  .cat-sheet-footer {
    position: sticky;
    bottom: 0;
    z-index: 4;
    display: flex;
    gap: 10px;
    align-items: center;
    margin-top: 8px;
    padding: 12px 20px calc(12px + env(safe-area-inset-bottom));
    background: var(--surface-1); /* fallback si color-mix no está soportado */
    background: color-mix(in srgb, var(--surface-1) 92%, transparent);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    backdrop-filter: blur(10px) saturate(140%);
    border-top: 1px solid var(--border);
  }
  .cat-sheet-clear {
    flex: 0 0 auto;
    min-height: 48px;
    padding: 0 18px;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-strong);
    background: transparent;
    color: var(--ink-soft);
    font-weight: 700;
    font-size: .875rem;
    cursor: pointer;
    transition: background .2s, color .2s, border-color .2s, transform .1s;
  }
  .cat-sheet-clear:hover { background: var(--surface-2); color: var(--ink); }
  .cat-sheet-clear:active { transform: scale(.97); }
  .cat-sheet-apply {
    flex: 1 1 auto;
    min-height: 48px;
    padding: 0 20px;
    border-radius: var(--radius-lg);
    border: none;
    background: var(--brand);
    color: #fff;
    font-weight: 800;
    font-size: .9375rem;
    letter-spacing: -.01em;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    box-shadow: 0 10px 24px -8px rgba(124,58,237,.55);
    transition: background .2s, transform .1s, box-shadow .2s;
  }
  .cat-sheet-apply:hover { background: var(--brand-hover); }
  .cat-sheet-apply:active { transform: scale(.98); }
  .cat-sheet-clear:focus-visible,
  .cat-sheet-apply:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
}

@media (prefers-reduced-motion: reduce) {
  .cat-sidebar,
  .cat-sidebar-overlay { transition: none; }
}

/* ── Filter section ── */
.filter-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}

.filter-section { border-bottom: 1px solid var(--border); padding-bottom: 12px; margin-bottom: 4px; }
.filter-section:last-child { border-bottom: none; }

.filter-toggle-btn {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: 8px 0;
  min-height: 44px;
  cursor: pointer;
  background: none;
  border: none;
  color: var(--ink);
}
.filter-toggle-btn i.fa-chevron-down { transition: transform .3s; font-size: .625rem; color: var(--ink-soft); }
.filter-section.open .filter-toggle-btn i.fa-chevron-down { transform: rotate(180deg); }

.filter-body { display: none; padding-top: 4px; }
.filter-section.open .filter-body { display: block; }

/* ── Header row: toggle (crece) + botón de fijar (fijo) ── */
.filter-head { display: flex; align-items: center; gap: 2px; }
.filter-head .filter-toggle-btn { flex: 1 1 auto; }
/* Contador de opciones del grupo, junto al título. */
.filter-opt-count { font-weight: 600; color: var(--ink-soft); font-size: .72rem; margin-left: 6px; }
/* Conteo por opción (cuántos productos la cumplen dado el resto de filtros). */
.opt-count { color: var(--ink-soft); font-size: .72rem; margin-left: 4px; }
.filter-pill .opt-count { opacity: .85; }
.filter-pill.active .opt-count { color: inherit; opacity: .9; }
/* Fijar grupo: lo mantiene desplegado y persiste (localStorage). */
.filter-pin {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; min-width: 36px;
  background: none; border: none; cursor: pointer;
  color: var(--ink-soft); border-radius: 8px;
  transition: color .2s, background .2s, transform .2s;
}
.filter-pin i { font-size: .8rem; transition: transform .2s; }
.filter-pin:hover { background: var(--surface-2, rgba(124,58,237,.08)); color: var(--ink); }
.filter-pin:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.filter-pin.pinned { color: var(--brand); }
.filter-pin.pinned i { transform: rotate(-30deg); }
@media (hover: none) {
  .filter-pin { width: 44px; height: 44px; min-width: 44px; }
}
/* En móvil (bottom sheet) "fijar" no aplica —la hoja es transitoria— y la
   chincheta añade una segunda acción confusa por fila. Se oculta; el chevron
   sigue expandiendo/colapsando cada grupo. */
@media (max-width: 1023px) {
  .filter-pin { display: none; }
}

/* ── Filter pills ── */
.filter-pill {
  display: inline-flex;
  align-items: center;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  font-size: .75rem;
  font-weight: 700;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all .2s;
  min-height: 32px;
}
.filter-pill:hover { border-color: var(--brand); color: var(--brand); }
.filter-pill.active { background: var(--brand); color: #fff; border-color: var(--brand); }

/* ── Filter checkbox ── */
.filter-check {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  min-height: 36px;
  cursor: pointer;
  font-size: .75rem;
  color: var(--ink-soft);
  transition: color .2s;
}
.filter-check:hover { color: var(--brand); }
.filter-check input[type="checkbox"] { accent-color: var(--brand); }

/* Touch targets ≥44px en breakpoints táctiles (≤1023px). Los pills (32px),
   checks (36px) y chips-x (22px) eran sub-44px y fallaban WCAG táctil. */
@media (max-width: 1023px) {
  .filter-pill { min-height: 44px; padding: 10px 16px; }
  .filter-check { min-height: 44px; padding: 10px 0; }
  .filter-check input[type="checkbox"] { width: 20px; height: 20px; }
}

/* ── Price range ── */
.price-inputs { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.price-input {
  width: 46%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 6px 8px;
  font-size: .8125rem;
  font-weight: 700;
  color: var(--ink);
  text-align: right;
}
.price-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }

.range-wrap { position: relative; height: 24px; margin: 12px 0; }
.range-track {
  position: absolute; top: 50%; left: 0; right: 0; height: 4px;
  background: var(--border); border-radius: var(--radius-pill);
  transform: translateY(-50%);
}
.range-progress {
  position: absolute; top: 50%; height: 4px;
  background: var(--brand-grad); border-radius: var(--radius-pill);
  transform: translateY(-50%); z-index: 1;
}
.range-input {
  position: absolute; top: 50%; left: 0; right: 0; width: 100%;
  height: 24px; transform: translateY(-50%);
  -webkit-appearance: none; appearance: none;
  pointer-events: none; background: none; z-index: 2; margin: 0;
}
.range-input::-webkit-slider-thumb {
  -webkit-appearance: none; pointer-events: auto;
  width: 20px; height: 20px; border-radius: 50%;
  background: #fff; border: 2px solid var(--brand);
  box-shadow: 0 4px 8px rgba(0,0,0,.1); cursor: pointer;
  transition: transform .2s;
}
.range-input::-webkit-slider-thumb:hover { transform: scale(1.15); }
.range-input::-moz-range-thumb {
  pointer-events: auto; width: 20px; height: 20px;
  border-radius: 50%; background: #fff; border: 2px solid var(--brand);
  box-shadow: 0 4px 8px rgba(0,0,0,.1); cursor: pointer;
}

/* ── Trust strip ── */
/* Prueba de confianza on-page: los tres hechos que cierran la venta (garantía,
   factura fiscal, técnicos propios) vivían sólo en el <meta description> y nunca
   se renderizaban. Una banda delgada y sobria bajo el toolbar, sobre la rejilla.
   No es un banner: hairline arriba/abajo, sin fondo llamativo, icono en púrpura
   disciplinado y texto ≥4.5:1. Una línea en desktop; envuelve en móvil. */
.cat-trust {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
  padding: 12px 4px;
  margin-bottom: 24px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.cat-trust-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.3;
}
.cat-trust-item i {
  color: var(--brand);
  font-size: .875rem;
  width: 16px;
  text-align: center;
  flex-shrink: 0;
}

/* ── Toolbar ── */
.cat-toolbar {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 24px;
}

.cat-pill-bar {
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  -ms-overflow-style: none;
  scrollbar-width: none;
  flex: 1;
  min-width: 0;
  /* Fade-mask al borde derecho: señala "hay más categorías" cuando la barra
     scrollea horizontalmente, sin un scrollbar visible. */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
}
.cat-pill-bar::-webkit-scrollbar { display: none; }

/* El contenedor del scroller de categorías. min-width:0 deja que el
   flex hijo scrollee horizontalmente sin desbordar la toolbar (antes se
   forzaba overflow:hidden en el padre, lo que truncaba el contador). */
.toolbar-lead { min-width: 0; }
.toolbar-count { flex-shrink: 0; }

/* Móvil: el contador compite por espacio con el botón Filtros y la barra de
   categorías scrollable, truncándose a "47 D…". Le damos su propia fila para
   que se lea completo. */
/* Barra de herramientas móvil (grid): [Filtros] [Ordenar] en la primera fila,
   contador y categorías a lo ancho debajo. display:contents deja que los hijos
   de .toolbar-lead se coloquen en el grid del toolbar junto al <select> de
   orden (que es su hermano), sin tocar el DOM ni el layout de desktop. */
@media (max-width: 639px) {
  .cat-toolbar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: 10px;
    padding: 10px;
  }
  .cat-toolbar > .toolbar-lead { display: contents; }
  #mobileFilterBtn { grid-column: 1; grid-row: 1; width: 100%; justify-content: center; }
  .sort-select { grid-column: 2; grid-row: 1; width: 100%; }
  .toolbar-count { grid-column: 1 / -1; grid-row: 2; width: 100%; padding-left: 2px; }
  .cat-pill-bar { grid-column: 1 / -1; grid-row: 3; width: 100%; }
}

.cat-pill {
  padding: 8px 16px;
  border-radius: var(--radius-lg);
  font-size: .75rem;
  font-weight: 700;
  white-space: nowrap;
  border: none;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all .2s;
  min-height: 36px;
}
.cat-pill:hover { background: var(--surface-2); }
.cat-pill.active { background: var(--brand); color: #fff; box-shadow: var(--shadow-sm); }

.sort-select {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  font-size: .75rem;
  font-weight: 700;
  color: var(--ink);
  padding: 8px 32px 8px 12px;
  min-height: 36px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%236b5580' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 6l3.5 4 3.5-4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
}
.sort-select:focus { outline: none; border-color: var(--brand); }

/* ── A11y: foco de teclado visible + touch targets ≥44px en móvil ── */
.filter-check input[type="checkbox"]:focus-visible,
.cat-pill:focus-visible,
.sort-select:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
@media (max-width: 1023px) {
  .cat-pill { min-height: 44px; }
  /* Glifo de orden (↕) a la izquierda para que "Destacados" se lea como un
     control de ORDEN, no como una etiqueta. El caret de la derecha se conserva. */
  .sort-select {
    min-height: 44px;
    padding-left: 34px;
    background-image:
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' fill='none' stroke='%236b5580' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round' viewBox='0 0 24 24'%3E%3Cpath d='M7 4v16M7 4l-3 3M7 4l3 3M17 20V4M17 20l-3-3M17 20l3-3'/%3E%3C/svg%3E"),
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%236b5580' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 6l3.5 4 3.5-4z'/%3E%3C/svg%3E");
    background-repeat: no-repeat, no-repeat;
    background-position: left 12px center, right 10px center;
  }
  .active-filters-clear { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ── Subcategoría (2º nivel): aparece tras elegir una categoría principal ──
   Las categorías principales se leen como TABS (.cat-pill); las subcategorías,
   un peldaño abajo en la jerarquía, se leen como CHIPS de filtro. Fila propia
   bajo el toolbar, scrollable en móvil (mismo fade-mask que la barra de
   categorías). Visible siempre que la categoría activa exponga subtipos. */
.cat-subrow {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: -12px 0 22px;
  padding: 2px 2px 2px 0;
  animation: subrow-in .22s cubic-bezier(0.16,1,0.3,1);
}
.cat-subrow[hidden] { display: none; }
.cat-subrow-label {
  flex-shrink: 0;
  font-size: .6875rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--ink-soft);
}
.cat-subbar {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow-x: auto;
  -ms-overflow-style: none;
  scrollbar-width: none;
  flex: 1;
  min-width: 0;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
}
.cat-subbar::-webkit-scrollbar { display: none; }
.subcat-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  white-space: nowrap;
  padding: 6px 14px;
  min-height: 34px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--ink-soft);
  font-size: .75rem;
  font-weight: 700;
  cursor: pointer;
  transition: background .18s, color .18s, border-color .18s;
}
.subcat-pill:hover { border-color: var(--brand); color: var(--brand); }
.subcat-pill.active { background: var(--brand); color: #fff; border-color: var(--brand); }
.subcat-pill:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.subcat-pill .opt-count { color: inherit; opacity: .7; margin-left: 2px; }
.subcat-pill.active .opt-count { opacity: .85; }
@media (hover: none) { .subcat-pill { min-height: 44px; padding: 8px 16px; } }
@keyframes subrow-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .cat-subrow { animation: none; } }

/* ── Product card ── */
.cat-product-card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100%;
  transition: transform .4s cubic-bezier(0.2,0.8,0.2,1),
              box-shadow .4s cubic-bezier(0.2,0.8,0.2,1),
              border-color .4s cubic-bezier(0.2,0.8,0.2,1);
  contain: layout style paint;
  content-visibility: auto;
  contain-intrinsic-size: auto 480px;
}
/* Las últimas cards de la rejilla NO usan content-visibility: cuando una fila
   final queda parcialmente bajo el fold, `content-visibility:auto` las salta y
   pinta su placeholder vacío (contain-intrinsic-size) en vez del contenido —
   se leía como una card fantasma rota. Forzar render en las 4 últimas (cubre
   la fila de cierre de la rejilla hasta 4-col en pantallas anchas) elimina ese
   vacío. El costo de perf es nulo: la página está capada a ~12 cards. */
.cat-product-card:nth-last-child(-n+4) {
  content-visibility: visible;
  contain-intrinsic-size: auto;
}
.cat-product-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 20px 40px -12px rgba(124,58,237,.16);
  border-color: var(--brand-soft);
}
/* Foco de teclado diseñado: el enlace de título es la parada de tab primaria de
   la card. Anillo morado visible sobre el título (y sobre la card si algún
   descendiente lo propaga) para navegación por teclado. */
.cat-product-card:focus-within {
  border-color: var(--brand-soft);
}
.card-title-link {
  color: inherit;
  text-decoration: none;
  outline: none;
}
.card-title-link:hover { color: var(--brand); }
.card-title-link:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 2px;
}
/* El enlace de la media cubre el panel de foto y hereda el layout de centrado
   que antes tenía el <picture>. No participa en el orden de tab (aria-hidden). */
.card-media-link {
  position: relative;
  z-index: 1;
  display: flex;
  width: 100%;
  height: 100%;
  align-items: center;
  justify-content: center;
}
@media (max-width: 1023px) {
  .cat-product-card:hover { transform: none; box-shadow: none; }
  .cat-product-card:active { transform: scale(.98); transition-duration: .15s; }
}

/* Panel de foto consistente en AMBOS temas (patrón estándar e-commerce).
   Antes el wrap era `background:#fff` full-bleed: en dark, las fotos de
   producto con fondo blanco real punzaban rectángulos cegadores en la rejilla.
   Ahora el wrap es una superficie neutra (--surface-0) y la foto se asienta en
   un panel interno claro, enmarcado (radio + borde sutil + padding). Así el
   producto pertenece a la card y se ve intencional en claro y oscuro, en vez de
   un bloque blanco a sangre. */
.card-img-wrap {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-0);
  position: relative;
  overflow: hidden;
  padding: 12px;
}
/* Panel interno: la zona blanca queda recortada con radio y un borde tenue,
   leyéndose como un soporte de producto deliberado (no full-bleed cegador). */
.card-img-wrap::before {
  content: "";
  position: absolute;
  inset: 12px;
  border-radius: var(--radius-md);
  background: #fff;
  border: 1px solid var(--border);
}
[data-theme="dark"] .card-img-wrap { background: var(--surface-2); }
/* En dark el panel interno baja a un blanco roto suave (no #fff puro): la foto
   sigue sobre superficie clara — estándar e-commerce — pero sin el brillo que
   cansa la vista en la rejilla oscura. */
[data-theme="dark"] .card-img-wrap::before {
  background: #f1edf6;
  border-color: rgba(255,255,255,.06);
}

/* Móvil: aplanar la imagen para densificar la página. La imagen cuadrada a
   ancho completo creaba cards de ~1500px (página de ~18700px). 16/10 corta
   ~60% de la altura del media y deja comparar sin scroll infinito. */
@media (max-width: 639px) {
  .card-img-wrap { aspect-ratio: 16 / 10; padding: 8px; }
  .card-img-wrap::before { inset: 8px; }
  .card-img-wrap img { padding: 8px; }
}
/* Badges + botón compartir fuera del cuerpo del producto en TODO el rango táctil
   2-up (<1024px). Antes esto estaba capado a 639px: en la banda 640–1023px los
   badges Oferta/Nuevo volvían a la esquina superior-izq y se solapaban con la
   laptop (centrada por object-fit:contain sobre la foto cuadrada del 2-up). Los
   bajamos a la franja inferior (letterbox de la imagen contenida) en una fila
   horizontal compacta. El padding interno del panel deja ese borde libre. */
/* Selectores con .cat-product-card (especificidad 0,2,0) para GANAR sobre las
   reglas base .card-badges/.card-share-btn (0,1,0) sin depender del orden de
   fuente: el bug era que la base venía DESPUÉS y revertía los badges al top.
   Aplica en TODO el rango táctil 2-up (<=1023px), incluido el móvil de 390px. */
@media (max-width: 1023px) {
  .cat-product-card .card-badges {
    top: auto;
    /* bottom:12px (antes 17px): a 17px la fila Oferta/Nuevo rozaba el borde inferior
       del teclado de la laptop sobre la foto 16/10 aplanada (móvil 2-up); bajarla 5px
       despeja el letterbox sin solaparse con el botón compartir (36px, bottom:10px),
       que sigue a su derecha en la misma franja. */
    bottom: 12px;
    left: 12px;
    /* Deja libre la esquina inferior-derecha para el botón compartir (36px) con
       holgura: el botón ocupa right:10px→46px; right:60px deja ~14px de aire
       entre la última badge y el botón para que respiren sin tocarse. */
    right: 60px;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 5px;
  }
  .cat-product-card .card-badges .pl-badge { font-size: .625rem; padding: 2px 7px; }
  /* Botón compartir: a la franja inferior-derecha y más compacto, despejando el
     cuerpo de la laptop. Sigue ≥36px de área táctil real. */
  .cat-product-card .card-share-btn {
    top: auto;
    bottom: 10px;
    right: 10px;
    width: 36px;
    height: 36px;
  }
  /* Precio viejo tachado en su PROPIA línea bajo el precio (nowrap), para que no
     se recorte contra el overflow:hidden de la card en la rejilla 2-up estrecha
     (≤639px incluido). Antes el fix solo cubría 640-1023px y fallaba a 390px. */
  .cat-product-card .card-old-price {
    display: block;
    margin-left: 0;
    margin-top: 4px;
  }
}

.card-img-wrap img {
  position: relative;
  z-index: 1;
  width: 100%; height: 100%;
  /* Respira dentro del panel interno: el producto no toca el borde del panel. */
  padding: 10px;
  object-fit: contain;
  transition: transform .5s cubic-bezier(0.2,0.8,0.2,1);
}
.cat-product-card:hover .card-img-wrap img { transform: scale(1.05); }

.card-badges { position: absolute; top: 12px; left: 12px; z-index: 10; display: flex; flex-direction: column; gap: 6px; }

/* A11y: contraste AA de los badges de la card SÓLO en tema claro. Los colores de
   token base (--ok/--warn/--err) sobre su *-soft claro caían <4.5:1 para texto
   pequeño (~.625rem): ok 3.15:1, warn 3.07:1, err 4.41:1. Oscurecemos el texto
   por variante hasta ≥4.5:1 sobre su mismo fondo soft. Alcance catálogo-scoped
   (no toca los tokens globales de plum.css, que afectan otras páginas) y limitado
   a claro con :not([data-theme="dark"]) — en oscuro el texto es claro sobre soft
   translúcido y ya cumple; oscurecerlo lo rompería. Ratios post-fix: ok 4.79:1,
   warn 4.84:1, err 5.91:1. */
:root:not([data-theme="dark"]) .cat-product-card .card-badges .pl-badge--ok   { color: #15803d; }
:root:not([data-theme="dark"]) .cat-product-card .card-badges .pl-badge--warn { color: #b45309; }
:root:not([data-theme="dark"]) .cat-product-card .card-badges .pl-badge--err  { color: #b91c1c; }

.card-info { padding: 20px; flex: 1; display: flex; flex-direction: column; }

.card-brand {
  font-size: .6875rem;
  font-weight: 800;
  color: var(--brand);
  text-transform: uppercase;
  letter-spacing: .08em;
  margin-bottom: 4px;
}

.card-model {
  font-size: .875rem;
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  margin-bottom: 16px;
  min-height: 2.4em;
}

/* min-height ≈ 3 filas de spec (fila ~15px a .6875rem/1.4 + gap 10px): productos
   con 1-2 specs (p.ej. una consola) ya no colapsan a una card corta junto a las
   laptops de 5 specs, manteniendo la rejilla pareja. flex:1 sigue absorbiendo el
   sobrante en cards con más specs. */
.card-specs { list-style: none; padding: 0; margin: 0 0 24px; flex: 1; display: flex; flex-direction: column; gap: 10px; min-height: 65px; }
.card-spec {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: .6875rem;
  color: var(--ink-soft);
}
.card-spec i { color: var(--brand); width: 16px; text-align: center; flex-shrink: 0; }
.card-spec-text {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
}

.card-footer {
  padding-top: 16px;
  border-top: 1px solid var(--border);
  margin-top: auto;
}

/* Price = dominant decision datum: large + heavy */
.card-price {
  font-size: 1.4rem;
  line-height: 1.1;
  font-weight: 800;
  letter-spacing: -.01em;
  color: var(--ink);
  margin-bottom: 12px;
  /* "RD$ 85,000" se partía en 2 líneas en una card y cabía en 1 en la vecina,
     rompiendo la consistencia del dato más importante en la rejilla 2-up. El
     precio nunca debe quebrarse. */
  white-space: nowrap;
}
.card-old-price {
  font-size: .75rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: line-through;
  margin-left: 8px;
}

/* Footer actions: detail (primary, purple) + compact WhatsApp (secondary).
   The whole card is the primary link to the product; WhatsApp is a secondary
   chat action — purple-outline by default, WhatsApp-green only on hover/icon. */
.card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.card-wa-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-shrink: 0;
  background: transparent;
  color: var(--brand);
  font-weight: 700;
  border-radius: var(--radius-pill);
  padding: 0 14px;
  min-height: 44px;
  font-size: .8125rem;
  border: 1.5px solid var(--brand);
  cursor: pointer;
  transition: background .2s, color .2s, border-color .2s;
}
.card-wa-btn i { color: #25D366; transition: color .2s; }
.card-wa-btn:hover { background: #25D366; color: #fff; border-color: #25D366; }
.card-wa-btn:hover i { color: #fff; }
.card-wa-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Cart drawer checkout keeps the solid-green primary treatment (single,
   intentional WhatsApp order action — not a repeated grid element). */
.cart-wa-btn {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: #25D366;
  color: #fff;
  font-weight: 700;
  border-radius: var(--radius);
  padding: 12px;
  min-height: 48px;
  font-size: .9375rem;
  border: none;
  cursor: pointer;
  transition: background .28s cubic-bezier(.2,.8,.2,1), box-shadow .28s, transform .28s;
  box-shadow: var(--shadow-sm);
}
.cart-wa-btn:hover:not(:disabled) { background: #1fb855; transform: translateY(-2px); box-shadow: 0 12px 28px rgba(37,211,102,.30); }
.cart-wa-btn:active:not(:disabled) { transform: translateY(0); }
.cart-wa-btn:disabled { opacity: .5; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) { .cart-wa-btn:hover:not(:disabled), .cart-wa-btn:active:not(:disabled) { transform: none; } }

.card-detail-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 1;
  min-height: 44px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  font-size: .8125rem;
  font-weight: 700;
  color: #fff;
  text-decoration: none;
  transition: background .2s;
}
.card-detail-link:hover { background: var(--brand-hover); color: #fff; }
.card-detail-link:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.card-share-btn {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 10;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(255,255,255,.9);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .2s, color .2s, transform .2s;
  color: var(--ink);
}
.card-share-btn:hover { background: var(--brand); color: #fff; transform: scale(1.1); }
.card-share-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* En dark, el rgba blanco .9 se leía como un parche claro sobre la rejilla
   oscura. Usamos la superficie token --surface-3 para que el botón pertenezca
   a la card en vez de flotar como mancha brillante. */
[data-theme="dark"] .card-share-btn { background: var(--surface-3); color: var(--ink); }

/* ── Product grid ── */
.product-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
/* Móvil (≥360px): 2 columnas compactas para densificar y permitir comparar
   dos equipos a la vez sin scroll interminable. El umbral anterior (420px)
   dejaba 1-col en pantallas de 390px (iPhone 12/13/14), produciendo una torre
   de ~16.7k px. Bajarlo a 360px materializa el 2-col en el móvil real. */
@media (min-width: 360px) { .product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } }
@media (min-width: 640px) { .product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; } }
@media (min-width: 1280px) { .product-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Pantallas anchas: al ir full-bleed el área del grid crece mucho; pasamos a
   4 columnas para conservar la densidad de card (~300-380px) en vez de estirar
   3 cards a lo ancho de un monitor grande. */
@media (min-width: 1600px) { .product-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ── Cart drawer ── */
.cart-overlay {
  position: fixed; inset: 0; z-index: 1010;
  background: rgba(0,0,0,.5);
  opacity: 0; pointer-events: none;
  transition: opacity .25s;
}
.cart-overlay.open { opacity: 1; pointer-events: auto; }

.cart-drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 100%; max-width: 384px; z-index: 1011;
  background: var(--surface-1);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  transform: translateX(100%);
  transition: transform .25s cubic-bezier(0.2,0.8,0.2,1);
}
.cart-drawer.open { transform: translateX(0); }

.cart-header {
  padding: 24px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.cart-body { flex: 1; overflow-y: auto; padding: 24px; }
.cart-body:empty::after { content: ''; }

.cart-item {
  display: flex;
  gap: 12px;
  padding: 12px;
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  border: 1px solid var(--border);
  margin-bottom: 12px;
}
.cart-item-img {
  width: 64px; height: 64px;
  border-radius: var(--radius);
  overflow: hidden; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
}
.cart-item-img img { width: 56px; height: 56px; object-fit: contain; }

.cart-footer {
  padding: 24px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

/* ── Empty state ── */
.cat-empty {
  padding: 80px 24px;
  text-align: center;
}
.cat-empty-icon {
  width: 80px; height: 80px;
  background: var(--brand-soft);
  border-radius: var(--radius-xl);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 24px;
  font-size: 1.5rem;
  color: var(--brand);
}

/* ── Pagination ── */
.cat-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* Reducido de 40px: junto con el padding-bottom menor del contenedor, cierra
     la banda vacía entre paginación y footer en desktop. */
  margin-top: 32px;
}
.cat-page-btn {
  width: 44px; height: 44px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  cursor: pointer;
  transition: background .2s, border-color .2s, color .2s;
}
.cat-page-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.cat-page-btn:hover { background: var(--surface-2); border-color: var(--brand); }
.cat-page-btn:disabled { opacity: .4; cursor: not-allowed; }

/* ── Active filter chips ── */
.active-filters {
  display: none;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
}
.active-filters.has-items { display: flex; }
/* Móvil: fila única scrolleable (en vez de envolver) para no empujar la rejilla
   hacia abajo. Mismo fade-mask que las barras de categorías. */
@media (max-width: 639px) {
  .active-filters.has-items {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding-bottom: 2px;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent 100%);
  }
  .active-filters::-webkit-scrollbar { display: none; }
  .active-filters > * { flex: 0 0 auto; }
}
.active-filters-label {
  font-size: .75rem;
  font-weight: 700;
  color: var(--ink-soft);
  margin-right: 2px;
}
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 6px 6px 12px;
  min-height: 32px;
  border-radius: var(--radius-pill);
  background: var(--brand-soft);
  color: var(--brand);
  border: 1px solid transparent;
  font-size: .75rem;
  font-weight: 700;
  line-height: 1;
  max-width: 220px;
}
.filter-chip > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.filter-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--brand);
  cursor: pointer;
  flex-shrink: 0;
  transition: background .2s, color .2s;
}
.filter-chip-x:hover { background: var(--brand); color: #fff; }
.filter-chip-x:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* Touch: el botón "quitar" del chip activo era 22px. Subimos chip y x a un
   área táctil ≥44px en ≤1023px sin agrandar el icono visual. */
@media (max-width: 1023px) {
  .filter-chip { min-height: 44px; padding: 8px 8px 8px 14px; }
  .filter-chip-x { width: 32px; height: 32px; }
}
.active-filters-clear {
  font-size: .75rem;
  font-weight: 700;
  color: var(--ink-soft);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: underline;
  padding: 4px;
}
.active-filters-clear:hover { color: var(--brand); }

/* Count badge on the filter buttons (mobile toolbar + FAB) */
.filter-count-badge {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 4px;
  border-radius: var(--radius-pill);
  background: #fff;
  color: var(--brand);
  font-size: .6875rem;
  font-weight: 800;
  line-height: 1;
}
.filter-count-badge.show { display: inline-flex; }

/* (FAB de filtros eliminado — el panel de filtros es ahora una bottom sheet con
   barra de acción propia; el botón "Filtros" del toolbar la abre.) */

/* ── Stagger cards ── */
.cat-product-card { opacity: 0; animation: pl-fadeInUp .4s cubic-bezier(0.2,0.8,0.2,1) forwards; }
.cat-product-card:nth-child(1) { animation-delay: 0ms; }
.cat-product-card:nth-child(2) { animation-delay: 40ms; }
.cat-product-card:nth-child(3) { animation-delay: 80ms; }
.cat-product-card:nth-child(4) { animation-delay: 120ms; }
.cat-product-card:nth-child(5) { animation-delay: 160ms; }
.cat-product-card:nth-child(6) { animation-delay: 200ms; }
.cat-product-card:nth-child(7) { animation-delay: 240ms; }
.cat-product-card:nth-child(8) { animation-delay: 280ms; }
.cat-product-card:nth-child(9) { animation-delay: 320ms; }
.cat-product-card:nth-child(10) { animation-delay: 340ms; }
.cat-product-card:nth-child(11) { animation-delay: 360ms; }
.cat-product-card:nth-child(12) { animation-delay: 380ms; }

@media (prefers-reduced-motion: reduce) {
  .cat-product-card { animation: none !important; opacity: 1; }
}

/* Móvil 2-up (≥360 y <640): compactar el interior del card para que dos
   columnas quepan sin estirar la altura. Mantiene jerarquía precio/CTA. */
@media (min-width: 360px) and (max-width: 639px) {
  .card-info { padding: 14px; }
  .card-model { margin-bottom: 12px; }
  .card-specs { gap: 7px; margin-bottom: 16px; }
  /* Precio en una sola línea consistente en columnas estrechas (≈160px en un
     viewport de 360px): clamp escala el tamaño para que el precio nowrap quepa
     sin quebrarse aun en el valor más largo (p.ej. "RD$ 199,000"), manteniendo
     el peso dominante. El precio viejo tachado baja para no empujar la línea. */
  .card-price { font-size: clamp(1rem, 4.4vw, 1.2rem); margin-bottom: 10px; }
  .card-old-price { font-size: .6875rem; margin-left: 6px; }
  .card-actions { flex-direction: column; align-items: stretch; gap: 8px; }
  .card-detail-link, .card-wa-btn { width: 100%; }
}

/* Tablet/phone-landscape 2-up (640–1023px): el precio actual es `nowrap` y el
   precio viejo tachado se anexaba inline (margin-left). Juntos excedían el ancho
   de contenido de la card (~330px) y el old-price se recortaba contra el
   overflow:hidden ("RD$ 130,6…"). El precio es el dato de decisión #1: nunca
   debe recortarse ni partirse. Bajamos el old-price a su propia línea (block)
   bajo el precio actual, así ambos caben completos sin competir por el ancho. */
@media (min-width: 640px) and (max-width: 1023px) {
  .card-old-price {
    display: block;
    margin-left: 0;
    margin-top: 4px;
  }
}

/* ── Loader ── */
.cat-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px 0;
  gap: 16px;
}

/* ── Notifications ── */
.notif-area {
  position: fixed; top: 96px; right: 24px; z-index: 60;
  display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
.notif-item {
  background: var(--surface-1);
  color: var(--ink);
  padding: 12px 16px;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 280px;
  pointer-events: auto;
  border: 1px solid var(--border);
  animation: pl-fadeInUp .3s ease forwards;
}

/* ── Mobile body lock ── */
body.filters-open {
  overflow: hidden;
  position: fixed;
  width: 100%;
  height: 100%;
  overscroll-behavior: none;
  touch-action: none;
}

/* ── Search input in sidebar ── */
.sidebar-search {
  position: relative;
  margin-bottom: 12px;
}
.sidebar-search i {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-soft);
  font-size: .875rem;
}
.sidebar-search input {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 10px 16px 10px 36px;
  font-size: .875rem;
  color: var(--ink);
}
.sidebar-search input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* ── Mobile filter close ── */
.sidebar-close-mobile {
  display: none;
}
@media (max-width: 1023px) {
  .sidebar-close-mobile { display: flex; }
}
