

:root {
  --bg-header:   #0F263E;
  --bg-sidebar:  #ffffff;
  --bg-body:     #F5F5F5;
  --bg-card:     #ffffff;
  --bg-table-h:  #fdf5f5;
  --bg-row-alt:  #fdfcfc;
  --bg-sub:      #faf6f6;

  --fg-header:   #f0e8e8;
  --fg-main:     #0F263E;
  --fg-muted:    #6b7194;
  --fg-dim:      #9da3bf;

  --accent:        #F30256;
  --accent-soft:   rgba(243, 2, 86, 0.10);
  --accent-orange: #f07a1a;
  --fg-ok:       #16a34a;
  --fg-warn:     #ca8a04;
  --fg-bad:      #dc2626;
  --fg-info:     #2563eb;

  --border:      #ede0e0;
  --border-soft: #f5edee;

  --font:      'Inter', 'Segoe UI', Arial, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
  --radius:    8px;
  --radius-sm: 5px;
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
  --shadow:    0 2px 8px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 16px rgba(0,0,0,0.10), 0 2px 6px rgba(0,0,0,0.06);
}

html {
  /* 🆕 Quita el destello azul del resaltado táctil por defecto del
     navegador móvil (Android sobre todo) al tocar cualquier elemento
     tocable — global, en el elemento raíz, para que no aparezca en
     ningún sitio de la app (se notó primero en la ficha, pero afecta
     por igual a cualquier tarjeta/botón/enlace en cualquier pantalla). */
  -webkit-tap-highlight-color: transparent;
  /* 🔒 Bug real encontrado con vídeo — segunda vuelta, medición precisa:
     comparando el % de píxeles rosa del footer fotograma a fotograma,
     se localizaron momentos EXACTOS donde cae a 0% (no un simple
     desplazamiento) — y en esos fotogramas se ven DOS FICHAS COMPLETAS
     lado a lado (la saliente y la entrante, cada una con su propia
     tarjeta de info). El contenido se vuelve genuinamente más ancho que
     la pantalla durante la transición. overscroll-behavior (intento
     anterior) solo evita el rebote/gesto nativo — NO evita un
     desbordamiento horizontal genuino si el contenido de verdad es más
     ancho que el viewport.
     🔒 Tercera vuelta: cambiado de hidden a clip — hidden fuerza (por
     especificación CSS) que overflow-y compute a "auto" si no lo
     tiene puesto explícitamente, lo que en OTROS contenedores (body,
     luego #cat-vista-ficha) ha roto el scroll vertical dos veces
     seguidas al convertirlos en su propio contenedor de scroll. clip
     evita el desbordamiento horizontal sin ese efecto secundario. */
  overscroll-behavior-y: none;
  overscroll-behavior-x: none;
  overflow-x: clip;
}
body {
  background: var(--bg-body);
  color: var(--fg-main);
  font-family: var(--font);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
  overscroll-behavior-x: none;
  /* 🔒 Confirmado DOS VECES con vídeo: overflow-x:hidden aquí en body
     rompe el scroll vertical de las fichas. La vez anterior lo achaqué
     al filtro CSS de "agotado" (ya arreglado aparte) y lo restauré
     pensando que ya no había riesgo — pero el scroll se ha vuelto a
     romper con él puesto, así que SÍ era body quien lo rompía después
     de todo. Trasladado a los contenedores específicos de la ficha
     (#cat-vista-ficha, .cat-ficha-scroll — ver más abajo), que es
     donde realmente ocurre el ensanchamiento durante el swipe, sin
     necesidad de tocar el overflow-y de body en absoluto. */
}

/* ════════════════════════════════════════════
   CABECERA
   ════════════════════════════════════════════ */
header {
  background: var(--bg-header);
  border-bottom: 2px solid var(--accent);
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 500;
}

.header-top {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  padding: 8px 20px 6px;
  position: relative;
  background: #F30256;
}

/* Todos los textos del header-top en blanco sobre rojo */
.header-top,
.header-top .header-brand h1,
.header-top .header-brand p,
.header-top #cache-label,
.header-top .usuario-zone,
.header-top #usuario-nombre,
.header-top .usuario-icono,
.header-top #btn-cambiar-usuario {
  color: #ffffff;
}

.header-top #usuario-nombre.sin-usuario { color: rgba(255,255,255,0.7); }

.header-top #btn-actualizar {
  background: rgba(255,255,255,0.18);
  color: #ffffff;
  border-color: rgba(255,255,255,0.35);
}

.header-top #btn-actualizar:hover {
  background: rgba(255,255,255,0.30);
  border-color: #fff;
}

.header-top .usuario-zone {
  background: rgba(255,255,255,0.15);
  border-color: rgba(255,255,255,0.30);
}

.header-brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  justify-content: center;
}

#header-escena-movil { display: none; }

.header-brand h1 {
  font-family: var(--font-mono);
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 2.5px;
  white-space: nowrap;
  text-transform: uppercase;
}

.header-brand p {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  color: var(--fg-dim);
  white-space: nowrap;
}

/* ════════════════════════════════════════════
   LAYOUT PRINCIPAL
   ════════════════════════════════════════════ */
main {
  flex: 1;
  display: flex;
  min-height: 0;
}

/* ── Contenido derecho ───────────────────────────────────────────── */
#content {
  flex: 1;
  padding: 22px 28px;
  overflow-y: auto;
  min-width: 0;
}

.panel { display: none; }
.panel.active { display: block; }

#producto-info-bar.visible { display: block; }

/* ════════════════════════════════════════════
   MODAL
   ════════════════════════════════════════════ */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.60);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9000;
  backdrop-filter: blur(2px);
}

.modal-box {
  background: var(--bg-card);
  border-radius: 12px;
  box-shadow: 0 16px 48px rgba(0,0,0,0.28), 0 4px 16px rgba(0,0,0,0.12);
  width: 90%;
  max-width: 860px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  background: var(--bg-header);
  border-bottom: 2px solid var(--accent);
  flex-shrink: 0;
}

.modal-header span {
  font-family: var(--font);
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--fg-header);
  letter-spacing: 0.4px;
}

/* ── Modal de confirmación de baja (Almacén móvil) — se abre arriba ────── */
.modal-overlay.modal-overlay-top {
  align-items: flex-start;
  padding-top: 70px;
}
.btn-modal-cancelar, .btn-modal-aceptar {
  flex: 1 1 0;
  padding: 11px 12px;
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-size: 0.85rem;
  cursor: pointer;
  border: none;
}
.btn-modal-cancelar {
  background: var(--bg-sub);
  color: var(--fg-mid);
  border: 1px solid var(--border);
}
.cat-limpiar-activo {
  background: #f9c74f !important;
  color: #3a2e00 !important;
  border-color: #f9c74f !important;
}
.btn-modal-aceptar {
  background: #c62828;
  color: #fff;
}

.modal-cerrar {
  background: transparent;
  border: none;
  color: var(--fg-dim);
  font-size: 1rem;
  cursor: pointer;
  padding: 3px 7px;
  border-radius: 5px;
  transition: background 0.15s, color 0.15s;
  line-height: 1;
}
.modal-cerrar:hover { background: var(--accent); color: #fff; }

.cat-modal-guia-header-botones { display: flex; align-items: center; gap: 4px; }
.cat-modal-guia-btn-copiar {
  background: transparent;
  border: none;
  color: var(--fg-dim);
  font-size: 0.95rem;
  cursor: pointer;
  padding: 3px 7px;
  border-radius: 5px;
  transition: background 0.15s, color 0.15s;
  line-height: 1;
}
.cat-modal-guia-btn-copiar:hover { background: var(--accent); color: #fff; }

#modal-historico .modal-box {
  width: 96%;
  max-width: 1200px;
}

#modal-duplicados .modal-box {
  background: var(--bg-card);
  border-radius: 12px;
  padding: 24px 28px;
  width: min(640px, 94vw);
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 16px 48px rgba(0,0,0,0.25);
}

#modal-duplicados .modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}

#modal-duplicados .modal-header h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--fg-bad);
}

