/* ============================================================
   BIKE OR DIE — estilos (estética monocromática B&N, cruda / underground)
   Sistema heredado del proyecto hermano. Sin barra de navegación:
   los únicos controles fijos son el bloque de cuenta + carrito.

   La identidad no cambia. Lo que se sistematiza aquí es la MEDIDA:
   toda distancia, alto de control, tamaño de letra y sombra sale de
   una variable, para que ninguna pantalla invente sus propios valores.
   ============================================================ */
/* Las fuentes (Anton, Inter, JetBrains Mono) se sirven desde el propio sitio
   (app/static/fonts/). Antes venían de Google Fonts: primero como @import aquí
   —cuatro descargas en serie—, luego como <link> en el HTML. Aun así la hoja de
   fonts.googleapis.com bloqueaba el primer pintado (~0.8 s en Lighthouse
   móvil) y abría dos conexiones más. Ahora son tres .woff2 del mismo dominio,
   que reutilizan la conexión de esta hoja y no bloquean nada: con
   font-display:swap el texto sale con la fuente de respaldo y cambia al llegar.

   Sólo el subconjunto "latin" de Google, con su mismo unicode-range: ya trae
   acentos, ñ, ¿¡, las comillas “ ” de los nombres y el €. Inter y JetBrains
   Mono son fuentes variables: un archivo cubre todos los pesos.

   Para cambiar una fuente: bajar el .woff2 nuevo CON OTRO NOMBRE (llevan la
   versión de Google en el nombre). Se piden desde aquí sin ?v=, así que un
   mismo nombre con otro contenido tardaría horas en verse (caché del
   navegador y de Cloudflare). */
