/* =========================================================================
   WARRAPOP · PANEL — SISTEMA DE DISEÑO "LA LONJA"
   Fichas, reinicio y primitivos. Las decisiones y el porqué largo están en
   `panel/docs/diseno.md`; aquí va el porqué corto, al lado del código.
   =========================================================================

   LA IDEA EN UNA LÍNEA
   La pantalla es un TABLÓN DE PRECIOS, no una app de tarjetas. Todo está
   impreso sobre papel: filetes de un pelo, columnas, cifras a la derecha.
   Nada levita, nada tiene profundidad, nada se mueve solo salvo el latido
   del monitor — que se mueve porque su trabajo es decir "sigo vivo".

   LOS DOS GESTOS QUE DAN EL CARÁCTER (y no hay un tercero)
   1. EL CANTO  — una columna de margen a la izquierda, de ancho fijo, con un
      filete vertical que recorre la lista entera sin interrumpirse. Dentro
      van los metadatos (sello, hora, web). El contenido siempre empieza en
      la misma x. Es el esqueleto que se reconoce de un vistazo.
   2. LA REGLA  — cada aviso lleva una regla de precio con escala FIJA de 0 a
      2× la referencia y la referencia SIEMPRE en el centro. Como la escala es
      idéntica en todas las filas, una lista larga se lee como un PATRÓN: todo
      lo que cae a la izquierda del centro es oportunidad. Se decide sin leer
      un número. Y si no hay referencia NO hay regla: se imprime "sin
      referencia" y punto (un 0 que en realidad es "no se sabe" no puede
      parecer un 0).

   LO QUE SE HEREDA DE `web/estilo.css`
   Cero sombras · un solo acento · dos voces tipográficas con `tabular-nums`
   en toda cifra · rejilla de 4 px · tacto de 44 px · `data-theme` como única
   autoridad del tema · ninguna información sólo por color.

   LO QUE SE ROMPE, Y POR QUÉ
   · Papel por defecto (era oscuro): el uso que decide una compra es el móvil
     al sol, y el terminal negro es LO GENÉRICO de este género.
   · El acento baja de luz (#1FD6AE → #0B6F5C / #2ED3AE): el neón es la firma
     de otra categoría de producto, y el #1FD6AE no llegaba a AA sobre blanco.
   · Radios de 3 px (eran 6 en todo): un tablón está cortado a escuadra.
   · Barra opaca (era `backdrop-filter`): recomponer esa capa en cada
     fotograma se paga justo donde vive este producto, el scroll de listas
     largas.
   · El canto del aviso ya no es sólo color: cambia de ANCHURA y de TRAMA.
     Está medido que los cuatro colores no se distinguen entre sí (ver §8).
   ========================================================================= */


/* =========================================================================
   1. FICHAS · TEMA PAPEL (el de por defecto)
   Los contrastes anotados son la ratio WCAG 2.1 en el PEOR de los cuatro
   fondos del tema. Ninguno se ha puesto a ojo.
   ========================================================================= */
:root{
  color-scheme: light;   /* que los controles nativos y el scroll acompañen */

  /* --- Superficies. Papel cálido, no blanco puro: el blanco puro es una
     captura de pantalla, el papel está elegido. ------------------------- */
  --papel:    #FFF8DE;   /* fondo de página: miel clara (Gangabee, 28 sep) */
  --papel-2:  #FBEDB8;   /* bandas, cabeceras, pies */
  --carta:    #FFFDF4;   /* superficie de bloque */
  --carta-2:  #FFF7DA;   /* campo de formulario, fila abierta */

  /* --- Filetes. Son la jerarquía entera: aquí no hay sombras. ---------- */
  --filete:   #E3CF8A;   /* 1,37:1 — separador. NO llega a 3:1 A PROPÓSITO:
                            no transmite información por sí mismo y subirlo
                            convierte una lista de 300 filas en una reja. */
  --filete-2: #857233;   /* 3,13:1 — borde de CONTROL. Éste sí debe verse
                            (WCAG 1.4.11) y por eso pasa de 3:1. */

  /* --- Tinta ---------------------------------------------------------- */
  --tinta:    #17130A;   /* 13,92:1 */
  --tinta-2:  #4B4331;   /*  6,12:1 — secundario y rótulos */
  --tinta-3:  #5E5540;   /*  4,74:1 — metadatos de 11-12 px. Pasa AA porque
                            aquí el metadato PEQUEÑO es media interfaz. */

  /* --- El acento. UNO solo, y es verde azulado por aritmética, no por
     gusto: los cuatro avisos ocupan azul, violeta, ámbar y naranja; el verde
     es "correcto" y el rojo "error". El teal es la única región libre. --- */
  --acento:        #17130A;   /* abeja: el acento es el NEGRO, y lo que va encima, amarillo */
  --acento-fuerte: #000000;
  --sobre-acento:  #FFC915;   /* 12,4:1 sobre --acento */
  /* El amarillo de la marca: barra, pestaña activa, botón al pasar por encima. Como FONDO,
     nunca como letra sobre papel (sobre la miel clara no llega a 1,5:1) */
  --miel:          #FFC915;
  --barra-fondo:   #17130A;
  --barra-tinta:   #F4E7B8;

  /* --- Los cuatro tipos de aviso. Ver §8: el color es el CUARTO canal. -- */
  --nuevo:  #1F5AA8;   /* ✨ 5,66:1 */
  --bajada: #6B3BBF;   /* 💸 5,82:1 */
  --chollo: #8A5600;   /* 💎 5,12:1 — sobre papel el "oro" tiene que ser ocre */
  --joya:   #B34117;   /* 🔥 4,74:1 */

  /* --- Estado del sistema, separado de los avisos ---------------------- */
  --ok:     #0A6B4B;   /* 5,43:1 */
  --aviso:  #7A5300;   /* 5,70:1 */
  --error:  #B3261E;   /* 5,44:1 */

  /* Cuánto color se mezcla con el fondo para un velo (hover, fondo de nota).
     Se declara como ficha porque en pizarra hace falta casi el doble. */
  --velo:        8%;
  --velo-fuerte: 16%;

  /* Flecha del <select>. Va como URL de datos con el color YA metido dentro
     porque un SVG de fondo no puede leer `currentColor`; por eso hay una por
     tema. El `#` va como %23 o la URL se corta. */
  --flecha: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.6 6 6.4 11 1.6' fill='none' stroke='%234B4331' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* --- Voces tipográficas. Del sistema: ni CDN ni fuente embebida. Meter
     80-300 KB de base64 en una herramienta que se abre veinte veces al día no
     lo paga el carácter, que aquí lo dan el canto y la regla, no la letra. */
  --ui:   -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", "Segoe UI Mono",
          "Roboto Mono", Consolas, "Liberation Mono", monospace;

  /* --- Escala tipográfica (paso ≈1,25) -------------------------------- */
  --f11:.6875rem; --f12:.75rem;   --f13:.8125rem; --f15:.9375rem;
  --f17:1.0625rem; --f20:1.25rem; --f26:1.625rem; --f34:2.125rem;

  --lh-1:1.15;   /* cifras y titulares */
  --lh-2:1.35;   /* listas y controles */
  --lh-3:1.55;   /* prosa */

  /* --- Rejilla de 4 px. Separación SIEMPRE con `gap`: los márgenes se
     colapsan y se discuten entre sí, `gap` no. -------------------------- */
  --e1:.25rem;  --e2:.5rem;   --e3:.75rem; --e4:1rem;
  --e5:1.25rem; --e6:1.5rem;  --e8:2rem;   --e10:2.5rem; --e12:3rem;

  /* --- Radios. A escuadra casi todo: la esquina redonda dice "tarjeta de
     app" y esto es un tablón. ------------------------------------------ */
  --r-1:3px;        /* botones, campos, chips, sellos */
  --r-2:6px;        /* SÓLO miniaturas de foto: ahí sí es una foto */
  --r-redondo:999px;/* SÓLO contadores y el punto del latido */

  /* --- Medidas fijas -------------------------------------------------- */
  --tacto:44px;     /* objetivo táctil mínimo: dedo gordo, en la calle */
  --ancho-max:100%;   /* A TODO EL ANCHO (Jorge, 26 sep: "expande las tarjetas de los módulos para que
     aprovechen el ancho de pantalla, si no hay que scrollear mucho más"; antes 1400px).
     Lo usa el armazón de página, no los primitivos.
     Igual que `--e5`/`--e6`: son escalones de la escala, y una escala con
     agujeros es peor que una escala con pasos sin estrenar. No son sobras. */
  /* Ancho de la columna de margen (gesto 1). 92 px MEDIDOS, no elegidos: el
     sello más ancho ("💎 Chollo" / "💸 Bajada") mide 67 px y la marca de web
     más larga ("wallapop") 64, más 12+12 de relleno → 91. Se probó con 64 en
     pantalla estrecha y recortaba las palabras a "CHOL", "NUEV", "BAJA" — o
     sea, se cargaba el canal que NO depende del color, que es justo el que
     tiene que aguantar. Una columna de margen que no cabe no es un margen. */
  --canto:92px;

  /* --- Movimiento. Techo duro de 240 ms. ------------------------------ */
  --dur-1:90ms;     /* respuesta al dedo */
  --dur-2:150ms;    /* entrar y salir */
  --dur-3:240ms;    /* desplegar un detalle */
  --curva:cubic-bezier(.2,.7,.35,1);            /* sale rápido y frena */
  --curva-simetrica:cubic-bezier(.4,0,.2,1);    /* lo que va y vuelve */
}