/* ════════════════════════════════════════════════════════════════
   RESPONSIVE MÓVIL  ·  max-width: 768 px
   ════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .solo-movil      { display: block; }

  /* 🔒 Bug real encontrado con vídeo: durante el arrastre horizontal
     entre fichas, la cabecera y el footer "temblaban" — se desplazaban
     en sincronía con el gesto durante unos fotogramas, para volver solo
     al soltar. header usa position:sticky (obliga al navegador a
     recalcular su posición constantemente respecto al contenedor de
     scroll, justo lo que cambia en cada fotograma del arrastre) —
     cambiado aquí a fixed, mismo aspecto visual pero sin esa
     recalculación continua. Además, ambos (cabecera y footer, este
     último YA era fixed) se fuerzan a su propia capa de composición GPU
     aislada (transform:translateZ(0)) — evita que el repintado
     constante de la ficha durante el gesto los arrastre visualmente con
     ella, un efecto conocido en Android/Chrome cuando hay mucha
     animación simultánea cerca de elementos fixed/sticky. */
  header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    transform: translateZ(0);
    will-change: transform;
  }
  #cat-footer-bar {
    transform: translateZ(0);
    will-change: transform;
  }
  /* 🆕 Centrado exacto SOLO en móvil — aquí #cat-footer-reset sigue
     siendo un hijo real de #cat-footer-bar (en escritorio, app.js lo
     traslada a la barra de búsqueda; ver .cat-footer-boton sin position
     en la regla base, para no romper ese comportamiento ahí). Antes era
     un hijo flex normal (justify-content:space-between), así que el
     botón Grid apareciendo al lado (con más o menos ancho) cambiaba el
     reparto de espacio y lo desplazaba un poco — ahora es siempre el
     centro EXACTO del footer, sea lo que sea que aparezca a los lados. */
  .cat-footer-boton {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }

  /* 🆕 Icono de la cámara del escáner de barcode — movido de junto al
     textbox de búsqueda a la cabecera, en el sitio donde antes estaba
     btn-tienda (el logo de cambio Almacén/Catálogo, ya quitado). Mismo
     posicionamiento absoluto e icono circular que btn-tienda tenía
     (ver #btn-tienda.btn-tienda-movil, ahora huérfano) — pero fijado
     aquí directamente, sin depender de ninguna clase añadida por JS
     (app.js, que no está disponible para confirmar ese mecanismo). */
  #cat-btn-escaner {
    position: absolute;
    top: 50%;
    right: 42px;
    transform: translateY(-50%);
    z-index: 2;
    margin: 0 !important;
    padding: 4px !important;
    line-height: 1;
    border-radius: 50%;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    overflow: hidden;
    font-size: 0.95rem;
  }

  /* 🆕 Marco fino del color de la cabecera en la barra "Estás en" —
     antes tenía un borde gris apagado en móvil (el rosa solo se
     aplicaba en escritorio). El del textbox de búsqueda va en un
     bloque posterior (ver más abajo en el fichero) — aquí perdería
     contra la regla base .cat-search-row .cat-input-buscar, que tiene
     la misma especificidad y aparece después. */
  .cat-search-row .cat-estas-en {
    border-color: #F30256;
    /* 🆕 Totalmente pegada al textbox de arriba (sin margen) — antes
       tenía 5px de la regla base. Tipo flex con ancho ajustado a su
       propio contenido (display:inline-flex + width:fit-content), no
       ancho completo como antes — así nunca depende de "dejarle sitio"
       a la cámara en la misma fila; simplemente ocupa lo que necesite y
       ya está. Relleno/letra un pelín más ajustados, por si aun así el
       contenido es largo. */
    margin-top: 0;
    padding: 4px 8px;
    font-size: 0.74rem;
    display: inline-flex;
    align-items: center;
    width: fit-content;
    max-width: 100%;
  }

  /* 🆕 Barra de flechas inferior (Atrás/Reset/Adelante) SIEMPRE visible
     en móvil — antes parpadeaba (desaparecía y volvía a aparecer) al
     cambiar de ficha por swipe o scroll. Su mostrar/ocultar dinámico
     vive en app.js (fuera de este fichero); esta regla la fuerza a
     quedarse fija con independencia de lo que haga ese JS. */
  #cat-footer-bar {
    display: flex !important;
  }

  /* Animación de entrada para todos los modales del catálogo — se
     aplica por "animation" (no "transition") porque esta SÍ dispara
     automáticamente con el simple cambio de display:none a flex, sin
     necesitar tocar el JS de apertura de cada modal individualmente.
     Fondo con fundido rápido; caja con deslizamiento suave desde abajo
     + fundido, encajando con que los modales ya se anclan arriba con
     cabecera oscura (patrón "hoja que sube desde abajo", habitual en
     apps móviles). */
  #cat-modal-filtros,
  #cat-modal-guia,
  #cat-modal-favoritos,
  #cat-modal-confirmar,
  #cat-modal-proveedor,
  #cat-modal-telegram {
    animation: catModalFondoEntra 0.2s ease !important;
  }
  #cat-modal-filtros .modal-box,
  #cat-modal-filtros .cat-modal-filtros-box,
  #cat-modal-guia .modal-box,
  #cat-modal-favoritos .modal-box,
  #cat-modal-confirmar .modal-box,
  #cat-modal-confirmar .cat-modal-confirmar-box,
  #cat-modal-proveedor .modal-box,
  #cat-modal-telegram .modal-box {
    animation: catModalCajaEntra 0.5s ease-out !important;
  }
  /* Animación de salida — se marca con esta clase justo ANTES de poner
     display:none (ver _cerrarModalCatalogo en catalogo.js), con un
     pequeño retraso para que la animación llegue a verse antes de que
     el modal desaparezca del todo. */
  #cat-modal-filtros.cat-modal-cerrando,
  #cat-modal-guia.cat-modal-cerrando,
  #cat-modal-favoritos.cat-modal-cerrando,
  #cat-modal-confirmar.cat-modal-cerrando,
  #cat-modal-proveedor.cat-modal-cerrando,
  #cat-modal-telegram.cat-modal-cerrando {
    animation: catModalFondoSale 0.2s ease !important;
  }
  #cat-modal-filtros.cat-modal-cerrando .modal-box,
  #cat-modal-guia.cat-modal-cerrando .modal-box,
  #cat-modal-favoritos.cat-modal-cerrando .modal-box,
  #cat-modal-confirmar.cat-modal-cerrando .modal-box,
  #cat-modal-proveedor.cat-modal-cerrando .modal-box,
  #cat-modal-telegram.cat-modal-cerrando .modal-box {
    animation: catModalCajaSale 0.2s ease !important;
  }

  /* Botón GRID (vista densa) — exclusivo de escritorio, sin función ni
     sentido en móvil (que ya usa su propia paginación de 6 en 6). */
  #cat-btn-grid { display: none !important; }
  #cat-btn-fullscreen { display: none !important; }

  /* Módulo de Propuestas — exclusivo de escritorio, sin superficie
     alguna en móvil. Blindaje CSS además del guard JS matchMedia. */
  #cat-btn-propuestas,
  #cat-ficha-btn-propuesta,
  #cat-modal-propuestas,
  #cat-modal-propuestas-guardar,
  #cat-modal-propuestas-preview,
  #cat-modal-cliente-ficha { display: none !important; }

  /* Tallas de la ficha, 2 por línea en móvil — con los textos ya
     abreviados (Kimo/FY/FN, sin "Comprometidos:" completo, sin
     "años"/"meses") sobra espacio de sobra para el doble por fila que
     antes, cuando cada badge ocupaba el ancho completo. Se aplica tanto
     a un producto individual (.cat-ficha-tallas) como a cada género
     dentro de un Grupo/Pareja (.cat-bundle-grupo). */
  .cat-bundle-grupo-titulo { grid-column: 1 / -1; }

  /* Imagen de la ficha fija arriba — el resto del contenido (título,
     precio, tallas, relacionados...) se desliza por debajo de ella al
     hacer scroll, en vez de desplazarse todo junto como una única
     página larga.
     🔒 position:sticky no servía aquí: solo se mantiene fija mientras el
     scroll está DENTRO de su propio contenedor padre (.cat-ficha-
     izquierda, corto — imagen+título+precio nada más). En cuanto el
     scroll entraba en la zona de tallas (ya fuera de ese contenedor), el
     sticky se soltaba sin remedio. position:fixed no tiene esa
     limitación — se calcula por JS (measureImagenFichaFija en
     catalogo.js) y se mantiene fija durante TODO el scroll de la
     página. El placeholder (mismo alto que la imagen) reserva su hueco
     para que el resto del contenido no arranque tapado debajo. */
  .cat-ficha-izquierda { display: block; }

  /* Almacén en móvil — mismo tema oscuro que el Catálogo, para que no
     haya diferencia visual entre los dos módulos. Antes usaba el tema
     claro por defecto de toda la app (fondo blanco) porque nunca se le
     había dado uno propio, a diferencia de #panel-catalogo. */
  #panel-almacen {
    --bg-body:     #000;
    --bg-card:     #1a1a1a;
    --bg-sub:      #161616;
    --bg-row-alt:  #1f1f1f;
    --fg-main:     #f5f5f5;
    --fg-muted:    #b8bcc8;
    --fg-dim:      #8a8e9c;
    --border:      rgba(255,255,255,0.16);
    --border-soft: rgba(255,255,255,0.11);
    background: #000;
  }

  /* Modales del Almacén — mismo tema oscuro que el resto del módulo en
     móvil (ver #panel-almacen justo arriba). Estos modales NO son
     descendientes de #panel-almacen en el DOM: los dinámicos (Alta,
     Buscar por ubicación, Duplicados/Reservada) se crean sobre
     document.body la primera vez que se abren, y los estáticos
     (Confirmar baja, Histórico) viven en index.html junto al resto de
     modales de toda la app, no dentro del panel — así que no heredan las
     variables de #panel-almacen por herencia normal y hacía falta
     redefinirlas aquí explícitamente (mismo patrón que ya usa el
     Catálogo para sus propios modales, ej. #cat-modal-propuestas). */
  #modal-alta,
  #modal-buscar-ubicacion,
  #modal-duplicados,
  #modal-confirmar-baja,
  #modal-historico {
    --bg-card:     #1a1a1a;
    --bg-sub:      #161616;
    --bg-row-alt:  #1f1f1f;
    --fg-main:     #f5f5f5;
    --fg-mid:      #f5f5f5;
    --fg-muted:    #b8bcc8;
    --fg-dim:      #8a8e9c;
    --border:      rgba(255,255,255,0.16);
    --border-soft: rgba(255,255,255,0.11);
    color: var(--fg-main);
  }
  /* .zona-pill fija su fondo en #fff a mano (no usa variable) — sin este
     ajuste, dentro de estos dos modales oscuros se vería como una pastilla
     blanca suelta sobre fondo negro en vez de fundirse con el tema. */
  #modal-alta .zona-pill,
  #modal-buscar-ubicacion .zona-pill {
    background: var(--bg-sub);
    color: var(--fg-main);
  }

  /* Cabecera de estos 5 modales con el mismo rosa que usa el Catálogo en
     las suyas (#F30256, ej. #cat-modal-telegram .modal-header) en vez del
     azul marino genérico (--bg-header) del resto de la app. */
  #modal-alta .modal-header,
  #modal-buscar-ubicacion .modal-header,
  #modal-duplicados .modal-header,
  #modal-historico .modal-header {
    background: #F30256;
    border-bottom: 2px solid #fff;
  }
  #modal-alta .modal-header span,
  #modal-buscar-ubicacion .modal-header span,
  #modal-historico .modal-header span,
  #modal-duplicados .modal-header h3 {
    color: #fff;
  }

  /* Todos los botones de estos modales con el texto en blanco. Incluye
     .modal-close-btn (la "✕"), que no tenía NINGUNA regla base en todo
     el CSS — solo existía #modal-duplicados .modal-close-btn — así que en
     Alta/Buscar por ubicación salía con el estilo por defecto del
     navegador (fondo gris claro, texto negro) encima de la cabecera
     rosa. Se cubren también los estados hover/disabled para que no
     oscurezcan el texto al interactuar. */
  #modal-alta button,
  #modal-buscar-ubicacion button,
  #modal-duplicados button,
  #modal-confirmar-baja button,
  #modal-historico button {
    color: #fff;
  }
  #modal-alta .modal-close-btn,
  #modal-buscar-ubicacion .modal-close-btn {
    background: rgba(255,255,255,0.12);
  }
  #modal-alta .modal-close-btn:hover,
  #modal-buscar-ubicacion .modal-close-btn:hover,
  #modal-duplicados .modal-close-btn:hover {
    background: rgba(255,255,255,0.25);
  }
  #modal-alta .btn-secondary:hover:not(:disabled),
  #modal-buscar-ubicacion .btn-secondary:hover:not(:disabled),
  #modal-duplicados .btn-secondary:hover:not(:disabled) {
    color: #fff;
  }
  #modal-alta button:disabled,
  #modal-buscar-ubicacion button:disabled,
  #modal-duplicados button:disabled,
  #modal-confirmar-baja button:disabled,
  #modal-historico button:disabled {
    color: rgba(255,255,255,0.5);
  }

  /* ── Hint barcode ───────────────────────────────────────────── */
  .barcode-scan-hint {
    display: block;
    font-size: 0.78rem;
    color: var(--fg-muted);
    background: color-mix(in srgb, var(--fg-warn) 9%, transparent);
    border: 1px solid color-mix(in srgb, var(--fg-warn) 24%, transparent);
    border-radius: var(--radius-sm);
    padding: 7px 12px;
    margin-top: 2px;
    line-height: 1.5;
  }

  /* ── Header ─────────────────────────────────────────────────── */
  /* Cabecera global (logo) siempre visible al hacer scroll — SOLO móvil.
     .header-tabs ya está oculta en móvil (ver más abajo), así que lo único
     que se fija es .header-top (logo + usuario + admin). body recibe
     padding-top para compensar el hueco que deja de ocupar en el flujo
     normal al pasar a position:fixed. */
  header {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 500;
  }
  body {
    padding-top: var(--header-alto-movil, 44px);
  }

  .header-top {
    padding: 0 8px;
    flex-wrap: wrap;
    gap: 0;
    min-height: 33px;
  }

  /* Usuario y Admin anclados a las esquinas por posición absoluta — así no
     dependen de si el resto del contenido cabe en una sola línea (con flex
     + wrap, el botón Admin podía acabar "cayendo" a una segunda línea). */
  .header-top .usuario-zone {
    position: absolute;
    top: 50%;
    left: 8px;
    transform: translateY(-50%);
    z-index: 2;
    padding: 2px 8px !important;
  }
  #btn-admin.btn-admin-movil {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    z-index: 2;
  }

  /* Grupo lupa+Filtros — misma familia de anclaje absoluto que usuario/
     tienda/admin, arriba. "left" se calcula por JS (ver app.js) según el
     ancho REAL del usuario en cada momento (el nombre puede variar de
     longitud) — con position:absolute no se puede expresar en CSS puro
     "justo después de este otro elemento de ancho variable". */
  #cat-header-controles {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
  }

  .header-brand { display: none !important; }

  /* ── Escenas Halloween — cabecera sin título en móvil ──────────── */
  #header-escena-movil {
    display: block;
    position: absolute;
    inset: 0;
    overflow-x: hidden;
    overflow-y: visible;
    pointer-events: none;
  }

  /* Recorrido de un lado a otro de toda la cabecera, con ida y vuelta (sin
     acotar por el usuario/engranaje Admin — se pueden solapar). Al llegar
     al extremo se VOLTEA (scaleX) para "darse la vuelta" en vez de seguir
     de espaldas. El volteo se hace en una ventana mínima (instantáneo, no
     interpolado) para evitar el aplastamiento visual que dio problemas con
     el zombi. */
  .he-viajera {
    position: absolute;
    top: 38%;
  }
  /* Los GIFs son más altos (40-46px) que los emoji — con el mismo ancla
     "top:38%" quedaban demasiado bajos, casi saliéndose por debajo de la
     cabecera. Se suben para que queden centrados igual que el resto. */
  .he-viajera.he-viajera-gif { top: -6px; }

  /* Algunos GIFs vienen "de fábrica" mirando a la izquierda en vez de a la
     derecha — el sistema de recorrido asume que todos miran a la derecha
     por defecto (y los voltea al llegar al otro extremo). Para los que no
     cumplen esa asunción, se voltea la imagen en sí (no el contenedor que
     controla el sentido del recorrido), corrigiendo su orientación de
     partida sin tocar la lógica de ida-y-vuelta. */
  .he-flip-fix { transform: scaleX(-1); }
  .he-viajera.der { animation-name: he-viaje-der; }
  .he-viajera.izq { animation-name: he-viaje-izq; }
  .he-viajera {
    animation-duration: 10s;
    animation-timing-function: ease-in-out;
    animation-iteration-count: 1;
  }
  @keyframes he-viaje-der {
    0%     { left: -14%; transform: scaleX(1); }
    49.7%  { left: 92%;  transform: scaleX(1); }
    50%    { left: 92%;  transform: scaleX(-1); }
    99.7%  { left: -14%; transform: scaleX(-1); }
    100%   { left: -14%; transform: scaleX(1); }
  }
  @keyframes he-viaje-izq {
    0%     { left: 92%;  transform: scaleX(-1); }
    49.7%  { left: -14%; transform: scaleX(-1); }
    50%    { left: -14%; transform: scaleX(1); }
    99.7%  { left: 92%;  transform: scaleX(1); }
    100%   { left: 92%;  transform: scaleX(-1); }
  }

  /* GIFs animados (ya traen su propia animación de fotogramas incorporada;
     aquí solo se les aplica el recorrido de la cabecera vía .he-viajera) */
  .he-gif { display: block; width: auto; }
  .he-payaso1     { height: 44px; }
  .he-payaso2     { height: 44px; }
  .he-payaso3     { height: 44px; }
  .he-frankenstein{ height: 44px; }
  .he-bruja-gif   { height: 40px; }
  .he-momia       { height: 42px; }
  .he-esqueleto   { height: 46px; }
  .he-astronauta  { height: 40px; }
  .he-enano       { height: 46px; }
  .he-fantasma-gif{ height: 42px; }
  .he-gnomo       { height: 44px; }
  .he-ladron      { height: 40px; }
  .he-payaso4     { height: 44px; }
  .he-policia     { height: 40px; }
  .he-santa       { height: 44px; }
  .he-vikingo     { height: 46px; }
  .he-acrobata    { height: 46px; }
  .he-aguila      { height: 38px; }
  .he-alien2      { height: 42px; }
  .he-andando1    { height: 44px; }
  .he-andando2    { height: 42px; }
  .he-andando3    { height: 42px; }
  .he-angel       { height: 42px; }
  .he-bebe        { height: 38px; }
  .he-bebe-bailando { height: 44px; }
  .he-boxeo       { height: 42px; }
  .he-cavernicola { height: 42px; }
  .he-conejo      { height: 44px; }
  .he-cupido1     { height: 32px; }
  .he-cupido2     { height: 36px; }
  .he-cupido3     { height: 38px; }
  .he-halloween-gif { height: 42px; }
  .he-ladron2     { height: 30px; }
  .he-misterioso  { height: 42px; }
  .he-murcielago3 { height: 30px; }
  .he-payaso5     { height: 36px; }
  .he-payaso6     { height: 46px; }
  .he-vaquero     { height: 44px; }

  /* Chistera saludando — se dispara en cada búsqueda (ver
     window.mostrarSombreroBusqueda en index.html), estática en el centro */
  .he-sombrero-wrap { position: absolute; top: 50%; right: 74px; transform: translateY(-50%); }
  .he-sombrero { font-size: 1.9rem; display: inline-block; animation: he-saludo 2.4s ease-in-out 1; transform-origin: bottom left; }
  @keyframes he-saludo {
    0%, 12%, 100% { transform: rotate(0deg) translateY(0); }
    32%           { transform: rotate(-38deg) translateY(-4px); }
    55%           { transform: rotate(-38deg) translateY(-4px); }
    78%           { transform: rotate(0deg) translateY(0); }
  }

  #cache-label         { display: none; }
  #btn-no-leidos-badge { font-size: 0.72rem; }

  #btn-actualizar {
    padding: 4px 8px;
    font-size: 0.66rem;
  }

  .cache-zone { gap: 5px; }

  /* ── Pestañas — scroll horizontal ───────────────────────────── */
  .header-tabs {
    overflow-x: auto;
    overflow-y: hidden;
    flex-wrap: wrap;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .header-tabs::-webkit-scrollbar { display: none; }

  .tab-btn {
    padding: 8px 13px 7px;
    font-size: 0.68rem;
    white-space: nowrap;
    flex-shrink: 0;
  }

  /* Módulos no disponibles en móvil: solo Almacén queda operativo.
     Las bajas de FNX/HW/Tienda se hacen desde Almacén — no se dan altas en móvil. */
  .tab-btn[data-tab="consultas"],
  .tab-btn[data-tab="fnx"],
  .tab-btn[data-tab="hw"],
  .tab-btn[data-tab="tienda"],
  .tab-btn[data-tab="listados"],
  .tab-btn[data-tab="bajas_masivas"] { display: none !important; }

  /* Con un único módulo disponible no hace falta barra de pestañas —
     se libera espacio en la zona superior (el botón Admin se traslada
     junto al usuario, ver .btn-admin-movil) */
  .header-tabs { display: none !important; }

  /* Botón Admin trasladado a la cabecera superior — solo icono (ruedecita) */
  .btn-admin-label { display: none; }
  #btn-catalogo-label { display: inline; } /* Catálogo no tiene icono, necesita su texto */
  #btn-admin.btn-admin-movil {
    margin: 0 6px 0 0 !important;
    padding: 4px 8px !important;
    font-size: 0.9rem;
    line-height: 1;
    border-radius: 50%;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }

  /* ── Sidebar — oculto ────────────────────────────────────────── */
  #sidebar { display: none !important; }

  /* ── Layout ──────────────────────────────────────────────────── */
  main { flex-direction: column; }

  #content {
    padding: 10px 10px 24px;
    width: 100%;
  }
  #content:has(#panel-catalogo.active) {
    overflow-y: visible;
  }

  /* ── Cache progress ──────────────────────────────────────────── */
  #cache-progress-bar { display: none !important; }

  /* 🆕 Aviso de "No leídos" (barcodes pendientes) — pedido
     explícitamente, oculto en móvil. */
  #btn-no-leidos-badge { display: none !important; }

  /* ── Botones genéricos ───────────────────────────────────────── */
  .btn-primary {
    padding: 11px 18px;
    font-size: 0.88rem;
  }

  /* ── Búsqueda — row completo ─────────────────────────────────── */
  .search-row {
    flex-wrap: wrap;
    gap: 8px;
  }

  /* Todos los search-input al 100% */
  .search-input {
    width: 100% !important;
    flex: none !important;
    min-width: unset !important;
    font-size: 1rem;
    padding: 10px 14px;
  }

  /* ── Panel usuario ───────────────────────────────────────────── */
  #panel-usuario {
    right: 10px;
    left: 10px;
    top: 54px;
  }

  /* ── Miniatura móvil ─────────────────────────────────────────── */
  .mob-thumbnail {
    display: block;
    float: right;
    width: 56px !important;
    height: 56px !important;
    max-width: 56px !important;
    max-height: 56px !important;
    object-fit: contain;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    margin: 0 0 6px 8px;
    flex-shrink: 0;
  }

  /* ── Consultas + Almacén: tabla → tarjetas ───────────────────── */
  #resultado .tabla-variantes,
  #resultado-almacen .tabla-variantes {
    border: none;
    box-shadow: none;
    background: transparent;
    border-radius: 0;
  }

  #resultado .tabla-variantes thead,
  #resultado-almacen .tabla-variantes thead {
    display: none;
  }

  #resultado .tabla-variantes tbody tr.fila-variante,
  #resultado-almacen .tabla-variantes tbody tr.fila-variante {
    display: block;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 12px;
    margin-bottom: 8px;
    box-shadow: var(--shadow-sm);
  }

  #resultado .tabla-variantes tbody tr.fila-variante td,
  #resultado-almacen .tabla-variantes tbody tr.fila-variante td {
    display: block !important;
    padding: 2px 0 !important;
    border: none;
  }

  /* Talla + SKU — línea principal */
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(1),
  #resultado-almacen .tabla-variantes tbody tr.fila-variante td:nth-child(1) {
    font-weight: 700;
    font-size: 0.92rem;
    margin-bottom: 5px;
    flex-wrap: wrap !important;
  }

  /* Consultas: Ubicación, Stock, Comprometidos y Caja Tienda en línea */
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(2),
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(3),
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(4),
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(5) {
    display: inline-block !important;
    font-size: 0.82rem;
    padding: 1px 10px 1px 0 !important;
    color: var(--fg-mid);
  }

  /* Resaltar valor de ubicación en consultas */
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(2):not(:empty)::before { content: "📍 "; }
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(3):not(:empty)::before { content: "Stock: "; font-size: 0.75rem; color: var(--fg-muted); }
  #resultado .tabla-variantes tbody tr.fila-variante td:nth-child(4):not(:empty)::before { content: "Comp: "; font-size: 0.75rem; color: var(--fg-muted); }

  /* Almacén: Stock y Caja Tienda en línea */
  #resultado-almacen .tabla-variantes tbody tr.fila-variante td:nth-child(2),
  #resultado-almacen .tabla-variantes tbody tr.fila-variante td:nth-child(3) {
    display: inline-block !important;
    font-size: 0.82rem;
    padding: 1px 10px 1px 0 !important;
    color: var(--fg-mid);
  }
  #resultado-almacen .tabla-variantes tbody tr.fila-variante td:nth-child(2):not(:empty)::before { content: "Stock: "; font-size: 0.925rem; color: var(--fg-muted); }

  /* Almacén: talla + SKU comparten una línea propia (antes el SKU iba en
     su propia línea aparte) */
  .alm-talla-sku-linea {
    display: flex;
    align-items: baseline;
    width: 100%;
    gap: 8px;
  }
  #resultado-almacen .alm-talla-label {
    color: #fff;
    font-size: 0.95rem;
  }
  #resultado-almacen .talla-sku {
    margin: 0;
    color: #fff;
    font-weight: 400;
    font-size: 0.95rem;
  }

  /* Botón "Baja" — dentro de la línea de ubicación, entre el valor y el
     botón Histórico (antes: Ubicación → Histórico; ahora: Ubicación → Baja
     → Histórico). Mismo tamaño que Histórico para que ambos casen bien. */
  .ubic-inline-wrap .btn-baja-ubic {
    display: inline-block !important;
    flex-shrink: 0;
    padding: 3px 10px;
    font-size: 0.72rem;
  }

  /* Almacén: Histórico — mismo tamaño que Baja Ubicación */
  #resultado-almacen .btn-historico {
    padding: 3px 10px;
    font-size: 0.72rem;
    margin-right: 0;
  }

  /* Almacén: ubicación + Histórico en la misma línea (sin etiqueta "Ubicación",
     Histórico se añade justo a continuación del valor) */
  #resultado-almacen .ubic-inline-sep  { display: none !important; }
  #resultado-almacen .ubic-inline-wrap {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 6px;
    width: 100%;
    margin-top: 4px;
    font-size: 0.9rem;
  }
  /* El valor de ubicación nunca se parte en dos líneas (ej. "FUNI-31c").
     Fuente reducida respecto al valor de escritorio para que quepa junto
     al botón Histórico sin recortarse ni requerir scroll oculto. */
  #resultado-almacen .ubic-inline-wrap .ubic-valor,
  #resultado-almacen .ubic-inline-wrap .ubic-vacio {
    font-size: 1rem !important;
    white-space: nowrap !important;
    flex-shrink: 0;
  }
  #resultado-almacen .ubic-inline-wrap .btn-historico {
    flex-shrink: 0;
  }

  /* Filas de almacenes secundarios — visibles en móvil (solo Consultas; en
     Almacén las sucursales van dentro del bloque de Stock, ver .alm-sucursales-mov) */
  #resultado .tabla-variantes tbody tr.fila-almacen-secundario {
    display: block;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 6px 12px;
    margin-bottom: 4px;
    font-size: 0.82rem;
    color: var(--fg-mid);
  }
  #resultado .tabla-variantes tbody tr.fila-almacen-secundario td {
    display: inline-block !important;
    padding: 1px 8px 1px 0 !important;
    border: none;
  }

  /* Comprometidos en Almacén — línea propia debajo del stock disponible, mismo bloque */
  .alm-comprometidos-mov {
    display: block;
    color: var(--fg-bad);
    font-weight: 700;
    font-size: 0.925rem;
    margin-top: 2px;
  }

  /* Stock de sucursales en Almacén — línea(s) debajo del stock, mismo bloque */
  .alm-sucursales-mov {
    display: block;
    font-size: 0.925rem;
    color: var(--fg-warn);
    font-weight: 600;
    margin-top: 2px;
  }

  /* Grupos/parejas — desglose de disfraces que lo componen (metacampo bundle_option).
     Cada producto componente muestra TODAS sus variantes con el mismo formato
     que una búsqueda normal (misma clase .tabla-variantes, reutilizada). */
  .alm-bundle-info {
    width: 100%;
    margin-top: 6px;
    padding: 10px;
    background: color-mix(in srgb, var(--accent) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
    border-radius: var(--radius-sm);
  }
  .alm-bundle-producto {
    margin-bottom: 14px;
  }
  .alm-bundle-producto:last-child { margin-bottom: 0; }
  .alm-bundle-producto-titulo {
    font-size: 0.925rem;
    font-weight: 700;
    color: var(--fg-main);
    margin-bottom: 6px;
    padding-bottom: 4px;
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
  }
  .alm-bundle-tabla { width: 100%; }
  .alm-bundle-tabla tbody tr.fila-variante {
    margin-bottom: 6px !important;
  }
  .alm-bundle-tabla tbody tr.fila-variante:last-child { margin-bottom: 0 !important; }

  /* Columna del bloque "Contiene" — ancho completo, no la columna Talla (50%) */
  #resultado-almacen .tabla-variantes tbody tr.fila-variante td.alm-td-bundle {
    width: 100% !important;
  }

  /* Talla — ahora con el tamaño que antes tenía el Stock.
     !important necesario: la regla de td:nth-child(1) podría fijar otro tamaño */
  #resultado-almacen .alm-talla-grande {
    font-size: 1.175rem !important;
    color: #fff;
  }
  /* Stock disponible (DisfracesJarana HQ) — ahora con el tamaño que antes
     tenía la talla (0.84rem, tamaño base de tabla).
     !important necesario: la regla de td:nth-child(2) (más específica) fijaba otro tamaño */
  #resultado-almacen .alm-td-stock {
    font-size: 0.84rem !important;
  }

  /* ── CONSULTAS ───────────────────────────────────────────────── */
  #panel-consultas .search-input {
    width: 100% !important;
    flex: none !important;
  }

  /* ── ALMACÉN — solo consulta + baja de ubicación ─────────────── */
  #alm-libres-row            { display: none !important; }
  #btn-alm-ver-movimientos   { display: none !important; } /* historial de movimientos oculto en móvil */
  .fila-zona-selector { display: none !important; }
  .btn-baja           { display: none !important; } /* oculta también "Baja Tienda" */
  .btn-baja2alm       { display: none !important; }
  .btn-abrir-listado  { display: none !important; }

  /* Buscador de Almacén: input y botón en la misma línea (no apilados) */
  #panel-almacen .search-row {
    flex-wrap: nowrap;
  }
  #input-almacen {
    width: auto !important;
    flex: 1 1 auto !important;
    min-width: 0 !important;
    font-size: 0.75rem !important;
    padding: 8px 11px !important;
  }
  #btn-buscar-alm {
    flex-shrink: 0;
    padding: 8px 13px !important;
    font-size: 0.66rem !important;
  }

  /* Miniatura de producto en Almacén: +25% sobre el tamaño general móvil (120px → 150px) */
  #producto-info-bar-alm .mob-thumbnail {
    width: 150px !important;
    height: 150px !important;
    max-width: 150px !important;
    max-height: 150px !important;
    margin: 0 0 8px 0;
    cursor: pointer;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }

  /* Miniatura ampliada — al pulsar sobre ella */
  #producto-info-bar-alm .mob-thumbnail.mob-thumbnail-expandido {
    width: 100% !important;
    height: auto !important;
    max-width: 320px !important;
    max-height: none !important;
    margin: 0 auto 8px !important;
  }

  /* Zoom x2 tipo lupa al mantener pulsada la miniatura 1s — el punto de
     ampliación (transform-origin) lo va fijando JS según dónde está el dedo,
     centrado por defecto al activarse. Se amplía en el sitio donde está la
     imagen (sin sacarla a posición fija centrada en pantalla).
     `touch-action:none` en la imagen (ver almacen.js) es lo que evita que
     mover el dedo dispare el scroll de la página de fondo — sin eso, el
     navegador interpretaba el arrastre como gesto de scroll aunque fuera
     sobre la propia imagen, que es lo que fallaba en el primer intento. */
  #producto-info-bar-alm .mob-thumbnail.mob-thumbnail-zoom {
    transform: scale(2);
    position: relative;
    top: 58px;
    z-index: 20;
    transition: transform 0.12s ease-out, top 0.12s ease-out;
    touch-action: none;
  }

  /* Descripción corta mostrada bajo la miniatura ampliada */
  .alm-desc-expand {
    font-size: 0.8rem;
    line-height: 1.4;
    color: #fff;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    margin: 0 0 10px 0;
    white-space: pre-wrap;
  }

  /* Texto de nombre y SKU en Almacén — fuente 2pt más pequeña, en blanco */
  #producto-info-nombre-alm { font-size: 0.9rem; color: #fff; }
  #producto-info-sku-alm    { font-size: 0.875rem; color: #fff; }
  #producto-info-precio-alm {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--fg-ok);
    margin-top: 2px;
  }
  .alm-precio-tachado {
    color: var(--fg-muted);
    text-decoration: line-through;
    font-weight: 400;
    margin-left: 4px;
  }

  /* Baja Tienda sí visible en móvil — comparte clase .btn-baja con estilos
     base; la clase específica revierte el ocultamiento solo para este botón
     (Baja Ubicación tiene su propia regla más arriba, con flex nowrap) */
  #resultado-almacen .btn-baja-tienda {
    display: inline-block !important;
    margin-top: 6px;
  }

  /* Scroll horizontal para tablas de variantes */
  #resultado,
  #resultado-almacen { overflow-x: auto; }

  /* ── FNX / HW / TIENDA ───────────────────────────────────────── */
  .fnx-seccion { padding: 14px 12px; }

  .fnx-alta-row {
    flex-wrap: wrap;
    gap: 8px;
  }

  /* Input de escaneo: línea completa */
  .fnx-scan-input {
    width: 100% !important;
    flex: 1 1 100% !important;
    min-width: unset !important;
  }

  /* Número de caja */
  .fnx-caja-label { flex-shrink: 0; }
  .fnx-caja-input { width: 72px; flex-shrink: 0; }

  /* Modo automático: línea completa */
  .hw-auto-label { flex: 1 1 100%; }

  /* Input búsqueda sección baja */
  .search-input-short {
    width: 100% !important;
    flex: none !important;
    min-width: unset !important;
  }

  /* ── Tablas de baja FNX / HW / Tienda → tarjetas ────────────── */
  #fnx-resultado-baja    .tabla-variantes,
  #hw-resultado-baja     .tabla-variantes,
  #tienda-resultado-baja .tabla-variantes {
    border: none;
    box-shadow: none;
    background: transparent;
    border-radius: 0;
  }

  #fnx-resultado-baja    .tabla-variantes thead,
  #hw-resultado-baja     .tabla-variantes thead,
  #tienda-resultado-baja .tabla-variantes thead {
    display: none;
  }

  #fnx-resultado-baja    .tabla-variantes tbody tr,
  #hw-resultado-baja     .tabla-variantes tbody tr,
  #tienda-resultado-baja .tabla-variantes tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 12px;
    margin-bottom: 8px;
    box-shadow: var(--shadow-sm);
  }

  #fnx-resultado-baja    .tabla-variantes tbody td,
  #hw-resultado-baja     .tabla-variantes tbody td,
  #tienda-resultado-baja .tabla-variantes tbody td {
    display: inline-flex;
    align-items: center;
    padding: 2px 0;
    border: none;
    vertical-align: unset;
  }

  /* Col 1: SKU */
  #fnx-resultado-baja    .tabla-variantes tbody td:nth-child(1),
  #hw-resultado-baja     .tabla-variantes tbody td:nth-child(1),
  #tienda-resultado-baja .tabla-variantes tbody td:nth-child(1) {
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 0.88rem;
    color: var(--fg-main);
    flex: 1 0 42%;
  }

  /* Col 3: Talla */
  #fnx-resultado-baja    .tabla-variantes tbody td:nth-child(3),
  #hw-resultado-baja     .tabla-variantes tbody td:nth-child(3),
  #tienda-resultado-baja .tabla-variantes tbody td:nth-child(3) {
    font-size: 0.80rem;
    color: var(--fg-muted);
  }

  /* Col 4: Caja */
  #fnx-resultado-baja    .tabla-variantes tbody td:nth-child(4),
  #hw-resultado-baja     .tabla-variantes tbody td:nth-child(4),
  #tienda-resultado-baja .tabla-variantes tbody td:nth-child(4) {
    font-size: 0.92rem;
    font-weight: 700;
    flex: 1;
  }

  /* Col 2: Título — segunda línea */
  #fnx-resultado-baja    .tabla-variantes tbody td:nth-child(2),
  #hw-resultado-baja     .tabla-variantes tbody td:nth-child(2),
  #tienda-resultado-baja .tabla-variantes tbody td:nth-child(2) {
    flex: 1 1 100%;
    font-size: 0.82rem;
    color: var(--fg-muted);
    order: 10;
  }

  /* Col 5: Checkbox */
  #fnx-resultado-baja    .tabla-variantes tbody td:last-child,
  #hw-resultado-baja     .tabla-variantes tbody td:last-child,
  #tienda-resultado-baja .tabla-variantes tbody td:last-child {
    margin-left: auto;
  }

  /* Botón "Dar de baja seleccionadas" */
  #fnx-resultado-baja    .btn-primary[style*="margin-top"],
  #hw-resultado-baja     .btn-primary[style*="margin-top"],
  #tienda-resultado-baja .btn-primary[style*="margin-top"] {
    width: 100% !important;
    margin-left: 0 !important;
  }

  /* ── Modales — desde abajo (excepto Histórico, ver más abajo) ─── */
  .modal-overlay { align-items: flex-end; }

  .modal-box {
    width: 100% !important;
    max-width: 100% !important;
    max-height: 88vh;
    border-radius: 14px 14px 0 0;
  }

  .modal-inventario { width: 100% !important; }

  /* Modal Histórico y Modal de confirmación (Baja Ubicación) — se abren
     desde ARRIBA de la pantalla en móvil; el resto sigue desde abajo */
  #modal-historico.modal-overlay,
  #modal-confirmar.modal-overlay {
    align-items: flex-start;
  }
  #modal-historico .modal-box,
  #modal-confirmar .modal-box {
    border-radius: 0 0 14px 14px;
  }

  /* ── Flash messages — centrado ───────────────────────────────── */
  .flash-msg {
    left: 12px;
    right: 12px;
    bottom: 16px;
    text-align: center;
  }

  /* ── Flash inline Almacén — mobile: bloque bajo el buscador ─────── */
  .alm-flash-inline:not(:empty) {
    display: block;
    margin-top: 6px;
    margin-left: 0;
    width: 100%;
    text-align: center;
  }

  /* Flechas de navegación entre productos (Atrás/Adelante) — ocultas en
     móvil, sustituidas por gesto de swipe. Se mantienen en escritorio. */
  #btn-alm-atras, #btn-alm-adelante { display: none !important; }

  /* 🆕 Swipe entre fichas del Catálogo — deslizamiento lateral PURO (sin
     desvanecimiento). Se anima .cat-ficha-imagen-wrap, .cat-ficha-info-fija
     y .cat-ficha-scroll (imagen, título/precio/desc, tallas/relacionados)
     juntos: la ficha actual se desliza hacia un lado (salida) mientras la
     nueva entra desde el lado contrario (entrada), en la misma dirección
     del dedo — toda la ficha se mueve como un bloque, imagen incluida.
     🔒 El transform se aplica DIRECTAMENTE sobre .cat-ficha-imagen-wrap
     (position:fixed en móvil), nunca sobre su padre .cat-ficha-izquierda
     — un transform en un ANTEPASADO de un elemento fijo lo convierte en
     su nuevo contenedor de referencia, rompiendo su posicionamiento
     (saltos raros); puesto directamente sobre el propio elemento fijo no
     tiene ese problema. */
  .cat-ficha-anim-adelante {
    animation: cat-ficha-entra-derecha 0.16s ease-out;
  }
  .cat-ficha-anim-atras {
    animation: cat-ficha-entra-izquierda 0.16s ease-out;
  }
  @keyframes cat-ficha-entra-derecha {
    from { transform: translateX(100%); }
    to   { transform: translateX(0); }
  }
  @keyframes cat-ficha-entra-izquierda {
    from { transform: translateX(-100%); }
    to   { transform: translateX(0); }
  }
  .cat-ficha-sale-izquierda {
    animation: cat-ficha-sale-izquierda 0.09s ease-in forwards;
  }
  .cat-ficha-sale-derecha {
    animation: cat-ficha-sale-derecha 0.09s ease-in forwards;
  }
  @keyframes cat-ficha-sale-izquierda {
    from { transform: translateX(0); }
    to   { transform: translateX(-100%); }
  }
  @keyframes cat-ficha-sale-derecha {
    from { transform: translateX(0); }
    to   { transform: translateX(100%); }
  }

  /* 🆕 Fondo blanco SOLO durante la transición de swipe entre fichas —
     añadida/quitada por JS a AMBOS elementos (son hermanos, no
     descendiente uno del otro — ver _crearFondoImagenFicha en
     catalogo.js, que inserta #cat-ficha-imagen-fondo "beforebegin" de
     .cat-ficha-imagen-wrap). El resto del tiempo el fondo es negro
     (normal), incluido mientras dura el gesto de "toque para reducir la
     imagen", que no debe verse afectado por este blanco. */
  .cat-ficha-imagen-transicion-blanca {
    background: #fff !important;
  }

  /* 🆕 Vista previa de SOLO IMAGEN de la ficha vecina durante el
     arrastre en vivo — todo el posicionamiento/tamaño se fija en línea
     desde JS (ver _obtenerPreviewImagenLayer/_rellenarPreviewImagen en
     catalogo.js), sin depender de esta hoja de estilos ni de la
     propiedad CSS aspect-ratio (fallo real encontrado: si el navegador
     no la soporta bien, el contenedor se queda sin alto definido y la
     imagen colapsa a tamaño "miniatura"). Esta regla es solo un
     respaldo/documentación — el cssText en línea ya cubre lo esencial.
     pointer-events:none — puramente visual. */
  #cat-ficha-swipe-preview-img-layer {
    display: none;
  }

}