@font-face{
  font-family:'Anton'; font-style:normal; font-weight:400; font-display:swap;
  src:url('../fonts/anton-v27-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Inter'; font-style:normal; font-weight:300 700; font-display:swap;
  src:url('../fonts/inter-v20-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 500; font-display:swap;
  src:url('../fonts/jetbrains-mono-v24-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  /* ---------- color (paleta B&N, sin tonos cálidos) ---------- */
  --ink:#0e0e0e;
  --paper:#ffffff;
  --paper-dim:#ececec;
  --paper-dim2:#d4d4d4;
  --oxide:#2b2b2b;
  /* Gris de texto secundario. Historia: #8a8a8a daba 3.0:1 sobre blanco y no
     pasaba AA; #6f6f6f lo subió a 5.0:1. Al volverse el contenido un panel
     gris al 85%, ese #6f6f6f cayó a 2.9:1 contra las zonas oscuras de la foto
     de fondo. #4a4a4a da 5.13:1 en ese peor caso. */
  --concrete:#4a4a4a;
  --concrete-dark:#9a9a9a;      /* mismo papel, pero sobre fondo negro */
  --concrete-soft:#8a8a8a;      /* sólo para elementos NO textuales */
  --safety:#e8e8e8;
  --line:rgba(14,14,14,0.14);
  --line-strong:rgba(14,14,14,0.30);
  --cart-btn:#4a4a4a;
  --alerta:#8a1c1c;
  --alerta-bg:#fbf4f4;
  /* Confirmación. La paleta es B&N a propósito y el rojo era el único color;
     este verde se eligió con la MISMA oscuridad que --alerta (7.9:1 sobre
     blanco) para que "guardado" y "cuidado" se lean como pareja y no como dos
     diseños distintos. Sólo se usa en el panel, nunca en la tienda. */
  --exito:#1c5c2e;
  --exito-bg:#f3f8f4;

  /* Cabecera del panel: era el único sitio con colores a pelo (#fff, #6a6a6a,
     #d0d0d0) sobre fondo oscuro, así que no seguía a la paleta al cambiarla. */
  --admin-barra:#0e0e0e;
  --admin-barra-texto:#ffffff;
  --admin-barra-tenue:#d0d0d0;
  --admin-barra-linea:#6a6a6a;

  /* ---------- escala de espaciado (múltiplos de 4) ---------- */
  --space-1:4px;
  --space-2:8px;
  --space-3:12px;
  --space-4:16px;
  --space-5:20px;
  --space-6:24px;
  --space-8:32px;
  --space-10:40px;
  --space-12:48px;
  --space-16:64px;

  /* ---------- trazo, radio y sombra ----------
     El radio es 0 a propósito: la identidad es cuadrada y cruda. */
  --radio:0px;
  --borde:1.5px;
  --borde-fuerte:2px;
  --sombra-fija:0 2px 8px rgba(0,0,0,.30);
  --sombra-flotante:0 2px 10px rgba(0,0,0,.35);

  /* ---------- tamaños de control ---------- */
  --ctrl-h:44px;            /* mínimo táctil, formularios y botones */
  --ctrl-h-sm:40px;         /* controles compactos dentro de tablas */
  --ctrl-h-fija:52px;       /* bloque fijo cuenta+carrito, escritorio */
  --ctrl-h-fija-sm:44px;    /* el mismo bloque, en móvil */
  /* Recuadro del logo fijo: del mismo alto que los botones de la derecha.
     Lleva el logo combinado completo. A este tamaño su texto no se lee en
     escritorio ni en tablet; el cliente lo prefiere así antes que agrandar
     el header, y el logo se ve entero en la portada (.bg-photo). */
  --logo-lado:var(--ctrl-h-fija);
  --logo-lado-sm:var(--ctrl-h-fija-sm);
  --ctrl-pad-x:24px;
  --ctrl-pad-x-sm:16px;

  /* ---------- escala tipográfica ---------- */
  --fs-9:9px;
  --fs-10:10px;
  --fs-11:11px;
  --fs-12:12px;
  --fs-13:13px;
  --fs-14:14px;
  --fs-15:15px;
  --fs-16:16px;
  --fs-18:18px;
  --fs-22:22px;
  --fs-26:26px;
  --fs-30:30px;
  --precio-lg:38px;         /* sin uso desde que se quitó la ficha */
  --precio-md:30px;         /* fila de la tienda */

  /* ---------- foco visible ---------- */
  --foco-grosor:3px;
  --foco-offset:2px;

  /* ---------- transición ---------- */
  --t:.15s ease;
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;color:var(--ink);font-family:'Inter',Helvetica,Arial,sans-serif;}
body{background:var(--ink);min-height:100vh;line-height:1.5;}
a{color:inherit;text-decoration:none;}
button{font-family:inherit;cursor:pointer;}
img{max-width:100%;}
.hidden{display:none !important;}

/* Los glifos geométricos (▲ ◐ ★ ✓ ✕) son parte del tono crudo del diseño:
   se fuerza su presentación como TEXTO para que ningún sistema los pinte
   como emoji de color y rompa el blanco y negro. */
.badge, .conf-sello, .art .glyph, .ayuda, .filtro, .section-label,
.breadcrumb span, .admin-nav a{font-variant-emoji:text;}

/* Cifras tabulares: precios, totales y existencias no bailan de ancho. */
.price, .price-antes, .totals, .num, .stat .num, .conf-folio,
table.cart-table td, .resumen-lista{font-variant-numeric:tabular-nums;}

/* Utilidad: visible sólo para lectores de pantalla (encabezados de columna
   que en pantalla van vacíos, por ejemplo). */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* ---------- foco siempre visible (accesibilidad con teclado) ---------- */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible{
  outline:var(--foco-grosor) solid var(--ink); outline-offset:var(--foco-offset);
}
/* Sobre superficies oscuras el aro negro es invisible: se invierte. */
.acct-btn:focus-visible, .cart-fab:focus-visible, .acct-name:focus-visible,
.acct-perfil:focus-visible, .ig-fab:focus-visible,
.admin-top a:focus-visible, .admin-top button:focus-visible,
footer.site a:focus-visible, .btn-primary:focus-visible,
.size-btn.activo:focus-visible, .filtro.activo:focus-visible{
  outline:var(--foco-grosor) solid #fff;
  outline-offset:var(--foco-offset);
  box-shadow:0 0 0 calc(var(--foco-grosor) + var(--foco-offset)) var(--ink);
}

/* ---------- fondo fijo a pantalla completa: el logo combinado ----------
   bg-portada.webp es el logo blanco sobre negro, recortado al arte de
   img/logo.jpeg (el original que mandó la marca). No va a `cover`
   como iba la foto: es vertical y se comería el texto. Va entero, centrado
   sobre negro: de ancho, el 84 % de la pantalla en móvil y el 54 % del alto
   en pantallas anchas (min() elige el que toque; el alto sale del ancho).
   Para cambiarlo, archivo con OTRO nombre: se pide sin ?v= (ver base.html). */
.bg-photo{
  /* lvh y NO dvh: dvh cambia de valor mientras se oculta la barra del
     navegador al hacer scroll, y como el tamaño del fondo depende del alto,
     cada cambio de alto la re-escalaba. En el móvil se veía como si la imagen
     hiciera zoom al bajar y lo deshiciera al volver arriba. lvh es fijo. */
  position:fixed; inset:0; height:100vh; height:100lvh; z-index:0;
  background-image:url('../img/bg-portada.webp');
  background-size:min(84vw, 54vh) auto;
  background-size:min(84vw, 54lvh) auto;
  background-position:center 45%; background-repeat:no-repeat;
  background-color:#000;
}
/* Sin velo encima: aquí había un degradado a BLANCO en el último 22 % de la
   pantalla. Hacía que el negro del banner se lavara hacia gris justo antes del
   panel, y como el panel es semitransparente (ver main), su gris cambiaba de
   tono según quedara sobre el negro o sobre el blanco. Sin el velo, el negro
   llega hasta el borde del panel y el panel se ve del mismo gris en toda la
   pantalla. El logo sigue asomando detrás del panel al bajar. */

/* ---------- logo fijo (arriba a la izquierda) ----------
   El logo es blanco sobre transparente, así que va dentro de un cuadro negro:
   sobre la foto del fondo y sobre el blanco del contenido se lee igual de bien. */
.brand-fab{
  position:fixed; top:var(--space-4); left:var(--space-5); z-index:50;
  width:var(--logo-lado); height:var(--logo-lado);
  display:flex; align-items:center; justify-content:center;
  background:var(--cart-btn); box-shadow:var(--sombra-fija);
  transition:background var(--t);
}
.brand-fab:hover{background:#333;}
.brand-fab img{width:90%; height:90%; object-fit:contain; display:block;}
@media (max-width:720px){
  /* Mismo tope y margen que .top-actions en móvil, para que los tres
     recuadros queden en una sola línea. */
  .brand-fab{
    top:var(--space-3); left:var(--space-3);
    width:var(--logo-lado-sm); height:var(--logo-lado-sm);
  }
}

/* ============================================================
   BLOQUE FIJO: CUENTA + CARRITO (arriba a la derecha)
   Un solo renglón, sin envolver, con separación uniforme entre
   botones (>= 8px, mínimo táctil) en cualquier ancho.
   ============================================================ */
.top-actions{
  position:fixed; top:var(--space-4); right:var(--space-5); z-index:50;
  display:flex; align-items:center; justify-content:flex-end;
  flex-wrap:nowrap; gap:var(--space-2);
  /* Se reserva SIEMPRE el ancho del logo más su margen: este bloque no puede
     crecer hasta taparlo, pase lo que pase con el idioma o el nombre de quien
     entró. En móvil el reparto lo termina de arreglar el menú ☰. */
  max-width:calc(100vw - var(--logo-lado) - var(--space-12));
}
.inline-form{display:inline-flex; align-items:center; gap:var(--space-2); margin:0; flex-wrap:wrap;}
.top-actions .inline-form{flex-wrap:nowrap;}

/* ---------- menú de la barra (idioma, moneda, cuenta) ----------
   En escritorio va desplegado y el botón ☰ no existe: son dos filas flex
   anidadas que se ven como un solo renglón. En móvil todo esto se colapsa
   detrás del botón (ver el bloque de 720px más abajo). */
.top-menu{display:flex; align-items:center; gap:var(--space-2);}
.menu-panel{display:flex; align-items:center; gap:var(--space-2); flex-wrap:nowrap;}
.menu-fab{display:none;}
/* La moneda salió de la barra: satura el escritorio y su sitio está junto a
   los precios (.moneda-inline). Aquí sólo sobrevive dentro del menú de móvil.
   El selector lleva .top-actions delante para ganarle a la regla general de
   .conmutador, que va más abajo en la hoja y si no la pisaba. */
.top-actions .conmutador-moneda{display:none;}

.acct-btn{
  height:var(--ctrl-h-fija); line-height:var(--ctrl-h-fija);
  display:inline-block; padding:0 var(--ctrl-pad-x-sm);
  background:var(--cart-btn); color:#fff; border:none;
  font-family:'Inter',sans-serif; font-size:var(--fs-12); font-weight:600;
  letter-spacing:.04em; text-transform:uppercase;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  box-shadow:var(--sombra-fija); transition:background var(--t);
}
.acct-btn:hover{background:#333;}
.acct-btn:active{background:var(--ink);}
/* .ghost antes usaba 72% de opacidad: sobre el blanco de la página el texto
   blanco caía a 3.0:1. A 92% queda en 7:1 y se conserva el efecto de vidrio. */
.acct-btn.ghost{background:rgba(74,74,74,.92); backdrop-filter:blur(4px);}
.acct-btn.ghost:hover{background:#333;}

/* Saludo por hora del día */
.acct-name{
  height:var(--ctrl-h-fija); line-height:var(--ctrl-h-fija);
  display:inline-block; padding:0 var(--space-3);
  max-width:34vw; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  background:rgba(74,74,74,.92); backdrop-filter:blur(4px); color:#fff;
  font-family:'Inter',sans-serif; font-size:var(--fs-12); letter-spacing:.02em;
  box-shadow:var(--sombra-fija); transition:background var(--t);
}
.acct-name:hover{background:#333;}
/* Versión compacta para móvil: sólo iniciales */
/* Enlace al perfil, sólo visible en móvil (ahí se oculta el saludo). Antes
   pintaba las iniciales del nombre y por eso se llamaba de otra forma; ahora
   lleva un icono genérico, así que ya no hace falta regla de tipografía. */
.acct-perfil{
  display:none; width:var(--ctrl-h-fija); height:var(--ctrl-h-fija);
  align-items:center; justify-content:center;
  background:rgba(74,74,74,.92); backdrop-filter:blur(4px); color:#fff;
  box-shadow:var(--sombra-fija); transition:background var(--t);
}
.acct-perfil:hover{background:#333;}

.cart-fab{
  position:relative; flex-shrink:0;
  width:56px; height:var(--ctrl-h-fija); background:var(--cart-btn); color:#fff;
  border:none; display:flex; align-items:center; justify-content:center;
  box-shadow:var(--sombra-fija); transition:background var(--t);
}
.cart-fab:hover{background:#333;}
.cart-fab:active{background:var(--ink);}
.cart-fab-count{
  position:absolute; top:var(--space-1); right:6px;
  min-width:18px; height:18px; padding:0 var(--space-1);
  background:#fff; color:var(--ink); border-radius:var(--radio);
  font-family:'JetBrains Mono',monospace;
  font-size:var(--fs-10); font-weight:700; line-height:18px; text-align:center;
}

@media (max-width:720px){
  /* Arriba SÓLO caben tres cosas: el logo a la izquierda, y el carrito y el
     menú a la derecha. El resto (idioma, moneda, cuenta) se va dentro de ☰.
     Antes iba todo desplegado y el bloque crecía hasta tapar el logo. */
  .top-actions{
    right:var(--space-3); top:var(--space-3);
    max-width:calc(100vw - var(--logo-lado-sm) - var(--space-8));
  }
  .acct-btn{
    height:var(--ctrl-h-fija-sm); line-height:var(--ctrl-h-fija-sm);
    padding:0 var(--space-3); font-size:var(--fs-11);
  }
  .acct-name{display:none;}
  .acct-perfil{
    display:flex; width:var(--ctrl-h-fija-sm); height:var(--ctrl-h-fija-sm);
  }
  .cart-fab{height:var(--ctrl-h-fija-sm); width:48px;}

  /* Jerarquía pedida: logo — carrito — ☰. El carrito va antes que el menú
     aunque en el HTML esté después, para que el pulgar lo encuentre primero. */
  .cart-fab{order:1;}
  .top-menu{order:2;}

  .menu-fab{
    display:flex; align-items:center; justify-content:center; flex-shrink:0;
    width:var(--ctrl-h-fija-sm); height:var(--ctrl-h-fija-sm);
    background:var(--cart-btn); color:#fff; border:none;
    box-shadow:var(--sombra-fija); transition:background var(--t);
  }
  .menu-fab:hover{background:#333;}
  .menu-fab:active{background:var(--ink);}
  .menu-fab:focus-visible{
    outline:var(--foco-grosor) solid #fff; outline-offset:var(--foco-offset);
    box-shadow:0 0 0 calc(var(--foco-grosor) + var(--foco-offset)) var(--ink);
  }

  .top-actions .conmutador-moneda{display:flex;}

  /* Panel desplegable anclado bajo la barra. Nace cerrado y lo abre ui.js con
     la clase .abierto. No hay respaldo "sin JavaScript" a propósito: sin JS el
     catálogo tampoco pinta tallas, así que la tienda no funciona de todos
     modos, y un menú a medio abrir sería peor. */
  .top-menu .menu-panel{
    display:none;
    position:fixed; z-index:60;
    top:calc(var(--space-3) + var(--ctrl-h-fija-sm) + var(--space-2));
    right:var(--space-3);
    flex-direction:column; align-items:stretch; gap:var(--space-2);
    min-width:190px; max-width:calc(100vw - var(--space-6));
    padding:var(--space-3);
    background:var(--ink); box-shadow:var(--sombra-flotante);
  }
  .top-menu.abierto .menu-panel{display:flex;}
  .menu-panel .conmutador{width:100%;}
  .menu-panel .conmutador-btn{flex:1;}
  /* Sobre el panel negro, el activo de siempre (fondo --ink) desaparecía: era
     el mismo color que el panel. Aquí se invierte a blanco sobre negro, que
     además es el contraste de la marca. */
  .menu-panel .conmutador-btn.activo{background:#fff; color:var(--ink);}
  .menu-panel .acct-btn,
  .menu-panel .inline-form,
  .menu-panel .inline-form .acct-btn{width:100%; text-align:center;}
  .menu-panel .acct-perfil{width:100%;}
}
@media (max-width:420px){
  .acct-btn{padding:0 var(--space-2); font-size:var(--fs-10); letter-spacing:.02em;}
}

/* ---------- selector de moneda junto a los precios ----------
   Vive donde el precio importa (encabezado del catálogo y bloque de totales),
   no en la barra de arriba. En móvil se esconde: ahí el conmutador está en ☰,
   y repetirlo dos veces en la misma pantalla sólo confunde. */
.moneda-inline{
  display:none; align-items:center; gap:var(--space-2);
  margin:0 0 var(--space-4);
}
.moneda-inline-txt{
  font-family:'Inter',sans-serif; font-size:var(--fs-11); font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--concrete);
}
.moneda-inline .conmutador{box-shadow:none;}
.moneda-inline .conmutador-btn{height:var(--ctrl-h-sm); line-height:var(--ctrl-h-sm);}
/* Junto a un encabezado de sección va en el mismo renglón, alineado a la
   derecha; sobre los totales va pegado a la caja. */
.moneda-fila{
  display:flex; align-items:baseline; justify-content:space-between;
  flex-wrap:wrap; gap:var(--space-3);
}
.moneda-fila .moneda-inline{margin-bottom:0;}
.moneda-fila > .section-label{margin-bottom:0;}
@media (min-width:721px){
  .moneda-inline{display:flex;}
}

/* ---------- sin nav bar: sólo aire donde iría ----------
   El hero ya ocupa su propio alto en el flujo (ver .home-hero), así que este
   espaciador queda a cero. Se conserva la clase para no tocar la plantilla. */
.spacer-hero{display:none;}

main{
  max-width:1000px; margin:0 auto;
  padding:var(--space-6) var(--space-4) var(--space-16);
  position:relative; z-index:10; min-height:60vh;
  /* Semitransparente a propósito: deja asomar el fondo (.bg-photo) al hacer scroll
     (efecto de capas). Sólo el contenedor: textos y botones van opacos para
     no perder contraste. Gris claro al 85%, como la referencia. */
  background:rgba(232,232,232,.85);
}
/* Páginas sin hero: espacio para que el bloque fijo no tape el contenido.
   16 (tope) + 52 (alto del control y del logo) + 24 (aire) = 92px.
   En móvil: 12 + 44 + 16 = 72. */
main.con-aire{padding-top:92px;}
@media (max-width:720px){ main.con-aire{padding-top:72px;} }

/* ---------- hero de la portada ----------
   Antes iba en position:absolute superpuesto a .spacer-hero, con un
   margin-top negativo. Ese montaje era frágil: cualquier cambio de alto del
   viewport (barra de direcciones del móvil, zoom) descuadraba la relación
   entre el hero y el espaciador y al volver a subir daba la sensación de
   toparse con un tope. Ahora el hero ocupa su propio alto EN EL FLUJO: el
   scroll es completamente normal y no hay superposición que pueda desfasarse.

   Sobre la unidad de alto: aquí decía dvh "para que la página no se reajuste
   al ocultarse la barra del navegador", pero es al revés. dvh es la unidad
   DINÁMICA: su valor cambia justo mientras la barra se oculta, y era eso lo
   que reajustaba el hero durante el scroll. lvh es el alto con la barra
   retraída y no se mueve, que es lo que se buscaba. */
.home-hero{
  position:relative; width:100%; height:84vh; height:84lvh;
  pointer-events:none; z-index:5;
}
@media (max-width:720px){ .home-hero{height:78vh; height:78lvh;} }

/* ---------- etiquetas de sección ---------- */
.section-label{
  display:flex; align-items:center; gap:var(--space-3);
  margin:var(--space-6) 0 var(--space-5);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12); font-weight:500;
  line-height:1.4; letter-spacing:.12em;
  text-transform:uppercase; color:var(--concrete);
}
.section-label:first-child{margin-top:0;}
.section-label::after{content:""; flex:1; border-top:1.5px dashed var(--line);}
.section-label a{color:var(--concrete); transition:color var(--t);}
.section-label a:hover{color:var(--ink);}

/* ============================================================
   TIENDA — LISTA UNO POR UNO (una fila completa por producto)
   ============================================================ */
.shop-list{display:flex; flex-direction:column; gap:0;}
.shop-row{
  display:grid; grid-template-columns:300px minmax(0,1fr); gap:var(--space-8);
  padding:var(--space-8) 0; border-bottom:1.5px solid var(--line); align-items:start;
}
.shop-row:first-child{padding-top:var(--space-2);}
.shop-row:last-child{border-bottom:none;}
@media (max-width:680px){
  .shop-row{grid-template-columns:minmax(0,1fr); gap:var(--space-4); padding:var(--space-6) 0;}
}

/* galería
   Fondo BLANCO liso: 12 de las 34 fotos del catálogo son PNG con fondo
   transparente, y encima del rayado de relleno se les veía la trama por
   detrás de la prenda mientras las otras 22 salían sobre blanco. Dos
   acabados distintos en la misma fila.

   El rayado no se pierde: es del hueco «Foto pendiente», que es lo único
   que debe parecer un espacio vacío. Ese hueco es un <div class="art">
   pelado; la caja con foto es un <button class="art art-zoom">. */
.row-gallery .art, .art{
  position:relative; aspect-ratio:1/1; width:100%;
  background:var(--paper);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
  border:var(--borde) solid var(--ink);
}
.art:not(.art-zoom){
  background:
    repeating-linear-gradient(135deg, rgba(14,14,14,0.05) 0 2px, transparent 2px 10px),
    var(--paper-dim);
}
/* El <picture> que envuelve la foto grande TIENE que llenar la caja.

   No es cosmético: `.art img` pide `height:100%`, y un porcentaje sólo se
   resuelve si el padre tiene una altura definida. Mientras la foto fue un
   <img> suelto, el padre era `.art` —cuadrada por `aspect-ratio`— y el 100 %
   valía 300 px. Al envolverla en <picture> para ofrecer AVIF, el padre pasó a
   ser un elemento de altura automática: el porcentaje se quedó en `auto`, la
   foto se dibujó a su proporción natural sobre el ancho de la caja y lo que
   sobraba se lo comía el `overflow:hidden`. La sudadera vertical (550x1200)
   acababa midiendo 622 px dentro de una caja de 300 y se veía el 48 % de en
   medio: medio torso, sin capucha ni bastilla. Y el `object-fit` de abajo no
   podía hacer nada, porque la caja del <img> ya tenía la proporción de la foto.

   Lo vigila tests/test_encuadre_catalogo.py con un navegador de verdad, que es
   la única forma de ver esto: el HTML que sale de Flask es correcto. */
.art picture{display:block; width:100%; height:100%;}
/* `contain` y no `cover`: las fotos van de 0.26 a 1.50 de proporción y en una
   caja cuadrada `cover` recortaba lo que sobraba —a las prendas verticales
   les cortaba mangas y bastilla—. Con `contain` se ve la prenda entera y lo
   que sobra queda en blanco, que es el fondo de la caja. El respiro evita
   que la foto toque el marco. */
.art img{
  width:100%; height:100%; object-fit:contain; display:block;
  padding:var(--space-3);
}
.art .glyph{font-family:'Anton',sans-serif; font-size:52px; color:var(--paper-dim2);}
.art .pending{
  position:absolute; bottom:var(--space-2); left:var(--space-2);
  background:var(--ink); color:var(--paper);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-9); letter-spacing:.08em;
  text-transform:uppercase; padding:var(--space-1) var(--space-2);
}
.art::before{
  content:""; position:absolute; inset:0; background:rgba(255,255,255,0);
  transition:background .2s ease; z-index:2;
}
.art:hover::before{background:rgba(255,255,255,.18);}
/* La caja de la foto en el catálogo es un botón que abre el visor. */
.art-zoom{padding:0; font:inherit; color:inherit; cursor:zoom-in;}

.flag-oferta{
  position:absolute; top:0; right:0; z-index:3;
  background:var(--ink); color:#fff; padding:var(--space-1) var(--space-3);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  line-height:1.4; letter-spacing:.06em;
}

.thumb-row{display:flex; gap:var(--space-2); margin-top:var(--space-2);}
.thumb-sq{
  /* El max-width evita que una sola miniatura se estire a todo el ancho y
     parezca una segunda foto grande. Con las 3 de siempre no cambia nada. */
  flex:1 1 0; max-width:calc((100% - var(--space-2) * 2) / 3);
  aspect-ratio:1; border:var(--borde) solid var(--ink); background:var(--paper);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
  padding:0; cursor:pointer; transition:border-color var(--t);
}
.thumb-sq:hover{border-color:var(--concrete);}
/* Mismo encuadre que la foto grande: con `cover` la miniatura enseñaba un
   recorte que no era el que aparecía al pulsarla. */
.thumb-sq img{
  width:100%; height:100%; object-fit:contain; display:block;
  padding:var(--space-1);
}
.thumb-sq .pending{font-family:'JetBrains Mono',monospace; font-size:var(--fs-9); color:var(--concrete);}
/* Con una sola foto no hay nada que elegir. Es una clase y no :empty porque
   la plantilla deja saltos de línea dentro del div. */
.thumb-row.vacia{display:none;}
/* Cuál se está viendo en grande: las demás quedan atenuadas. */
.thumb-sq img{opacity:.6; transition:opacity var(--t);}
.thumb-sq:hover img, .thumb-sq.activo img{opacity:1;}
.thumb-sq.activo{border-width:var(--borde-fuerte);}


/* información del producto */
.row-info h3{
  font-family:'Inter',sans-serif; font-weight:600; font-size:var(--fs-22);
  line-height:1.25; margin:0 0 var(--space-1);
}
.row-info h3 a:hover{text-decoration:underline;}
.mat{font-size:var(--fs-14); color:var(--concrete); margin:0 0 var(--space-6); line-height:1.5;}

/* ---------- selectores de color y talla ---------- */
.selector{margin-bottom:var(--space-6);}
.selector-label{
  display:block; font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  line-height:1.4; letter-spacing:.06em; text-transform:uppercase;
  color:var(--concrete); margin-bottom:var(--space-2);
}
.selector-label strong{color:var(--ink); font-weight:500;}

.swatches{display:flex; flex-wrap:wrap; gap:var(--space-2);}
.swatch{
  display:inline-flex; align-items:center; gap:var(--space-2);
  min-height:var(--ctrl-h); padding:var(--space-2) var(--space-3);
  background:#fff; border:var(--borde) solid var(--line-strong); color:var(--ink);
  font-size:var(--fs-13); line-height:1.2;
  transition:border-color var(--t), background var(--t);
}
.swatch:hover{border-color:var(--ink);}
.swatch:active{background:var(--paper-dim);}
.swatch.activo{border-color:var(--ink); border-width:var(--borde-fuerte); background:var(--safety); font-weight:600;}
.swatch.agotado{opacity:.5; cursor:not-allowed;}
.swatch.agotado .swatch-txt{text-decoration:line-through;}
.swatch-chip{
  width:16px; height:16px; border:1.5px solid var(--ink);
  background:var(--muestra, #8a8a8a); display:inline-block; flex-shrink:0;
}

.sizes{display:flex; flex-wrap:wrap; gap:var(--space-2);}
.size-btn{
  min-width:52px; min-height:var(--ctrl-h); padding:var(--space-2) var(--space-3);
  background:#fff; border:var(--borde) solid var(--line-strong); color:var(--ink);
  font-family:'Inter',sans-serif; font-size:var(--fs-14); font-weight:500;
  transition:border-color var(--t), background var(--t);
}
.size-btn:hover:not(:disabled){border-color:var(--ink);}
.size-btn.activo{background:var(--ink); color:#fff; border-color:var(--ink);}
.size-btn.agotado, .size-btn:disabled{
  opacity:.4; cursor:not-allowed; text-decoration:line-through;
  background:var(--paper-dim); border-style:dashed;
}
.stock-nota{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11); color:var(--concrete);
  margin:var(--space-2) 0 0; min-height:16px; line-height:1.4;
}
.stock-nota.alerta{color:var(--alerta); font-weight:500;}

/* ---------- precios ---------- */
.precio-bloque{
  display:flex; align-items:baseline; gap:var(--space-3);
  flex-wrap:wrap; margin-top:var(--space-2);
}
.price{
  font-family:'Inter',sans-serif; font-size:var(--precio-md); font-weight:300;
  letter-spacing:-.01em; line-height:1.1;
}
.price-antes{
  font-size:var(--fs-18); font-weight:300; color:var(--concrete);
  text-decoration:line-through; line-height:1.1;
}
/* Tachado chico dentro de tablas y resúmenes (bolsa, checkout): va encima del
   precio que se cobra, en su propio renglón. */
.price-antes.mini{display:block; font-size:var(--fs-12);}
.price-note{font-size:var(--fs-12); color:var(--concrete); margin:var(--space-2) 0 var(--space-4);}

/* ---------- distintivos ---------- */
.badge-row{display:flex; flex-wrap:wrap; gap:var(--space-2); margin-bottom:var(--space-6);}
.badge{
  border:1px solid var(--line-strong); background:var(--safety);
  padding:var(--space-2) var(--space-3);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
  line-height:1.3; letter-spacing:.04em;
  text-transform:uppercase; color:var(--ink);
  display:inline-flex; align-items:center; gap:var(--space-2);
}
.badge-oferta{background:var(--ink); color:#fff; border-color:var(--ink);}
.badge.mini{font-size:var(--fs-9); padding:var(--space-1) var(--space-2);}

/* ============================================================
   BOTONES — tres pesos, un solo alto, una sola escala de padding
   ============================================================ */
.btn-bag, .btn-primary, .btn-ghost{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:var(--ctrl-h); padding:var(--space-3) var(--ctrl-pad-x);
  border-radius:var(--radio);
  font-family:'Inter',sans-serif; font-size:var(--fs-13); font-weight:500;
  letter-spacing:.06em; text-transform:uppercase; text-align:center;
  line-height:1.2;
  transition:background var(--t), color var(--t), border-color var(--t);
}

.btn-bag{background:transparent; color:var(--ink); border:var(--borde) solid var(--ink);}
.btn-bag:hover:not(:disabled){background:var(--ink); color:#fff;}
.btn-bag:active:not(:disabled){background:var(--oxide); border-color:var(--oxide);}
.btn-bag:disabled{opacity:.45; cursor:not-allowed; border-style:dashed;}

.btn-primary{background:var(--ink); color:#fff; border:var(--borde) solid var(--ink);}
.btn-primary:hover:not(:disabled){background:var(--oxide); border-color:var(--oxide);}
.btn-primary:active:not(:disabled){background:#000;}
.btn-primary:disabled{opacity:.45; cursor:not-allowed;}
.btn-primary.ancho{width:100%;}
.btn-primary.peligro{background:var(--alerta); border-color:var(--alerta);}
.btn-primary.peligro:hover:not(:disabled){background:#6d1616; border-color:#6d1616;}

.btn-ghost{background:transparent; color:var(--ink); border:var(--borde) solid var(--ink);}
.btn-ghost:hover:not(:disabled){background:var(--ink); color:#fff;}
.btn-ghost:active:not(:disabled){background:var(--oxide); border-color:var(--oxide); color:#fff;}
.btn-ghost:disabled{opacity:.45; cursor:not-allowed; border-style:dashed;}
.btn-ghost.ancho{width:100%;}
/* Compacto para celdas de tabla: sigue alineando con .qty-input (40px). */
.btn-ghost.mini{
  min-height:var(--ctrl-h-sm); padding:var(--space-2) var(--space-3);
  font-size:var(--fs-11); letter-spacing:.04em;
}
/* Confirmación junto al botón al añadir a la bolsa (no se recarga la página) */
.aviso-bolsa{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  letter-spacing:.04em; text-transform:uppercase; line-height:1.4;
  margin:var(--space-3) 0 0; padding:var(--space-2) var(--space-3);
  border-left:3px solid var(--ink); background:var(--safety); color:var(--ink);
}
.aviso-bolsa.alerta{border-left-color:var(--alerta); color:var(--alerta); background:var(--alerta-bg);}

.btn-ghost.peligro{border-color:var(--alerta); color:var(--alerta);}
.btn-ghost.peligro:hover{background:var(--alerta); border-color:var(--alerta); color:#fff;}

/* En pantallas táctiles ningún control compacto baja del mínimo de 44px. */
@media (pointer:coarse){
  .btn-ghost.mini, .qty-input, .filtro{min-height:var(--ctrl-h);}
}

/* Acceso con Google: mismo alto y misma escala que el resto de botones, pero
   con el logo a la izquierda y en minúsculas. Las condiciones de uso de Google
   piden que su marca se lea tal cual, así que este es el único botón del sitio
   que NO va en mayúsculas ni con el interletrado del resto. */
.btn-google{
  display:flex; align-items:center; justify-content:center; gap:var(--space-3);
  width:100%; min-height:var(--ctrl-h); padding:var(--space-3) var(--ctrl-pad-x);
  border:var(--borde) solid var(--ink); border-radius:var(--radio);
  background:var(--paper); color:var(--ink);
  font-family:'Inter',sans-serif; font-size:var(--fs-13); font-weight:500;
  line-height:1.2; text-align:center; text-decoration:none;
  transition:background var(--t), border-color var(--t);
}
.btn-google:hover{background:var(--safety);}
.btn-google:active{background:var(--paper-dim);}
/* El logo conserva sus cuatro colores en cualquier estado: es marca ajena y no
   se le cambia el color ni se le aplica el invertido del hover. */
.btn-google-logo{flex:none; width:18px; height:18px;}

/* Separador entre el formulario de siempre y el acceso con Google. La palabra
   va sobre el fondo, con una línea a cada lado. */
.sep-o{
  display:flex; align-items:center; gap:var(--space-3);
  margin:var(--space-5) 0 var(--space-4);
}
.sep-o::before, .sep-o::after{
  content:""; flex:1 1 auto; height:var(--borde); background:var(--line-strong);
}
.sep-o span{
  flex:none; font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  letter-spacing:.06em; text-transform:uppercase; color:var(--concrete);
}

/* Se usa en las tablas del panel de administración. */
.fila-agotada{opacity:.55;}

/* ============================================================
   CARRITO Y CHECKOUT
   ============================================================ */
.breadcrumb{display:flex; gap:var(--space-2); flex-wrap:wrap; margin:0 0 var(--space-6);}
.breadcrumb span{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  line-height:1.2; letter-spacing:.06em;
  padding:var(--space-2) var(--space-3); border:1px solid var(--line-strong);
  color:var(--concrete);
}
.breadcrumb span.active{background:var(--ink); color:#fff; border-color:var(--ink);}

/* Sombras laterales que avisan que la tabla se desplaza en horizontal.

   contain:paint es lo que impide que el ancho de la tabla se propague al
   documento: sólo con overflow-x:auto, en móvil la página entera se movía
   en horizontal (en 375px se desplazaba ~246px) además de la tabla. */
.tabla-scroll{
  overflow-x:auto; -webkit-overflow-scrolling:touch; contain:paint;
  background:
    linear-gradient(to right, #fff 30%, rgba(255,255,255,0)) left center,
    linear-gradient(to left,  #fff 30%, rgba(255,255,255,0)) right center,
    radial-gradient(farthest-side at 0 50%,   rgba(14,14,14,.20), rgba(14,14,14,0)) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(14,14,14,.20), rgba(14,14,14,0)) right center;
  background-repeat:no-repeat;
  background-size:40px 100%, 40px 100%, 14px 100%, 14px 100%;
  background-attachment:local, local, scroll, scroll;
}
/* El aro de foco va por dentro: contain:paint recorta cualquier trazo que
   salga de la caja, y ui.js hace enfocable esta zona cuando desborda. */
.tabla-scroll:focus-visible{
  outline:var(--foco-grosor) solid var(--ink);
  outline-offset:calc(var(--foco-grosor) * -1);
}

table.cart-table{
  width:100%; border-collapse:collapse; margin-bottom:var(--space-2); min-width:560px;
}
table.cart-table th{
  text-align:left; font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  letter-spacing:.06em; text-transform:uppercase; color:var(--concrete);
  padding:var(--space-3) var(--space-2);
  border-bottom:var(--borde) solid var(--ink); white-space:nowrap;
}
table.cart-table td{
  padding:var(--space-4) var(--space-2); border-bottom:1px solid var(--line);
  font-size:var(--fs-14); vertical-align:middle; line-height:1.45;
}
/* Columnas numéricas: alineadas a la derecha para poder compararlas de un vistazo. */
table.cart-table th.num, table.cart-table td.num{text-align:right;}
table.cart-table th.acciones, table.cart-table td.acciones{text-align:right; white-space:nowrap;}
table.cart-table tbody tr:last-child td{border-bottom:none;}
table.cart-table.compacta{min-width:0;}
table.cart-table.compacta th{padding:var(--space-2);}
table.cart-table.compacta td{padding:var(--space-2);}
.prod-name{font-weight:600; display:block;}
.variant{font-size:var(--fs-12); color:var(--concrete); display:block; font-weight:400;}
/* Ambas clases se usan como <span> dentro de una celda y también sobre el
   propio <td>. En el segundo caso display:block sacaba a la celda de la
   rejilla de la tabla (borde suelto y sin alineado vertical). */
td.prod-name, td.variant{display:table-cell; vertical-align:middle;}
/* ---------- miniatura del producto en la bolsa ----------
   Va DEBAJO del nombre, dentro de la misma celda. Cuadrada y con marco negro:
   el catálogo ya trata así sus fotos (.thumb-sq) y la bolsa no inventa un
   lenguaje distinto. */
.cart-thumb{
  display:block; width:64px; height:64px; margin-top:var(--space-2);
  background:var(--ink); border:var(--borde) solid var(--ink);
  overflow:hidden; flex-shrink:0; transition:transform var(--t), box-shadow var(--t);
}
.cart-thumb img{width:100%; height:100%; object-fit:cover; display:block;}
.cart-thumb:hover{transform:translateY(-2px); box-shadow:var(--sombra-flotante);}
.cart-thumb .pending{
  display:flex; align-items:center; justify-content:center; height:100%;
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
  letter-spacing:.06em; color:var(--concrete-dark);
}

/* La "escalera": con dos o más artículos cada miniatura entra un escalón más
   que la de arriba, y el conjunto se lee en diagonal ascendente en vez de como
   una columna recta. El tope de 4 escalones evita que en una bolsa larga la
   última foto se salga de la celda. */
table.cart-table.escalera .cart-thumb{
  margin-left:calc(min(var(--escalon), 4) * 18px);
}
/* Pantallas chicas: el mismo gesto, con la mitad de paso, porque la celda no
   da para 72px de sangría. */
@media (max-width:520px){
  table.cart-table.escalera .cart-thumb{
    margin-left:calc(min(var(--escalon), 4) * 9px);
  }
}
/* Quien pidió que no le muevan la interfaz no ve el salto al pasar el cursor. */
@media (prefers-reduced-motion: reduce){
  .cart-thumb, .cart-thumb:hover{transition:none; transform:none;}
}

.fila-problema{background:var(--alerta-bg);}
.fila-oculta{opacity:.6;}
.texto-alerta{color:var(--alerta); font-weight:600;}
/* Celda de cantidad: input y botón comparten alto y se separan 8px. */
.celda-cantidad .inline-form{align-items:center; gap:var(--space-2);}
.celda-cantidad .stock-nota{margin-top:var(--space-2);}

.qty-input{
  width:72px; padding:var(--space-2); border:var(--borde) solid var(--ink);
  font-family:inherit; font-size:var(--fs-14); min-height:var(--ctrl-h-sm);
  background:#fff; color:var(--ink); text-align:right; font-variant-numeric:tabular-nums;
}
.qty-input.ancho-precio{width:104px;}

.totals{
  margin-top:var(--space-6); max-width:340px; margin-left:auto;
  font-variant-numeric:tabular-nums;
}
.totals.sin-margen{margin-top:var(--space-2); max-width:none;}
.totals .row{
  display:flex; justify-content:space-between; align-items:baseline;
  padding:var(--space-2) 0; font-size:var(--fs-14); gap:var(--space-4);
}
.totals .row.total{
  font-weight:700; font-size:var(--fs-16);
  border-top:var(--borde) solid var(--ink);
  margin-top:var(--space-2); padding-top:var(--space-3);
}
/* La línea del descuento resta: se marca en el color de acento para que se
   lea distinta del subtotal y del envío de un vistazo. */
.totals .row.descuento{color:var(--oxide); font-weight:600;}
.envio-tachado{color:var(--concrete); margin-right:var(--space-1);}

/* ============================================================
   CÓDIGO DE DESCUENTO (bolsa y pago)
   ============================================================ */
.cupon-caja{
  margin-top:var(--space-6); max-width:340px; margin-left:auto;
  border:var(--borde) dashed var(--concrete); padding:var(--space-3);
}
.totals.sin-margen + .cupon-caja, .cupon-caja + .totals{margin-top:var(--space-4);}
.cupon-caja label{
  display:block; font-size:var(--fs-12); font-weight:600;
  margin-bottom:var(--space-2);
}
.cupon-fila{display:flex; gap:var(--space-2);}
.cupon-fila input{
  flex:1 1 auto; min-width:0;
  padding:var(--space-2) var(--space-3);
  border:var(--borde) solid var(--ink); background:#fff;
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-14);
  text-transform:uppercase;
}
.cupon-fila input::placeholder{text-transform:none; letter-spacing:0;}
.cupon-fila button{flex:0 0 auto;}
.cupon-puesto{display:flex; justify-content:space-between; align-items:center; gap:var(--space-3);}
.cupon-codigo{
  font-family:'JetBrains Mono',monospace; font-weight:700;
  letter-spacing:.06em; display:block;
}
.cupon-detalle{font-size:var(--fs-12); color:var(--concrete); display:block;}

/* En el pago, la caja del cupón ocupa el ancho del resumen. */
aside .cupon-caja{max-width:none; margin-left:0;}

/* Estados de los códigos en el panel. */
.estado-activo{background:var(--ink); color:#fff;}
.estado-programado{background:var(--paper-dim); color:var(--oxide); border-style:dashed;}
.estado-vencido{background:#fff; color:var(--alerta); border-color:var(--alerta); text-decoration:line-through;}
.estado-agotado{background:#fff; color:var(--oxide); border-width:var(--borde-fuerte);}
.estado-desactivado{background:var(--paper-dim); color:var(--concrete); border-style:dotted;}

/* Acciones de pie de página: lo secundario a la izquierda, lo principal
   siempre pegado a la derecha aunque no haya botón a la izquierda. */
.acciones-bolsa{
  display:flex; justify-content:space-between; align-items:center;
  gap:var(--space-4); flex-wrap:wrap; margin-top:var(--space-8);
}
.acciones-derecha{display:flex; gap:var(--space-3); flex-wrap:wrap; margin-left:auto;}
@media (max-width:520px){
  .acciones-bolsa, .acciones-derecha{width:100%;}
  .acciones-bolsa > form, .acciones-bolsa > a, .acciones-derecha > a{flex:1 1 100%;}
  .acciones-bolsa > form .btn-ghost, .acciones-bolsa > a, .acciones-derecha > a{width:100%;}
}
.candado{
  border-left:3px solid var(--ink); padding:var(--space-3) var(--space-4);
  background:var(--safety); margin-top:var(--space-6);
}
.candado a{text-decoration:underline;}

.checkout-grid{display:grid; grid-template-columns:minmax(0,1fr) 360px; gap:var(--space-8); align-items:start;}
@media (max-width:820px){ .checkout-grid{grid-template-columns:minmax(0,1fr); gap:var(--space-6);} }
/* El resumen del pedido acompaña al llenado de la dirección. */
@media (min-width:821px){
  .checkout-grid > aside{position:sticky; top:92px;}
}
.resumen-lista{list-style:none; margin:0 0 var(--space-3); padding:0; font-size:var(--fs-13);}
.resumen-lista li{
  display:flex; justify-content:space-between; gap:var(--space-3);
  padding:var(--space-2) 0; border-bottom:1px solid var(--line);
  line-height:1.45;
}
.resumen-lista li > span:last-child{white-space:nowrap;}
.resumen-lista em{color:var(--concrete); font-style:normal; font-size:var(--fs-12);}

/* métodos de pago */
.pay-methods{display:flex; flex-direction:column; gap:var(--space-2); margin-bottom:var(--space-6);}
.pay-opcion{
  display:grid; grid-template-columns:auto minmax(0,1fr); gap:var(--space-1) var(--space-3);
  align-items:center;
  padding:var(--space-4); border:var(--borde) solid var(--line-strong); cursor:pointer;
  transition:border-color var(--t), background var(--t);
}
.pay-opcion:hover{border-color:var(--ink);}
.pay-opcion:has(input:checked){border-color:var(--ink); border-width:var(--borde-fuerte); background:var(--safety);}
.pay-opcion.deshabilitada{opacity:.6; cursor:not-allowed;}
.pay-opcion input{width:18px; height:18px; grid-row:span 2; accent-color:var(--ink); margin:0;}
.pay-nombre{
  font-weight:600; font-size:var(--fs-14); line-height:1.3;
  display:inline-flex; align-items:center; gap:var(--space-2); flex-wrap:wrap;
}
.pay-desc{
  grid-column:2; font-family:'JetBrains Mono',monospace;
  font-size:var(--fs-11); line-height:1.45; color:var(--concrete);
}

/* ---------- confirmación ---------- */
.confirmacion{text-align:center; padding:var(--space-8) 0 var(--space-3);}
.conf-sello{
  width:72px; height:72px; margin:0 auto var(--space-5);
  border:var(--borde-fuerte) solid var(--ink);
  display:flex; align-items:center; justify-content:center;
  font-size:34px; line-height:1; color:var(--ink);
}
.conf-sello.alerta{border-color:var(--alerta); color:var(--alerta);}
.conf-sello.espera{border-style:dashed; color:var(--concrete);}
.conf-titulo{
  font-family:'Anton',sans-serif; font-weight:400; text-transform:uppercase;
  font-size:clamp(26px,5vw,38px); line-height:1.05; margin:0 0 var(--space-3);
}
.conf-texto{font-size:var(--fs-15); color:var(--oxide); max-width:52ch; margin:0 auto var(--space-4); line-height:1.6;}
.conf-folio{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-18); letter-spacing:.12em;
  border:var(--borde) solid var(--ink); display:inline-block;
  padding:var(--space-2) var(--space-4); margin:var(--space-2) 0 0;
}

/* ---------- estado del pedido ---------- */
.estado{
  display:inline-block; font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
  line-height:1.4; letter-spacing:.08em; text-transform:uppercase;
  padding:var(--space-1) var(--space-2);
  border:1px solid var(--ink); background:var(--safety); white-space:nowrap;
}
.estado.grande{font-size:var(--fs-12); padding:var(--space-2) var(--space-3);}
.estado-pagado{background:var(--ink); color:#fff;}
.estado-enviado{background:var(--oxide); color:#fff;}
.estado-entregado{background:#fff; color:var(--ink); border-width:var(--borde-fuerte);}
.estado-cancelado{background:#fff; color:var(--alerta); border-color:var(--alerta); text-decoration:line-through;}
/* Antes usaba el gris claro sobre gris: no llegaba al mínimo de contraste. */
.estado-pendiente{background:var(--paper-dim); color:var(--oxide); border-style:dashed;}

/* ============================================================
   FORMULARIOS
   ============================================================ */
.form-card{max-width:460px; margin:0 auto var(--space-8);}
.form-card h1, .form-card h2{
  font-family:'Anton',sans-serif; font-weight:400; text-transform:uppercase;
  font-size:var(--fs-30); line-height:1.1; margin:0 0 var(--space-4);
}
.form-intro{font-size:var(--fs-14); color:var(--oxide); line-height:1.6; margin:0 0 var(--space-6);}
.form-pie{font-size:var(--fs-13); color:var(--concrete); margin:var(--space-4) 0 0; text-align:center;}
.form-pie + .form-pie{margin-top:var(--space-2);}
.form-pie a{text-decoration:underline; color:var(--ink);}
/* Botón alterno debajo del principal: separación igual a la del resto. */
.form-reenvio{margin-top:var(--space-3);}

.field{margin-bottom:var(--space-4);}
.field.sin-margen{margin-bottom:0;}
.field label, .etiqueta{
  display:block; font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  line-height:1.4; letter-spacing:.06em; text-transform:uppercase;
  color:var(--concrete); margin-bottom:var(--space-2);
}
.field label .req, .etiqueta .req{color:var(--alerta); font-weight:700;}
.field input, .field select, .field textarea{
  width:100%; padding:var(--space-3); border:var(--borde) solid var(--ink);
  border-radius:var(--radio);
  font-family:inherit; font-size:var(--fs-16); min-height:var(--ctrl-h);
  background:#fff; color:var(--ink);
  transition:border-color var(--t), box-shadow var(--t);
}
/* Sólo lectura: se puede seleccionar y copiar, pero no editar. */
.field input[readonly], .field textarea[readonly]{
  background:var(--paper-dim); color:var(--oxide); border-color:var(--line-strong);
}
/* Deshabilitado: además del gris, borde punteado y cursor bloqueado. */
.field input:disabled, .field select:disabled, .field textarea:disabled{
  background:var(--paper-dim); color:var(--concrete);
  border-color:var(--line-strong); border-style:dashed; cursor:not-allowed;
}
/* Error: se marca al salir del campo (:user-invalid), no en cada tecla.
   El grosor extra va por box-shadow para que la caja no se mueva. */
.field input:user-invalid, .field select:user-invalid, .field textarea:user-invalid,
.field input[aria-invalid="true"], .field.con-error input, .field.con-error select{
  border-color:var(--alerta);
  box-shadow:inset 0 0 0 1px var(--alerta);
  background:var(--alerta-bg);
}
.campo-error{
  display:block; color:var(--alerta); font-size:var(--fs-12); font-weight:500;
  margin:var(--space-2) 0 0; line-height:1.45;
}
.field textarea{min-height:96px; resize:vertical; line-height:1.6;}
.input-color{padding:var(--space-1); height:var(--ctrl-h); min-height:var(--ctrl-h); cursor:pointer;}
.codigo-input{
  font-family:'JetBrains Mono',monospace; font-size:28px; letter-spacing:.4em;
  text-align:center; padding:var(--space-4); text-indent:.4em;
}
/* Campos seguros de Mercado Pago (Checkout API). Dentro de cada uno vive un
   iframe servido por la pasarela —ahí se teclea el número de tarjeta—, así que
   la caja se pinta aquí y el contenido lo pinta ellos. Sin altura explícita el
   iframe colapsa a cero y el campo parece que no existe. */
.mp-campo{
  width:100%; padding:0 var(--space-3); border:var(--borde) solid var(--ink);
  border-radius:var(--radio); background:#fff;
  height:var(--ctrl-h); min-height:var(--ctrl-h);
  display:flex; align-items:center;
}
.mp-campo iframe{width:100%; height:100%; border:0; display:block;}

.ayuda{font-size:var(--fs-12); color:var(--concrete); margin:var(--space-2) 0 0; line-height:1.5;}
.leyenda-obligatorio{font-size:var(--fs-12); color:var(--concrete); margin:0 0 var(--space-4);}
.check{
  display:flex; align-items:center; gap:var(--space-2); font-size:var(--fs-14);
  margin-bottom:var(--space-2); cursor:pointer; min-height:var(--ctrl-h);
  line-height:1.4;
}
.check input{width:18px; height:18px; flex-shrink:0; accent-color:var(--ink); margin:0;}
.check.mini{font-size:var(--fs-12); margin-bottom:0; min-height:var(--ctrl-h-sm);}
.grid-2{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:0 var(--space-4);}
@media (max-width:520px){ .grid-2{grid-template-columns:minmax(0,1fr);} }

/* ---------- mensajes ---------- */
.flash-zone{margin-bottom:var(--space-6);}
/* En el panel el flash se queda pegado bajo la barra. La ficha de producto
   mide varias pantallas: al borrar una talla de abajo del todo, la página
   recargaba arriba y el mensaje quedaba fuera de la vista del que lo provocó.
   El "top" es la altura de la barra pegajosa del panel. */
.admin-wrap .flash-zone{position:sticky; top:72px; z-index:30;}
.admin-wrap .flash{box-shadow:var(--sombra-fija);}
@media (max-width:860px){ .admin-wrap .flash-zone{top:118px;} }
@media (max-width:520px){ .admin-wrap .flash-zone{top:106px;} }

/* Vacío dentro de una caja, sin el aire de una sección entera. */
.vacio.compacto{padding:var(--space-3) 0; font-size:var(--fs-13);}
.flash{
  padding:var(--space-3) var(--space-4); margin-bottom:var(--space-2);
  font-size:var(--fs-14); line-height:1.5; border:var(--borde) solid var(--ink);
}
.flash:last-child{margin-bottom:0;}
.flash.ok{background:var(--safety);}
.flash.error{background:var(--ink); color:#fff;}

.vacio{color:var(--concrete); font-size:var(--fs-14); padding:var(--space-5) 0; line-height:1.6;}
.vacio a{text-decoration:underline; color:var(--ink);}

/* ---------- cuenta del cliente ---------- */
.cuenta-grid{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--space-6); margin-bottom:var(--space-6);
}
@media (max-width:720px){ .cuenta-grid{grid-template-columns:minmax(0,1fr);} }
/* Las dos cajas tienen distinto número de campos: el botón de cada una se
   empuja al fondo para que ambos queden sobre la misma línea. */
.cuenta-caja{
  border:var(--borde) solid var(--line-strong); padding:var(--space-5);
  display:flex; flex-direction:column;
}
.cuenta-caja form{display:flex; flex-direction:column; flex:1;}
.cuenta-caja form .btn-primary{margin-top:auto; align-self:flex-start;}
.caja-titulo{
  font-family:'Inter',sans-serif; font-size:var(--fs-16); font-weight:600;
  line-height:1.3; margin:0 0 var(--space-4);
  padding-bottom:var(--space-3); border-bottom:var(--borde) solid var(--line);
}
.aviso-verificar{
  border:var(--borde) solid var(--ink); background:var(--safety);
  padding:var(--space-4); margin-bottom:var(--space-6);
  font-size:var(--fs-14); display:flex; align-items:center;
  gap:var(--space-4); flex-wrap:wrap; line-height:1.5;
}
.aviso-verificar .btn-ghost{margin-left:auto;}
@media (max-width:520px){ .aviso-verificar .btn-ghost{margin-left:0; width:100%;} }
.pedido-cabecera{
  display:flex; justify-content:space-between; align-items:flex-start;
  gap:var(--space-4); flex-wrap:wrap; margin-bottom:var(--space-6);
}
.pedido-folio{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-22);
  line-height:1.2; letter-spacing:.08em; margin:0 0 var(--space-1);
}
.direccion{font-size:var(--fs-14); line-height:1.7; color:var(--oxide); margin:0;}

/* ---------- páginas de error ---------- */
.error-page{text-align:center; padding:var(--space-10) 0 var(--space-8);}
.error-codigo{
  font-family:'Anton',sans-serif; font-size:clamp(70px,16vw,140px);
  line-height:.9; color:var(--paper-dim2);
}
.error-titulo{
  font-family:'Anton',sans-serif; font-weight:400; text-transform:uppercase;
  font-size:clamp(24px,5vw,36px); line-height:1.05; margin:var(--space-2) 0 var(--space-3);
}
.error-msg{font-size:var(--fs-15); color:var(--oxide); max-width:46ch; margin:0 auto var(--space-6); line-height:1.6;}
.error-acciones{display:flex; gap:var(--space-4); justify-content:center; flex-wrap:wrap;}

/* ---------- pie de página ---------- */
footer.site{
  background:var(--ink); color:#fff; text-align:center;
  padding:var(--space-8) var(--space-4); position:relative; z-index:10;
}
footer.site .foot-brand{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12);
  line-height:1.4; letter-spacing:.14em; text-transform:uppercase;
}
footer.site .foot-note{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete-dark);   /* antes #a9a297: único tono cálido de la paleta */
  max-width:60ch; margin:var(--space-2) auto 0; line-height:1.7;
}
footer.site .foot-note a{color:#fff; text-decoration:underline;}

/* ---------- Instagram flotante: un botón, una cuenta por marca ----------
   El botón es el <summary> de un <details>. La lista se abre al tocarlo; en
   dispositivos con cursor, ui.js la abre también al pasar por encima (con CSS
   no se puede: un <details> cerrado no pinta su contenido, lo diga o no el
   `display`). */
.ig-menu{position:fixed; right:var(--space-4); bottom:var(--space-4); z-index:30;}
.ig-fab{
  list-style:none; cursor:pointer;
  width:var(--ctrl-h); height:var(--ctrl-h); border-radius:var(--radio);
  background:var(--ink); color:#fff; border:1.5px solid #fff;
  display:flex; align-items:center; justify-content:center;
  box-shadow:var(--sombra-flotante); transition:background var(--t);
}
.ig-fab:hover{background:var(--oxide);}
.ig-fab::-webkit-details-marker{display:none;}
/* El aire entre la lista y el botón es padding de .ig-lista, no margen: así
   el cursor no "sale" del menú al cruzarlo y la lista no se cierra. */
.ig-lista{
  display:none; position:absolute; right:0; bottom:100%;
  padding-bottom:var(--space-2);
}
.ig-menu[open] .ig-lista{display:block;}
.ig-lista-caja{
  min-width:220px; background:var(--ink); border:1.5px solid #fff;
  box-shadow:var(--sombra-flotante);
}
.ig-lista a{
  display:block; padding:10px 14px; color:#fff;
  font-family:'Inter',sans-serif; font-size:var(--fs-12); font-weight:600;
  line-height:1.3; letter-spacing:.02em; white-space:nowrap;
  transition:background var(--t);
}
.ig-lista a + a{border-top:1px solid #444;}
.ig-lista a small{
  display:block; font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
  font-weight:400; line-height:1.4; letter-spacing:.08em; text-transform:uppercase;
  color:#aaa;
}
.ig-lista a:hover, .ig-lista a:focus-visible{background:var(--oxide);}

/* ============================================================
   PANEL DE ADMINISTRACIÓN (layout propio, fondo blanco)
   ============================================================ */
.admin-body{background:var(--paper);}
.admin-top{
  position:sticky; top:0; z-index:40; background:var(--admin-barra); color:var(--admin-barra-texto);
  display:flex; align-items:center; gap:var(--space-4); flex-wrap:wrap;
  padding:var(--space-3) var(--space-5); border-bottom:var(--borde-fuerte) solid var(--ink);
}
.admin-marca{
  font-family:'Anton',sans-serif; font-size:var(--fs-18); text-transform:uppercase;
  line-height:1.2; letter-spacing:.04em; display:flex; align-items:baseline; gap:var(--space-2);
}
/* La barra del panel ya es negra: el logo blanco entra sin recuadro. Aquí va
   sólo la calavera (logo-calavera.png): el logo combinado a 30 px no se lee. */
.admin-marca img{width:30px; height:30px; object-fit:contain; align-self:center;}
.admin-marca span{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
  letter-spacing:.16em; color:var(--concrete-dark); text-transform:uppercase;
}
.admin-nav{display:flex; gap:var(--space-2); flex-wrap:wrap; flex:1;}
.admin-nav a{
  padding:var(--space-2) var(--space-3); font-size:var(--fs-12);
  letter-spacing:.06em; text-transform:uppercase;
  color:var(--admin-barra-tenue); border:1px solid transparent;
  min-height:var(--ctrl-h-sm); display:inline-flex; align-items:center;
  transition:color var(--t), border-color var(--t), background var(--t);
}
.admin-nav a:hover{color:var(--admin-barra-texto); border-color:var(--admin-barra-linea);}
.admin-nav a.activo{background:var(--admin-barra-texto); color:var(--ink); font-weight:600; border-color:var(--admin-barra-texto);}
.admin-user{display:flex; align-items:center; gap:var(--space-3); flex-shrink:0;}
.admin-quien{font-family:'JetBrains Mono',monospace; font-size:var(--fs-11); color:var(--admin-barra-tenue);}
.admin-user .btn-ghost{border-color:var(--admin-barra-texto); color:var(--admin-barra-texto);}
.admin-user .btn-ghost:hover{background:var(--admin-barra-texto); color:var(--ink);}
@media (max-width:860px){
  .admin-top{gap:var(--space-3); padding:var(--space-3) var(--space-4);}
  .admin-nav{order:3; flex-basis:100%; flex:1 0 100%;}
  .admin-user{margin-left:auto;}
}
/* A 375px la barra se comía 165px de alto —un cuarto de la pantalla— porque
   las cinco secciones se repartían en dos filas con el tipo a tamaño de
   escritorio. Se aprieta sin bajar de los 44px de zona pulsable. */
@media (max-width:520px){
  .admin-top{padding:var(--space-2) var(--space-3); gap:var(--space-2);}
  .admin-marca{font-size:var(--fs-14);}
  .admin-marca img{width:24px; height:24px;}
  .admin-marca span{display:none;}       /* "panel" lo dice ya el contexto */
  .admin-quien{display:none;}            /* sólo hay un administrador */

  /* Las cinco secciones en UNA fila que se desliza, en vez de dos filas
     apiladas. La barra es pegajosa: cada renglón suyo es un renglón que el
     contenido no tiene en todas las pantallas del panel. Deslizar una barra
     de navegación es un patrón conocido; robarle 50px a un móvil, no. */
  .admin-nav{
    flex-wrap:nowrap; overflow-x:auto; gap:var(--space-1);
    scrollbar-width:none; -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;
    margin:0 calc(var(--space-3) * -1); padding:0 var(--space-3);
  }
  .admin-nav::-webkit-scrollbar{display:none;}
  .admin-nav a{
    flex:0 0 auto; padding:var(--space-2) var(--space-3);
    font-size:var(--fs-11); scroll-snap-align:start;
  }
}

.admin-wrap{
  position:relative; z-index:10; background:var(--paper); min-height:100vh;
  max-width:1180px; margin:0 auto;
  padding:var(--space-6) var(--space-5) var(--space-16);
}
/* La barra del panel es pegajosa, así que un salto a #inventario o a #fotos
   dejaba el título justo DEBAJO de ella: se llegaba al sitio correcto sin
   poder leer a dónde se había llegado. El margen de desplazamiento reserva
   el alto de la barra. */
.admin-wrap [id]{scroll-margin-top:78px;}
@media (max-width:860px){ .admin-wrap [id]{scroll-margin-top:130px;} }
@media (max-width:520px){ .admin-wrap [id]{scroll-margin-top:118px;} }
.admin-cabecera{
  display:flex; justify-content:space-between; align-items:center;
  gap:var(--space-4); flex-wrap:wrap; margin-bottom:var(--space-6);
}
/* La acción de la derecha se pega al borde. El :not(:first-child) es
   necesario: en las pantallas donde la cabecera sólo lleva el título, ese
   título es a la vez primer y último hijo y se iría a la derecha. */
.admin-cabecera > :last-child:not(:first-child){margin-left:auto;}
@media (max-width:520px){
  .admin-cabecera > :last-child:not(:first-child){margin-left:0;}
}
.admin-h1{
  font-family:'Anton',sans-serif; font-weight:400; text-transform:uppercase;
  font-size:var(--fs-30); line-height:1.1; margin:0;
}
.admin-h2{
  font-family:'Inter',sans-serif; font-size:var(--fs-18); font-weight:600;
  line-height:1.3; margin:var(--space-10) 0 var(--space-4);
  padding-bottom:var(--space-2); border-bottom:var(--borde) solid var(--line);
}
/* Aclaración a media voz junto al título ("2 piezas o menos por talla"): es
   contexto del aviso, no parte del nombre de la sección. */
.admin-h2-nota{
  font-weight:400; font-size:var(--fs-13); color:var(--concrete);
  text-transform:none; letter-spacing:0;
}
/* Lista de archivos elegidos, con el color de cada uno antes de subirlos. */
.lista-fotos{margin-top:var(--space-4);}
.lista-fotos:empty{display:none;}
.fila-foto{
  display:flex; align-items:center; gap:var(--space-3);
  padding:var(--space-2) 0; border-bottom:1px dashed var(--line);
}
.fila-foto:last-child{border-bottom:0;}
.fila-foto-nombre{
  flex:1 1 auto; min-width:0; font-size:var(--fs-12);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.fila-foto select{flex:0 0 auto; max-width:190px;}

/* Selector de color bajo cada miniatura ya subida. */
.foto-mover{margin-bottom:var(--space-2);}
.foto-mover select{width:110px; font-size:var(--fs-11); padding:2px;}

.peso-fotos{margin:var(--space-2) 0 0; font-variant-numeric:tabular-nums;}
.peso-fotos.alerta{color:var(--alerta); font-weight:600;}

.umbral-selector{display:flex; align-items:center; gap:var(--space-2); flex-wrap:wrap;}
.umbral-selector .btn-ghost.activo{
  background:var(--ink); color:var(--paper); border-color:var(--ink);
}

/* Tallas bajas de un mismo producto, en una sola celda. Antes cada talla era
   una fila entera y una prenda con 12 tallas se comía la pantalla. */
.tallas-bajas{display:flex; flex-wrap:wrap; gap:var(--space-2);}
.talla-chip{
  display:inline-flex; align-items:baseline; gap:var(--space-2);
  border:1px solid var(--line-strong); padding:2px var(--space-2);
  font-size:var(--fs-12); white-space:nowrap; background:var(--paper);
}
.talla-chip b{font-family:'JetBrains Mono',monospace; font-weight:700;}
.talla-chip.agotada{
  border-color:var(--alerta); color:var(--alerta);
}
.talla-chip.agotada b{text-transform:uppercase; font-size:var(--fs-10); letter-spacing:.06em;}

.admin-caja{border:var(--borde) solid var(--line-strong); padding:var(--space-5); margin-bottom:var(--space-6);}
.admin-caja > :last-child{margin-bottom:0;}
.admin-caja.peligro-caja{border-color:var(--alerta);}
.admin-columnas{display:grid; grid-template-columns:minmax(0,1fr) 340px; gap:var(--space-6); align-items:start;}
@media (max-width:900px){ .admin-columnas{grid-template-columns:minmax(0,1fr);} }
/* Las tablas de 7-8 columnas (productos, pedidos) necesitan más ancho mínimo:
   así se desplazan en horizontal en lugar de aplastar las columnas. */
table.cart-table.ancha{min-width:900px;}

/* ============================================================
   TARJETA DEL PANEL - un solo componente

   Antes la pantalla mezclaba tres tratamientos: las cifras de resumen
   en caja clara, las cinco graficas en caja oscura con radio de 20px,
   y las dos tablas sin caja ninguna, flotando sobre el fondo. Ahora
   todo el contenido va en la MISMA caja, y lo unico oscuro son las
   tres tarjetas KPI, que quedan como acento a proposito.
   ============================================================ */
.tarjeta{
  background:var(--paper);
  border:var(--borde) solid var(--ink);
  padding:var(--space-4);
}

/* tarjetas de resumen - todas las etiquetas caen sobre la misma linea */
/* 172px de minimo: las cuatro que quedaron tras fusionar caben en un solo
   renglon al ancho maximo del panel. Las otras dos viven ahora en el pie de
   su tarjeta KPI. */
.stat-row{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(172px,1fr));
  gap:var(--space-3); margin:var(--space-5) 0 var(--space-8);
}
/* La caja la pone .tarjeta; aqui solo el acomodo de las dos lineas. */
.stat{display:flex; flex-direction:column; gap:var(--space-1);}
.stat .num{
  font-family:'Anton',sans-serif; font-size:clamp(19px, 1.8vw, 26px);
  line-height:1.15; word-break:break-word;
}
.stat .lbl{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-10); text-transform:uppercase;
  letter-spacing:.06em; color:var(--concrete); line-height:1.4;
  margin-top:auto; padding-top:var(--space-1);
}

/* ---- migas ---- */
.migas{
  display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete); margin-bottom:var(--space-4);
}
.migas a{color:var(--concrete); text-decoration:underline; text-underline-offset:3px;}
.migas a:hover{color:var(--ink);}
.migas [aria-current="page"]{color:var(--ink); font-weight:600;}
.migas [aria-hidden="true"]{color:var(--concrete-soft);}

/* ---------- tarjetas que envuelven una tabla ----------
   El titulo va DENTRO de la caja, como en las graficas, para que la tarjeta
   se lea como una unidad y no como un borde suelto alrededor de una tabla. */
.tarjeta-tabla{padding:var(--space-5); margin-bottom:var(--space-6);}
.tarjeta-tabla > .admin-h2:first-child,
.tarjeta-tabla > .admin-cabecera:first-child{margin-top:0;}
.tarjeta-tabla > .tabla-scroll:last-child,
.tarjeta-tabla > .vacio:last-child{margin-bottom:0;}

/* ============================================================
   GRAFICAS DEL PANEL

   Misma caja que todo lo demas. Dentro, la regla de color se mantiene
   pero INVERTIDA respecto a la version oscura: el negro es siempre el
   dato principal y el gris el secundario, nunca al reves y nunca un
   color saturado.
   ============================================================ */
:root{
  /* Solo lo que siguen usando las tarjetas KPI, que se quedan oscuras. */
  --panel-fondo:#141414;
  --panel-borde:#262626;
  --panel-texto:#fafafa;
  --panel-tenue:#8a8a8a;
  --panel-radio:20px;
}

.graficas-grid{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--space-5); margin-bottom:var(--space-6);
}
.graficas-grid .ancha{grid-column:1 / -1;}
@media (max-width:820px){
  .graficas-grid{grid-template-columns:minmax(0,1fr);}
  .graficas-grid .ancha{grid-column:auto;}
}

.grafica-caja{
  padding:var(--space-5);
  display:flex; flex-direction:column; gap:var(--space-3);
}

/* --- fila superior: rotulo ---
   Es un <h2> en las tarjetas de grafica -da nombre a la region y deja
   navegar por encabezados- y un <p> en las KPI, donde el dato es el
   contenido. Se fija el peso porque un <h2> viene en negrita por defecto. */
.caja-rotulo{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
  font-weight:500; text-transform:uppercase; letter-spacing:.14em;
  color:var(--concrete); margin:0; line-height:1.4;
}

/* --- el dato que resume la grafica --- */
.caja-cifra{
  margin:0; display:flex; align-items:baseline; gap:var(--space-2);
  flex-wrap:wrap; line-height:1.1;
}
.caja-cifra b{
  font-family:'Anton',sans-serif; font-weight:400;
  font-size:clamp(26px, 2.6vw, 34px); color:var(--ink);
  letter-spacing:.01em;
}
.caja-cifra span{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete);
}

/* flex-basis auto (= los 280px de alto) + grow: el lienzo absorbe el espacio
   sobrante cuando la rejilla estira la caja, y las graficas quedan alineadas
   entre si sin perder nunca un alto definido, que Chart.js necesita. */
.lienzo{position:relative; height:280px; flex:1 1 auto; min-width:0;}
@media (max-width:520px){ .lienzo{height:240px;} }

/* --- pie: etiqueta a la izquierda, cifra a la derecha --- */
.caja-pie{
  margin:0; display:flex; align-items:baseline;
  justify-content:space-between; gap:var(--space-3);
  padding-top:var(--space-3);
  border-top:var(--borde) solid var(--line);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
}
.caja-pie span{color:var(--concrete);}
.caja-pie b{
  color:var(--ink); font-weight:600;
  text-align:right; min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* Mientras el fetch de metricas no responde. Un lienzo en blanco se lee como
   "no hay datos"; estas barras dicen que todavia vienen. */
.grafica-caja.cargando .lienzo{
  background:
    linear-gradient(90deg, transparent 0 8%, var(--paper-dim) 8% 22%, transparent 22% 30%,
                    var(--paper-dim) 30% 44%, transparent 44% 52%,
                    var(--paper-dim) 52% 66%, transparent 66% 74%,
                    var(--paper-dim) 74% 88%, transparent 88%)
    bottom / 100% 70% no-repeat;
  animation:esqueleto 1.2s ease-in-out infinite;
}
.grafica-caja.cargando .lienzo::after{
  content:"Cargando..."; position:absolute; inset:auto 0 0 0;
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete); text-align:center; padding-top:var(--space-2);
}
@keyframes esqueleto{0%,100%{opacity:1;} 50%{opacity:.45;}}
@media (prefers-reduced-motion: reduce){
  .grafica-caja.cargando .lienzo{animation:none;}
}
.vacio-grafica{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete); text-align:center; margin:0; line-height:1.5;
}

/* ---------- tarjetas KPI ----------
   Lo UNICO oscuro de la pantalla, y a proposito: ahora que el resto es
   claro, son las tres cifras que la vista encuentra primero. */
.kpi-row{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(300px,1fr));
  gap:var(--space-5); margin:var(--space-5) 0;
}
.kpi{
  /* Sin esto, el ancho minimo del contenido (la cifra va en nowrap) manda
     sobre la pista de la rejilla y la tarjeta empuja la pagina a lo ancho. */
  min-width:0;
  background:var(--panel-fondo);
  border:var(--borde) solid var(--panel-borde);
  border-radius:var(--panel-radio);
  padding:var(--space-5);
  display:flex; flex-direction:column; gap:var(--space-3);
  color:var(--panel-texto);
}
/* Los bloques que comparte con las graficas se repintan para fondo oscuro.
   Van despues de la definicion clara, asi que ganan por orden y por peso. */
.kpi .caja-rotulo{color:var(--panel-tenue);}
.kpi .caja-pie{border-top-color:var(--panel-borde);}
.kpi .caja-pie span{color:var(--panel-tenue);}
.kpi .caja-pie b{color:var(--panel-texto);}

/* Alineado ARRIBA, no abajo. Con flex-end, la tarjeta sin pie (ticket
   promedio) empujaba su cifra al fondo y quedaba casi cien pixeles mas
   baja que las otras dos: las tres cifras deben leerse en la misma linea. */
.kpi-cuerpo{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--space-4); flex:1;
}
.kpi-datos{min-width:0;}
.kpi-cifra{
  font-family:'Anton',sans-serif; font-weight:400;
  font-size:clamp(24px, 2.2vw, 34px); line-height:1.05;
  color:var(--panel-texto); margin:0;
  /* Una cantidad de dinero no se parte nunca: "MX$62,110.0" en una linea y
     el "0" suelto en la siguiente no es un numero, es un error de lectura. */
  white-space:nowrap;
}
/* Sin verde ni rojo: la paleta es B&N y el signo ya dice el sentido.
   Lo que separa "subio" de "bajo" es el peso, no el tono. */
.kpi-cambio{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  margin:var(--space-2) 0 0; line-height:1.4;
  color:var(--panel-tenue);
}
.kpi-cambio.sube{color:var(--panel-texto); font-weight:600;}
/* Cede ancho antes que la cifra: si algo tiene que encogerse, que sea el
   dibujo de la tendencia y no el numero. Centrada respecto al bloque de
   texto, que es mas alto que ella. */
.kpi-chispa{
  width:96px; height:46px; flex:0 1 96px; min-width:0;
  align-self:center; position:relative;
}
@media (max-width:520px){ .kpi-chispa{display:none;} }

/* formularios en línea del panel */
.fila-form{
  display:flex; gap:var(--space-3); align-items:flex-end; flex-wrap:wrap;
  margin:var(--space-4) 0;
}
.fila-form.compacta{margin:var(--space-3) 0 0;}
.fila-form .field{min-width:140px; flex:0 1 auto;}
.fila-form input[type="file"]{
  font-size:var(--fs-13); padding:var(--space-2); border:var(--borde) dashed var(--ink);
  background:var(--paper); min-height:var(--ctrl-h); max-width:100%;
}
.acciones-celda{display:flex; gap:var(--space-2); flex-wrap:wrap; justify-content:flex-end;}

/* colores del panel */
.colores-lista{display:flex; flex-direction:column; gap:var(--space-5); margin-top:var(--space-5);}
.color-tarjeta{border:var(--borde) solid var(--line-strong); padding:var(--space-4); background:#fcfcfc;}
.color-cabecera{
  display:flex; align-items:center; gap:var(--space-4); flex-wrap:wrap;
  padding-bottom:var(--space-3); margin-bottom:var(--space-3);
  border-bottom:var(--borde) solid var(--line);
}
.color-cabecera > form{margin-left:auto;}
@media (max-width:520px){ .color-cabecera > form{margin-left:0;} }
.chip-color{display:inline-flex; align-items:center; gap:var(--space-2); font-size:var(--fs-13); line-height:1.3;}
/* Varios chips seguidos dentro de una celda: separación explícita. */
.chips-lista{display:flex; flex-wrap:wrap; gap:var(--space-1) var(--space-3);}

/* fotos del panel */
.fotos-color{margin-top:var(--space-4); padding-top:var(--space-4); border-top:1px dashed var(--line);}
.fotos-grid{display:flex; flex-wrap:wrap; gap:var(--space-4); margin:var(--space-3) 0;
  list-style:none; padding:0;}
.foto-mini{margin:0; width:110px; text-align:center; position:relative;}
.foto-material-badge{
  display:block; margin-top:2px; font-size:var(--fs-11); color:var(--concrete);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* ---- reordenar fotos arrastrando ----
   El número no es decoración: dice cuál sale de portada en la tienda, así que
   va sobre la miniatura y no debajo, donde se perdería entre los controles. */
.foto-orden{
  position:absolute; top:0; left:0; z-index:2;
  min-width:22px; height:22px; padding:0 var(--space-1);
  display:flex; align-items:center; justify-content:center;
  background:var(--ink); color:var(--paper);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11); font-weight:600;
}
/* La primera de cada grupo es LA portada: se marca aparte porque es la única
   posición que el cliente ve sí o sí. */
.es-reordenable .foto-mini:first-child .foto-orden{background:var(--exito);}
.es-reordenable .foto-mini:first-child img{border-color:var(--exito); border-width:var(--borde-fuerte);}

.foto-agarre{
  position:absolute; top:0; right:0; z-index:2;
  width:26px; height:26px; display:none;
  align-items:center; justify-content:center;
  background:var(--ink); color:var(--paper); cursor:grab;
  font-size:14px; line-height:1; touch-action:none;
}
/* El asa sólo aparece si SortableJS llegó: si no, no hay nada que arrastrar
   y un asa muerta sería una promesa falsa. Los botones ←/→ no dependen de él. */
.se-arrastra .foto-agarre{display:flex;}
.se-arrastra .foto-agarre:active{cursor:grabbing;}
/* En pantalla táctil el agarre es el único modo de arrastrar sin pelear con
   el scroll, así que ahí crece hasta el mínimo táctil. */
@media (pointer:coarse){
  .se-arrastra .foto-agarre{width:var(--ctrl-h-sm); height:var(--ctrl-h-sm); font-size:18px;}
}

/* Reordenar con el teclado. Arrastrar no llega al teclado, así que estos
   botones son el camino accesible, no un extra: van visibles siempre, no
   sólo al pasar el ratón, para que se puedan tabular y encontrar. */
.foto-mover-orden{display:flex; gap:var(--space-1); margin-bottom:var(--space-2);}
.foto-mover-orden .btn-ghost{
  flex:1; min-width:0; padding:0; justify-content:center;
  min-height:var(--ctrl-h-sm); font-size:var(--fs-13); line-height:1;
}
.foto-mover-orden .btn-ghost:disabled{
  opacity:.3; cursor:not-allowed; border-color:var(--line);
}
.foto-mover-orden .btn-ghost:disabled:hover{background:none; color:inherit;}
@media (pointer:coarse){
  .foto-mover-orden .btn-ghost{min-height:var(--ctrl-h);}
}

.foto-fantasma{opacity:.35;}
.foto-elegida img{border-color:var(--exito); box-shadow:var(--sombra-flotante);}
.fotos-grid.guardando{opacity:.6; pointer-events:none;}
.fotos-grid.guardado .foto-orden{background:var(--exito);}

/* ---- reordenar productos (misma mecánica que las fotos, en lista) ---- */
.orden-lista{list-style:none; margin:var(--space-4) 0 0; padding:0; display:flex; flex-direction:column; gap:var(--space-2);}
.orden-fila{
  display:flex; align-items:center; gap:var(--space-3);
  padding:var(--space-2) var(--space-3);
  border:var(--borde) solid var(--line-strong); background:var(--paper);
}
.orden-fila .foto-orden{position:static; min-width:1.6em; text-align:center;}
.orden-fila .foto-agarre{position:static; display:flex;}
.se-arrastra .orden-fila .foto-agarre{cursor:grab;}
.es-reordenable .orden-fila:first-child{border-color:var(--exito);}
.orden-thumb{width:48px; height:48px; object-fit:contain; background:var(--paper); border:1px solid var(--line-strong); flex:none;}
.orden-thumb.sin-foto{display:flex; align-items:center; justify-content:center; color:var(--concrete); font-size:22px;}
.orden-nombre{flex:1; font-weight:500;}
.orden-precio{white-space:nowrap;}
.orden-fila.fila-oculta{opacity:.55;}
.orden-fila .foto-mover-orden{margin:0;}
.orden-lista.guardando{opacity:.6; pointer-events:none;}
.orden-lista.guardado .orden-fila:first-child{border-color:var(--exito);}

/* ---- flechas de orden de las opciones de estampado ---- */
.mover-flechas{display:flex; flex-direction:column; gap:2px;}
.mover-flechas .btn-ghost:disabled{opacity:.35; cursor:default;}

/* ---- avisos flotantes (acciones que no recargan la página) ---- */
.avisos-flotantes{
  position:fixed; right:var(--space-5); bottom:var(--space-5); z-index:80;
  display:flex; flex-direction:column; gap:var(--space-2);
  max-width:min(360px, calc(100vw - var(--space-8)));
}
.aviso-flotante{
  margin:0; padding:var(--space-3) var(--space-4);
  border:var(--borde) solid var(--exito); border-left-width:var(--space-1);
  background:var(--exito-bg); color:var(--exito);
  font-size:var(--fs-13); box-shadow:var(--sombra-flotante);
  animation:aviso-entra .2s ease;
}
.aviso-flotante.error{
  border-color:var(--alerta); background:var(--alerta-bg); color:var(--alerta);
  cursor:pointer;
}
.aviso-flotante.saliendo{opacity:0; transition:opacity .3s ease;}
@keyframes aviso-entra{from{opacity:0; transform:translateY(8px);} to{opacity:1; transform:none;}}
@media (prefers-reduced-motion: reduce){
  .aviso-flotante{animation:none;}
}
@media (max-width:520px){
  .avisos-flotantes{left:var(--space-4); right:var(--space-4); bottom:var(--space-4); max-width:none;}
}

/* ---- modal de confirmación ---- */
.modal-confirmar{
  border:var(--borde-fuerte) solid var(--ink); padding:0;
  max-width:min(440px, calc(100vw - var(--space-8)));
  background:var(--paper); color:var(--ink); box-shadow:var(--sombra-flotante);
}
.modal-confirmar::backdrop{background:rgba(14,14,14,.55);}
.modal-caja{padding:var(--space-5); display:flex; flex-direction:column; gap:var(--space-3);}
.modal-titulo{
  font-family:'Anton',sans-serif; text-transform:uppercase; letter-spacing:.03em;
  font-size:var(--fs-18); line-height:1.2; margin:0; color:var(--alerta);
  display:flex; align-items:center; gap:var(--space-2);
}
.modal-texto{margin:0; font-size:var(--fs-14); line-height:1.5;}
.modal-nota{
  margin:0; font-size:var(--fs-12); color:var(--alerta);
  background:var(--alerta-bg); border-left:var(--space-1) solid var(--alerta);
  padding:var(--space-2) var(--space-3);
}
.modal-botones{display:flex; gap:var(--space-3); justify-content:flex-end; flex-wrap:wrap;}
.modal-botones button{min-height:var(--ctrl-h);}
@media (max-width:420px){
  .modal-botones{flex-direction:column-reverse;}
  .modal-botones button{width:100%;}
}
.foto-mini img{
  width:110px; height:110px; object-fit:contain; background:var(--paper);
  border:var(--borde) solid var(--ink);
  display:block; margin-bottom:var(--space-2);
}

/* miniatura de la lista de productos: de un vistazo se ve a quién le falta foto */
.celda-foto{display:inline-flex; flex-direction:column; align-items:center; gap:var(--space-1);}
.celda-foto img{
  width:56px; height:56px; object-fit:contain; background:var(--paper);
  border:1px solid var(--line-strong);
  display:block;
}
.celda-foto .ayuda, .celda-foto .texto-alerta{font-size:var(--fs-11); white-space:nowrap;}
.marca-sin-foto{
  width:56px; height:56px; display:flex; align-items:center; justify-content:center;
  border:1px dashed var(--alerta); background:var(--alerta-bg);
  color:var(--alerta); font-size:22px; line-height:1;
}
.celda-foto.sin-foto:hover .marca-sin-foto{background:var(--alerta); color:var(--paper);}

/* ============================================================
   VISOR DE FOTO (sustituye a la vieja página de detalle)
   ============================================================ */
.visor{
  position:fixed; inset:0; z-index:100;
  display:flex; align-items:center; justify-content:center;
  padding:var(--space-6);
  /* Escritorio: el visor NO intercepta el ratón. Si lo hiciera, al abrirse
     bajo el cursor dispararía el mouseleave del botón y se cerraría solo,
     en bucle. El clic pasa de largo y lo recoge el listener del documento. */
  pointer-events:none;
  /* Siempre presente, sólo invisible. Antes se mostraba y luego se animaba en
     el cuadro siguiente, y si el navegador retrasaba ese cuadro el visor se
     quedaba visible pero a opacidad 0. Ahora todo cuelga de una sola clase.
     visibility se retrasa hasta el final para que la salida también se vea. */
  opacity:0; visibility:hidden;
  transition:opacity .16s ease, visibility 0s linear .16s;
}
.visor.abierto{
  opacity:1; visibility:visible;
  transition:opacity .16s ease, visibility 0s;
}
.visor-fondo{position:absolute; inset:0; background:rgba(14,14,14,.74);}
.visor-marco{
  position:relative; z-index:1; margin:0;
  /* Más contenido que antes (760px): a pantalla casi completa el salto se
     sentía brusco y tapaba la ficha que el usuario está leyendo. */
  max-width:min(520px, 86vw); max-height:78vh;
  display:flex; align-items:center; justify-content:center;
}
.visor-marco img{
  display:block; width:auto; height:auto;
  max-width:100%; max-height:78vh; object-fit:contain;
  /* El visor va sobre un velo oscuro: sin este blanco, un PNG transparente
     se ampliaba con la prenda flotando sobre negro. */
  background:var(--paper);
  border:var(--borde-fuerte) solid var(--ink);
  box-shadow:0 18px 60px rgba(0,0,0,.55);
}

/* Entrada suave: aparecer de golpe era lo que lo hacía sentir tosco,
   sobre todo abriéndose con el simple paso del cursor. */
.visor-marco{transform:scale(.97); transition:transform .16s ease;}
.visor.abierto .visor-marco{transform:scale(1);}
@media (prefers-reduced-motion: reduce){
  .visor, .visor-marco{transition:none;}
}
.visor-cerrar{display:none;}

/* Táctil: aquí sí recibe toques (para cerrar tocando fuera) y sale la ✕.
   Exige TAMBIÉN .abierto: cerrado nunca debe capturar el toque. Al desaparecer
   con retardo, un visor cerrado pero aún "visible" se comía el siguiente
   toque sobre la foto y parecía que ya no se podía volver a abrir. */
.visor.abierto.visor-tactil{
  pointer-events:auto;
  /* Hueco arriba para la ✕: va FUERA del recuadro, no encima de la prenda. */
  padding-top:calc(44px + var(--space-6));
}
.visor.visor-tactil .visor-cerrar{
  display:flex; align-items:center; justify-content:center;
  /* bottom:100% la deja apoyada justo por encima del borde superior de la
     foto, alineada a su esquina derecha: nunca tapa la prenda. */
  position:absolute; bottom:100%; right:0; margin-bottom:var(--space-2); z-index:2;
  width:44px; height:44px;            /* mínimo táctil */
  background:var(--ink); color:var(--paper); border:none;
  font-size:var(--fs-18); line-height:1;
}
/* Con la ✕ ocupando arriba, la foto baja su tope para que todo quepa. */
.visor.visor-tactil .visor-marco{max-height:68vh;}
.visor.visor-tactil .visor-marco img{max-height:68vh;}
body.sin-scroll{overflow:hidden;}

/* filtros y paginación */
.filtros{display:flex; gap:var(--space-2); flex-wrap:wrap; margin-bottom:var(--space-5);}
.filtro{
  padding:var(--space-2) var(--space-3); border:1px solid var(--line-strong);
  font-family:'JetBrains Mono',monospace;
  font-size:var(--fs-11); letter-spacing:.06em; text-transform:uppercase; color:var(--concrete);
  min-height:var(--ctrl-h-sm); display:inline-flex; align-items:center;
  transition:border-color var(--t), color var(--t), background var(--t);
}
.filtro:hover{border-color:var(--ink); color:var(--ink);}
.filtro.activo{background:var(--ink); color:var(--paper); border-color:var(--ink);}
.paginacion{
  display:flex; align-items:center; gap:var(--space-4);
  justify-content:center; flex-wrap:wrap; margin-top:var(--space-6);
}
.paginacion .ayuda{margin:0;}

/* ---------- respeta la preferencia de movimiento reducido ---------- */
@media (prefers-reduced-motion: reduce){
  *{animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;}
}

/* ============================================================
   PANEL EN PANTALLA PEQUEÑA: las tablas se vuelven fichas

   El administrador surte inventario y sube fotos desde el celular. Las tablas
   de Productos y Pedidos tienen ocho columnas con inputs dentro: a 375px eso
   es scroll horizontal permanente sobre controles diminutos. Aquí cada fila
   se apila como una ficha y cada celda lleva delante el nombre de su columna,
   que sale del atributo data-etiqueta de la plantilla.

   Se aplica a toda .cart-table que NO sea .compacta. Las compactas (tallas de
   un color, inventario de la ficha) ya declaran min-width:0 y caben; las demás
   arrastran el min-width de 560px —900px las .ancha— que es justo lo que hacía
   que la primera versión de esto siguiera desbordando: apilar las celdas no
   sirve de nada si la tabla conserva un ancho mínimo mayor que la pantalla.
   ============================================================ */
@media (max-width:820px){

  /* 1. Quitar el ancho mínimo. Sin esto todo lo demás es decorativo.
        El selector lleva `table` delante para ganarle a table.cart-table.ancha
        sin recurrir a !important. */
  .admin-wrap table.cart-table:not(.compacta){min-width:0; width:100%;}

  /* 2. El envoltorio deja de desplazarse: ya no hay nada que desplazar.
        Sin :has(), para no depender del soporte del navegador. */
  .admin-wrap .tabla-scroll{overflow-x:visible; background:none; contain:none;}

  /* 3. La tabla se convierte en lista de fichas. */
  .admin-wrap .cart-table:not(.compacta),
  .admin-wrap .cart-table:not(.compacta) tbody,
  .admin-wrap .cart-table:not(.compacta) tr,
  .admin-wrap .cart-table:not(.compacta) td{display:block; width:auto;}

  /* La cabecera desaparece: su información pasa a cada celda. Se oculta
     fuera de pantalla y no con display:none, para no sacarla del árbol de
     accesibilidad, que es lo que relaciona cada celda con su columna. */
  .admin-wrap .cart-table:not(.compacta) thead{
    position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%);
  }

  .admin-wrap .cart-table:not(.compacta) tr{
    border:var(--borde) solid var(--line-strong);
    padding:var(--space-2) var(--space-4);
    margin-bottom:var(--space-4);
  }
  .admin-wrap .cart-table:not(.compacta) tbody tr:last-child td{border-bottom:none;}

  /* Cada celda: etiqueta a la izquierda, valor a la derecha. */
  .admin-wrap .cart-table:not(.compacta) td{
    border:0; border-top:1px solid var(--line);
    padding:var(--space-3) 0; text-align:left;
    display:grid; grid-template-columns:minmax(84px, 32%) minmax(0,1fr);
    gap:var(--space-3); align-items:start;
    /* Sin esto la rejilla estira cada hijo a todo el ancho: la insignia de
       estado se convertía en una banda de lado a lado. Lo que sí debe ocupar
       la fila entera (inputs, formularios) lo pide más abajo. */
    justify-items:start;
  }
  .admin-wrap .cart-table:not(.compacta) tr td:first-child{border-top:0;}
  /* Todo el contenido de la celda, a la columna del valor. El grid coloca
     los hijos uno por hueco, así que una celda con dos elementos (nombre +
     material) mandaba el segundo a la columna de la etiqueta. */
  .admin-wrap .cart-table:not(.compacta) td[data-etiqueta] > *{grid-column:2;}
  .admin-wrap .cart-table:not(.compacta) td[data-etiqueta]::before{
    content:attr(data-etiqueta);
    font-family:'JetBrains Mono',monospace; font-size:var(--fs-10);
    text-transform:uppercase; letter-spacing:.06em; color:var(--concrete);
    line-height:1.3;
  }

  /* 4. Nada de nowrap: en una ficha estrecha el texto TIENE que partir. */
  .admin-wrap .cart-table:not(.compacta) td,
  .admin-wrap .cart-table:not(.compacta) td.acciones,
  .admin-wrap .cart-table:not(.compacta) td.num{white-space:normal;}
  .admin-wrap .cart-table:not(.compacta) td.num{text-align:left;}

  /* La celda de acciones no tiene columna que anunciar: ocupa el ancho entero
     y sus botones se reparten la fila. */
  .admin-wrap .cart-table:not(.compacta) td:not([data-etiqueta]){
    display:block; padding-top:var(--space-4);
  }
  .admin-wrap .cart-table:not(.compacta) .acciones-celda{
    justify-content:stretch; gap:var(--space-3);
  }
  .admin-wrap .cart-table:not(.compacta) .acciones-celda > *{flex:1 1 auto;}

  /* 5. Objetivo táctil de 44px en todo lo que se pulsa dentro de una ficha.
        Fuera de aquí los .mini miden 40px para alinearse con .qty-input en
        una fila de tabla; en la ficha ya no hay fila con la que alinearse. */
  .admin-wrap .cart-table:not(.compacta) .btn-ghost.mini,
  .admin-wrap .cart-table:not(.compacta) .qty-input,
  .admin-wrap .cart-table:not(.compacta) .check.mini{
    min-height:var(--ctrl-h); font-size:var(--fs-13);
  }
  .admin-wrap .cart-table:not(.compacta) .btn-ghost.mini{justify-content:center;}
  .admin-wrap .cart-table:not(.compacta) input[type=checkbox]{
    width:24px; height:24px; flex:0 0 auto;
  }
  .admin-wrap .cart-table:not(.compacta) .qty-input,
  .admin-wrap .cart-table:not(.compacta) .qty-input.ancho-precio{width:100%;}

  /* Los formularios de celda (oferta, publicar) se apilan en vez de apretarse. */
  .admin-wrap .cart-table:not(.compacta) .inline-form{
    display:flex; flex-wrap:wrap; gap:var(--space-2); align-items:center; width:100%;
  }
  .admin-wrap .cart-table:not(.compacta) .inline-form .btn-ghost.mini{flex:1 1 auto;}

  /* La miniatura y su pie dejan de ir centrados: en la ficha van con el resto. */
  .admin-wrap .cart-table:not(.compacta) .celda-foto{
    flex-direction:row; align-items:center; gap:var(--space-3);
  }

  /* 6. Los filtros de estado de Pedidos, al mínimo táctil. */
  .admin-wrap .filtro{min-height:var(--ctrl-h); padding:var(--space-2) var(--space-4);}

  /* 7. Menos aire lateral: a 375px, 20px por lado se comen el contenido. */
  .admin-wrap{padding-left:var(--space-4); padding-right:var(--space-4);}
}

/* ============================================================
   IDIOMA Y MONEDA · MATERIAL DE ESTAMPADO · PÁGINAS LEGALES
   ============================================================ */

/* ---------- conmutadores ES/EN y MXN/USD ----------
   Dos botones pegados donde el activo se ve hundido. Van en la misma fila que
   cuenta y bolsa, con la misma altura de control, para que no parezcan un
   añadido: son los mismos controles fijos de siempre, sólo que ahora hay más. */
.conmutador{
  display:inline-flex; align-items:stretch;
  box-shadow:var(--sombra-fija);
}
.conmutador .inline-form{display:flex;}
.conmutador-btn{
  height:var(--ctrl-h-fija); line-height:var(--ctrl-h-fija);
  padding:0 calc(var(--ctrl-pad-x-sm) * .75);
  background:rgba(74,74,74,.92); color:#fff; border:none; cursor:pointer;
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12); font-weight:500;
  letter-spacing:.06em;
  transition:background var(--t);
}
.conmutador-btn:hover{background:#333;}
/* El activo va en negro pleno: es el mismo salto de contraste que usa la
   miniatura seleccionada, para no inventar otro lenguaje de "esto está
   elegido". */
.conmutador-btn.activo{background:var(--ink); color:#fff;}
.conmutador .inline-form:first-child .conmutador-btn{border-right:1px solid rgba(255,255,255,.18);}

/* ---------- selector de material del estampado ---------- */
.materiales{display:flex; flex-wrap:wrap; gap:var(--space-2);}
.material-op{
  display:flex; flex-direction:column; gap:2px;
  padding:8px 12px; cursor:pointer;
  border:1px solid var(--line-strong); background:var(--paper);
  transition:border-color var(--t), background var(--t);
}
.material-op:hover{border-color:var(--ink);}
.material-op.activo{border-color:var(--ink); background:var(--paper-dim);}
/* El radio se oculta pero SIGUE en el DOM y enfocable: sin él no hay
   navegación con teclado ni lector de pantalla, y la etiqueta se queda sin
   nada que anunciar. */
.material-op input[type="radio"]{position:absolute; opacity:0; width:0; height:0;}
.material-op:focus-within{outline:var(--foco-grosor) solid var(--ink); outline-offset:2px;}
.material-nombre{
  font-family:'Inter',sans-serif; font-size:var(--fs-13); font-weight:600;
}
.material-precio{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12);
  color:var(--concrete);
}

/* ============================================================
   PÁGINAS LEGALES
   ============================================================
   No estrenan sistema visual: se apoyan en el que ya usa la tienda. La
   etiqueta «Documento legal» es el mismo tratamiento que «COLOR:» y «TALLA:»
   en la ficha de producto (mono, versalitas, interletrado, gris concreto), los
   métodos de pago son `.badge` y la tabla de países rueda dentro del
   `.tabla-scroll` del panel. Lo único propio de aquí es el ritmo de lectura
   —medida de línea, jerarquía de títulos y el número de sección al margen—,
   que ninguna otra página necesitaba.

   Todo lo que se pinta viene de app/legal_docs.py. */

/* Fondo OPACO, y no el translúcido de `main`.

   No es un capricho de estilo: la regla del sitio ya dice que el contenedor va
   semitransparente para dejar asomar el fondo pero que «textos y botones van
   opacos para no perder contraste» (ver `main`). En el resto de páginas el
   texto son etiquetas y párrafos sueltos y el 85 % basta; aquí son treinta
   secciones y trece mil píxeles de lectura seguida sobre el fondo,
   que es justo donde ese 85 % deja de alcanzar. Así que estas páginas cobran
   su propia hoja: el panel translúcido sigue detrás, alrededor. */
.legal-doc{
  max-width:78ch; margin:0 auto;
  background:var(--paper); border:1px solid var(--line);
  padding:var(--space-8);
}
@media (max-width:720px){
  .legal-doc{padding:var(--space-5) var(--space-4);}
}

/* La etiqueta de encima del título: misma receta que .selector-label. */
.legal-eyebrow{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  line-height:1.4; letter-spacing:.12em; text-transform:uppercase;
  color:var(--concrete); margin:0 0 var(--space-3);
}
.legal-titulo{
  font-family:'Anton',sans-serif; font-size:var(--fs-26); line-height:1.15;
  letter-spacing:.01em; text-transform:uppercase; margin:0 0 var(--space-3);
}
.legal-fecha{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12);
  color:var(--concrete); margin:0 0 var(--space-6);
  padding-bottom:var(--space-4); border-bottom:1.5px solid var(--line);
}
/* El aviso de que manda la versión en español. Sólo sale en inglés. */
.legal-prevalece{
  font-size:var(--fs-13); line-height:1.6; color:var(--concrete);
  border-left:2px solid var(--line-strong); padding-left:var(--space-3);
  margin:0 0 var(--space-3); max-width:62ch;
}
.legal-intro{margin-bottom:var(--space-8);}

/* ---------- tabla de contenidos ----------
   Son documentos de 17 y 30 secciones; sin esto, encontrar «Derechos ARCO» es
   bajar a ciegas. Dos columnas en escritorio para que no ocupe una pantalla. */
.legal-indice{
  background:var(--paper-dim); border:1px solid var(--line);
  padding:var(--space-5) var(--space-6); margin-bottom:var(--space-8);
}
.legal-indice-lista{
  list-style:none; margin:var(--space-3) 0 0; padding:0;
  columns:2; column-gap:var(--space-6);
}
.legal-indice-lista li{break-inside:avoid; margin-bottom:var(--space-2);}
.legal-indice-lista a{
  display:flex; gap:var(--space-2); align-items:baseline;
  font-size:var(--fs-14); line-height:1.45; color:var(--ink);
  text-decoration:none; border-bottom:1px solid transparent;
}
.legal-indice-lista a:hover{border-bottom-color:var(--ink);}
.legal-indice-num{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete); min-width:2ch; flex:none;
}
@media (max-width:760px){
  .legal-indice-lista{columns:1;}
}

/* ---------- secciones ---------- */
.legal-seccion{margin-bottom:var(--space-8);}
/* `scroll-margin-top` por la barra fija: sin esto, saltar a #seccion-14 deja
   el título justo debajo del encabezado y parece que no pasó nada. */
.legal-seccion[id]{scroll-margin-top:110px;}
/* Mismo tramo que main.con-aire: ahí el logo de 128 px cae encima del texto. */
@media (min-width:721px) and (max-width:1319px){
  .legal-seccion[id]{scroll-margin-top:160px;}
}
.legal-h2{
  display:flex; gap:var(--space-3); align-items:baseline;
  font-family:'Anton',sans-serif; font-size:var(--fs-18); line-height:1.25;
  letter-spacing:.02em; text-transform:uppercase;
  margin:0 0 var(--space-3); padding-top:var(--space-4);
  border-top:1.5px solid var(--line);
}
/* El número de la sección es el del cliente, y va al margen para que el
   título se lea solo. */
.legal-num{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-13);
  font-weight:500; color:var(--concrete); flex:none; min-width:2.5ch;
}
.legal-h3{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12);
  letter-spacing:.08em; text-transform:uppercase; color:var(--concrete);
  margin:var(--space-5) 0 var(--space-2);
}
.legal-texto{
  font-family:'Inter',sans-serif; font-size:var(--fs-15); line-height:1.7;
  max-width:72ch; margin:var(--space-3) 0;
}
.legal-lista{
  font-size:var(--fs-15); line-height:1.7; max-width:72ch;
  margin:var(--space-3) 0; padding-left:var(--space-5);
}
.legal-lista li{margin-bottom:var(--space-1);}
.legal-lista-num{list-style:decimal;}

/* ---------- ficha de datos (el titular, los contactos) ---------- */
.legal-datos{
  display:grid; grid-template-columns:minmax(11rem,auto) minmax(0,1fr);
  gap:var(--space-1) var(--space-4);
  margin:var(--space-4) 0; padding:var(--space-4) var(--space-5);
  background:var(--paper-dim); border:1px solid var(--line);
  font-size:var(--fs-14); line-height:1.6;
}
.legal-datos dt{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  letter-spacing:.06em; text-transform:uppercase; color:var(--concrete);
  padding-top:2px;
}
.legal-datos dd{margin:0;}
@media (max-width:560px){
  .legal-datos{grid-template-columns:minmax(0,1fr); gap:0;}
  .legal-datos dd{margin-bottom:var(--space-3);}
}

/* ---------- tarjetas ----------
   Donde el documento del cliente ya agrupa cosas (las categorías de datos del
   Aviso, los cuatro derechos ARCO), se agrupan también en pantalla. Es sólo
   presentación: el texto de cada tarjeta es el suyo, palabra por palabra. */
.legal-tarjetas{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:var(--space-3); margin:var(--space-4) 0;
}
.legal-tarjeta{
  background:var(--paper-dim); border:1px solid var(--line-strong);
  padding:var(--space-4) var(--space-5);
}
.legal-tarjeta-titulo{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink);
  margin:0 0 var(--space-2);
}
.legal-tarjeta-texto{
  font-size:var(--fs-14); line-height:1.6; color:var(--concrete); margin:0;
}

/* Aviso de que un documento está incompleto. No es un pendiente interno —eso
   es .legal-pendiente— sino algo que la clientela DEBE leer: que lo que tiene
   delante es una versión mínima. Por eso va en el flujo del texto y no
   escondido. */
.legal-provisional{
  background:var(--safety); border-left:3px solid var(--ink);
  padding:var(--space-4) var(--space-5); margin:0 0 var(--space-6);
  font-size:var(--fs-14); line-height:1.6; max-width:62ch;
}

/* ---------- lo que el cliente todavía no entrega ----------
   Chillón a propósito. Un hueco en blanco se publica sin que nadie lo note;
   esto no. */
.legal-pendiente{
  background:var(--ink); color:#fff;
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-12);
  letter-spacing:.04em; padding:2px var(--space-2); white-space:normal;
}

/* ---------- tabla de países (política de envíos) ---------- */
.legal-tabla{
  width:100%; border-collapse:collapse; font-size:var(--fs-14);
  margin-top:var(--space-3);
}
.legal-tabla th, .legal-tabla td{
  text-align:left; padding:var(--space-2) var(--space-3);
  border-bottom:1px solid var(--line);
}
.legal-tabla thead th{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  letter-spacing:.06em; text-transform:uppercase; color:var(--concrete);
  border-bottom:1.5px solid var(--line-strong);
  position:sticky; top:0; background:var(--paper); z-index:1;
}
.legal-tabla tbody th{font-weight:400;}
.legal-tabla .num{text-align:right; font-variant-numeric:tabular-nums;}
/* El código ISO del país, delante del nombre. Ancho fijo para que la columna
   de nombres arranque alineada en las 70 filas. */
.legal-codigo{
  display:inline-block; min-width:3.2ch; margin-right:var(--space-2);
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete);
}
/* Con 70 filas, la tabla rueda dentro de su caja y no estira la página. */
.legal-doc .tabla-scroll{max-height:70vh; overflow-y:auto;}

.legal-pagos{
  list-style:none; display:flex; flex-wrap:wrap; gap:var(--space-2);
  margin:var(--space-3) 0 0; padding:0;
}

/* ---------- hub /legal ---------- */
.legal-hub-lista{
  list-style:none; margin:var(--space-6) 0 var(--space-8); padding:0;
  display:grid; gap:var(--space-3);
}
.legal-hub-item a{
  display:block; padding:var(--space-5) var(--space-6);
  background:var(--paper-dim); border:1px solid var(--line-strong);
  color:inherit; text-decoration:none; transition:border-color var(--t);
}
.legal-hub-item a:hover{border-color:var(--ink);}
.legal-hub-titulo{
  font-family:'Anton',sans-serif; font-size:var(--fs-18); line-height:1.2;
  letter-spacing:.02em; text-transform:uppercase; margin:0 0 var(--space-2);
}
.legal-hub-desc{font-size:var(--fs-14); line-height:1.6; margin:0;}
.legal-hub-fecha{
  font-family:'JetBrains Mono',monospace; font-size:var(--fs-11);
  color:var(--concrete); margin:var(--space-2) 0 0;
}

/* ---------- navegación cruzada del pie de cada documento ---------- */
.legal-cruzada{
  margin-top:var(--space-8); padding-top:var(--space-5);
  border-top:1.5px solid var(--line);
}
.legal-cruzada-lista{
  list-style:none; margin:var(--space-3) 0 var(--space-4); padding:0;
  display:grid; gap:var(--space-2);
}
.legal-cruzada-lista a{
  display:block; padding:var(--space-3) var(--space-4);
  border:1px solid var(--line); color:inherit; text-decoration:none;
  transition:border-color var(--t);
}
.legal-cruzada-lista a:hover{border-color:var(--ink);}
.legal-cruzada-titulo{display:block; font-size:var(--fs-14); font-weight:500;}
.legal-cruzada-desc{
  display:block; font-size:var(--fs-12); color:var(--concrete);
  margin-top:2px;
}

/* ---------- impresión ----------
   Un documento legal se imprime y se guarda. En papel no hay scroll, así que
   la tabla de países tiene que salir entera; y el índice de anclas no sirve
   de nada, pero ocupa una página. */
@media print{
  .legal-doc{border:0; padding:0; max-width:none;}
  .legal-doc .tabla-scroll{max-height:none; overflow:visible;}
  .legal-tabla thead th{position:static;}
  .legal-indice{display:none;}
  .legal-seccion{break-inside:avoid;}
  .legal-h2{break-after:avoid;}
  /* Que el pendiente se vea también impreso: es su razón de ser. */
  .legal-pendiente{
    background:transparent; color:#000; border:1.5px solid #000;
  }
}