/* =========================================================================
   2. FICHAS · TEMA PIZARRA
   NO es la inversión del claro. Es una pizarra verde-grafito y no un negro
   puro: el negro absoluto con texto claro produce halo en monoespaciada
   pequeña, que es justo la que más hay aquí.
   El tema lo manda `data-theme` y SÓLO `data-theme`. En esta hoja no existe
   ni una regla `prefers-color-scheme`: el sistema se consulta UNA vez, en el
   arranque JS, para sembrar el atributo en la primera visita. Dos fuentes de
   verdad acaban descuadrándose.
   ========================================================================= */
:root[data-theme="pizarra"]{
  color-scheme: dark;

  --papel:#0F1514; --papel-2:#151C1A; --carta:#18201E; --carta-2:#1E2725;
  --filete:#364340;      /* 1,48:1 */
  --filete-2:#647270;    /* 3,05:1 */
  --tinta:#E7EDEA;       /* 12,90:1 */
  --tinta-2:#A2AFAB;     /*  6,75:1 */
  --tinta-3:#849490;     /*  4,82:1 */

  --acento:#FFC915;        /* abeja también a oscuras: miel sobre negro */
  --acento-fuerte:#FFD84D;
  --sobre-acento:#17130A;
  --miel:#FFC915; --barra-fondo:#0B0A06; --barra-tinta:#F4E7B8;

  --nuevo:#7FB0FF; --bajada:#C79BFF; --chollo:#F3C05C; --joya:#FF8A5C;
  --ok:#43D69F; --aviso:#F3C05C; --error:#FF7C72;

  /* Sobre oscuro un velo del 8% no se ve: hace falta casi el doble. */
  --velo:14%; --velo-fuerte:24%;

  --flecha: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.6 6 6.4 11 1.6' fill='none' stroke='%23A2AFAB' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Los cuatro avisos: cada uno fija su color (--c) y la FORMA de su canto.
   Se declaran aparte de los temas porque la forma no cambia con el tema —
   sólo cambia color, y si al cambiar de tema se mueve algo es un fallo. */
[data-aviso="new"]{
  --c:var(--nuevo);  --canto-ancho:1px;
  --canto-trama:linear-gradient(var(--c),var(--c));                /* un pelo */
}
[data-aviso="price_drop"]{
  --c:var(--bajada); --canto-ancho:3px;
  /* discontinua: algo se ha MOVIDO */
  --canto-trama:repeating-linear-gradient(180deg,
      var(--c) 0 7px, transparent 7px 12px);
}
[data-aviso="bargain"]{
  --c:var(--chollo); --canto-ancho:3px;
  --canto-trama:linear-gradient(var(--c),var(--c));                /* macizo */
}
[data-aviso="treasure"]{
  --c:var(--joya);   --canto-ancho:7px;
  /* doble línea: el más ancho de los cuatro, imposible de confundir */
  --canto-trama:repeating-linear-gradient(90deg,
      var(--c) 0 2px, transparent 2px 5px, var(--c) 5px 7px);
}


/* =========================================================================
   3. REINICIO
   ========================================================================= */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--papel);
  color:var(--tinta);
  font:var(--f15)/var(--lh-3) var(--ui);
  /* Que ninguna pila del sistema invente una negrita falsa: con las
     monoespaciadas de Windows deforma el ancho y descuadra las columnas. */
  font-synthesis-weight:none;
  overscroll-behavior-y:none;   /* nada de rebote en un puesto de vigilancia */
}
h1,h2,h3,h4,p,figure,blockquote{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
button,input,select,textarea{font:inherit;color:inherit;margin:0}
img,svg,video{display:block;max-width:100%}
table{border-collapse:collapse;border-spacing:0}
[hidden]{display:none !important}   /* el único !important del sistema:
   `.btn{display:inline-flex}` le ganaba al atributo y dejaba botones muertos
   visibles en pantalla. */
[inert]{cursor:default;user-select:none}
:target{scroll-margin-top:var(--e12)}

a{color:var(--acento);text-underline-offset:3px;text-decoration-thickness:1px}
a:hover{color:var(--acento-fuerte)}

h1,h2,h3{line-height:var(--lh-1);letter-spacing:-.02em;text-wrap:balance}
h1{font-size:var(--f26);font-weight:700}
h2{font-size:var(--f17);font-weight:600}
h3{font-size:var(--f15);font-weight:600}
p{max-width:66ch;text-wrap:pretty}

/* Barra de scroll discreta: forma parte del filete, no de la interfaz. */
*{scrollbar-width:thin;scrollbar-color:var(--filete-2) transparent}


/* =========================================================================
   4. FOCO
   Siempre visible, en los dos temas, también con ratón. El trazo del acento
   sobre un botón que YA es del color del acento no se vería: por eso lleva un
   anillo interior de 1 px del color del papel.
   Ese `box-shadow` es la ÚNICA sombra de todo el sistema y no es profundidad:
   es señalización. No cuenta como excepción a "cero sombras".
   ========================================================================= */
:focus-visible{
  outline:2px solid var(--acento);
  outline-offset:2px;
  box-shadow:0 0 0 1px var(--carta);
  border-radius:var(--r-1);
}
/* Quitar el contorno cuando NO es foco de teclado es correcto; quitarlo del
   todo, no. Aquí sólo se silencia el `:focus` viejo. */
:where(a,button,input,select,textarea,summary):focus:not(:focus-visible){outline:none}
/* Al cambiar de pantalla se lleva el foco al contenedor a propósito, para que
   el lector de pantalla siga el hilo. No es un control: no se marca. */
[data-region]:focus,[data-region]:focus-visible{outline:none;box-shadow:none}


/* =========================================================================
   5. MOVIMIENTO
   ========================================================================= */
/* `!important` aquí sí: esta regla tiene que ganarle a CUALQUIER animación
   declarada después, incluidas las que escriba quien use el sistema. Es el
   único sitio del CSS donde se usa aparte de `[hidden]`. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms !important;
    animation-iteration-count:1 !important;
    transition-duration:1ms !important;
    scroll-behavior:auto !important;
  }
}

/* REGLA MECÁNICA DE "CERO SOMBRAS", para poder comprobarla:
   en todo el sistema, cada `box-shadow` tiene desplazamiento 0 y difuminado
   0 — o sea, es un ANILLO, no una sombra. Sólo hay dos: el anillo interior
   del foco (§4) y el del latido (§12). Si aparece un `box-shadow` con
   desplazamiento o `blur`, es profundidad y no va en este diseño. */


/* =========================================================================
   6. VOCES Y UTILIDADES DE TEXTO
   ========================================================================= */

/* La voz de dato. TODA cifra, hora, porcentaje, identificador y registro. */
.dato{
  font-family:var(--mono);
  font-variant-numeric:tabular-nums;
  letter-spacing:-.01em;
}

/* La CIFRA: enteros gordos, céntimos y moneda chicos y apagados. El ojo
   compara los enteros en columna; los céntimos son una nota al pie. */
.cifra{
  font-family:var(--mono);
  font-variant-numeric:tabular-nums;
  font-weight:700;
  letter-spacing:-.02em;
  line-height:var(--lh-1);
  white-space:nowrap;
}
.cifra .dec,
.cifra .uni{font-size:.78em;font-weight:600;opacity:.62}

/* Rótulo de sección/campo: mono, versalitas, espaciado. Sólo para ≤3
   palabras — las versalitas en texto corrido no se leen. */
.rotulo{
  font-family:var(--mono);
  font-size:var(--f11);
  font-weight:500;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--tinta-2);
  line-height:var(--lh-2);
}
.rotulo-apagado{color:var(--tinta-3)}