/* ── Búsqueda ─────────────────────────────────────────────────────────── */
/* Ya NO es fija (position:fixed) — ahora es un desplegable que
   aparece/desaparece con la lupa de la cabecera (ver catalogo.js),
   empujando el contenido de abajo al abrirse, ocultándose del todo al
   cerrarse. Fondo semitransparente A PROPÓSITO (antes tenía que ser
   opaca por ser fija encima de todo) — se ve el contenido de detrás.
   Mismo elemento DOM que catalogo.js traslada entre #cat-vista-grid y
   #cat-ficha-buscador-slot — funciona igual estés donde estés. */
.cat-search-row {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 400;
  display: flex;
  flex-direction: column;
  padding: 8px 12px 7px;
  background: transparent;
  border-radius: 0 0 10px 10px;
  transform: translateY(-110%);
  transition: transform 0.22s ease;
  pointer-events: none;
}
.cat-search-row-fila1 {
  display: flex;
  gap: 8px;
  align-items: center;
}
.cat-search-row.cat-search-abierta {
  transform: translateY(0);
  pointer-events: auto;
}
@media (min-width: 769px) {
  /* Todos los botones del Catálogo, animados: leve elevación al pasar el
     ratón, encogimiento al pulsar — sensación táctil consistente en
     Almacén/Medidas/Inicio, flechas Atrás/Reset/Adelante, chips de
     filtro, cerrar modales, etc. Por clase directamente (no por
     ancestro #panel-catalogo — la barra de búsqueda se mueve de sitio
     por JS y podía no estar anidada ahí en el momento del hover). */
  .cat-btn-icono,
  .cat-chip,
  .cat-footer-flecha,
  .cat-footer-boton,
  .cat-ficha-btn-accion,
  .cat-ficha-chip-inicio,
  .cat-chip-inicio-filtros,
  .cat-modal-guia-btn-copiar,
  .cat-btn-favoritos-limpiar,
  .cat-modal-confirmar-btn,
  .cat-fav-fila-borrar,
  .modal-cerrar {
    transition: transform 0.1s ease, background-color 0.15s ease,
                border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
  }
  .cat-btn-icono:hover,
  .cat-chip:hover,
  .cat-footer-flecha:hover,
  .cat-footer-boton:hover,
  .cat-ficha-btn-accion:hover,
  .cat-ficha-chip-inicio:hover,
  .cat-chip-inicio-filtros:hover,
  .cat-modal-guia-btn-copiar:hover,
  .cat-btn-favoritos-limpiar:hover,
  .cat-modal-confirmar-btn:hover,
  .cat-fav-fila-borrar:hover,
  .modal-cerrar:hover {
    transform: translateY(-1px);
  }
  .cat-btn-icono:active,
  .cat-chip:active,
  .cat-footer-flecha:active,
  .cat-footer-boton:active,
  .cat-ficha-btn-accion:active,
  .cat-ficha-chip-inicio:active,
  .cat-chip-inicio-filtros:active,
  .cat-modal-guia-btn-copiar:active,
  .cat-btn-favoritos-limpiar:active,
  .cat-modal-confirmar-btn:active,
  .cat-fav-fila-borrar:active,
  .modal-cerrar:active {
    transform: scale(0.92);
  }

  /* Miniaturas del grid: leve elevación + sombra al pasar el ratón */
  .cat-card {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
  }
  .cat-card:hover {
    transform: scale(0.95) translateY(-4px);
    box-shadow: 0 10px 24px rgba(0,0,0,0.45);
  }

  /* Filas de talla en la ficha: mismo efecto que las miniaturas del grid */
  .cat-talla-row {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
  }
  .cat-talla-row:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(0,0,0,0.4);
  }

  /* Escritorio: fija arriba siempre visible (no flotante ni animada) — el
     carrusel/grid se adapta por debajo. Ancho generoso y sin salto de
     línea: caben el campo + Jarana + Atrás/Reset/Adelante, todo en una
     sola fila (fila1) — la barra "Estás en" va en su propia línea justo
     debajo, dentro del mismo contenedor. */
  .cat-search-row {
    position: fixed;
    top: 0;
    left: 300px;
    right: auto;
    width: 46%;
    min-width: 580px;
    max-width: 820px;
    transform: none;
    pointer-events: auto;
    background: var(--bg-card);
    border-radius: 0 0 8px 0;
    z-index: 50;
  }
  .cat-search-row-fila1 {
    flex-wrap: nowrap;
    gap: 5px;
  }
  .cat-search-row .cat-input-buscar {
    flex: 0 1 140px;
    min-width: 90px;
  }
  .cat-estas-en {
    background: rgba(243,2,86,0.12);
    border: 1px solid rgba(243,2,86,0.35);
  }
  .cat-estas-en strong { color: #F30256; }
}
.cat-search-row .cat-input-buscar {
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 10px;
  font-size: 0.94rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--fg-main);
}

/* Barra "Estás en [término]" — solo visible con una búsqueda de texto
   activa (se muestra/oculta por JS). Ocupa su propia línea, justo debajo
   del campo de búsqueda + iconos, dentro del mismo contenedor que se
   mueve entero a donde haga falta (grid, ficha, escritorio, móvil). */
.cat-estas-en {
  margin-top: 5px;
  padding: 5px 10px;
  background: #000;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  color: var(--fg-main);
  font-weight: 600;
}
.cat-estas-en strong { color: #e53935; font-weight: 700; }

.cat-btn-icono {
  background: transparent;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-sm);
  color: #fff;
  font-size: 0.85rem;
  width: 30px;
  height: 30px;
  cursor: pointer;
  flex-shrink: 0;
}
.cat-btn-icono-activo { background: var(--accent); color: #fff; border-color: var(--accent); }
/* "Solo en tienda" (Jarana) activo — amarillo, distinto del resto de
   botones activos (que comparten la clase genérica de arriba). */
#cat-btn-jarana.cat-btn-icono-activo {
  background: #f9c74f;
  color: #3a2e00;
  border-color: #f9c74f;
}

.cat-chips-row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-start;
  gap: 5px;
  padding: 0 12px 6px;
  overflow-x: auto;
  scrollbar-width: none;
}
.cat-chips-row-2 { padding-bottom: 10px; justify-content: flex-start; }

/* ── Filtros: chip de cabecera + modal ───────────────────────────────── */
/* Se pone en verde mientras haya algún filtro de género/tipo activo —
   así se ve de un vistazo en la cabecera que hay filtros aplicados. */
/* Lupa + Filtros van juntos en un grupo (#cat-header-controles) para que
   se muevan y separen como una sola unidad, sin depender de márgenes
   sueltos en cada botón que puedan chocar con otras reglas. Dentro del
   grupo se anula el margin-left:auto / margin-right heredado de
   .btn-admin-tab — el espaciado entre ellos lo da el gap del contenedor. */
#cat-header-controles {
  margin-left: 0;
  margin-right: 0;
  gap: 3px !important;
}
#cat-header-controles .btn-admin-tab {
  margin-left: 0;
  margin-right: 0;
}
#btn-catalogo-filtros {
  color: #000;
}

#btn-catalogo-filtros.cat-filtros-activo {
  background: #2e7d32;
  border-color: #2e7d32;
  color: #fff;
}
.cat-modal-filtros-overlay {
  align-items: flex-start;
  padding-top: 0;
}
.cat-modal-filtros-box {
  width: 100%;
  max-width: 100%;
  height: 100vh;
  height: 100dvh;
  max-height: 100vh;
  max-height: 100dvh;
  border-radius: 0;
  padding: 8px 10px 6px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: linear-gradient(180deg, #0d0d0d 0%, #000 260px);
  display: flex;
  flex-direction: column;
}
.cat-modal-filtros-box .cat-chips-row {
  flex-wrap: wrap;
  overflow-x: visible;
  justify-content: flex-start;
}
/* Disfraces/Complementos/Decoración como control segmentado — una franja
   agrupada, no píldoras sueltas. */
.cat-modal-filtros-box .cat-chips-row-2 {
  flex-wrap: wrap;
  overflow-x: visible;
  justify-content: flex-start;
  background: #161616;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  padding: 4px;
  gap: 4px;
  margin-bottom: 6px;
  flex-shrink: 0;
}
.cat-modal-filtros-box .cat-chips-row-2 .cat-chip {
  flex: 1 1 auto;
  background: transparent;
  border: none;
  border-radius: 9px;
  color: var(--fg-muted);
  padding: 7px 11px;
  font-size: 0.82rem;
}
.cat-modal-filtros-box .cat-chips-row-2 .cat-chip[data-tipo="decoracion"] {
  flex-basis: 100%;
}
.cat-modal-filtros-box .cat-chips-row-2 .cat-chip-activo {
  background: #F30256;
  color: #fff;
}
.cat-check-talla-todas {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 3px;
  padding: 4px 10px;
  background: #131313;
  border: 1px solid rgba(255,255,255,0.09);
  border-radius: 7px;
  font-size: 0.74rem;
  color: var(--fg-muted);
  cursor: pointer;
  user-select: none;
  flex-shrink: 0;
}
.cat-check-talla-todas input[type="checkbox"] {
  width: 15px;
  height: 15px;
  accent-color: #F30256;
  cursor: pointer;
}
.cat-fila-talla-todas-inicio {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 3px;
  flex-wrap: wrap;
}
.cat-fila-talla-todas-inicio .cat-check-talla-todas { margin: 0; }
.cat-chip-inicio-filtros {
  padding: 4px 10px;
  background: #131313;
  border: 1px solid rgba(255,255,255,0.09);
  border-radius: 7px;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--fg-muted);
  cursor: pointer;
  flex-shrink: 0;
}
.cat-fila-talla-todas-inicio .cat-btn-proveedor {
  display: inline-block;
  width: auto;
  padding: 4px 10px;
  border-left: 1px solid rgba(255,255,255,0.09);
  border-radius: 7px;
  font-size: 0.74rem;
  margin-bottom: 0;
  flex-shrink: 0;
}
.cat-chip-inicio-filtros:hover { border-color: #F30256; color: #F30256; }

.cat-modal-filtros-box #cat-filtros-generos {
  flex-shrink: 0;
}
.cat-modal-filtros-box .cat-modal-filtros-botones {
  position: sticky;
  flex-shrink: 0;
}
.cat-modal-filtros-botones {
  display: flex;
  gap: 8px;
  padding: 6px 2px 2px;
  margin-top: auto;
  border-top: 1px solid rgba(255,255,255,0.08);
  position: sticky;
  bottom: 0;
  background: #000;
  flex-shrink: 0;
}
.cat-modal-filtros-botones .btn-modal-cancelar,
.cat-modal-filtros-botones .btn-modal-aceptar {
  flex: 1 1 0;
  padding: 10px 12px;
  border-radius: 10px;
  font-weight: 700;
  font-size: 0.86rem;
  cursor: pointer;
  border: none;
}
.cat-modal-filtros-botones .btn-modal-cancelar {
  background: var(--bg-sub);
  color: var(--fg-mid);
  border: 1px solid var(--border);
}
.cat-modal-filtros-botones .btn-modal-aceptar {
  background: var(--accent);
  color: #fff;
}
.cat-chips-row::-webkit-scrollbar { display: none; }
.cat-chip {
  flex-shrink: 0;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5.5px 12px;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--fg-muted);
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
  transition: background .15s, color .15s, border-color .15s;
}
.cat-chip-activo {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* Bloque de género + sus tallas/edades — cada uno en su propia tarjeta,
   con un acento de color a la izquierda (identidad del Catálogo). Todo
   apilado verticalmente uno debajo de otro (Hombre, Mujer, Niño, Niña,
   Bebé, en ese orden fijo). */
.cat-filtro-genero-bloque {
  background: #131313;
  border: 1px solid rgba(255,255,255,0.07);
  border-left: 3px solid rgba(243,2,86,0.55);
  border-radius: 9px;
  padding: 7px 11px 8px;
  margin-bottom: 6px;
}
.cat-filtro-genero-titulo {
  display: inline-block;
  background: #1e1e1e;
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 6px;
  padding: 4px 12px;
  margin: 0 0 7px;
  font-size: 0.76rem;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--fg-muted);
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
/* Título en rosa cuando ese género tiene alguna talla marcada — deja de
   estarlo en cuanto se desmarca la última. */
.cat-genero-titulo-activo {
  background: #F30256;
  color: #fff;
  border-color: #F30256;
}
.cat-chips-tallas {
  flex-wrap: wrap;
  justify-content: flex-start;
  overflow-x: visible;
  gap: 5px;
}
.cat-chips-tallas .cat-chip {
  background: #1e1e1e;
  border-color: rgba(255,255,255,0.1);
}
.cat-chips-tallas .cat-chip.cat-chip-activo {
  background: #F30256;
  border-color: #F30256;
  color: #fff;
}
.cat-modal-filtros-box .cat-chips-tallas .cat-chip {
  padding: 4px 10px;
  font-size: 0.74rem;
}

/* ── Grid de miniaturas ─────────────────────────────────────────────── */
/* Carrusel horizontal de páginas de 6, con scroll nativo del navegador
   (overflow-x:auto + scroll-snap) — el contenido sigue al dedo en tiempo
   real mientras arrastras, y la VELOCIDAD del gesto decide si avanza a
   la siguiente página o vuelve a la actual; el rebote en los extremos lo
   da el propio sistema operativo (momentum scroll), sin nada que
   programar a mano. */
/* ── Pie de página del Catálogo ─────────────────────────────────────── */
/* Tema oscuro real del Catálogo — SOLO aquí, redefiniendo las variables
   de color (se propagan solas a todo lo que ya usa var(--fg-main) etc.,
   sin tener que tocar cada regla suelta una por una). Antes se veía
   oscuro solo porque Brave tenía el modo oscuro forzado activado (invierte
   fondo Y texto a la vez); nuestras variables reales son para fondo
   claro (--fg-main es un azul oscuro, casi invisible sobre negro de
   verdad) — de ahí que haga falta redefinir también el texto, no solo
   el fondo. No toca Almacén ni el resto de módulos. */
#panel-catalogo {
  position: relative;
  --bg-body:     #000;
  --bg-card:     #1a1a1a;
  --bg-sub:      #161616;
  --bg-row-alt:  #1f1f1f;
  --fg-main:     #f5f5f5;
  --fg-muted:    #b8bcc8;
  --fg-dim:      #8a8e9c;
  --border:      rgba(255,255,255,0.16);
  --border-soft: rgba(255,255,255,0.11);
  background: #000;
  /* Evita el gesto nativo de "tirar hacia abajo para recargar la página"
     (pull-to-refresh) dentro de todo el Catálogo. */
  overscroll-behavior-y: none;
  /* 🔒 Evita el gesto nativo de "deslizar desde el borde para volver
     atrás" de Chrome/Android — competía con el swipe horizontal propio
     entre fichas (ver el candado en html/body para la explicación
     completa del bug que causaba). */
  overscroll-behavior-x: none;
}
#cat-vista-grid,
#cat-vista-ficha {
  background: #000; /* 🔒 REVERTIDO a negro — el cambio a blanco tenía un efecto secundario demasiado amplio: varios elementos (tallas, "Relacionados"...) usan texto claro sin fondo propio, pensados para un contenedor oscuro. Se quedaban casi invisibles por falta de contraste. La causa real del "escalón negro" en las transiciones se busca por otro camino, más concreto. */
  overscroll-behavior-y: none;
  overscroll-behavior-x: none;
  position: relative; /* ancla el overlay flotante de búsqueda (.cat-search-row) */
}
/* 🔒 Bug real encontrado (tercera vuelta): overflow-x:hidden aquí
   forzaba, por la propia especificación CSS, que overflow-y computara
   a "auto" — convirtiendo a #cat-vista-ficha en su propio contenedor
   de scroll. Como no tiene alto fijo (crece con su contenido), nunca
   desborda a SÍ MISMO, así que no había nada que desplazar aquí
   dentro — pero al pasar a ser "el contenedor de scroll", el
   navegador dejaba de propagar el gesto vertical hacia arriba, a la
   página. Exactamente el mismo mecanismo que ya rompía el scroll en
   body, trasladado sin querer aquí (además este es el MISMO elemento
   con los listeners táctiles del swipe — vistaFicha). overflow-x:clip
   bloquea el desbordamiento horizontal sin disparar esa regla de
   auto-cómputo del otro eje (clip, a diferencia de hidden/auto/scroll,
   no obliga al otro eje a dejar de ser "visible"). */
#cat-vista-ficha {
  overflow-x: clip;
}
/* 🔒 Bug real encontrado: filter en un antepasado (#cat-vista-ficha)
   crea un nuevo contexto de posicionamiento para sus hijos
   position:fixed (.cat-ficha-imagen-wrap/-2) — dejan de anclarse al
   viewport real y pasan a anclarse a #cat-vista-ficha en su lugar. Esto
   causaba DOS problemas a la vez: la imagen "se descolgaba" del borde
   superior (su `top` en píxeles ya no correspondía a la posición real
   bajo la cabecera), y probablemente también rompía el scroll vertical
   de TODA la ficha — una vez la imagen pasa a comportarse como
   position:absolute respecto a #cat-vista-ficha (en vez de fixed de
   verdad), SÍ cuenta dentro de su altura de scroll, pudiendo disparar
   scrollHeight a un valor absurdo. El velo se aplica ahora directamente
   a los elementos que lo necesitan — .cat-ficha-imagen-wrap/-2 son
   fixed en sí mismos (aplicarles filter a ELLOS no afecta a su propio
   anclaje, solo al de HIJOS fixed que no tienen), y .cat-ficha-scroll
   es de flujo normal, sin hijos fixed. */
.cat-ficha-agotada .cat-ficha-imagen-wrap,
.cat-ficha-agotada .cat-ficha-imagen-wrap-2,
.cat-ficha-agotada .cat-ficha-scroll {
  filter: brightness(0.82);
  opacity: 0.86;
}
/* En móvil, este contenedor no existe a efectos de layout — sus hijos
   (imagen + info) se comportan exactamente como si fueran hijos directos
   de #cat-vista-ficha, igual que antes de envolverlos. Solo cobra forma
   propia en escritorio (ver media query), donde sí necesita apilarlos de
   forma independiente a la columna derecha. */
.cat-ficha-izquierda { display: contents; }
/* El modal de Filtros vive fuera de #panel-catalogo en el HTML (junto al
   resto de modales) — mismas variables oscuras repetidas aquí, si no, se
   quedaría con el tema claro por defecto. */
#cat-modal-filtros {
  --bg-card:     #1a1a1a;
  --fg-main:     #f5f5f5;
  --fg-muted:    #b8bcc8;
  --border:      rgba(255,255,255,0.16);
  --border-soft: rgba(255,255,255,0.11);
  --bg-sub:      #161616;
}
#cat-modal-guia,
#cat-modal-favoritos,
#cat-modal-confirmar {
  --bg-card:     #1a1a1a;
  --fg-main:     #f5f5f5;
  --fg-muted:    #b8bcc8;
  --fg-dim:      #8a8e9c;
  --border:      rgba(255,255,255,0.16);
  --border-soft: rgba(255,255,255,0.11);
  --bg-sub:      #161616;
}
/* Módulo Propuestas — texto en blanco puro (no el gris casi-blanco
   --fg-main del resto de modales del catálogo) en TODAS sus superficies:
   el modal de cabecera (En curso/Histórico, con los steppers de tallas
   ya integrados), el de guardado (cliente) y el de previsualización. Se
   pidió expresamente. Al redefinir la variable aquí, todo lo que la usa
   dentro (incluida la tabla de medidas reutilizada, .cat-guia-tabla)
   hereda el blanco puro automáticamente, sin tocar esa clase en ningún
   otro sitio del catálogo. */
#cat-modal-propuestas,
#cat-modal-propuestas-guardar,
#cat-modal-propuestas-preview,
#cat-modal-cliente-ficha {
  --bg-card:     #1a1a1a;
  --fg-main:     #ffffff;
  --fg-muted:    #ffffff;
  --fg-mid:      #ffffff;
  --fg-dim:      #ffffff;
  --border:      rgba(255,255,255,0.16);
  --border-soft: rgba(255,255,255,0.11);
  --bg-sub:      #161616;
  /* .modal-box nunca fija un color base — sin esto, cualquier texto sin
     regla explícita (desplegables nativos, elementos nuevos que se nos
     olvide colorear...) hereda el color por defecto de la página en vez
     del blanco de estas variables. Al fijarlo aquí, todo lo de dentro
     parte de blanco salvo que algo lo pise a propósito (--accent,
     --fg-dim en avisos, etc.). */
  color: var(--fg-main);
}
#cat-modal-confirmar {
  z-index: 9300;
  align-items: flex-start;
  padding-top: 20px;
}
.cat-modal-confirmar-box {
  max-width: 320px;
  width: 88%;
  padding: 24px 20px 20px;
  text-align: center;
}
.cat-modal-confirmar-icono { font-size: 2rem; margin-bottom: 10px; }
.cat-modal-confirmar-msg {
  font-size: 0.92rem;
  color: var(--fg-main);
  font-weight: 600;
  margin-bottom: 20px;
  line-height: 1.4;
}
.cat-modal-confirmar-botones { display: flex; gap: 10px; }
.cat-modal-confirmar-btn {
  flex: 1 1 0;
  padding: 10px;
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--border);
}
.cat-modal-confirmar-btn-cancelar {
  background: var(--bg-sub);
  color: var(--fg-muted);
}
.cat-modal-confirmar-btn-aceptar {
  background: #c62828;
  border-color: #c62828;
  color: #fff;
}
/* Guía de tallas: anclada arriba de la pantalla (no centrada), con la
   cabecera "Guía de tallas" en negro — a diferencia del resto de
   modales, que siguen centrados con el azul marino de --bg-header. */
#cat-modal-guia {
  align-items: flex-start;
  padding-top: 20px;
}
#cat-modal-guia .modal-header {
  background: #000;
}

/* La imagen ocupa el ancho de pantalla completo (tamaño real, no
   transform) — su alto es automático, en su proporción NATURAL, sin
   recortar (cover) ni dejar franjas en blanco a los lados. */
/* Semáforo de tallas agrupado por género — usado en tarjetas de Parejas/
   Grupos (grid, parecidos, relacionados y ficha). */
.cat-zoom-tallas-genero {
  padding: 6px 4px 2px;
}
/* Dentro de una miniatura normal — más compacto, la tarjeta es mucho más
   pequeña. */
.cat-card-img-wrap .cat-zoom-tallas-genero {
  position: absolute;
  left: 2px; right: 2px; bottom: 2px;
  padding: 2px;
  background: transparent;
  border-radius: 4px;
}
.cat-card-img-wrap .cat-zoom-genero-fila {
  gap: 2px;
  margin-bottom: 2px;
}
.cat-card-img-wrap .cat-zoom-genero-etiqueta {
  font-size: 0.56rem;
  color: #000;
  font-weight: 700;
  min-width: 30px;
}
.cat-zoom-tallas-genero-compacto .cat-zoom-genero-etiqueta {
  font-weight: 800;
  min-width: 14px;
  font-size: 0.68rem;
}
.cat-card-img-wrap .cat-zoom-genero-fila .cat-card-talla-badge {
  font-size: 0.5rem;
  padding: 1px 3px;
}
.cat-zoom-genero-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-bottom: 4px;
}
.cat-zoom-genero-etiqueta {
  font-size: 0.92rem;
  font-weight: 700;
  color: #000;
  margin-right: 2px;
  min-width: 42px;
}