/* Metadato de canto: hora, web, contador. */
.meta{
  font-family:var(--mono);
  font-size:var(--f11);
  font-variant-numeric:tabular-nums;
  color:var(--tinta-3);
  line-height:var(--lh-2);
}

.oculto-visual{
  position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;
}

/* Diferencia contra la referencia. El signo va en el TEXTO (lo pone el JS),
   no sólo en el color: "-32%" ya dice hacia dónde. */
.dif{font-family:var(--mono);font-size:var(--f12);font-weight:700;font-variant-numeric:tabular-nums}
.dif[data-signo="abajo"]{color:var(--ok)}     /* por debajo = oportunidad */
.dif[data-signo="arriba"]{color:var(--tinta-2)}


/* =========================================================================
   7. GESTO 1 · EL CANTO
   Una columna de margen de ancho fijo con un filete vertical que recorre la
   lista ENTERA.

   ⚠️ El filete NO se pinta en el fondo de `.lista`, que era lo primero que se
   probó: `.lista` es el CONTENEDOR de las consultas `@container`, y una
   consulta de contenedor no puede dar estilo a su propio contenedor. Con el
   filete ahí, `--canto` no se podía ensanchar en pantalla grande.
   Va en un `::after` de cada fila, con `inset-block:0`, que cubre también el
   relleno vertical: las filas son contiguas, así que los trozos se suman en
   una línea continua. El `border-bottom` de la fila la cruza, y eso es lo que
   se quiere — es la retícula de un libro de cuentas.
   ========================================================================= */
.lista{
  container-type:inline-size;   /* la misma lista se enseña a ancho completo
     y dentro de una columna de 320 px: quien manda es el CONTENEDOR, no la
     ventana. */
  container-name:lista;
  background:var(--carta);
  border-block:1px solid var(--filete);
}

/* El filete vertical del canto. Un `::after` porque el `::before` de la fila
   ya lleva el canto de color del tipo de aviso. */
.fila::after,
.corte-dia::after,
.esqueleto-fila::after{
  content:"";position:absolute;inset-block:0;left:var(--canto);
  width:1px;background:var(--filete);
}

.fila{
  position:relative;
  display:grid;
  grid-template-columns:var(--canto) minmax(0,1fr) max-content;
  column-gap:var(--e4);
  row-gap:var(--e2);
  align-items:start;
  padding:var(--e3) var(--e4) var(--e3) 0;
  border-bottom:1px solid var(--filete);
  /* Suelo de altura, escrito como la SUMA de sus piezas y no como un número
     mágico: relleno + dos líneas de título + hueco + el alto de la regla. Es
     la red por si llega una fila sin regla; la altura la fijan de verdad los
     dos huecos de abajo. */
  min-height:calc(var(--e3) * 2 + var(--f15) * var(--lh-2) * 2 + var(--e2) + 20px);
  /* Hover: SÓLO fondo y filete. Nada cambia de sitio — en una lista de 300
     filas, un salto de 1 px al pasar el ratón es un tic nervioso. */
  transition:background var(--dur-1) var(--curva);
}
.fila:last-child{border-bottom:0}
.fila:hover{background:color-mix(in srgb,var(--acento) var(--velo),transparent)}
.fila[aria-selected="true"],
.fila:has(> .fila-canto input:checked){
  background:color-mix(in srgb,var(--acento) var(--velo-fuerte),transparent);
}

/* El canto de color del tipo de aviso: ancho y TRAMA propios (§8). */
.fila[data-aviso]::before{
  content:"";
  position:absolute;inset-block:0;left:0;
  width:var(--canto-ancho);
  background:var(--canto-trama);
}

/* EL CANTO ES UN MARGEN, NO CONTENIDO: NUNCA HACE CRECER LA FILA.
   Va posicionado, fuera del flujo de la rejilla, y recortado a la altura de
   la fila. Medido en el banco: dejándolo como celda normal, las filas que
   traían además la marca de la web pasaban de 94 a 118 px — la columna de
   metadatos decidía el ritmo de la lista entera, que es justo al revés de lo
   que se quería. Lo que no quepa en el canto se recorta; si algo no cabe es
   que sobra, porque el canto se consulta sin mirar. */
.fila-canto{
  position:absolute;inset-block:0;left:0;width:var(--canto);
  display:grid;align-content:start;justify-items:start;
  gap:var(--e1);
  padding:var(--e3) var(--e3) 0;
  overflow:hidden;
}
/* MODO SELECCIÓN. La casilla ocupa el sitio de una línea del canto, y el
   canto no puede crecer porque es un margen. Así que algo tiene que salir: se
   va la marca de la web, que es el metadato menos decisivo — el TIPO y la
   HORA se quedan. Medido: con la casilla puesta las cuatro piezas suman 87 px
   y en el canto caben 82. Se apaga con una regla en vez de confiar en que
   quien escriba la pantalla se acuerde. */
.fila-canto:has(input[type="checkbox"]) .marca-web{display:none}
.fila-cuerpo{grid-column:2;min-width:0;display:grid;gap:var(--e1)}

/* Columna de cifra. Todo lo que modifica al precio —el anterior tachado, el
   porcentaje— va AQUÍ, apilado debajo, y no en el renglón del título: es del
   precio de lo que habla. Además así el cuerpo sólo contiene el título, que
   es lo que permite fijar la altura de la fila (abajo). */
.fila-cifra{
  grid-column:3;justify-self:end;text-align:right;
  display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:baseline;
  gap:2px var(--e2);
  /* Toda esta columna son cifras: interlineado de cifra, no de prosa. Con el
     1,55 del cuerpo la segunda línea sobraba 3 px y la fila crecía. */
  line-height:var(--lh-1);
}
/* El precio vigente se lleva su renglón entero; el anterior tachado y el
   porcentaje comparten el de abajo. Los tres apilados hacían crecer la fila
   23 px por encima de las demás (medido): esta columna tampoco puede mandar
   sobre el ritmo de la lista.
   El vigente sí va solo: dos cifras del mismo peso en un renglón se leen como
   una sola. El tachado y el signo del porcentaje ya los separan. */
.fila-cifra .cifra{flex-basis:100%;font-size:var(--f20)}
.fila-cifra .antes{
  font-family:var(--mono);font-size:var(--f12);font-variant-numeric:tabular-nums;
  color:var(--tinta-3);text-decoration:line-through;
}

/* ALTURA DE FILA CONSTANTE — y se consigue fijando los DOS huecos que varían,
   no deseándolo. Medido en el banco de pruebas: sin esto las mismas cuatro
   filas medían 127, 106, 96 y 65 px, y una lista así no se barre con el
   pulgar ni deja las agujas de la regla a la misma altura.
   Hueco 1, el título: exactamente DOS líneas, siempre. Un título de una línea
   deja hueco, y se acepta a cambio del ritmo — en estas webs los títulos
   largos son la norma.
   ⚠️ `align-self:start` + `max-height` son imprescindibles: como elemento de
   rejilla el título se estiraba a lo alto de la fila, y entonces el recorte
   ponía los puntos suspensivos pero DEJABA VER la tercera línea. */
.fila-titulo{
  align-self:start;
  font-size:var(--f15);font-weight:600;line-height:var(--lh-2);
  letter-spacing:-.01em;
  overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  height:calc(var(--f15) * var(--lh-2) * 2);
}

/* Hueco 2, la regla: mide lo mismo haya regla o haya "sin referencia". Ésa es
   la mitad honesta del gesto — el hueco no desaparece, se explica.
   La regla cruza de lado a lado empezando DESPUÉS del canto: así todas las
   agujas caen a la misma x y forman una segunda columna invisible. */
.fila-regla{
  grid-column:2 / -1;
  height:20px;overflow:hidden;
  display:flex;flex-direction:column;justify-content:center;
}
/* Los pies de la regla ("0 · ref 200 € · 400") son de la VISTA DE DETALLE.
   En una lista no aportan —la escala es la misma en todas las filas, que es
   justo el gesto— y romperían la altura constante. Se apagan aquí en vez de
   confiar en que nadie los pegue: acordarse no es un mecanismo. */
.fila-regla .regla-pies{display:none}

/* Miniatura del anuncio. Único sitio con radio de 6 px, y ÚNICO sitio sin
   tratamiento: aquí una foto es una prueba (se juzga el estado de un objeto
   por ella). Prohibido oscurecerla, desaturarla o velarla en tema pizarra. */
.miniatura{
  width:44px;height:44px;border-radius:var(--r-2);object-fit:cover;
  background:var(--papel-2);border:1px solid var(--filete);
}

/* Corte de día: el salto de página de un libro de cuentas. Se ahorra una
   columna de fecha por fila y se gana ritmo vertical. */
.corte-dia{
  display:grid;grid-template-columns:var(--canto) 1fr;
  align-items:center;column-gap:var(--e4);
  padding:var(--e2) var(--e4) var(--e2) 0;
  background:var(--papel-2);
  border-bottom:1px solid var(--filete);
  position:sticky;top:0;z-index:2;
}
.corte-dia > .rotulo{padding-inline:var(--e3)}

/* El CANTO no cambia de ancho con el contenedor: es el esqueleto y tiene que
   caer en la misma x en las dos pantallas. Lo que sí cambia con el sitio
   disponible es la foto, que a 44 px en una columna ancha se queda pequeña
   para juzgar el estado de un objeto — y para eso está.
   ⚠️ La consulta se aplica a los DESCENDIENTES: `.lista` es el contenedor que
   la resuelve, y una consulta de contenedor nunca da estilo a su contenedor.
   Poner aquí `.lista{…}` no hace nada, y en silencio. */
@container lista (min-width:700px){
  .miniatura{width:56px;height:56px}
}


/* =========================================================================
   8. LOS CUATRO AVISOS · EL SELLO
   Medido sobre estos mismos hexadecimales, simulando dicromatismo: en
   pizarra `nuevo` y `bajada` quedan a distancia 13 de 765 en protanopía, y en
   papel `chollo` y `joya` a 30 en deuteranopía. O sea: LOS CUATRO COLORES NO
   SON DISTINGUIBLES para parte de la población, y no se arregla moviendo
   tonos (cuatro categorías que además esquivan verde, rojo, ámbar y teal no
   dejan cuatro tonos separables en visión dicromática).
   Por eso hay cuatro canales y el color es el CUARTO:
     1) icono  ✨ 💸 💎 🔥  (siluetas distintas, `aria-hidden`)
     2) palabra escrita     NUEVO · BAJADA · CHOLLO · JOYA
     3) anchura y trama del canto (§2) — se lee en gris y de reojo
     4) color
   ========================================================================= */
.sello{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--mono);font-size:var(--f11);font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;
  color:var(--c,var(--tinta-2));
  /* Sello de tampón: un filete debajo, NO una caja de color. Trescientas
     pastillas de color en una lista son una pantalla de confeti. */
  border-bottom:2px solid color-mix(in srgb,var(--c,var(--tinta-2)) 55%,transparent);
  padding-bottom:2px;
  white-space:nowrap;
}
.sello .icono{font-size:var(--f12);line-height:1}

/* Marca de procedencia (Wallapop, Vinted, eBay…). Va en TEXTO.
   Prohibido usar los colores corporativos de esas webs: chocan con el acento
   y con los cuatro avisos. */
.marca-web{
  font-family:var(--mono);font-size:var(--f11);color:var(--tinta-3);
  border:1px solid var(--filete);border-radius:var(--r-1);
  padding:1px 5px;white-space:nowrap;
}


/* =========================================================================
   9. GESTO 2 · LA REGLA DE PRECIO
   Escala FIJA de 0 a 2× la referencia; la referencia siempre al 50%. El JS
   sólo tiene que poner `--pos` (0%..100%): el tramo se deduce solo con
   min()/max(), así que no hay dos números que puedan descuadrarse.
   ========================================================================= */
/* Registrada para poder interpolarla y para que un valor basura no rompa el
   dibujo. ⚠️ `inherits:true` es obligatorio: `--pos` se pone en `.regla` y la
   leen la aguja y el tramo, que son NIETOS. Con `inherits:false` los hijos
   recibirían el valor inicial y todas las agujas se quedarían en el centro. */
@property --pos{ syntax:"<percentage>"; inherits:true; initial-value:50%; }

.regla{display:block;--pos:50%}

.regla-pista{
  display:block;position:relative;height:8px;
  background:var(--papel-2);
  /* Graduación cada 25%: esto es una REGLA, no una barra de progreso. Es la
     graduación lo que dice "la escala es la misma en todas las filas". */
  background-image:repeating-linear-gradient(90deg,
      var(--filete) 0 1px, transparent 1px 25%);
  border:1px solid var(--filete);
  border-radius:var(--r-1);
}