/* ── Carruseles de la pantalla de inicio (Más vendidos/Parejas/Grupos/Recomendados/Ofertas/Colecciones) ── */
#cat-inicio-wrap {
  display: flex;
  flex-direction: column;
  overflow-y: auto; /* 🔒 antes 'hidden' — con más de 3 carruseles no caben todos en el alto disponible; se permite scroll vertical para ver los que queden ocultos debajo */
  overflow-x: hidden;
}
.cat-carrusel-seccion {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

/* 🆕 Secciones de entretenimiento (ruleta/memory/quiz) — a diferencia de
   los carruseles, su altura NO la calcula el JS de carruseles (no son
   parte de _estadoCarrusel) — se dejan con altura natural según su
   contenido, con un padding propio para que no queden pegadas entre sí
   al hacer scroll por #cat-inicio-wrap. */
.cat-entretenimiento-seccion {
  flex: 0 0 auto;
  padding-bottom: 16px;
}

/* ── Ruleta "¿De qué me disfrazo?" ── */
/* 🆕 Selector de géneros — chips, color de la cabecera cuando activos */
.cat-ruleta-generos-selector {
  display: flex;
  gap: 5px;
  justify-content: center;
  flex-wrap: wrap;
  padding: 0 4px 6px;
  margin-top: 14px; /* espacio respecto al título — aumentado, pedido explícitamente */
}
.cat-ruleta-genero-chip {
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 14px;
  padding: 4px 11px;
  color: #fff;
  font-size: 0.74rem;
  font-weight: 600;
  cursor: pointer;
}
.cat-ruleta-genero-chip.cat-ruleta-genero-activo {
  background: var(--accent);
  border-color: var(--accent);
}
.cat-ruleta-genero-chip:active {
  opacity: 0.7;
}
.cat-ruleta-carretes {
  display: flex;
  gap: 2px;
  padding: 4px 2px;
  justify-content: center;
}
.cat-ruleta-carrete {
  flex: 1 1 0;
  /* 🆕 Ancho/alto controlados por variables CSS — el JS las recalcula
     cada vez que cambia cuántos géneros están activos, así cada
     carrete crece cuando hay menos seleccionados. Valores por defecto
     = los de siempre (5 activos), por si el JS no llegara a fijarlas. */
  max-width: var(--cat-ruleta-ancho, 94px);
  height: var(--cat-ruleta-alto, 110px);
  border-radius: 8px;
  overflow: hidden; /* la "ventanilla" — solo se ve 1 imagen de la tira a la vez */
  position: relative;
  background: #1e1e1e;
  border: 2px solid rgba(255,255,255,0.15);
  box-shadow: inset 0 6px 10px -6px rgba(0,0,0,0.6), inset 0 -6px 10px -6px rgba(0,0,0,0.6); /* sombra arriba/abajo — sugiere profundidad, como una tragaperras real */
}
/* 🆕 Solo pulsable de verdad tras aterrizar (ver JS) — el cursor de
   "mano" da la pista visual de que se puede tocar para ir a la ficha. */
.cat-ruleta-carrete.cat-ruleta-aterrizado {
  cursor: pointer;
}
.cat-ruleta-tira {
  position: absolute;
  top: 0; left: 0; width: 100%;
  /* la posición vertical (translateY) la controla el JS al girar */
}
.cat-ruleta-tira img {
  display: block;
  width: 100%;
  height: var(--cat-ruleta-alto, 110px); /* misma altura que la ventanilla — cada imagen ocupa exactamente un "cajón" de la tira */
  object-fit: cover;
}
.cat-ruleta-carrete.cat-ruleta-girando {
  border-color: var(--accent);
  box-shadow: 0 0 10px 1px rgba(243,2,86,0.5), inset 0 6px 10px -6px rgba(0,0,0,0.6), inset 0 -6px 10px -6px rgba(0,0,0,0.6);
}
.cat-ruleta-btn-girar {
  display: block;
  margin: 10px auto 0;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 20px;
  padding: 8px 26px;
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
}
.cat-ruleta-btn-girar:active { transform: scale(0.96); }
.cat-ruleta-btn-girar:disabled { opacity: 0.6; }
/* 🆕 Aviso de "combo de familia" — aparece con una entrada animada
   (rebote + fade-in) cuando los 5 resultados de la ruleta comparten
   colección. La animación se relanza cada vez que se añade la clase de
   nuevo (el JS fuerza un reflow entre tirada y tirada para que se
   pueda repetir, no solo la primera vez). */
.cat-ruleta-combo-aviso {
  text-align: center;
  color: #fff;
  font-weight: 700;
  font-size: 0.88rem;
  margin: 10px auto 0;
  padding: 8px 14px;
  max-width: 320px;
  background: linear-gradient(135deg, #F30256, #ff8a00);
  border-radius: 12px;
  opacity: 0;
}
/* 🆕 "Ver Colección" — solo aparece en combos con colección real de
   Shopify detrás (supercombo/coleccion), pedido explícitamente. */
.cat-ruleta-combo-ver-coleccion {
  display: block; /* 🔒 antes inline-block — quedaba pegado al final del mensaje en la misma línea si cabía, apenas se notaba separación */
  margin-top: 12px; /* 🔒 antes 6px — más espacio, pedido explícitamente */
  font-size: 0.78rem;
  font-weight: 800;
  text-decoration: underline;
  cursor: pointer;
  opacity: 0.95;
}
.cat-ruleta-combo-aviso-mostrar {
  animation: cat-ruleta-combo-entrada 0.5s ease-out forwards, cat-ruleta-combo-brillo 1.2s ease-in-out 0.5s 2;
}
@keyframes cat-ruleta-combo-entrada {
  0%   { opacity: 0; transform: scale(0.6) translateY(-8px); }
  60%  { opacity: 1; transform: scale(1.08) translateY(0); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes cat-ruleta-combo-brillo {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.3); }
}
/* 🆕 Combos "grandes" (Pleno/Doble Combo Pareja) — aviso todavía más
   vistoso: algo más grande, con un brillo más intenso. */
.cat-ruleta-combo-aviso.cat-ruleta-combo-aviso-grande {
  font-size: 1rem;
  padding: 10px 16px;
  max-width: 340px;
}
.cat-ruleta-combo-aviso.cat-ruleta-combo-aviso-grande.cat-ruleta-combo-aviso-mostrar {
  animation: cat-ruleta-combo-entrada 0.5s ease-out forwards, cat-ruleta-combo-brillo 0.9s ease-in-out 0.5s 3;
}
/* 🆕 Resalta los carretes cuyo resultado forma parte de un combo — un
   poco más grandes que el resto, para que se vea de un vistazo cuáles
   son los que combinan entre sí. */
.cat-ruleta-carrete.cat-ruleta-combo-resaltado {
  transform: scale(1.1);
  z-index: 2;
  border-color: var(--accent);
  box-shadow: 0 0 10px 2px rgba(243,2,86,0.6), inset 0 6px 10px -6px rgba(0,0,0,0.6), inset 0 -6px 10px -6px rgba(0,0,0,0.6);
  transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
}
/* Golpe seco al aterrizar cada carrete — un pequeño rebote de escala */
.cat-ruleta-carrete.cat-ruleta-aterrizado {
  animation: cat-ruleta-golpe 0.28s ease-out;
}
@keyframes cat-ruleta-golpe {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* ── Memory de disfraces ── */
.cat-memory-subtitulo {
  text-align: center;
  color: var(--accent);
  font-weight: 700;
  font-size: 0.87rem;
  margin: 6px 0 10px; /* 🔒 antes -4px arriba — pegaba el subtítulo justo debajo del título, sin ningún hueco */
}
.cat-memory-dificultad {
  display: flex;
  justify-content: center;
  gap: 6px;
  padding: 0 10px 8px;
}
.cat-memory-dificultad-btn {
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 14px;
  padding: 5px 12px;
  color: #fff;
  font-size: 0.76rem;
  font-weight: 600;
  cursor: pointer;
}
.cat-memory-dificultad-activa {
  background: var(--accent);
  border-color: var(--accent);
}
.cat-memory-tablero {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 5px;
  padding: 4px 4px;
  max-width: 420px;
  margin: 0 auto;
}
.cat-memory-sin-parejas {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--fg-muted, #8a90a0);
  font-size: 0.82rem;
  padding: 20px 10px;
}
/* 🆕 Difícil (12 pares = 24 cartas) — 4 columnas dejaría las cartas
   demasiado altas/desproporcionadas; con 6 columnas quedan más
   parecidas en tamaño a las de las otras dos dificultades. */
.cat-memory-tablero.cat-memory-tablero-dificil {
  grid-template-columns: repeat(6, 1fr);
  max-width: 460px;
}
.cat-memory-carta {
  aspect-ratio: 3 / 4;
  perspective: 600px;
  cursor: pointer;
}
.cat-memory-carta-interior {
  position: relative;
  width: 100%;
  height: 100%;
  /* 🔒 Curva con un pelín de "rebote" al final (overshoot), en vez de
     una transición lineal/ease plana — se siente mucho más resultona,
     como si la carta "chascara" al girar. Memory es el juego estrella,
     merece la pena el extra de pulido aquí. */
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
  transform-style: preserve-3d;
}
.cat-memory-carta.cat-memory-volteada .cat-memory-carta-interior,
.cat-memory-carta.cat-memory-acertada .cat-memory-carta-interior {
  transform: rotateY(180deg);
}
/* 🆕 Efecto "pop" al acertar — la carta entera da un pequeño salto de
   escala con rebote justo al confirmarse la pareja. */
@keyframes cat-memory-pop-acierto {
  0%   { transform: rotateY(180deg) scale(1); }
  35%  { transform: rotateY(180deg) scale(1.18); }
  60%  { transform: rotateY(180deg) scale(0.95); }
  100% { transform: rotateY(180deg) scale(1); }
}
.cat-memory-carta.cat-memory-pop .cat-memory-carta-interior {
  animation: cat-memory-pop-acierto 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* 🆕 "Shake" al fallar — un temblor lateral rápido en las 2 cartas que
   NO forman pareja, antes de darles la vuelta de nuevo. */
@keyframes cat-memory-shake-fallo {
  0%, 100% { transform: rotateY(180deg) translateX(0); }
  20%  { transform: rotateY(180deg) translateX(-6px); }
  40%  { transform: rotateY(180deg) translateX(5px); }
  60%  { transform: rotateY(180deg) translateX(-4px); }
  80%  { transform: rotateY(180deg) translateX(3px); }
}
.cat-memory-carta.cat-memory-shake .cat-memory-carta-interior {
  animation: cat-memory-shake-fallo 0.4s ease-in-out;
}
.cat-memory-carta-cara {
  position: absolute;
  width: 100%; height: 100%;
  backface-visibility: hidden;
  border-radius: 7px;
  overflow: hidden;
}
.cat-memory-carta-dorso {
  background: linear-gradient(135deg, #2a1a3a, #1a0f26);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.4rem;
  border: 1px solid rgba(255,255,255,0.15);
}
.cat-memory-carta-frente {
  transform: rotateY(180deg);
  background: #1e1e1e;
}
.cat-memory-carta-frente img {
  width: 100%; height: 100%; object-fit: cover;
}
.cat-memory-carta.cat-memory-acertada .cat-memory-carta-frente {
  box-shadow: 0 0 0 2px var(--accent) inset;
}
/* 🆕 Brillo dorado breve tras acertar, además del "pop" de escala. */
@keyframes cat-memory-brillo-acierto {
  0%   { box-shadow: 0 0 0 2px var(--accent) inset, 0 0 14px 3px rgba(255,215,0,0.9); }
  100% { box-shadow: 0 0 0 2px var(--accent) inset, 0 0 0 0 rgba(255,215,0,0); }
}
.cat-memory-carta.cat-memory-pop .cat-memory-carta-frente {
  animation: cat-memory-brillo-acierto 0.7s ease-out;
}
/* 🆕 Cursor de "mano" en tarjetas ya acertadas — pulsable de verdad
   ahora (abre la ficha del producto), a diferencia de las demás. */
.cat-memory-carta.cat-memory-acertada {
  cursor: pointer;
}

/* ── Memory 2 jugadores ── */
.cat-memory-turnos {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 4px 0 8px;
  flex-wrap: wrap;
}
.cat-memory-marcador {
  color: #fff;
  font-size: 0.82rem;
  font-weight: 600;
  opacity: 0.65;
  transition: opacity 0.2s, transform 0.2s;
}
.cat-memory-marcador.cat-memory-marcador-activo {
  opacity: 1;
  transform: scale(1.08);
  color: var(--accent);
  font-weight: 800;
}
.cat-memory-turno-indicador {
  background: var(--accent);
  color: #fff;
  font-size: 0.87rem; /* 🔒 antes 0.76rem — 1 punto más grande, pedido explícitamente */
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 10px;
}
/* 🆕 Cuenta atrás por turno (solo modo 2 jugadores) — número grande,
   pulsando; se pone roja y pulsa más deprisa en el último segundo. */
.cat-memory-cuenta-atras {
  text-align: center;
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--accent);
  margin: 2px 0 4px;
  min-height: 1.9rem; /* 🔒 hueco fijo SIEMPRE reservado — antes display:none lo colapsaba del todo al ocultarse, desplazando el tablero hacia arriba en cada 2ª carta */
  line-height: 1.9rem;
  animation: cat-memory-cuenta-atras-pulso 1s ease-in-out infinite;
}
.cat-memory-cuenta-atras.cat-memory-cuenta-atras-urgente {
  color: #e94560;
  animation: cat-memory-cuenta-atras-pulso 0.4s ease-in-out infinite;
}
@keyframes cat-memory-cuenta-atras-pulso {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.15); }
}
/* 🔒 Excepción a la regla general de móvil (".modal-overlay {
   align-items: flex-end }", pensada para el resto de modales tipo
   "hoja que sube desde abajo") — este se pidió explícitamente
   centrado. */
#cat-modal-memory-2j.modal-overlay {
  align-items: center !important;
}
/* 🔒 CAUSA REAL de por qué no se veía fondo negro: este modal vive
   como hijo directo de <body> (no desciende de #panel-catalogo, que es
   donde --bg-card se redefine a oscuro) — sin esto, heredaba el blanco
   por defecto de toda la app. Mismo patrón ya usado para
   #cat-modal-propuestas y compañía (ver comentario ahí). */
#cat-modal-memory-2j {
  --bg-card:  #1a1a1a;
  --fg-main:  #ffffff;
  --fg-muted: #ffffff;
  --border:   rgba(255,255,255,0.16);
}
#cat-modal-memory-2j .modal-box {
  border-radius: 12px !important; /* 🔒 la regla general de móvil lo dejaba solo redondeado por arriba (forma de "hoja"); al centrarlo, tiene sentido redondearlo entero */
  border-left: 4px solid var(--accent); /* 🆕 detalle en UN LADO del color de la cabecera, pedido explícitamente — no marco completo */
}
.cat-modal-memory-2j-box {
  max-width: 320px;
  text-align: center;
  padding: 20px 18px;
}
.cat-modal-memory-2j-icono {
  font-size: 2.2rem;
  margin-bottom: 6px;
}
.cat-modal-memory-2j-titulo {
  font-size: 1.05rem;
  font-weight: 800;
  color: #fff;
  margin-bottom: 10px;
}
.cat-modal-memory-2j-lista {
  text-align: left;
  color: #ffffff; /* 🔒 antes var(--fg-muted, #b8b8c0) — letra blanca pura, pedido explícitamente */
  font-size: 0.85rem;
  line-height: 1.6;
  padding-left: 20px;
  margin-bottom: 16px;
}
.cat-modal-memory-2j-lista li { margin-bottom: 4px; }
.cat-memory-pie {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 10px;
}
.cat-memory-intentos {
  color: #fff;
  font-size: 0.85rem;
  font-weight: 600;
}
/* 🆕 Resultado final (tiempo + intentos) al completar el tablero */
.cat-memory-tiempo-resultado {
  text-align: center;
  color: #fff;
  font-weight: 700;
  font-size: 0.9rem;
  margin-top: 8px;
  min-height: 1.2em;
}
.cat-memory-tiempo-resultado:not(:empty) {
  animation: cat-ruleta-combo-entrada 0.5s ease-out; /* reutiliza la misma entrada animada del aviso de combo */
}

/* ── Hall of Fame — mejores registros (Memory y Puzle) ── */
.cat-records-tabla {
  max-width: 340px; /* 🔒 antes 280px — ampliado para que quepan 2 columnas lado a lado */
  margin: 10px auto 0;
  padding: 8px 10px;
  background: rgba(255,255,255,0.05);
  border-radius: 10px;
}
.cat-records-titulo {
  text-align: center;
  color: #fff;
  font-weight: 700;
  font-size: 0.91rem; /* 🔒 antes 0.8rem — 1 punto más grande, pedido explícitamente */
  margin-bottom: 4px;
}
/* 🆕 2 columnas lado a lado — 1-5 a la izquierda, 6-10 a la derecha
   (si hay), pedido explícitamente. */
.cat-records-columnas {
  display: flex;
  gap: 14px;
}
.cat-records-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.cat-records-item {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #fff; /* 🔒 antes var(--fg-muted, #8a90a0) — blanco puro también del 4º al 10º, pedido explícitamente (antes solo el top3 con medalla se veía así) */
  font-weight: 700; /* 🔒 antes solo lo tenía .cat-records-item-top3 */
  font-size: 0.78rem;
  padding: 2px 4px;
  border-radius: 5px;
}
.cat-records-item-top3 {
  color: #fff;
  font-weight: 700;
}
.cat-records-item-nuevo {
  background: rgba(243,2,86,0.25);
  animation: cat-ruleta-combo-brillo 1s ease-in-out 2;
}

.cat-carrusel-titulo {
  flex: 0 0 auto;
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  padding: 2px 10px 1px;
}
/* 🆕 Emoji temático por carrusel, con un flotar sutil continuo — combina
   los efectos "icono identificativo" y "animado discreto" en un solo
   elemento, en vez de dos por título (más limpio visualmente). Escala
   de tiempo distinta por sección (usando nth-child) para que no floten
   todos exactamente al mismo compás — se ve más natural/orgánico. */
.cat-carrusel-emoji {
  display: inline-block;
  margin-right: 6px;
  animation: cat-emoji-flotar 3s ease-in-out infinite;
}
.cat-carrusel-seccion:nth-child(2n) .cat-carrusel-emoji { animation-duration: 3.4s; animation-delay: -1s; }
.cat-carrusel-seccion:nth-child(3n) .cat-carrusel-emoji { animation-duration: 2.7s; animation-delay: -1.9s; }
/* 🆕 A medio camino entre la versión original (±3px/±6°, sin escala) y
   la última (±6px/±12°, con más escala) — pedido explícitamente "ni
   tan poco como al principio, ni tan animado como ahora". */
@keyframes cat-emoji-flotar {
  0%   { transform: translateY(0) rotate(0deg) scale(1); }
  25%  { transform: translateY(-4.5px) rotate(-9deg) scale(1.04); }
  50%  { transform: translateY(0) rotate(0deg) scale(1); }
  75%  { transform: translateY(2px) rotate(5deg) scale(0.98); }
  100% { transform: translateY(0) rotate(0deg) scale(1); }
}
/* 🆕 Aparición gradual (fade-in) de las tarjetas nuevas al llegar una
   tanda — en vez de aparecer de golpe. Se reproduce una sola vez sola
   (no infinite), no hace falta quitar la clase después. */
.cat-card-aparece {
  animation: cat-card-aparece-anim .35s ease-out;
}
@keyframes cat-card-aparece-anim {
  from { opacity: 0; transform: translateY(6px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.cat-carrusel-scroll {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0 10px 0;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* 🔒 mandatory → proximity: "mandatory" atrapaba el scroll SIEMPRE en
     la tarjeta más cercana, incluso a media inercia — se sentía frenado
     de más. "proximity" solo atrapa si ya se está cerca de un punto de
     parada, dejando que la inercia fluya con más naturalidad (más
     rápido al ojo) el resto del recorrido — sigue sin poder "volar"
     descontrolado como el scroll nativo puro de antes. */
  scroll-snap-type: x proximity;
}
.cat-carrusel-scroll::-webkit-scrollbar { display: none; }
/* Cada tarjeta dentro del carrusel tiene un ancho fijo (no depende del
   grid de 2 columnas, aquí es una fila que se desplaza) — el alto sigue
   al de su fila (flex:1 1 auto del contenedor), no un valor fijo, para
   aprovechar el espacio real que le toca a cada una de las 3 secciones. */
.cat-carrusel-scroll .cat-card {
  flex: 0 0 auto;
  align-self: stretch;
  height: 100%;
  aspect-ratio: 4 / 5;
  scroll-snap-align: start; /* 🆕 punto de "atrape" del scroll-snap de arriba */
}
/* 🆕 Efecto al tocar — la tarjeta bajo el dedo crece levemente y gana
   una sombra más marcada, dando sensación de "se levanta" de la fila.
   🔒 .cat-card-tocada en vez de :active — :active no es fiable en móvil
   durante un gesto de scroll (el navegador tarda en decidir si es un
   toque o un scroll antes de aplicarlo, y muchas veces ni llega a
   dispararse si el dedo se mueve). Añadida/quitada por JS en
   touchstart/touchend (catalogo.js), que sí es instantáneo de verdad.
   SIN transición a propósito — el gesto de scroll termina pronto, no da
   tiempo a apreciar una animación suave. z-index para que, al crecer,
   no quede tapada por las tarjetas vecinas. */
.cat-carrusel-scroll .cat-card.cat-card-tocada {
  transform: scale(1.06);
  box-shadow: 0 6px 18px rgba(0,0,0,0.45);
  z-index: 2;
  position: relative;
}

.cat-footer-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  height: 40px; /* 🆕 un poco más fino (antes 44px) */
  background: #F30256;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 6px;
  z-index: 500;
}
.cat-footer-flecha {
  background: transparent;
  border: none;
  color: #fff;
  font-size: 2.3rem; /* 🆕 reducido proporcionalmente (antes 2.6rem) — footer más fino, mismo margen de sobra sin desbordar */
  font-weight: 900;
  line-height: 1;
  padding: 0 16px;
  display: flex;
  align-items: center;
  height: 100%;
  cursor: pointer;
  transform: scaleX(1.35);
}
/* 🆕 El botón "←" se convierte en un chip "Atrás" mientras se está
   viendo una ficha (desde la primera, no solo a partir de la segunda)
   — pedido explícitamente, para volver siempre a donde se entró (grid,
   carrusel, o pantalla de juegos, según corresponda). El margen
   izquierdo no es un valor fijo — lo calcula _posicionarBotonGrid por
   JS, para que quede exactamente a mitad de camino entre el borde
   izquierdo del footer y el botón de inicio. */
#cat-footer-atras.cat-footer-boton-grid {
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.25);
  color: #fff;
  border-radius: 8px;
  padding: 6px 16px; /* 🔒 antes 4px 16px — un poco más alto, pedido explícitamente */
  height: auto;
  font-size: 0.87rem; /* 🔒 antes 0.76rem — 1 punto más grande, pedido explícitamente */
  line-height: 1.2;
  font-weight: 700;
  transform: none;
  white-space: nowrap;
}
.cat-footer-boton {
  background: transparent;
  border: none;
  color: #fff;
  font-size: 1.52rem; /* 🆕 el doble, aprox. (antes 0.76rem) */
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}
.cat-footer-boton:active,
.cat-footer-flecha:active {
  background: rgba(255,255,255,0.30);
}

.cat-grid {
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  overflow-anchor: none;
}
.cat-grid::-webkit-scrollbar { display: none; }

.cat-grid-pagina {
  flex: 0 0 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 2px;
  padding: 0 4px;
  height: 100%;
}
.cat-grid-mini {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  overflow: visible;
  scroll-snap-type: none;
  padding: 0;
  gap: 8px;
}
.cat-grid-vacio {
  text-align: center;
  color: var(--fg-dim);
  padding: 40px 20px;
  font-size: 0.85rem;
}

.cat-card {
  position: relative; /* 🆕 necesario para el velo de .cat-card-agotado, ver justo debajo */
  background: var(--bg-card);
  border: none;
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  /* Columna flexible: la imagen ocupa lo que sobra tras el título (que
     tiene tamaño fijo) — antes la imagen tenía una proporción fija
     (aspect-ratio) que no se adaptaba a la altura real de la fila del
     grid (1/3 del carrusel), y se desbordaba tapando el título de la
     fila siguiente. min-height:0 es imprescindible — sin ello, un hijo
     de grid no se deja encoger por debajo de su tamaño de contenido,
     aunque tenga flex/1fr por encima. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* Prueba: tarjetas un 5% más pequeñas, para caber más por pantalla en
     la paginación de 6 en 6. transform:scale reduce todo uniformemente
     (imagen, texto, badges) sin tener que recalcular cada medida suelta. */
  transform: scale(0.95);
  transform-origin: top center;
}
/* 🆕 Productos que cumplen filtro/talla pero ya no tienen stock
   comprable — el backend los ordena al final (campo "agotado"), aquí
   solo se refleja visualmente: algo menos de brillo/opacidad + un velo
   blanco translúcido por encima de toda la tarjeta (no oculta el
   contenido, solo lo apaga), para que al llegar a esta zona del grid se
   entienda de un vistazo que a partir de aquí son productos "no
   disponibles". Sigue siendo clicable con normalidad — es solo un
   aviso visual, no un bloqueo. */