/* El tramo entre lo que piden y la referencia: la distancia que importa. */
.regla-tramo{
  position:absolute;inset-block:0;
  left:min(var(--pos),50%);
  right:calc(100% - max(var(--pos),50%));
  background:var(--c,var(--acento));
  opacity:.35;
  transition:left var(--dur-3) var(--curva),right var(--dur-3) var(--curva);
}

/* La referencia. Sobresale por arriba y por abajo para que se lea como la
   marca central de una regla y no como parte del relleno. */
.regla-ref{
  position:absolute;inset-block:-3px;left:50%;
  width:2px;margin-left:-1px;background:var(--tinta-2);
}

/* La aguja: el precio que piden. Borde del color del papel para que se
   despegue del tramo sin necesitar sombra. */
.regla-aguja{
  position:absolute;top:50%;left:var(--pos);
  width:10px;height:10px;margin:-5px 0 0 -5px;
  border-radius:var(--r-redondo);
  background:var(--c,var(--acento));
  border:2px solid var(--carta);
  transition:left var(--dur-3) var(--curva);
}

.regla-pies{
  display:flex;justify-content:space-between;gap:var(--e2);
  margin-top:4px;
  font-family:var(--mono);font-size:var(--f11);font-variant-numeric:tabular-nums;
  color:var(--tinta-3);
}

/* SIN REFERENCIA NO HAY REGLA. No se dibuja media barra ni se inventa un
   centro: un 0 que en realidad es "no se sabe" no puede parecer un 0. */
/* El filete DISCONTINUO ocupa el sitio exacto de la regla que no se puede
   dibujar: se ve que ahí falta un dato, no que no había nada. Las medidas
   suman justo el alto del hueco de `.fila-regla` (1 + 4 + 15 = 20 px) para
   que una fila sin referencia mida lo mismo que las demás. */
.regla-sin{
  display:block;
  font-family:var(--mono);font-size:var(--f11);line-height:1.35;
  color:var(--tinta-3);
  border-top:1px dashed var(--filete-2);padding-top:4px;
}


/* =========================================================================
   10. BOTONES
   Alto de 44 px SIEMPRE, también el "chico": el chico baja de letra y de aire
   lateral, nunca de altura. Es lo que se pulsa con el dedo gordo en la calle.
   ========================================================================= */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--e2);
  min-height:var(--tacto);padding:0 var(--e4);
  border:1px solid var(--filete-2);border-radius:var(--r-1);
  background:var(--carta);color:var(--tinta);
  font-size:var(--f13);font-weight:600;line-height:var(--lh-2);
  text-decoration:none;cursor:pointer;white-space:nowrap;
  transition:background var(--dur-1) var(--curva),
             border-color var(--dur-1) var(--curva),
             transform var(--dur-1) var(--curva);
}
.btn:hover{background:color-mix(in srgb,var(--miel) 22%,var(--carta));border-color:var(--acento)}
.btn:active{transform:translateY(1px)}   /* la única traslación del sistema */

.btn.principal{
  background:var(--acento);border-color:var(--acento);color:var(--sobre-acento);
}
.btn.principal:hover{background:var(--acento-fuerte);border-color:var(--acento-fuerte)}

/* Fantasma: para acciones repetidas en una lista, donde un borde por fila
   sería una reja. */
.btn.fantasma{background:transparent;border-color:transparent;color:var(--tinta-2);font-weight:500}
.btn.fantasma:hover{color:var(--tinta);background:color-mix(in srgb,var(--acento) var(--velo),transparent);border-color:transparent}

.btn.peligro{color:var(--error);border-color:color-mix(in srgb,var(--error) 40%,var(--filete))}
.btn.peligro:hover{background:color-mix(in srgb,var(--error) var(--velo),var(--carta));border-color:var(--error)}

.btn.chico{padding:0 var(--e3);font-size:var(--f12);font-weight:500}

/* Botón de sólo icono: cuadrado, y SIEMPRE con `aria-label`. Prohibido en
   acciones que borren, pausen o envíen — ésas llevan palabra. */
.btn:has(> svg:only-child),
.btn.icono{width:var(--tacto);padding:0;flex:none}

.btn[disabled],.btn[aria-disabled="true"]{
  opacity:.45;cursor:not-allowed;
}
.btn[disabled]:hover,.btn[aria-disabled="true"]:hover{
  background:var(--carta);border-color:var(--filete-2);
}
.btn[aria-disabled="true"]{pointer-events:auto}  /* se deja enfocable para
   poder explicar POR QUÉ no se puede; `disabled` no lo permite. */

/* Cargando: el botón queda inerte y se pone en marcha una trama diagonal.
   Nada de spinners — un spinner no dice ni qué pasa ni cuánto falta; una
   trama en movimiento sobre el propio control dice "esto está trabajando". */
.btn[data-cargando]{
  cursor:progress;pointer-events:none;color:transparent;position:relative;
  background-image:repeating-linear-gradient(135deg,
      color-mix(in srgb,var(--tinta) 12%,transparent) 0 6px,
      transparent 6px 12px);
  /* El paso tiene que ser EXACTAMENTE el ancho de la baldosa o se ve la
     costura en cada vuelta. Para una trama a 135° con periodo P, la baldosa
     mide P·√2: 12 px → 16,97 px. */
  --trama-paso:16.97px;
  background-size:var(--trama-paso) var(--trama-paso);
  animation:trama-en-marcha 700ms linear infinite;
}
/* Una sola animación para las dos tramas; cada una pone su propio paso. */
@keyframes trama-en-marcha{to{background-position:var(--trama-paso) 0}}


/* =========================================================================
   11. CAMPOS
   Etiqueta SIEMPRE encima y visible: el `placeholder` como etiqueta
   desaparece justo cuando hace falta, al escribir.
   ========================================================================= */
.campo{display:grid;gap:5px;min-width:0}
.campo > label,
.campo > .rotulo{
  font-family:var(--mono);font-size:var(--f11);font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;color:var(--tinta-2);
}

.campo input:not([type="checkbox"]):not([type="radio"]),
.campo select,
.campo textarea{
  width:100%;min-height:var(--tacto);
  padding:var(--e2) var(--e3);
  background:var(--carta-2);color:var(--tinta);
  border:1px solid var(--filete-2);border-radius:var(--r-1);
  font-size:var(--f15);line-height:var(--lh-2);
  transition:border-color var(--dur-1) var(--curva),background var(--dur-1) var(--curva);
}
/* Las cifras se escriben en mono: si el campo de precio va en la voz de
   interfaz, lo que se teclea no cuadra con lo que luego se lee en la lista. */
.campo input[type="number"],
.campo input[inputmode="numeric"],
.campo input[inputmode="decimal"],
.campo input[type="time"],
.campo input[type="date"]{
  font-family:var(--mono);font-variant-numeric:tabular-nums;
}
.campo input:hover,.campo select:hover,.campo textarea:hover{border-color:var(--tinta-3)}
.campo input::placeholder,.campo textarea::placeholder{color:var(--tinta-3);opacity:1}

/* `textarea`: primitivo de primera clase, no un caso raro. Pegar una lista es
   la forma nativa de crear muchas variantes de golpe. */
.campo textarea{
  min-height:calc(var(--tacto) * 2);resize:vertical;
  font-family:var(--mono);font-size:var(--f13);line-height:var(--lh-2);
  field-sizing:content;max-height:40vh;   /* crece con lo pegado y para ahí */
}

/* `select`: se quita el dibujo nativo porque su altura no se puede llevar a
   44 px de forma fiable entre plataformas. La flecha es una URL de datos por
   tema (un SVG de fondo no puede leer `currentColor`). */