.cat-card-agotado {
  opacity: 0.82;
  filter: brightness(0.88);
}
.cat-card-agotado::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255,255,255,0.16);
  pointer-events: none; /* el velo es puramente visual — no debe robar el clic a la tarjeta */
  z-index: 1;
}
@media (max-width: 768px) {
  /* Rebote gomoso al asentarse en una página nueva tras deslizar (no
     mientras se desliza) — igual en todos los lados de la miniatura a
     la vez, sin depender de la dirección (usa el mismo "top center" de
     la base). Colocado después de la base, reforzado por el patrón
     repetido de fallos de orden en esta sesión (una regla de móvil
     situada antes que su base en el fichero pierde la partida). */
  .cat-grid-asentando .cat-card {
    animation: catCardAsentando 0.45s ease-in-out !important;
  }

  /* 🔒 Bug real encontrado: la regla base usa top:0 fijo, sin tener en
     cuenta la altura REAL de la cabecera (variable, según lo que
     quepa en cada pantalla) — por eso el textbox aparecía desplazado
     hacia abajo respecto a la cabecera. El elemento hermano (grid, ver
     línea ~5508) ya usa --cat-franja-top (calculado por JS en
     _ajustarAlturaGridCatalogo) para esto mismo — aquí faltaba. */
  .cat-search-row {
    top: var(--cat-franja-top, 0px);
  }

  /* 🆕 Más espacio entre el último carrusel ("Colecciones destacadas")
     y la Ruleta, pedido explícitamente. */
  #cat-ruleta-seccion {
    margin-top: 22px;
  }

  /* 🆕 Marco del color de la cabecera alrededor de los módulos
     completos de Ruleta y Memory, pedido explícitamente — el aviso de
     combo (creado por JS dentro de #cat-ruleta-seccion, ver
     _comprobarComboFamilia en catalogo.js) queda automáticamente
     enmarcado también, al ser un hijo de este mismo contenedor. Espacio
     extra entre ambos módulos (margin-bottom en la Ruleta), para que el
     aviso de combo tenga hueco de sobra antes del marco de Memory. */
  #cat-ruleta-seccion,
  #cat-memory-seccion {
    border: 2px solid var(--accent);
    border-radius: 12px;
    padding: 10px 8px 16px;
  }
  #cat-ruleta-seccion {
    margin-bottom: 28px;
  }
  #cat-memory-seccion {
    padding-bottom: 34px; /* 🔒 antes 16px (heredado) — más hueco libre al final, para que se vea claramente el marco de abajo tras la tabla de récords */
  }

  /* 🆕 Marco fino del color de la cabecera en el textbox de búsqueda —
     colocado aquí, DESPUÉS de la regla base .cat-search-row .cat-input-buscar
     (misma especificidad — dos clases — así que la que aparece después
     en el fichero es la que gana, mismo motivo documentado justo arriba). */
  .cat-search-row .cat-input-buscar {
    border-color: #F30256;
  }
  /* 🆕 Un punto más de tamaño de letra al escribir, mientras el buscador
     está desplegado — selector con más clases (3, en vez de 2) que la
     base, así gana por especificidad sin depender del orden en el
     fichero. */
  .cat-search-row.cat-search-abierta .cat-input-buscar {
    font-size: 1.03rem;
  }
  /* 🆕 Texto "Buscar" en vez de icono de lupa, pedido explícitamente —
     negrita y parpadeando suavemente para que se note su presencia. El
     parpadeo se detiene mientras el buscador ya está desplegado (con
     fondo de color propio, ver cat-lupa-activa) — parpadear ahí sería
     redundante. */
  .cat-lupa-texto {
    color: #2ecc71; /* color de arranque — el propio keyframe ya alterna entre los dos verdes */
    font-weight: 800;
    animation: cat-lupa-parpadeo 2.6s ease-in-out infinite; /* 🔒 antes 1.6s — más lento, pedido explícitamente */
  }
  .cat-lupa-texto.cat-lupa-activa {
    animation: none;
  }
  @keyframes cat-lupa-parpadeo {
    0%, 100% { color: #2ecc71; } /* verde más vivo */
    50%      { color: #0a6e3a; } /* 🔒 antes solo variaba la opacidad — ahora alterna entre 2 verdes distintos, pedido explícitamente */
  }
  /* 🆕 Icono de la lupa con el fondo del color de la cabecera mientras el
     buscador está desplegado — clase añadida/quitada por JS junto con
     cat-search-abierta (ver _abrirBuscador/_cerrarBuscador en catalogo.js). */
  #btn-catalogo-lupa.cat-lupa-activa {
    background: #F30256;
    border-color: #F30256;
    color: #fff;
  }
}
@media (max-width: 768px) {
  /* Rebote gomoso al aparecer cada tarjeta del grid — mismo estilo que
     los modales. Se dispara solo con la inserción real en el DOM
     (creación de la tarjeta), no con cada repintado — no hace falta
     ninguna clase ni JS aparte para esto.
     🔒 Encontrado el motivo real de la lentitud percibida: sin acotar,
     esto se aplicaba a TODAS las páginas de golpe (aunque solo se viera
     la primera — el resto ya está en el DOM, esperando a que se deslice
     hasta ellas) — con una búsqueda de 60 resultados (10 páginas de 6),
     eran 60 tarjetas rebotando a la vez, aunque 54 no se vieran
     todavía. Acotado a la primera página únicamente — las siguientes
     tienen su propio efecto al deslizar hasta ellas (ver
     _efectoPaginaAlLlegar en catalogo.js). */
  .cat-grid-pagina:first-child .cat-card {
    animation: catCardEntra 0.5s ease-out backwards;
  }
  .cat-grid-pagina:first-child .cat-card:nth-child(1)  { animation-delay: 0.00s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(2)  { animation-delay: 0.03s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(3)  { animation-delay: 0.06s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(4)  { animation-delay: 0.09s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(5)  { animation-delay: 0.12s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(6)  { animation-delay: 0.15s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(7)  { animation-delay: 0.18s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(8)  { animation-delay: 0.21s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(9)  { animation-delay: 0.24s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(10) { animation-delay: 0.27s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(11) { animation-delay: 0.30s; }
  .cat-grid-pagina:first-child .cat-card:nth-child(12) { animation-delay: 0.33s; }
}
.cat-card-mini { border-radius: var(--radius-sm); }

.cat-card-img-wrap { position: relative; width: 100%; flex: 1 1 auto; min-height: 0; background: var(--bg-sub); }
.cat-card-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cat-card-descuento {
  position: absolute; top: 6px; left: 6px;
  background: var(--accent); color: #fff;
  font-size: 0.66rem; font-weight: 700;
  padding: 2px 6px; border-radius: 4px;
  /* 🆕 Efecto 4 reforzado — pedido explícitamente "más animación".
     Combina escala + brillo (sombra de color que crece y mengua) + un
     ligero balanceo, en vez de solo escala. Misma clase que la ficha de
     producto (.cat-ficha-imagen-descuento la reutiliza), así que este
     cambio se aplica en los dos sitios a la vez. */
  animation: cat-descuento-pulso 1.6s ease-in-out infinite;
}
@keyframes cat-descuento-pulso {
  0%, 100% { transform: scale(1) rotate(0deg); box-shadow: 0 0 0 rgba(243,2,86,0); }
  35%      { transform: scale(1.15) rotate(-4deg); box-shadow: 0 0 10px 2px rgba(243,2,86,0.55); }
  70%      { transform: scale(1.05) rotate(3deg); box-shadow: 0 0 4px 1px rgba(243,2,86,0.3); }
}
/* 🆕 Contenedor único con el fondo blanco compartido por sku+título+
   descripción — antes cada uno tenía su propio background:#fff, y entre
   bloques adyacentes aparecía a veces una línea de 1px (redondeo de
   subpíxel del transform:scale(0.95) de la tarjeta). Con un solo fondo
   pintado de una vez para los tres, no hay ninguna costura posible. */
.cat-card-texto-wrap {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  background: #fff;
}
/* SKU del producto — entre los semáforos de talla y el título, solo
   escritorio. Negrita, negro. Aplica por igual en el grid de Inicio y
   en resultados de búsqueda — ambos reutilizan _htmlContenidoTarjeta(). */
.cat-card-sku {
  font-size: 0.90rem;
  font-weight: 700;
  color: #000;
  padding: 2px 5px 0;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cat-card-titulo {
  font-size: 0.72rem;
  font-weight: 700;
  color: #000;
  padding: 2px 5px;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 🆕 Título de las tarjetas de "Colecciones destacadas" — +4 puntos
   (~0.25rem) acumulados sobre el original, pedido explícitamente en dos
   pasos. Solo estas tarjetas conservan el título (el resto de
   carruseles lo quita por JS), así que un selector específico no afecta
   a nada más. */
#cat-carrusel-colecciones .cat-card-titulo {
  font-size: 0.97rem;
}
/* Descripción corta bajo el título — solo se genera en escritorio (ver
   catalogo.js). Recortada a 2 líneas, para no descuadrar el alto de la
   tarjeta si la descripción es larga. */
.cat-card-desc {
  font-size: 0.66rem;
  color: #000;
  padding: 0 5px 3px;
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cat-card-precio-overlay {
  position: absolute; top: 6px; right: 6px;
  background: #000; color: #fff;
  font-size: 0.66rem; font-weight: 700;
  padding: 2px 6px; border-radius: 4px;
}

.cat-card-tallas-overlay {
  position: absolute;
  left: 4px; right: 4px; bottom: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  justify-content: flex-start;
}
.cat-card-talla-badge {
  flex: 0 0 auto;
  box-sizing: border-box;
  text-align: center;
  font-size: 0.56rem;
  font-weight: 700;
  color: #444;
  background: rgba(255,255,255,0.90);
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: 4px;
  padding: 1px 4px;
  line-height: 1.3;
}
/* Semáforo real por talla — ya conectado a stock_vivo (webhook_server)
   Verde: más de 10 unidades · Amarillo: 1-10 · Rojo: agotado */
.cat-talla-verde    { background: #2e7d32; border-color: #2e7d32; color: #fff; }
.cat-talla-amarilla { background: #b8860b; border-color: #b8860b; color: #fff; }
.cat-talla-roja     { background: #c62828; border-color: #c62828; color: #fff; text-decoration: line-through; opacity: 0.7; }
.cat-talla-agotado  { background: #c62828; border-color: #c62828; color: #fff; }

/* ── Ficha de producto ───────────────────────────────────────────────── */
.cat-ficha-imagen-wrap {
  position: relative;
  margin: 0 14px; /* mismo margen que .cat-ficha-info-fija (padding:14px) — ancho idéntico */
  aspect-ratio: 1 / 1;
  background: #fff; /* 🆕 blanco permanente (antes #141414) — las fotos de producto ya tienen fondo blanco, así que cualquier hueco momentáneo (carga, cambio de imagen) se funde de forma invisible, en vez de dejar ver la imagen ANTERIOR mientras la nueva termina de cargar. Aplica a móvil y escritorio por igual. */
  overflow: hidden;
}
/* 🆕 Segunda imagen en ESCRITORIO — bug real encontrado con captura:
   sin ninguna regla aquí, wrap2 (insertado justo después de wrap1 en
   el DOM) caía en flujo normal, apilándose visualmente debajo de la
   principal en vez de ocupar su sitio. Misma caja EXACTA que wrap1
   (position:relative, mismo margen/aspect-ratio/fondo), oculta por
   defecto — _mostrarImagenFichaDesktop (catalogo.js) alterna
   display:none entre las dos, nunca las dos visibles a la vez. Al
   ocupar cada una el mismo hueco en el flujo normal (nunca
   position:fixed, eso es solo cosa de móvil), la que esté visible cae
   exactamente donde antes estaba la otra. */
.cat-ficha-imagen-wrap-2 {
  display: none;
  position: relative;
  margin: 0 14px;
  aspect-ratio: 1 / 1;
  background: #fff;
  overflow: hidden;
}
/* Flechas de navegación entre imagen principal/secundaria — SOLO
   escritorio (en móvil el toque en la imagen entera ya hace de
   navegación/encoger, mecanismo totalmente distinto e independiente).
   Ocultas por defecto vía HTML/JS (solo aparecen si el producto tiene
   2ª imagen) — aquí solo se fuerza que nunca se vean en móvil, pase lo
   que pase con ese display puesto por JS. */
.cat-ficha-flecha-img {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: rgba(0,0,0,0.45);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 3;
  transition: background 0.15s ease;
}
.cat-ficha-flecha-img:hover {
  background: rgba(0,0,0,0.7);
}
.cat-ficha-flecha-img-adelante { right: 10px; }
.cat-ficha-flecha-img-atras { left: 10px; }
@media (max-width: 768px) {
  .cat-ficha-flecha-img { display: none !important; }
}
@media (max-width: 768px) {
  /* 🔒 Encontrado el motivo real de que nunca se quedara fija: esta
     misma regla vivía en el bloque móvil temprano (línea ~1662), ANTES
     que la regla base de arriba en el propio fichero — con igual
     especificidad, en CSS gana la que aparece DESPUÉS en el código,
     sin importar si está dentro de un @media o no. La base
     (position:relative) pisaba silenciosamente a esta (position:fixed).
     Aquí, DESPUÉS de la base, sí gana de verdad. */
  .cat-ficha-imagen-wrap {
    position: fixed;
    left: 0;
    right: 0;
    z-index: 5;
    margin: 0; /* 🆕 sin margen lateral en móvil — imagen a sangre, pegada a ambos bordes de pantalla (antes 14px de la regla base, heredada de .cat-ficha-info-fija). Como transform-origin ya es "top left", esto también deja la miniatura (ver más abajo) pegada de verdad al borde izquierdo de la PANTALLA, no al borde de un margen. */
    transform-origin: top left;
    transition: transform 0.28s ease, box-shadow 0.28s ease;
  }
  /* Al hacer scroll, la imagen se encoge un 50% para dejar ver más
     información debajo — vuelve a su tamaño natural al llegar arriba
     del todo (ver _controlarImagenFichaScroll en catalogo.js). Alineada
     a la izquierda (transform-origin: top left), no centrada. Sombra
     añadida en el estado reducido, para reforzar visualmente que "se
     despega" un poco del contenido que empieza a asomar por debajo. */
  .cat-ficha-imagen-wrap.cat-imagen-reducida {
    transform: scale(0.5);
    box-shadow: 0 6px 18px rgba(0,0,0,0.5);
    background: #000; /* 🆕 negro al reducirse (antes se veía el blanco permanente de la regla base, por encima del fondo dedicado que ya se puso en negro) */
  }
  /* 🆕 Segunda imagen del producto — mismo mecanismo de escala que la
     principal de arriba, pero ESPEJADO: anclada a la derecha
     (transform-origin: top right, en vez de top left), así que reduce
     hacia el margen DERECHO y aumenta de DERECHA a IZQUIERDA. Oculta por
     completo (display:none) salvo en dos momentos: mientras la ficha
     está reducida (aquí, a la derecha, junto a la principal reducida a
     la izquierda) o mientras ELLA es la imagen grande actual de la
     ficha (ver _imagenGrandeActual en catalogo.js) — nunca a la vez. */
  .cat-ficha-imagen-wrap-2 {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    z-index: 5;
    margin: 0;
    transform-origin: top right;
    transition: transform 0.28s ease, box-shadow 0.28s ease;
    background: #fff;
    overflow: hidden;
  }
  .cat-ficha-imagen-wrap-2.cat-imagen2-reducida {
    transform: scale(0.5);
    box-shadow: 0 6px 18px rgba(0,0,0,0.5);
    background: #000;
  }
  #cat-ficha-imagen-placeholder {
    transition: height 0.28s ease;
  }
  /* Fondo negro fijo detrás de la imagen — NO se encoge con ella (scale
     reduce también el ancho, no solo el alto), así que sin este fondo
     propio se veía el contenido de detrás asomando por los laterales al
     hacerse pequeña. Altura fija (la natural, sin reducir), para que
     siempre tape el hueco completo pase lo que pase con el tamaño de la
     imagen. */
  #cat-ficha-imagen-fondo {
    position: fixed;
    left: 0;
    right: 0;
    z-index: 4;
    background: #fff; /* 🆕 blanco permanente (antes #000), coherente con .cat-ficha-imagen-wrap */
    transition: height 0.28s ease;
  }
}
.cat-ficha-imagen {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .15s ease;
  -webkit-touch-callout: none;
  -webkit-user-drag: none; user-select: none;
}

.cat-aviso-flotante {
  position: fixed;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%) translateY(20px);
  background: #1a1a1a;
  color: #fff;
  padding: 10px 20px;
  border-radius: 20px;
  font-size: 0.85rem;
  font-weight: 600;
  box-shadow: 0 6px 20px rgba(0,0,0,0.35);
  z-index: 9999;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  border: 1px solid #F30256;
}
.cat-aviso-flotante-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.cat-ficha-chip-inicio {
  background: var(--bg-sub);
  border: 1px solid var(--border);
  color: var(--fg-muted);
  width: 42px;
  height: 42px;
  padding: 0;
  font-size: 1.6rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  cursor: pointer;
}
.cat-ficha-chip-inicio-activo {
  background: #F30256;
  border-color: #F30256;
  color: #fff;
}

/* Fondo en blanco liso mientras carga — cubre TODA la pantalla de la ficha,
   no solo el hueco de la imagen, para que nunca se vea nada del producto
   anterior durante la transición (título, tallas, etc. incluidos). */
.cat-ficha-cargando {
  display: none;
  position: fixed; inset: 0; z-index: 9600;
  background: #000;
  align-items: flex-start; justify-content: center; /* 🆕 arriba, donde aparecerá la imagen real — antes centrado en medio de la pantalla */
  padding-top: 60px; /* aproxima la altura de cabecera + barra de búsqueda en móvil */
  font-size: 0.9rem; font-weight: 600; color: var(--fg-bad);
  text-align: center; padding-left: 20px; padding-right: 20px;
}

/* 🆕 Esqueleto de carga (Opción 3, parte 2) — cajas grises con un brillo
   animado que recorre de lado a lado, imitando dónde van a aparecer
   imagen/título/precio/tallas. Sustituye al vacío/negro plano anterior;
   el caso de error (fichaCargando.textContent) sigue siendo texto
   plano, sin tocar. */
.cat-ficha-skeleton {
  width: 100%;
  max-width: 380px;
  padding: 0 14px;
  text-align: left;
}
.cat-ficha-skeleton-img,
.cat-ficha-skeleton-linea,
.cat-ficha-skeleton-talla {
  background: linear-gradient(90deg, #1a1a1a 25%, #2a2a2a 37%, #1a1a1a 63%);
  background-size: 400% 100%;
  animation: cat-skeleton-shimmer 1.4s ease infinite;
  border-radius: var(--radius-sm);
}
.cat-ficha-skeleton-img {
  aspect-ratio: 1 / 1;
}
.cat-ficha-skeleton-linea {
  height: 16px;
  margin-top: 14px;
  border-radius: 4px;
}
.cat-ficha-skeleton-linea-titulo { width: 70%; }
.cat-ficha-skeleton-linea-precio { width: 35%; margin-top: 8px; }
.cat-ficha-skeleton-tallas {
  display: flex;
  gap: 8px;
  margin-top: 18px;
}
.cat-ficha-skeleton-talla {
  flex: 1;
  height: 44px;
}
@keyframes cat-skeleton-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* Zoom de imagen en escritorio — clic sobre la imagen de la ficha: fondo
   negro, imagen al 2x centrada, título debajo. Ocupa el panel central del
   Catálogo (no toda la pantalla, la barra lateral/pestañas siguen
   visibles). Oculto por defecto — se activa con la clase -activo. */
.cat-ficha-zoom-escritorio {
  display: flex;
  visibility: hidden;
  opacity: 0;
  position: fixed;
  z-index: 500;
  background: #000;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 10px;
  cursor: zoom-out;
  padding: 8px 40px 40px 70px;
  overflow-y: auto;
  transition: opacity 0.15s ease, visibility 0s linear 0.15s;
}

/* 🆕 Overlay del gesto "arrastrar hacia abajo para ampliar" en la ficha
   (solo móvil) — position:fixed, anclado por JS a la posición real de
   la imagen en el instante de empezar el gesto (mantiene la cabecera
   del producto en su sitio). z-index alto para dibujarse por ENCIMA
   del resto de la ficha (título, precio, tallas), tapándolo mientras
   dura el gesto, en vez de empujarlo hacia abajo. overflow-x:auto para
   el scroll horizontal nativo de verdad sobre la imagen ya ampliada
   (su ancho real en píxeles, fijado por JS, es mayor que el del
   contenedor — desbordamiento genuino, no simulado con transform). Sin
   overflow-y ni límite de alto — crece hacia abajo sin recortarse. */
.cat-ficha-zoom-arrastre-overlay {
  position: fixed;
  z-index: 550;
  background: #fff; /* mismo fondo que .cat-ficha-imagen-wrap, para que el cambio sea invisible */
  overflow-x: auto;
  overflow-y: visible;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.cat-ficha-zoom-arrastre-overlay::-webkit-scrollbar { display: none; }
.cat-ficha-zoom-arrastre-img {
  display: block;
  max-width: none; /* 🔒 imprescindible — sin esto, el ancho real fijado por JS quedaría limitado por defecto y nunca desbordaría de verdad */
  object-fit: cover;
}
.cat-ficha-zoom-escritorio-activo {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.15s ease, visibility 0s linear 0s;
}
.cat-ficha-zoom-escritorio-img {
  width: 616px; /* 🆕 684px − 10% (sustituye al 5% anterior, no se acumula) */
  max-width: 82%;
  max-height: 76vh;
  height: auto;
  object-fit: contain;
  margin-top: 0;
  transform: scale(0.94);
  opacity: 0;
  transition: transform 0.18s ease-out, opacity 0.18s ease-out;
}
.cat-ficha-zoom-escritorio-activo .cat-ficha-zoom-escritorio-img {
  transform: scale(1);
  opacity: 1;
}
.cat-ficha-zoom-escritorio-titulo {
  color: #fff;
  font-size: 1.2rem;
  font-weight: 700;
  text-align: center;
  max-width: 80%;
}

.cat-ficha-info-fija {
  margin: 0 5px;
  padding: 10px 0 8px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg-card);
}
@media (max-width: 768px) {
  /* Fondo negro completo + bordecito del color de cabecera — mismo
     estilo que los badges de talla (radio de esquina), pero con el
     color de acento en vez del gris tenue de siempre. */
  .cat-ficha-info-fija {
    background: #000;
    border: 2px solid var(--accent);
    border-bottom: 2px solid var(--accent);
    border-radius: var(--radius-sm);
    padding: 10px 12px 8px;
  }

  /* 🆕 Bug real encontrado con vídeo — "parpadeo negro" al entrar a una
     ficha desde el grid: el overlay de carga (fondo, arriba) usaba
     inset:0, cubriendo TODA la pantalla — incluida la cabecera rosa
     (barra de búsqueda, iconos), que desaparecía por completo durante
     la carga. Ahora empieza justo debajo de la cabecera (misma variable
     ya usada en otros overlays de la ficha), dejándola siempre visible
     — solo el contenido de debajo se ve negro con el esqueleto mientras
     carga, no la pantalla entera. */
  .cat-ficha-cargando {
    top: var(--header-alto-movil, 44px);
    left: 0; right: 0; bottom: 0;
    padding-top: 16px; /* antes 60px, aproximaba a mano la altura de cabecera que 'top' ya cubre ahora */
  }
}
.cat-ficha-titulo { font-size: 0.95rem; font-weight: 700; color: var(--fg-main); margin-bottom: 4px; }
.cat-ficha-precio-row { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
.cat-ficha-sku { font-size: 1.15rem; font-weight: 400; color: var(--fg-muted); }
.cat-ficha-proveedor { font-size: 0.78rem; color: var(--fg-dim); margin-top: 2px; }
.cat-ficha-precio { font-size: 1.15rem; font-weight: 700; color: var(--fg-main); }
.cat-ficha-compare { font-size: 0.8rem; color: var(--fg-dim); text-decoration: line-through; }
.cat-ficha-descuento { font-size: 0.75rem; font-weight: 700; color: var(--accent); }
.cat-ficha-chip-inicio { margin-left: auto; }

.cat-ficha-acciones-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 4px; }
.cat-ficha-btn-accion {
  background: var(--accent-soft); border: 1px solid var(--accent);
  color: var(--accent); border-radius: var(--radius-sm);
  padding: 5px 10px; font-size: 0.72rem; font-weight: 600; cursor: pointer;
}
.cat-ppta-stock.cat-talla-verde    { color: #8bffa1; }
.cat-ppta-stock.cat-talla-amarilla { color: #ffe27a; }
.cat-ppta-stock.cat-talla-roja     { color: #ffb3b3; }
.cat-ficha-pareja-disp-badge {
  font-size: 1.44rem; line-height: 1; color: #b8860b;
  display: inline-flex; align-items: center;
  cursor: pointer;
}
.cat-stock-hq {
  font-size: 1.18em; color: #fff;
  padding: 1px 7px;
  border-radius: 4px;
}
.cat-stock-hq-verde    { background: #2e7d32; }
.cat-stock-hq-amarilla { background: #b8860b; }
.cat-stock-hq-roja     { background: #c62828; }
.cat-stock-otra-sucursal { color: #f6c343; }

.cat-ficha-scroll { padding: 10px 5px 20px; }
@media (min-width: 769px) {
  .cat-ficha-scroll {
    /* 🆕 Recalculado: la columna izquierda ya no mide 380px reales — al
       bajar su zoom a 0.81225 (sin tocar el de esta columna, 0.855), su
       ancho real en pantalla es 380×0.81225≈308.66px. El hueco quedaba
       de más porque este margen seguía calculado para el ancho antiguo
       (380px). Real necesario: 308.66px (ancho real izquierda) + 40px
       (gap) = 348.66px, compensado por el zoom PROPIO de esta columna
       (÷0.855) para que el resultado final en pantalla sea ese real. */
    margin-left: 408px; /* (380×0.81225 + 40) ÷ 0.855 */
  }
}
.cat-ficha-desc {
  font-size: 0.85rem;
  color: var(--fg-main);
  background: transparent;
  text-align: justify;
  margin: 0 0 8px;
  padding: 0;
}
.cat-ficha-desc-linea { margin-bottom: 3px; }
.cat-ficha-desc-linea:last-child { margin-bottom: 0; }

.cat-ficha-tallas { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
@media (max-width: 768px) {
  /* 🔒 Dos badges de talla por línea en móvil — igual que con la imagen
     fija, esta regla vivía ANTES que la base de arriba (display:flex) en
     el propio fichero, y con igual especificidad perdía la partida.
     .cat-bundle-grupo no tenía este problema porque su base no fija
     "display" en absoluto — nada que ganarle ahí. Aquí, después de la
     base, sí gana de verdad. */
  .cat-ficha-tallas,
  .cat-bundle-grupo {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 6px;
  }
}
.cat-talla-row {
  background: var(--bg-sub);
  border: 2px solid var(--border-soft);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
}
.cat-talla-row.cat-talla-verde    { background: rgba(46,125,50,0.08); border-color: #2e7d32; }
.cat-talla-row.cat-talla-amarilla { background: rgba(184,134,11,0.08); border-color: #b8860b; }
.cat-talla-row.cat-talla-roja     { background: rgba(198,40,40,0.08); border-color: #c62828; }
.cat-talla-linea1 {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 0.94rem; font-weight: 600; color: var(--fg-main);
}
.cat-talla-linea2 {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 0.78rem; color: var(--fg-muted); margin-top: 2px;
}
/* 🆕 Todos los elementos de la caja de talla (SKU, ubicación, stock,
   sucursales...) un punto más de tamaño de fuente — SOLO en móvil.
   Colocado DESPUÉS de las reglas base de arriba (misma especificidad,
   así que el orden de aparición decide) — puesto antes perdería la
   partida contra ellas, exactamente el mismo problema que ya explica el
   candado de la vista densa un poco más abajo en este fichero.
   +0.0625rem ≈ +1px sobre cada tamaño base. */
@media (max-width: 768px) {
  .cat-talla-linea1         { font-size: 1.0025rem; }
  .cat-talla-ubicacion-2alm { font-size: 0.8825rem; }
  .cat-talla-comprometido   { font-size: 0.8125rem; }
  .cat-talla-linea2         { font-size: 0.8425rem; }
}

.cat-ficha-seccion { margin-bottom: 16px; }
.cat-ficha-seccion-titulo { font-size: 0.78rem; font-weight: 700; color: var(--fg-main); margin-bottom: 8px; }

.cat-bundle-cascada { display: flex; flex-direction: column; }
.cat-bundle-grupo { margin-bottom: 14px; }
.cat-bundle-grupo-titulo {
  font-size: 0.74rem; font-weight: 700; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.3px;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
}
.cat-bundle-grupo-sku {
  background: transparent;
  border: 1px solid var(--accent);
  color: #fff;
  border-radius: 999px;
  padding: 2px 9px;
  font-size: 0.92rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
  flex-shrink: 0;
}

.cat-modal-guia-box { max-width: 640px; width: 92%; }

/* Cabecera del modal: miniatura + título + resumen de tallas al lado */
.cat-modal-guia-cabecera {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 12px 14px 4px;
  flex-shrink: 0;
}
.cat-modal-guia-imagen {
  width: 84px; height: 84px;
  object-fit: cover;
  border-radius: 8px;
  background: #fff;
  flex-shrink: 0;
}
.cat-modal-guia-info { flex: 1 1 auto; min-width: 0; }
.cat-modal-guia-titulo {
  font-weight: 700; font-size: 0.95rem;
  color: var(--fg-main); margin-bottom: 6px;
}

/* Un bloque por género (Pareja/Grupo) — título + su propia tabla */
.cat-guia-bloque-genero { margin: 14px 14px 0; }
.cat-guia-bloque-genero:first-child { margin-top: 6px; }
.cat-guia-genero-titulo {
  font-size: 0.8rem; font-weight: 700; color: #F30256;
  text-transform: uppercase; letter-spacing: 0.3px;
  margin-bottom: 6px;
}
.cat-guia-sin-datos {
  padding: 20px 14px; text-align: center;
  color: var(--fg-dim); font-size: 0.85rem;
}

/* Modal de favoritos ("Inicio") */
.cat-modal-favoritos-box { max-width: 460px; width: 92%; }
#cat-modal-favoritos {
  align-items: flex-start;
  padding-top: 20px;
  z-index: 9200; /* por encima del modal de Filtros (9000), para poder abrirse sin cerrarlo primero */
}

/* Botón "Proveedor" — mismo acento a la izquierda que los bloques de
   talla (border-left, identidad visual del panel de filtros). Se pone
   del color de la cabecera en cuanto hay algún proveedor marcado (clase
   .cat-btn-proveedor-activo, aplicada por JS al confirmar con Ok). */
.cat-btn-proveedor {
  display: block;
  width: 100%;
  text-align: left;
  background: #131313;
  border: 1px solid rgba(255,255,255,0.07);
  border-left: 3px solid rgba(243,2,86,0.55);
  border-radius: 9px;
  padding: 10px 11px;
  margin-bottom: 6px;
  color: #b8bcc8;
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  cursor: pointer;
}
.cat-btn-proveedor-activo {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* Modal Proveedores — mismo ancho/posición que Favoritos, por encima del
   modal de Filtros para poder abrirse sin cerrarlo primero. */
.cat-modal-proveedor-box {
  max-width: 460px;
  width: 92%;
  max-height: calc(100vh - 220px); /* dentro del hueco que deja el padding-top+padding-bottom del overlay */
  overflow-y: auto;
  border: 2px solid var(--accent);
}
.cat-modal-proveedor-box .cat-modal-filtros-botones {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-shrink: 0; /* 🔒 imprescindible: .modal-box (base) sigue siendo flex-column, sin esto los botones podían encogerse a altura cero con muchos chips */
  background: #000;
}
#cat-proveedor-ok, #cat-proveedor-limpiar {
  display: block !important;
  visibility: visible !important;
  opacity: 1 !important;
}
#cat-modal-proveedor {
  align-items: center;
  padding: 40px 0;
  overflow-y: auto;
  z-index: 9200;
}
#cat-modal-proveedor .modal-header { background: #000; flex-shrink: 0; }
.cat-modal-proveedor-chips {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  justify-content: flex-start;
  gap: 6px;
  padding: 14px;
  background: #131313; /* mismo gris oscuro que los bloques de talla seleccionables (.cat-filtro-genero-bloque) — conjunto visual coherente con el resto del catálogo */
}
.cat-modal-proveedor-chips .cat-chip {
  flex: 0 0 auto;
  background: #1e1e1e;
  border-color: rgba(255,255,255,0.1);
  padding: 4px 10px;
  font-size: 0.98rem;
  color: #b8bcc8; /* mismo tono que el resto de chips del panel de filtros — fuera del alcance de #panel-catalogo, la variable --fg-muted heredada caía en su valor raíz (más azulado), de ahí la diferencia de color */
}
.cat-modal-proveedor-chips .cat-chip.cat-chip-activo {
  background: #F30256;
  border-color: #F30256;
  color: #fff;
}
#cat-modal-favoritos .modal-header {
  background: #000;
}
.cat-modal-favoritos-lista {
  max-height: 60vh;
  overflow-y: auto;
  padding: 6px 10px;
}
.cat-fav-fila {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--border-soft);
}
.cat-fav-fila-img {
  width: 48px; height: 48px;
  object-fit: cover;
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
  flex-shrink: 0;
}
.cat-fav-fila-titulo {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.85rem;
  color: var(--fg-main);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat-fav-fila-borrar {
  background: none;
  border: none;
  font-size: 1rem;
  cursor: pointer;
  padding: 4px 6px;
  flex-shrink: 0;
  opacity: 0.75;
}
.cat-fav-fila-borrar:hover { opacity: 1; }
.cat-modal-favoritos-pie {
  padding: 10px 14px 14px;
  display: flex;
  justify-content: center;
}
.cat-btn-favoritos-limpiar {
  background: rgba(198,40,40,0.12);
  border: 1px solid #c62828;
  color: #ff8a80;
  padding: 8px 18px;
  border-radius: var(--radius-sm);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
}

/* Tabla: talla como cabecera de columna, una fila por medida. Envuelta
   en un contenedor con su propio scroll horizontal — si hay muchas
   tallas (Grupos con 4-5 componentes, o tallas infantiles con muchos
   rangos), se desliza ahí dentro en vez de desbordar el ancho del modal
   o de la pantalla. */
.cat-guia-tabla-scroll {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-bottom: 14px;
  border-radius: 8px;
  border: 1px solid var(--border-soft);
}
.cat-guia-tabla { width: 100%; min-width: 100%; border-collapse: collapse; }
.cat-guia-tabla th {
  padding: 8px 9px; font-size: 0.72rem; font-weight: 700;
  color: #fff; text-align: center;
  white-space: nowrap;
  background: #F30256;
}
.cat-guia-tabla th:first-child {
  text-align: left;
  border-top-left-radius: 6px;
  position: sticky;
  left: 0;
  z-index: 3;
}
.cat-guia-tabla th:last-child { border-top-right-radius: 6px; }
.cat-guia-tabla tr:nth-child(even) td { background: rgba(243,2,86,0.05); }
.cat-guia-tabla td {
  padding: 8px 9px; font-size: 0.78rem;
  border-bottom: 1px solid var(--border-soft);
  text-align: center;
  white-space: nowrap;
  color: var(--fg-main); font-weight: 600;
}
.cat-guia-tabla td:first-child {
  color: var(--fg-muted); font-weight: 600; text-align: left;
  white-space: normal;
  max-width: 130px;
  /* Columna fija (sticky) al hacer scroll horizontal — con Parejas/Grupos
     puede haber muchas tallas/columnas y esta primera (el nombre de la
     medida) se perdía de vista al desplazar hacia la derecha. Fondo
     SÓLIDO (no la rgba semitransparente de las filas pares) — si no,
     se transparentaría el resto de columnas al pasar por debajo. */
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--bg-card);
  box-shadow: 2px 0 4px rgba(0,0,0,0.25);
}
.cat-guia-tabla tr:nth-child(even) td:first-child {
  background: #241a1d; /* versión sólida del tono rosa apagado de las filas pares — la rgba no sirve en un elemento sticky */
}
.cat-modal-guia-body { padding: 0 0 10px; overflow-y: auto; overflow-x: hidden; min-height: 0; }

/* ════════════════════════════════════════════════════════════════════
   CATÁLOGO EN ESCRITORIO — primera adaptación (miniaturas, inicio, grid
   de resultados). El pie de página (Atrás/Reset/Colecciones/Adelante) es
   una barra de navegación pensada solo para móvil — en escritorio ya
   está la barra de pestañas normal, así que se oculta entera. La
   estructura de scroll-snap horizontal por páginas de 6 (2 columnas) se
   mantiene igual que en móvil por ahora — este primer paso ajusta
   tamaños, no la disposición en columnas (eso quedaría para un segundo
   pase, una vez se vea cómo queda esto en pantalla real).
   ════════════════════════════════════════════════════════════════════ */
@media (min-width: 769px) {
  .cat-footer-bar { display: none !important; }

  /* Grid de resultados VERTICAL en escritorio — las "páginas" se apilan
     hacia abajo en vez de ponerse una al lado de otra; scroll con la
     rueda del ratón, nativo del navegador, sin tocar nada. En móvil
     sigue siendo horizontal (scroll-snap por páginas), sin cambios. */
  .cat-grid {
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
    scroll-snap-type: none; /* el snap por página no aplica al scroll vertical continuo */
  }
  .cat-grid-pagina {
    flex: 0 0 auto; /* alto natural (el de su contenido), no 100% del alto del grid */
    scroll-snap-align: none;
  }

  /* Grid de resultados en escritorio: 6 columnas fijas x 3 filas. Cada
     tarjeta llena su celda del grid (no fuerza su propio aspect-ratio
     externo, que competía con el número de columnas fijo) — la imagen de
     dentro ya mantiene su proporción real vía object-fit:cover. */
  .cat-grid-pagina {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    grid-template-rows: repeat(2, 1fr);
    max-width: none;
    margin: 0;
    gap: 14px;
    padding: 16px;
  }
  .cat-grid-pagina .cat-card {
    height: 100%;
    width: 100%;
  }
  .cat-grid-pagina .cat-card .cat-card-titulo { font-size: 1.2rem; }
  .cat-grid-pagina .cat-card .cat-card-desc   { font-size: 0.92rem; }
  .cat-card-precio-overlay,
  .cat-card-descuento { font-size: 0.76rem; padding: 3px 8px; }
  .cat-card-talla-badge { font-size: 0.66rem; padding: 2px 6px; }

  /* Modo GRID (vista densa) — activado con el botón ▦: tantas columnas
     como quepan (mínimo 140px cada una, responsive de verdad) x 3 filas,
     en vez del estándar fijo de 6x2. Toda la miniatura se reduce
     proporcionalmente (título, descripción, precio, badges de talla) —
     no solo el tamaño de la celda. */
  .cat-grid-pagina.cat-grid-pagina-denso {
    height: auto;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    grid-auto-rows: 250px;
    gap: 8px;
    padding: 10px;
  }
  .cat-grid-pagina-denso .cat-card {
    height: 100%;
    width: 100%;
  }
  .cat-grid-pagina-denso .cat-card .cat-card-titulo { font-size: 0.78rem; padding: 3px 6px; }
  .cat-grid-pagina-denso .cat-card .cat-card-desc   { display: none; } /* sin sitio en una tarjeta tan pequeña */
  .cat-grid-pagina-denso .cat-card .cat-zoom-tallas-genero { display: none; } /* Grupos/Parejas: el desglose Hombre/Mujer/Niño/Niña empujaba la imagen fuera del alto fijo de la tarjeta */
  .cat-grid-pagina-denso .cat-card-precio-overlay,
  .cat-grid-pagina-denso .cat-card-descuento { font-size: 0.62rem; padding: 2px 6px; }
  .cat-grid-pagina-denso .cat-card-talla-badge { font-size: 0.56rem; padding: 1px 4px; }

  /* Carruseles de inicio/vista previa: tarjetas un poco más anchas —
     con ratón, un objetivo más grande es más cómodo que en el dedo. */
  .cat-carrusel-scroll .cat-card { aspect-ratio: 3 / 2; }
  .cat-carrusel-titulo { font-size: 1.05rem; padding: 6px 14px 3px; }
  .cat-carrusel-scroll { padding: 0 14px 4px; gap: 10px; }

  /* Cursor de "agarrar" — indica que estos carruseles se pueden
     arrastrar con el ratón (clic+arrastrar), además de la rueda. */
  .cat-grid,
  .cat-carrusel-scroll {
    cursor: grab;
  }
  .cat-grid.cat-arrastrando,
  .cat-carrusel-scroll.cat-arrastrando {
    cursor: grabbing;
  }

  /* Atrás/Reset/Colecciones/Adelante reubicados en la fila de búsqueda
     (ver _configurarCatalogoEscritorio) — más pequeños que en el pie de
     página de móvil, para caber cómodos en esta fila compacta. */
  .cat-search-row .cat-footer-flecha {
    font-size: 2rem;
    padding: 4px 16px;
    margin: 0 4px;
    color: var(--fg-main);
    flex-shrink: 0;
  }
  .cat-search-row .cat-footer-boton {
    padding: 4px 16px;
    min-width: 90px;
    text-align: center;
    font-size: 1.28rem; /* el doble del tamaño anterior (0.64rem) */
    flex-shrink: 0;
    white-space: nowrap;
  }
  .cat-search-row #cat-btn-jarana {
    flex-shrink: 0;
  }

  /* ══════════════════════════════════════════════════════════════════
     FICHA DE PRODUCTO EN ESCRITORIO — rediseño completo. En móvil todo
     va apilado en una sola columna estrecha (imagen arriba, info debajo,
     tallas en filas). En escritorio eso se estiraba a todo el ancho del
     monitor sin ningún límite, dejando filas de texto larguísimas y mal
     legibles. Aquí se pasa a un diseño de dos columnas típico de ficha
     de producto (imagen a la izquierda, info a la derecha) con un ancho
     máximo centrado, y las tallas/bundle pasan de filas apiladas a una
     cuadrícula compacta que aprovecha el espacio en vez de desperdiciarlo
     en líneas eternas. */
  #cat-vista-ficha {
    max-width: 1480px;
    margin: 0;
    display: flex;
    align-items: flex-start;
    column-gap: 40px;
    padding: 100px 24px 40px;
  }
  .cat-ficha-izquierda {
    display: flex;
    flex-direction: column;
    width: 380px;
    position: fixed;
    /* 🆕 left/top compensados por el zoom: Chromium escala también los
       offsets de position:fixed del propio elemento al que se aplica
       "zoom" — con left:324px y zoom:0.812 el resultado real en pantalla
       sería menor, colándose bajo el panel lateral (300px). Dividiendo
       por el factor de zoom (324/0.812, 200/0.812) el resultado final en
       pantalla vuelve a ser el real pretendido (324px, 200px), mientras
       que el CONTENIDO (ancho, tipografías) sí se ve más pequeño. */
    left: 399px; /* 324px reales ÷ 0.81225 */
    top: 246px; /* 200px reales ÷ 0.81225 */
    z-index: 10; /* por debajo de la barra de búsqueda (z-index:50) — nunca debe quedar tapada, aunque hubiera algo de solape vertical */
    /* 🆕 Imagen + módulo de info al 81.225% (85.5% − 5% más pequeña, a
       petición — SOLO esta columna; la derecha con tallas/relacionados
       se queda en 0.855, sin tocar, ver .cat-ficha-scroll más abajo). En
       monitores de 24" (a diferencia de los 28" usados durante el
       desarrollo) la columna izquierda + descripción desbordaba
       verticalmente. No se aplica a #cat-vista-ficha entero porque ahí
       también vive la barra de búsqueda (compartida con el grid vía
       #cat-ficha-buscador-slot) — solo se reduce el contenido real de
       la ficha, no la barra. */
    zoom: 0.81225;
  }
  /* Franja negra de ancho completo tras la barra de búsqueda — la barra
     en sí tiene un ancho limitado (580-820px) y no cubre toda la
     pantalla, así que sin esto el contenido que se desplaza por detrás
     (parecidos, relacionados, la propia imagen de la izquierda) asomaba
     por los lados al hacer scroll. z-index entre la columna izquierda
     (10) y la barra de búsqueda (50), para quedar por delante del
     contenido pero por detrás de la barra en sí. */
  #cat-vista-ficha::before,
  #cat-vista-grid::before {
    content: "";
    position: fixed;
    top: var(--cat-franja-top, 0px); /* mismo top real que .cat-search-row, calculado por JS (_ajustarAlturaGridCatalogo) — antes fijo a 0, dejaba sin tapar el hueco entre 0 y altoHeader */
    left: 300px;
    right: 0;
    height: 95px; /* altura real de la barra de búsqueda + "Estás en", sin invadir el contenido de debajo */
    background: #000;
    z-index: 30;
    pointer-events: none;
  }
  .cat-ficha-imagen-wrap {
    max-width: none;
    margin: 0;
    border-radius: 10px;
  }
  .cat-ficha-chip-inicio {
    width: 46px;
    height: 46px;
    font-size: 1.75rem;
    margin-right: 10px;
  }
  .cat-ficha-info-fija {
    margin: 10px 0 12px;
    padding: 12px 14px;
    border-bottom: none;
    background: #131313;
    border: 1px solid rgba(255,255,255,0.07);
    border-left: 3px solid var(--accent);
    border-radius: 9px;
  }
  /* 🆕 +1pt (~0.083rem) en todos los textos del módulo de info bajo la imagen */
  .cat-ficha-titulo { font-size: 1.51rem; margin-bottom: 10px; line-height: 1.3; }
  .cat-ficha-desc { font-size: 1.07rem; line-height: 1.5; }
  .cat-ficha-precio-row { margin: 10px 0; }
  .cat-ficha-precio { font-size: 1.81rem; }
  .cat-ficha-sku { font-size: 1.81rem; font-weight: 400; }
  .cat-ficha-proveedor { font-size: 0.96rem; }
  .cat-ficha-acciones-row { margin-top: 4px; gap: 10px; }
  .cat-ficha-btn-accion { padding: 8px 14px; font-size: 1.01rem; }
  .cat-ficha-scroll {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    /* 🆕 0.855, SIN tocar — a petición, esta columna (tallas/relacionados)
       se queda en su tamaño anterior aunque la izquierda (imagen+info,
       ver .cat-ficha-izquierda) haya bajado más, a 0.81225. Las dos
       columnas ya NO comparten el mismo zoom a propósito. */
    zoom: 0.855;
  }

  /* Tallas: cuadrícula de tarjetas compactas en vez de filas apiladas de
     ancho completo — mucho más legible y aprovecha el espacio horizontal
     real de un monitor. */
  .cat-ficha-tallas {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 230px));
    justify-content: start;
    gap: 10px;
  }
  .cat-talla-row { padding: 8px 12px; }
  .cat-talla-linea1 { font-size: 1.13rem; }
  .cat-talla-comprometido { font-size: 0.88rem; }
  .cat-talla-linea2 { flex-wrap: nowrap; white-space: nowrap; font-size: 0.91rem; }
  .cat-talla-comprometido-txt { font-size: 0.91rem; }
  .cat-ficha-seccion-titulo { font-size: 0.91rem; }
  .cat-bundle-grupo-titulo { font-size: 0.87rem; }
  .cat-ficha-scroll .cat-card-titulo { font-size: 0.85rem; }

  /* Pareja/Grupo: cada género en su propia tarjeta con las tallas del
     componente también en cuadrícula, no apiladas de ancho completo —
     las filas de talla se añaden como hijos DIRECTOS de .cat-bundle-grupo
     (no dentro de un .cat-ficha-tallas), así que el grid va aquí. */
  .cat-bundle-cascada { display: block; }
  .cat-bundle-grupo {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 230px));
    justify-content: start;
    gap: 8px;
    align-items: start;
  }
  .cat-bundle-grupo-titulo { grid-column: 1 / -1; }

  /* Relacionados / Quizá te interese: más columnas, aprovechando el
     ancho real disponible en vez de quedarse en las 3 fijas de móvil. */
  .cat-grid-mini { grid-template-columns: repeat(4, 1fr); gap: 12px; }

  /* La búsqueda ya no flota por encima — ocupa espacio real arriba a la
     izquierda (25% de ancho) — el grid/carruseles necesitan hueco para
     no quedar tapados debajo de ella. */
  /* 🆕 Resultados (grid) reducidos al 90% en escritorio — mismo motivo
     que el 80% ya aplicado en la ficha (.cat-ficha-izquierda/.cat-ficha-scroll):
     en monitores de 24" (frente a los 28" usados en desarrollo) se veía
     demasiado grande. No se aplica a #cat-vista-ficha (ese ya tiene su
     propio 80% en sus columnas internas, no en el contenedor entero). */
  #cat-vista-grid { padding-top: 84px; zoom: 0.9; }

  /* La cámara (escaneo de código de barras) es una función pensada para
     el móvil — no tiene sentido en escritorio, se oculta. La lupa y el
     botón Filtros de la cabecera ya se ocultan por JS (ver app.js). */
  #cat-btn-escaner { display: none !important; }

  /* Panel lateral de filtros del Catálogo — sustituye a la imagen/ficha
     de producto que muestra el sidebar en el resto de módulos. Los
     elementos de dentro (chips, botones Ok/Limpiar) son los mismos que
     en móvil, reubicados aquí por JS (_configurarCatalogoEscritorio en
     app.js) — misma lógica, solo cambia dónde viven en la página. */
  #sidebar.sidebar-catalogo {
    background: linear-gradient(180deg, #0d0d0d 0%, #000 220px);
    background-color: #000;
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0;
    top: 52px; /* altura de la cabecera */
    width: 300px;
    height: calc(100vh - 52px);
    overflow-y: auto;
    z-index: 40;
  }
  /* Sin ninguna imagen/título/descripción de producto de otro módulo —
     el sidebar en Catálogo es solo el panel de filtros. Refuerzo por CSS
     de lo que ya se oculta por JS, por si acaso. */
  #sidebar.sidebar-catalogo #sidebar-img-placeholder,
  #sidebar.sidebar-catalogo #sidebar-img,
  #sidebar.sidebar-catalogo #sidebar-title,
  #sidebar.sidebar-catalogo #sidebar-desc {
    display: none !important;
  }
  #sidebar-catalogo-filtros {
    padding: 20px 10px 8px;
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
    background: #000;
    /* 🆕 Panel de filtros un 10% más pequeño (zoom:0.9) — el 20%
       anterior se veía demasiado reducido. Si el top/left de este panel
       está puesto por JS como estilo inline en este mismo elemento (a
       confirmar en app.js), el zoom también lo escalaría — avisar si
       se ve desplazado para compensarlo igual que se hizo con la ficha. */
    zoom: 0.9;
  }

  /* Tipo (Disfraces/Complementos/Decoración) como control segmentado —
     una sola franja agrupada, no píldoras sueltas flotando. */
  #sidebar-catalogo-filtros #cat-chips-row-2 {
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    background: #161616;
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
    padding: 3px;
    margin-bottom: 10px;
  }
  #sidebar-catalogo-filtros #cat-chips-row-2 .cat-chip {
    flex: 1 1 auto;
    background: transparent;
    border: none;
    border-radius: 6px;
    font-size: 0.8225rem;
    padding: 5px 8px;
    color: var(--fg-muted);
  }
  #sidebar-catalogo-filtros #cat-chips-row-2 .cat-chip[data-tipo="decoracion"] {
    flex-basis: 100% !important;
  }
  #sidebar-catalogo-filtros #cat-chips-row-2 .cat-chip-activo {
    background: #F30256;
    color: #fff;
  }

  /* Cada género en su propia tarjeta — separa visualmente los bloques,
     con un acento de color a la izquierda que coincide con la identidad
     del Catálogo (el mismo rosa de la cabecera). Todo apretado hacia
     arriba, sin aire de sobra, para que las 5 tarjetas quepan sin scroll. */
  #sidebar-catalogo-filtros .cat-filtro-genero-bloque {
    background: #131313;
    border: 1px solid rgba(255,255,255,0.07);
    border-left: 3px solid rgba(243,2,86,0.55);
    border-radius: 8px;
    padding: 8px 11px 9px;
    margin-bottom: 7px;
  }
  #sidebar-catalogo-filtros .cat-filtro-genero-titulo {
    display: inline-block;
    background: #1e1e1e;
    border: 1px solid rgba(255,255,255,0.14);
    border-radius: 6px;
    padding: 4px 11px;
    margin: 0 0 7px;
    font-size: 0.8025rem;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    color: var(--fg-muted);
    cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
  }
  #sidebar-catalogo-filtros .cat-genero-titulo-activo {
    background: #F30256;
    color: #fff;
    border-color: #F30256;
  }
  #sidebar-catalogo-filtros .cat-chips-row {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-bottom: 0;
  }
  #sidebar-catalogo-filtros .cat-chips-tallas .cat-chip {
    background: #1e1e1e;
    border-color: #fff;
    font-size: 0.7625rem;
    padding: 4px 8px;
  }
  #sidebar-catalogo-filtros .cat-chips-tallas .cat-chip.cat-chip-activo {
    background: #F30256;
    border-color: #F30256;
    color: #fff;
  }

  #sidebar-catalogo-filtros .cat-check-talla-todas {
    padding: 6px 9px;
    font-size: 0.8225rem;
    margin-top: 2px;
  }
  #sidebar-catalogo-filtros .cat-check-talla-todas input[type="checkbox"] {
    width: 14px;
    height: 14px;
  }

  /* Limpiar y Aceptar anclados abajo del todo del panel, a todo el
     ancho combinado — siempre a mano sin tener que bajar el scroll
     hasta el final de las 5 tarjetas. Filtros vuelven a confirmarse con
     Aceptar (sin búsqueda en vivo — se acumulaban clics y la respuesta
     del servidor no era instantánea, dando sensación de lentitud). */
  #sidebar-catalogo-filtros .cat-modal-filtros-botones {
    display: flex;
    gap: 6px;
    margin: 8px 0 0;
    padding-top: 8px;
    border-top: 1px solid rgba(255,255,255,0.08);
    position: sticky;
    bottom: 0;
    background: #000;
  }
  #sidebar-catalogo-filtros #cat-filtros-limpiar,
  #sidebar-catalogo-filtros #cat-filtros-ok {
    flex: 1 1 0;
    padding: 12px 10px;
    font-size: 1.0625rem;
    font-weight: 700;
  }

  /* 🆕 Todos los textos y números del panel lateral de filtros en
     blanco — géneros (Hombre/Mujer/Niño/Niña/Bebé), tallas, tipo
     (Disfraces/Complementos/Decoración), "Todas las tallas" e "Inicio".
     Colocado al final de este mismo bloque (después de todas las
     reglas base de arriba) para ganarles de verdad por orden de
     aparición — puesto antes perdería la partida contra ellas, misma
     especificidad. Solo escritorio (dentro de #sidebar-catalogo-filtros,
     que ya vive únicamente en este @media) — el modal de filtros de
     móvil no se toca. */
  #sidebar-catalogo-filtros .cat-filtro-genero-titulo,
  #sidebar-catalogo-filtros .cat-chips-tallas .cat-chip,
  #sidebar-catalogo-filtros #cat-chips-row-2 .cat-chip,
  #sidebar-catalogo-filtros .cat-check-talla-todas,
  #sidebar-catalogo-filtros .cat-chip-inicio-filtros,
  #sidebar-catalogo-filtros .cat-btn-proveedor {
    color: #fff;
  }

  /* 🆕 Mismo aumento de 1 punto (~1px, +0.0625rem) de tamaño de fuente
     que ya se aplicó a la caja de tallas de la ficha en móvil — aquí,
     en cambio, para TODO el panel lateral de filtros y SOLO en
     escritorio. "Inicio" y PROVEEDOR no tenían tamaño propio dentro de
     este panel todavía (heredaban el de su regla base, compartida con
     otros contextos) — añadido aquí mismo, con el prefijo de ID que ya
     gana por especificidad sin depender del orden. */
  #sidebar-catalogo-filtros .cat-chip-inicio-filtros,
  #sidebar-catalogo-filtros .cat-btn-proveedor {
    font-size: 0.8225rem;
  }

  /* El Catálogo ocupa el 100% del módulo (ancho y alto) — #content tiene
     un padding pensado para el resto de módulos (Consultas, Almacén...),
     que aquí se anula específicamente para panel-catalogo. */
  #content:has(#panel-catalogo.active),
  #content.content-catalogo {
    padding: 0;
    margin-left: 300px; /* el sidebar ahora es fixed (fuera del flujo normal) — sin este margen, el contenido quedaba tapado detrás de él */
  }
  #panel-catalogo {
    width: 100%;
    height: 100%;
  }

  /* Guía de tallas en escritorio: imagen+título a la izquierda (columna
     fija), tabla pegada justo a la derecha — en móvil van apiladas
     (imagen arriba, tabla debajo), aquí van una junto a la otra. Modal
     más grande — lo bastante para que quepan todas las medidas sin
     tener que reducir demasiado la tabla.
     🔒 CSS Grid en vez de flexbox con flex-wrap — el patrón anterior
     (header a 100% forzando el wrap, cabecera+cuerpo flotando en la fila
     siguiente) resultó frágil: con contenido variable (varios géneros de
     Grupo, cada uno con su propia tabla) el cuerpo podía colapsar a una
     altura mínima o quedar recortado sin usar el ancho disponible. Grid
     con filas/columnas explícitas es mucho más predecible aquí. */
  #cat-modal-proveedor {
    justify-content: flex-start;
    align-items: flex-start;
    padding-left: 320px; /* justo tras el panel lateral de filtros (300px de ancho) */
    padding-top: 520px;  /* entre los bloques de Niña y Bebé en el panel lateral de filtros */
    padding-bottom: 40px;
  }

  .cat-modal-guia-box {
    max-width: 1500px;
    width: 94%;
    max-height: 88vh;
    display: grid;
    grid-template-columns: 260px 1fr;
    grid-template-rows: auto 1fr;
  }
  #cat-modal-guia {
    padding-top: 110px;
    align-items: flex-start;
  }
  .cat-modal-guia-box .modal-header {
    grid-column: 1 / -1;
    grid-row: 1;
  }
  #cat-modal-guia-cabecera {
    grid-column: 1;
    grid-row: 2;
    flex-direction: column;
    align-items: flex-start;
    padding: 16px 16px 16px 18px;
    border-right: 1px solid var(--border-soft);
    overflow-y: auto;
  }
  .cat-modal-guia-imagen {
    width: 100%;
    height: 200px;
    margin-bottom: 10px;
  }
  .cat-modal-guia-titulo { font-size: 1.02rem; }
  #cat-modal-guia-body {
    grid-column: 2;
    grid-row: 2;
    min-width: 0;
    min-height: 0;
    padding: 16px 24px 16px 20px;
    overflow-y: auto;
  }
}