.campo select{
  appearance:none;
  padding-right:var(--e8);
  background-image:var(--flecha);
  background-repeat:no-repeat;
  background-position:right var(--e3) center;
  background-size:12px 8px;
}

/* ⚠️ Eran 46ch y la ayuda cortaba a media frase debajo de un campo ancho ("…si lo dejas
   vacío se usa el / nombre de usuario"): el campo mide ~560 px y 46ch son ~340, así que
   el renglón se partía sin motivo y parecía un fallo de maquetación. Visto en las capturas
   de la pantalla de entrada y del alta en lote. 62ch sigue dentro de la longitud de línea
   cómoda (45-75), y el `min(…,100%)` evita que se pase en una columna estrecha. */
.campo .ayuda{font-size:var(--f12);color:var(--tinta-3);line-height:var(--lh-2);max-width:min(62ch,100%)}

/* Error: filete de 2 px + icono + frase. Tres canales; el color es el
   tercero. `:user-invalid` y no `:invalid` — el segundo se dispara antes de
   que el usuario haya escrito nada y regaña por adelantado. */
.campo:has(input:user-invalid) > label,
.campo:has([aria-invalid="true"]) > label{color:var(--error)}
.campo input:user-invalid,
.campo textarea:user-invalid,
.campo [aria-invalid="true"]{
  border-width:2px;border-color:var(--error);
  background:color-mix(in srgb,var(--error) var(--velo),var(--carta-2));
}
.campo .mal{
  display:flex;align-items:flex-start;gap:6px;
  font-size:var(--f12);color:var(--error);line-height:var(--lh-2);max-width:52ch;
}
.campo .mal::before{content:"▲";font-size:9px;line-height:1.5;flex:none}

.campo :disabled,.campo [aria-disabled="true"]{opacity:.45;cursor:not-allowed}

/* Un campo que rellena un dato leído del sistema (sólo lectura) no se
   deshabilita: se marca. Deshabilitar esconde el valor del lector. */
.campo input[readonly]{background:var(--papel-2);border-style:dashed}


/* --- Casilla y radio -----------------------------------------------------
   Dibujadas a mano porque la nativa no llega al tamaño táctil sin escalarla,
   y escalar un control nativo lo deja borroso. */
.casilla,.radio{
  display:grid;grid-template-columns:auto minmax(0,1fr);
  align-items:start;gap:var(--e3);
  min-height:var(--tacto);padding-block:var(--e2);
  cursor:pointer;
}
/* `.fila-canto input` entra en la MISMA regla en vez de copiarla: la casilla
   de seleccionar una fila tiene que dibujarse igual que las demás, y tener la
   regla escrita en dos sitios es el fallo clásico de esta casa (se arregla
   una y la otra se queda). */
.casilla input,.radio input,.fila-canto input[type="checkbox"]{
  appearance:none;flex:none;
  width:20px;height:20px;margin:1px 0 0;
  background:var(--carta-2);
  border:1px solid var(--filete-2);
  cursor:pointer;position:relative;
  transition:background var(--dur-1) var(--curva),border-color var(--dur-1) var(--curva);
}
.casilla input,.fila-canto input[type="checkbox"]{border-radius:var(--r-1)}
.radio input{border-radius:var(--r-redondo)}

.casilla input:checked,.radio input:checked,
.fila-canto input[type="checkbox"]:checked{background:var(--acento);border-color:var(--acento)}

/* La marca de verificación: dos lados de una caja girada. Sin SVG, nítida a
   cualquier densidad de pantalla. */
.casilla input:checked::after,
.fila-canto input[type="checkbox"]:checked::after{
  content:"";position:absolute;left:6px;top:2px;
  width:5px;height:10px;
  border:solid var(--sobre-acento);border-width:0 2px 2px 0;
  transform:rotate(43deg);
}
.radio input:checked::after{
  content:"";position:absolute;inset:5px;
  border-radius:var(--r-redondo);background:var(--sobre-acento);
}
/* Indeterminada (selección parcial en una lista): raya, no palomita. */
.casilla input:indeterminate{background:var(--acento);border-color:var(--acento)}
.casilla input:indeterminate::after{
  content:"";position:absolute;left:4px;right:4px;top:8px;height:2px;background:var(--sobre-acento);
}

.casilla .txt,.radio .txt{font-size:var(--f13);line-height:var(--lh-2)}
.casilla .txt small,.radio .txt small{
  display:block;font-size:var(--f12);color:var(--tinta-3);margin-top:2px;
}
/* Marcada = la etiqueta gana peso. Refuerza el estado sin depender del color
   del cuadrito, que es de 20 px. */
.casilla:has(input:checked) > .txt,
.radio:has(input:checked) > .txt{font-weight:600}
.casilla:has(input:disabled),.radio:has(input:disabled){opacity:.45;cursor:not-allowed}


/* --- Interruptor ---------------------------------------------------------
   El canal de estado es la POSICIÓN del botón (izquierda/derecha), que no es
   color; el color sólo refuerza. Aun así la palabra del estado va en el HTML
   al lado: "Activa" / "En pausa". Un interruptor sin palabra obliga a
   recordar qué lado significa qué.
   Markup: <label class="interruptor"><input type="checkbox"><span class="txt">…</span></label>
   -------------------------------------------------------------------------*/
.interruptor{
  display:grid;grid-template-columns:auto minmax(0,1fr);
  align-items:center;gap:var(--e3);
  min-height:var(--tacto);cursor:pointer;
}
.interruptor input{
  appearance:none;flex:none;position:relative;
  width:40px;height:22px;border-radius:var(--r-redondo);
  background:var(--papel-2);border:1px solid var(--filete-2);
  cursor:pointer;
  transition:background var(--dur-2) var(--curva-simetrica),
             border-color var(--dur-2) var(--curva-simetrica);
}
.interruptor input::after{
  content:"";position:absolute;top:2px;left:2px;
  width:16px;height:16px;border-radius:var(--r-redondo);
  background:var(--tinta-2);
  transition:transform var(--dur-2) var(--curva-simetrica),background var(--dur-2) var(--curva-simetrica);
}
.interruptor input:checked{background:var(--acento);border-color:var(--acento)}
.interruptor input:checked::after{transform:translateX(18px);background:var(--sobre-acento)}
.interruptor:has(input:disabled){opacity:.45;cursor:not-allowed}
.interruptor .txt{font-size:var(--f13);line-height:var(--lh-2)}


/* =========================================================================
   12. CHIPS Y ETIQUETAS
   Rectangulares, no píldoras: la píldora se queda para lo único que lo es,
   un contador.
   ========================================================================= */
.chip{
  display:inline-flex;align-items:center;gap:6px;flex:none;
  min-height:var(--tacto);padding:0 var(--e3);
  background:transparent;color:var(--tinta-2);
  border:1px solid var(--filete-2);border-radius:var(--r-1);
  font-family:var(--mono);font-size:var(--f12);
  cursor:pointer;white-space:nowrap;
  transition:background var(--dur-1) var(--curva),color var(--dur-1) var(--curva),
             border-color var(--dur-1) var(--curva);
}
.chip:hover{color:var(--tinta);border-color:var(--tinta-3)}
.chip[aria-pressed="true"]{
  background:color-mix(in srgb,var(--acento) var(--velo),transparent);
  border-color:var(--acento);color:var(--acento);font-weight:600;
}
/* El marcado no puede ir sólo en el color: se le añade la palomita. */
.chip[aria-pressed="true"]::before{content:"✓";font-size:11px;line-height:1}
.chip .cuenta{
  font-variant-numeric:tabular-nums;color:var(--tinta-3);
  border-left:1px solid var(--filete);padding-left:6px;margin-left:2px;
}

/* Tira de filtros: es el único sitio donde se permite scroll horizontal, y
   lo declara ella misma. */
.tira{
  display:flex;gap:var(--e2);overflow-x:auto;padding:var(--e3) var(--e4);
  scrollbar-width:none;overscroll-behavior-x:contain;
}
.tira::-webkit-scrollbar{display:none}

/* Etiqueta estática (no se pulsa): estado de una búsqueda, procedencia… */
.etiqueta{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--mono);font-size:var(--f11);font-weight:500;
  letter-spacing:.06em;text-transform:uppercase;
  padding:2px 6px;border-radius:var(--r-1);
  border:1px solid var(--filete);color:var(--tinta-3);background:var(--papel-2);
}
.etiqueta[data-tono="ok"]{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 35%,transparent);background:color-mix(in srgb,var(--ok) var(--velo),transparent)}
.etiqueta[data-tono="aviso"]{color:var(--aviso);border-color:color-mix(in srgb,var(--aviso) 35%,transparent);background:color-mix(in srgb,var(--aviso) var(--velo),transparent)}
.etiqueta[data-tono="error"]{color:var(--error);border-color:color-mix(in srgb,var(--error) 35%,transparent);background:color-mix(in srgb,var(--error) var(--velo),transparent)}

/* Contador: lo único redondo del sistema. */
.contador{
  display:inline-grid;place-items:center;
  min-width:18px;height:18px;padding:0 5px;
  border-radius:var(--r-redondo);
  background:var(--acento);color:var(--sobre-acento);
  font-family:var(--mono);font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;
}

/* El latido: lo ÚNICO que se mueve solo en toda la interfaz, porque su
   trabajo es decir "sigo vivo". Con movimiento reducido se queda el anillo
   fijo: se quita la animación, no la información. */
.latido{
  display:inline-block;width:8px;height:8px;flex:none;
  border-radius:var(--r-redondo);background:var(--tinta-3);
}
.latido[data-vivo]{
  background:var(--ok);
  animation:latir 2.4s ease-out infinite;
}
@keyframes latir{
  0%  {box-shadow:0 0 0 0 color-mix(in srgb,var(--ok) 55%,transparent)}
  70% {box-shadow:0 0 0 7px transparent}
  100%{box-shadow:0 0 0 0 transparent}
}
@media (prefers-reduced-motion:reduce){
  .latido[data-vivo]{box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 30%,transparent)}
}


/* =========================================================================
   13. TARJETA
   La ÚNICA caja del sistema. Sin sombra, borde de 1 px. Y no se anidan: dos
   bordes anidados es una derrota del espaciado.
   ========================================================================= */
.tarjeta{
  background:var(--carta);
  border:1px solid var(--filete);
  border-radius:var(--r-1);
  overflow:hidden;         /* que las cabeceras a sangre no pisen la esquina */
  container-type:inline-size;
}
.tarjeta-cab{
  display:flex;align-items:center;justify-content:space-between;gap:var(--e3);
  padding:var(--e3) var(--e4);
  background:var(--papel-2);
  border-bottom:1px solid var(--filete);
}
.tarjeta-cab h2,.tarjeta-cab h3{font-size:var(--f13);font-weight:600;letter-spacing:.02em}
.tarjeta-cuerpo{padding:var(--e4);display:grid;gap:var(--e4)}
.tarjeta-pie{
  padding:var(--e3) var(--e4);
  background:var(--papel-2);
  border-top:1px solid var(--filete);
  display:flex;gap:var(--e2);align-items:center;flex-wrap:wrap;
}
/* Un cuerpo que sólo contiene una lista o una tabla NO lleva relleno: sumaría
   al margen que ya trae cada fila y las cifras dejarían de estar a la misma x
   que en una lista suelta. */
.tarjeta-cuerpo:has(> .lista),
.tarjeta-cuerpo:has(> .tabla-caja){padding:0;gap:0}
.tarjeta > .lista,
.tarjeta-cuerpo > .lista{border-block:0}

/* Marcador: la cifra de sala, la que se lee desde lejos. Rejilla de 1 px de
   hueco sobre el color del filete: las divisiones salen solas, sin bordes. */
.marcadores{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--filete)}
.marcador{background:var(--carta);padding:var(--e4);display:grid;gap:var(--e1)}
.marcador .cifra{font-size:var(--f34)}
.marcador .nota{font-size:var(--f12);color:var(--tinta-2)}
@container (min-width:640px){ .marcadores{grid-template-columns:repeat(4,1fr)} }


/* =========================================================================
   14. TABLA
   Sin cebra: las franjas alternas son una segunda retícula compitiendo con
   los filetes, y con cifras tabulares las columnas ya están alineadas solas.
   ========================================================================= */
.tabla-caja{overflow-x:auto;overscroll-behavior-x:contain}  /* el scroll lo
   declara el contenedor: en el cuerpo de la página está prohibido. */

.tabla{width:100%;font-size:var(--f13)}
.tabla th{
  position:sticky;top:0;z-index:1;
  text-align:left;font-family:var(--mono);font-size:var(--f11);font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;color:var(--tinta-2);
  background:var(--papel-2);
  padding:var(--e2) var(--e3);
  border-bottom:1px solid var(--filete-2);
  white-space:nowrap;
}
.tabla td{
  padding:var(--e2) var(--e3);
  border-bottom:1px solid var(--filete);
  vertical-align:top;
}
.tabla tbody tr:last-child td{border-bottom:0}
.tabla tbody tr:hover{background:color-mix(in srgb,var(--acento) var(--velo),transparent)}

/* Columna de cifras: mono, tabular y a la derecha. Es el motivo de que exista
   la segunda voz tipográfica. */
.tabla [data-num]{
  text-align:right;
  font-family:var(--mono);font-variant-numeric:tabular-nums;
  letter-spacing:-.01em;white-space:nowrap;
}
/* Cabecera ordenable: el sentido del orden va en un GLIFO, no sólo en color.
   ⚠️ Nada de `all:unset` aquí: reinicia también `outline` y, por
   especificidad, se llevaría por delante el `:focus-visible` del §4. Se
   reinicia a mano lo que estorba y se deja el foco en paz. */
.tabla th[aria-sort] button{
  background:none;border:0;padding:0;color:inherit;font:inherit;
  cursor:pointer;display:flex;align-items:center;gap:5px;
  min-height:calc(var(--tacto) - var(--e4));width:100%;
  letter-spacing:inherit;text-transform:inherit;
}
.tabla th[aria-sort="ascending"] button::after{content:"▲";font-size:8px}
.tabla th[aria-sort="descending"] button::after{content:"▼";font-size:8px}
.tabla th[aria-sort="none"] button::after{content:"↕";font-size:9px;opacity:.45}


/* =========================================================================
   15. NOTA / MENSAJE
   Usa el mismo lenguaje que el canto de los avisos —una franja de color a la
   izquierda— para que toda la interfaz diga "esto es de este tipo" de la
   misma manera. Nunca depende del color: icono + palabra + franja.
   ========================================================================= */
.nota{
  display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:var(--e2) var(--e3);align-items:start;
  padding:var(--e3) var(--e4);
  border:1px solid var(--filete);
  border-left:3px solid var(--tono,var(--filete-2));
  border-radius:var(--r-1);
  background:color-mix(in srgb,var(--tono,var(--tinta)) var(--velo),var(--carta));
  font-size:var(--f13);line-height:var(--lh-2);
}
.nota[data-tono="info"] {--tono:var(--acento)}
.nota[data-tono="ok"]   {--tono:var(--ok)}
.nota[data-tono="aviso"]{--tono:var(--aviso)}
.nota[data-tono="error"]{--tono:var(--error)}
.nota .icono{font-size:var(--f15);line-height:1.2}
.nota .titular{font-weight:600;color:var(--tono,var(--tinta))}
.nota p{max-width:52ch;margin-top:2px}
.nota .acciones{grid-column:2;display:flex;gap:var(--e2);flex-wrap:wrap;margin-top:var(--e1)}


/* =========================================================================
   16. BARRA DE PROGRESO
   Nunca sola: al lado va SIEMPRE su cifra. Una barra sin número no dice
   cuánto falta, que es lo único que se quiere saber.
   ========================================================================= */
/* `inherits:true` por lo mismo que `--pos`: el relleno es un `::before`, que
   hereda de `.progreso`. */
@property --v{ syntax:"<percentage>"; inherits:true; initial-value:0%; }

.progreso{
  display:block;position:relative;height:6px;--v:0%;
  background:var(--papel-2);
  border:1px solid var(--filete);border-radius:var(--r-1);
  overflow:hidden;
}
.progreso::before{
  content:"";position:absolute;inset-block:0;left:0;width:var(--v);
  background:var(--acento);
  transition:width var(--dur-3) var(--curva);
}
/* Indeterminado: trama en marcha, no un relleno que va y vuelve fingiendo
   que sabe algo. La trama dice "trabajando"; un relleno al 60% mentiría. */
.progreso[data-indeterminado]::before{
  width:100%;
  background:repeating-linear-gradient(135deg,
      var(--acento) 0 8px,
      color-mix(in srgb,var(--acento) 45%,var(--papel-2)) 8px 16px);
  --trama-paso:22.63px;   /* periodo 16 px × √2 (ver §10) */
  background-size:var(--trama-paso) var(--trama-paso);
  animation:trama-en-marcha 800ms linear infinite;
}
.progreso-fila{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--e3);align-items:center}
.progreso-fila .valor{font-family:var(--mono);font-size:var(--f12);font-variant-numeric:tabular-nums;color:var(--tinta-2)}


/* =========================================================================
   17. ESTADO VACÍO
   Tiene que distinguir TRES cosas distintas y decir cuál es. Confundir "no se
   ha podido saber" con "no hay nada" es el fallo clásico de esta casa: un 0
   que en realidad es un silencio se lee como un 0 de verdad.
   ========================================================================= */
.vacio{
  display:grid;justify-items:center;gap:var(--e3);
  padding:var(--e10) var(--e4);text-align:center;   /* el ÚNICO sitio donde se
     permite centrar texto. */
}
/* El glifo lo pone el CSS (`::after`, abajo) y el elemento va vacío y con
   `aria-hidden`: es decoración, la frase la lleva el titular. */
.vacio .icono{font-size:var(--f26);line-height:1;opacity:.5}
.vacio .titular{font-size:var(--f15);font-weight:600}
.vacio .detalle{font-size:var(--f13);color:var(--tinta-2);max-width:46ch;line-height:var(--lh-2)}

/* No hay nada todavía: se explica qué lo llenará. */
.vacio[data-causa="sin-datos"] .icono::after{content:"☐"}
/* Lo has filtrado tú: se ofrece deshacer el filtro, no se culpa al sistema. */
.vacio[data-causa="filtrado"] .icono::after{content:"⌕"}
/* NO SE HA PODIDO SABER: esto NO es cero. Se marca con el tono de aviso y se
   dice qué falló, porque un silencio leído como un 0 toma decisiones malas. */
.vacio[data-causa="sin-medir"]{
  border-left:3px solid var(--aviso);
  background:color-mix(in srgb,var(--aviso) var(--velo),transparent);
  justify-items:start;text-align:left;
  padding:var(--e4) var(--e4) var(--e4) calc(var(--e4) - 3px);
}
.vacio[data-causa="sin-medir"] .detalle{max-width:52ch}
.vacio[data-causa="sin-medir"] .icono{opacity:1}
.vacio[data-causa="sin-medir"] .icono::after{content:"▲";color:var(--aviso)}
.vacio[data-causa="sin-medir"] .titular{color:var(--aviso)}


/* =========================================================================
   18. ESQUELETO DE CARGA
   Con la FORMA de lo que va a llegar. Un esqueleto que no se parece al
   contenido produce un salto al cargar, y eso es peor que esperar.
   Late en opacidad, no en un brillo que barre: el barrido es movimiento
   lateral y aquí nada se desplaza.
   ========================================================================= */
.esqueleto{display:grid;gap:var(--e2)}
.esqueleto > *{
  background:var(--filete);border-radius:var(--r-1);
  animation:latir-opaco 1.4s ease-in-out infinite;
}
.esqueleto .linea{height:12px}
.esqueleto .linea.corta{width:45%}
.esqueleto .linea.media{width:70%}
.esqueleto .cifra{height:18px;width:88px;justify-self:end}
.esqueleto .foto{width:44px;height:44px;border-radius:var(--r-2)}
.esqueleto .barra{height:8px}   /* el hueco de la regla de precio */
@keyframes latir-opaco{0%,100%{opacity:.45}50%{opacity:.9}}

/* Esqueleto de una fila: copia exacta de la rejilla de `.fila` para que al
   llegar el dato no se mueva ni un píxel. */
.esqueleto-fila{
  position:relative;   /* para el filete del canto (`::after`) */
  display:grid;
  grid-template-columns:var(--canto) minmax(0,1fr) max-content;
  column-gap:var(--e4);row-gap:var(--e2);align-items:start;
  padding:var(--e3) var(--e4) var(--e3) 0;
  border-bottom:1px solid var(--filete);
}
.esqueleto-fila .foto{margin-inline:var(--e3)}
.esqueleto-fila .barra{grid-column:2 / -1}


/* =========================================================================
   19. ENTRADA DE CONTENIDO
   Lo nuevo aparece como se imprime: opacidad y el filete creciendo. Nada se
   desplaza de lado, nada escala. Techo duro de 240 ms.
   ========================================================================= */
@keyframes imprimir{
  from{opacity:0;clip-path:inset(0 0 100% 0)}
  to  {opacity:1;clip-path:inset(0 0 0 0)}
}
.entra{animation:imprimir var(--dur-2) var(--curva) both}


/* =========================================================================
   20. ALTO CONTRASTE DE WINDOWS
   Con los colores forzados, `background-color` y `color-mix` desaparecen: lo
   que aquí lleva la información (el canto del aviso, el foco, el borde de un
   control) tiene que seguir dibujándose con los colores del sistema.
   ========================================================================= */
@media (forced-colors:active){
  /* El canto del aviso sobrevive porque su ANCHURA y su TRAMA siguen siendo
     distintas aunque los cuatro colores se fundan en uno. Es exactamente el
     motivo de que el color fuera el cuarto canal y no el primero. */
  .fila[data-aviso]::before,
  .fila::after,.corte-dia::after,.esqueleto-fila::after{background:CanvasText}
  .btn,.chip,.campo input,.campo select,.campo textarea,
  .tarjeta,.nota,.progreso,.regla-pista{border-color:CanvasText}
  .btn.principal{background:Highlight;color:HighlightText;border-color:Highlight}
  .casilla input:checked,.radio input:checked,
  .interruptor input:checked{background:Highlight;border-color:Highlight}
  .regla-aguja{background:CanvasText;border-color:Canvas}
  .regla-ref{background:CanvasText}
  :focus-visible{outline-color:Highlight;box-shadow:none}
}
