/*
 * componentes.css — lo que se pone dentro de la página.
 *
 * La otra mitad de `app.css` (ver la cabecera de `base.css` para el porqué del
 * corte): botón, badge, campo, tabla, cifra, ficha. Piezas que se dibujan
 * dentro del armazón, no el armazón.
 *
 * Depende de `base.css` y se carga DESPUÉS: usa sus tokens y no redefine
 * ninguno. Un color literal acá es un error de revisión (§4 de la norma).
 */

/* --- Tarjeta -------------------------------------------------------------- */

.tarjeta {
    background: var(--color-superficie);
    border: 1px solid var(--color-borde);
    border-radius: var(--radio);
    box-shadow: var(--sombra);
    padding: var(--espacio);
    margin-bottom: var(--espacio);
}

/* --- Botones (§5.1) ------------------------------------------------------- */

.boton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--toque-minimo);
    /* Piso de ancho: sin él cada botón mide lo que mide su texto y una fila de
     * acciones queda con tres anchos distintos. Es piso, no ancho fijo — una
     * etiqueta más larga que esto ensancha el botón, y ahí lo que se acorta es
     * la etiqueta, no el token (§5.1). */
    min-width: var(--boton-ancho);
    padding: 0.6rem 1.1rem;
    border-radius: var(--radio);
    border: 1px solid transparent;
    font-size: 1rem;
    /* Explícita: `button` no hereda la del `body` y `a` sí, así que sin esto
     * el mismo botón medía 44 px como <button> y 45 px como <a>. */
    line-height: 1.25;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    text-align: center;
    /* Feedback de puntero y de pulsación, nada más: 120 ms de color y un
     * milímetro de hundida al apretar. Es `transform` y `background-color`,
     * las dos que no rehacen el layout (§8), y las dos solo mientras el
     * usuario está encima — un botón que se mueve solo sí está prohibido. */
    transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease;
}

/* La hundida vale para los tres colores de botón y es la única señal táctil
 * que tiene alguien con guantes, que no siente el clic del trackpad. */
.boton:active { transform: translateY(1px); }

.boton--primario { background: var(--color-primario); color: var(--color-superficie); }
.boton--primario:hover { background: var(--color-primario-hover); }

.boton--secundario {
    background: var(--color-superficie);
    color: var(--color-texto);
    border-color: var(--color-borde-campo);
}
.boton--secundario:hover { background: var(--color-fondo); }

.boton--peligro { background: var(--color-error); color: var(--color-superficie); }
.boton--peligro:hover { background: var(--color-error-hover); }

/* Excepción al piso de ancho: la landing y el login tienen su propio ritmo de
 * botones, definido en landing.css, y no son acciones de una pantalla de la
 * app sino el llamado a la acción de una página que convence.
 * (La otra excepción era "Salir" en la barra, donde 9,5 rem le comían el ancho
 * al nombre del tenant. Ya no existe: la cuenta se abre desde su ícono y sus
 * opciones no son `.boton` — ver `.sesion__opcion` en base.css.) */
.publico .boton { min-width: 0; }

/* --- Badges de estado (§5.2) ---------------------------------------------
 * El color nunca es la única señal: cada badge lleva su texto. */

.badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
}

.badge--al-dia,
.badge--activo,
.badge--vigente,
.badge--abierta,
.badge--abierto,
.badge--elegida,
.badge--confirmado,
.badge--saldada,
.badge--recibida,
.badge--ejecutada,
.badge--stock_normal,
.badge--ok,
.badge--cerrado       { background: var(--color-exito-fondo); color: var(--color-exito); }
.badge--atrasada,
.badge--vencida,
.badge--vencido,
.badge--pendiente_vencida,
.badge--parcial_vencida,
.badge--error,
.badge--sin_stock     { background: var(--color-error-fondo); color: var(--color-error); }
.badge--en_plazo,
.badge--pendiente_en_plazo,
.badge--parcial_en_plazo,
.badge--emitida,
.badge--programada,
.badge--abierto,
.badge--deposito,
.badge--reposicion_en_camino,
.badge--ingreso_extra { background: var(--color-info-fondo); color: var(--color-info); }
.badge--no_confirmado,
.badge--pendiente,
.badge--por_vencer,
.badge--bajo_minimo,
.badge--reposicion_parcial,
.badge--retiro        { background: var(--color-advertencia-fondo); color: var(--color-advertencia); }
.badge--borrador,
.badge--no_exigible,
.badge--sin_deuda,
.badge--cancelada,
.badge--cancelado,
.badge--cerrado,
.badge--anulada,
.badge--descartada,
.badge--inactivo      { background: var(--color-neutro-fondo); color: var(--color-neutro); }
.badge--propuesta     { background: var(--color-propuesta-fondo); color: var(--color-propuesta); }

/* --- Formularios (§5.3) --------------------------------------------------- */

.formulario { max-width: 46rem; }

/* `form.as_div` de Django envuelve cada campo en un <div>; `.campo` es el
 * mismo bloque escrito a mano en las plantillas. Se estilan juntos para que
 * un campo de capa 3 y uno de capa 2 no se vean distintos. */
.campo,
.formulario > div { margin-bottom: var(--espacio); }

.campo label,
.formulario label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.3rem;
}

/* Etiqueta siempre visible arriba, nunca como placeholder (§5.3). */
.campo .ayuda,
.formulario .helptext {
    display: block;
    color: var(--color-texto-suave);
    font-size: 0.85rem;
    margin-top: 0.25rem;
}

/* `.campo__error` es el mismo mensaje escrito por la tabla de líneas de un
 * documento, donde el campo no es un `.campo` sino una celda. Estaba en la
 * plantilla sin ninguna regla en el CSS —el error salía como texto corriente,
 * del mismo color que el dato— y es exactamente el caso que el §9 pide mirar
 * en las dos direcciones. */
.campo .error,
.campo__error,
.formulario .errorlist {
    display: block;
    margin: 0.25rem 0 0;
    padding: 0;
    list-style: none;
    color: var(--color-error);
    font-size: 0.85rem;
}

.formulario__acciones {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espacio-sm);
    margin-top: var(--espacio-lg);
}

/* La acción que cambia el ESTADO del registro —darlo de baja, reactivarlo—
 * va en su propio <form>, separada de las acciones del formulario: son dos
 * POST con distinto destino, y juntarlos en la misma fila pone "Eliminar" al
 * lado de "Guardar" con el mismo peso visual. Acá queda abajo, con aire de
 * por medio y un filete que la despega del formulario. */
.formulario__aparte {
    margin-top: var(--espacio-lg);
    padding-top: var(--espacio);
    border-top: 1px solid var(--color-borde);
}

/* Fila de accesos a otras pantallas (panel, atajos de una ficha). */
.accesos {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espacio-sm);
    margin-bottom: var(--espacio);
}

/* --- Las filas de acciones en celular --------------------------------------
 * El piso de 9,5 rem del §5.1 alinea una fila de botones en escritorio y en
 * celular hace lo contrario: dos botones no entran a 360 px, así que "Nuevo
 * cliente", "Exportar Excel" e "Importar Excel" quedaban de a uno por renglón
 * pero alineados a la izquierda, con medio renglón vacío al lado, y el
 * primario asomando fuera de la pantalla.
 *
 * En celular los botones se reparten el renglón y crecen hasta llenarlo: de a
 * dos donde entran dos, de a uno donde entra uno, sin medio renglón vacío al
 * lado ni nada asomando fuera de la pantalla. La acción primaria es la
 * excepción y se lleva el renglón entero: es la que la pantalla quiere que
 * hagas (§5.1), y compartir renglón la empata con la de al lado.
 *
 * Desde 30rem vuelven a su ancho natural y a ponerse en fila, que es donde el
 * piso de ancho hace lo que fue a hacer. */
.encabezado__acciones .boton,
.formulario__acciones .boton,
.filtros__acciones .boton,
.accesos .boton { flex: 1 1 40%; }

.encabezado__acciones .boton--primario,
.formulario__acciones .boton--primario { flex-basis: 100%; }

@media (min-width: 30rem) {
    .encabezado__acciones .boton,
    .formulario__acciones .boton,
    .filtros__acciones .boton,
    .accesos .boton { flex: 0 0 auto; }
}

input,
select,
textarea {
    width: 100%;
    min-height: var(--toque-minimo);
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--color-borde-campo);
    border-radius: var(--radio);
    background: var(--color-superficie);
    color: var(--color-texto);
    font-size: 1rem;   /* 16px: bajo eso iOS hace zoom al enfocar */
    font-family: inherit;
}

input[type="checkbox"] { width: auto; min-height: 0; }

/* La casilla es el único control que no lleva la etiqueta arriba: `as_div` la
 * emite igual que a los demás y quedaba el texto en una línea y un cuadradito
 * suelto abajo, que no se lee como un campo. Acá se recoloca sin tocar el
 * marcado, que lo genera Django.
 *
 * Es una reja con las tres celdas puestas a mano y ya no un `row-reverse`:
 * ese truco asumía DOS hijos (etiqueta y casilla) y funcionaba mientras el
 * campo no tuviera ayuda. Con ayuda, `as_div` emite etiqueta, ayuda y casilla
 * en ese orden, y al invertir la fila el rótulo terminaba a la derecha del
 * todo con la ayuda al medio. Con posiciones explícitas el orden del marcado
 * deja de importar: la casilla siempre a la izquierda, el rótulo a su lado y
 * la ayuda debajo, alineada con el rótulo. La fila llega al toque mínimo. */
.campo:has(> input[type="checkbox"]),
.formulario > div:has(> input[type="checkbox"]) {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    column-gap: 0.5rem;
    min-height: var(--toque-minimo);
}

.campo:has(> input[type="checkbox"]) > input[type="checkbox"],
.formulario > div:has(> input[type="checkbox"]) > input[type="checkbox"] { grid-area: 1 / 1; }

.campo:has(> input[type="checkbox"]) > label,
.formulario > div:has(> input[type="checkbox"]) > label {
    grid-area: 1 / 2;
    margin-bottom: 0;
}

.campo:has(> input[type="checkbox"]) > .helptext,
.formulario > div:has(> input[type="checkbox"]) > .helptext { grid-area: 2 / 2; }

input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] { border-color: var(--color-error); }

/* --- Filtros de listado -----------------------------------------------------
 * Fondo azul de marca desde el 2026-09-05 (antes era `--color-superficie`,
 * blanco liso). El borde usa `--color-primario-hover` —el mismo tono que ya
 * usa `.boton--primario:hover`— como filete apenas más oscuro que el fondo,
 * puramente decorativo: el panel ya se distingue de `--color-fondo` de sobra
 * por el cambio de color, el borde solo le da un borde nítido a ese cambio. */
/* El bloque es el ENVOLTORIO y el panel azul es `.filtros__form`: el selector
 * de qué filtros se ven flota en la esquina del panel (`__herramientas`) y
 * necesita un ancestro posicionado que NO sea el <form> —los formularios no
 * se anidan, así que ese selector, que trae su propio <form method="post">,
 * tiene que ser hermano del <form method="get"> de los valores. Todas las
 * reglas `.filtros <control>` de más abajo siguen valiendo: el form está
 * adentro. */
.filtros {
    position: relative;
    margin-bottom: var(--espacio);
}

/* Flex y no grid: el panel tiene un número VARIABLE de controles (el buscador,
 * más un input por cada campo propio de número o fecha que el tenant encienda),
 * y una grilla de columnas fijas o los apretaba o dejaba media columna vacía.
 * En flex cada control pide su ancho mínimo y el sobrante se lo lleva el
 * buscador, que es el que más lo aprovecha.
 *
 * `align-items: end` alinea por la BASE de los controles y no por su alto: un
 * campo con etiqueta visible y el buscador sin ella tienen alto distinto, y
 * sin esto el más bajo flotaba a media altura. */
.filtros__form {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: var(--espacio-sm) var(--espacio);
    /* Vertical apretado (`--espacio-sm`) y horizontal cómodo: el panel es una
     * barra sobre la tabla, no una tarjeta — cada renglón de más acá es una
     * fila menos de datos visible sin desplazar. El hueco extra a la derecha
     * es del selector flotante (`.filtros__herramientas`), y va en TODO ancho:
     * en celular el panel es de una columna y sin ese hueco la llave caía
     * justo encima del buscador, tapando el final de lo escrito. */
    padding: var(--espacio-sm) var(--espacio);
    padding-right: calc(var(--espacio) + var(--toque-minimo));
    background: var(--color-superficie-marca);
    border: 1px solid var(--color-primario-hover);
    border-radius: var(--radio);
}

/* Esquina superior derecha del panel, superpuesta: es una herramienta de
 * configuración, no un control del filtrado de todos los días, y ocupando
 * una celda del grid competía de igual a igual con los campos. `z-index`
 * porque el panel que abre tiene que quedar sobre los campos de abajo. */
.filtros__herramientas {
    position: absolute;
    top: var(--espacio-sm);
    right: var(--espacio-sm);
    z-index: 2;
}

/* Piso de 10rem y crece: un `<input type="date">` no baja de ahí sin cortar su
 * propio control nativo. */
.filtros__campo { display: grid; gap: 0.3rem; flex: 1 1 10rem; }

/* El buscador se queda con el sobrante: `flex-grow` alto contra el 1 de los
 * demás, y una base grande para que sea el último en partirse de renglón. Es
 * el control que de verdad usa el ancho — los otros son un número o una
 * fecha. */
.filtros__campo--buscar { flex: 6 1 18rem; }

/* Oculta para el ojo, presente para el lector de pantalla (§5.3, §8). No es
 * `display: none`: eso la sacaría también del árbol de accesibilidad y el
 * campo quedaría sin nombre, que es justo lo que la regla de "etiqueta
 * siempre visible" viene a evitar. */
.filtros__etiqueta--oculta {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Etiqueta de cada campo: sin esto heredaba `--color-texto` (texto oscuro)
 * del body y quedaba texto oscuro sobre fondo oscuro, ilegible. Crema plena,
 * 9,66:1 contra el fondo azul del panel — medido con la fórmula de
 * luminancia relativa de WCAG. */
.filtros__etiqueta { font-weight: 600; font-size: 0.9rem; color: var(--color-crema); }

/* En celular ocupan el renglón entero y arrancan DESDE LA IZQUIERDA, alineadas
 * con el buscador de arriba: ahí el sobrante horizontal no existe, así que un
 * `margin-left: auto` no las anclaba a ningún borde, solo las corría media
 * pantalla a la derecha y las dejaba flotando contra nada. Se pegan a la
 * derecha recién cuando hay barra ancha que anclar (media query de más
 * abajo). */
.filtros__acciones {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espacio-sm);
    flex-basis: 100%;
}

/* Sin el piso de 9,5 rem del §5.1: acá son DOS botones cortos («Filtrar»,
 * «Limpiar») compartiendo un renglón que en celular mide ~264 px una vez
 * descontados el padding del panel y el hueco de la llave. Con el piso puesto
 * el segundo no entraba y se apilaban, que es justo lo que el piso venía a
 * evitar en una fila de acciones ancha. Se reparten el renglón en partes
 * iguales y siguen midiendo 44 px de alto (§7). */
.filtros__acciones .boton { min-width: 0; }

/* Los <input>/<select> de dentro conservan su fondo blanco propio (regla
 * general de más abajo) y con eso ya se leen bien sobre el azul — la caja
 * blanca contrasta sola. Lo que no aguantaba el cambio es su BORDE:
 * `--color-borde-campo` da apenas 3,03:1 contra `--color-superficie-marca`,
 * a un pelo del piso de 3:1 que exige el SC 1.4.11 y expuesto a que
 * cualquier redondeo de render lo tire abajo, y encima se ve como dos grises
 * casi fundidos (gris azulado sobre azul) aunque el número pase raspando.
 * Crema plena da 9,66:1, con margen de sobra. Mismo criterio para el borde
 * de "Filtrar"/"Limpiar" (`.boton--secundario`), que por defecto hereda el
 * mismo borde-campo gris para su propio filete. */
/* Scoping a `.filtros__form` y no a `.filtros` a secas: el selector de qué
 * filtros se ven vive también dentro del bloque, pero su panel es superficie
 * BLANCA — un borde crema ahí no se vería contra nada. */
.filtros__form input,
.filtros__form select,
.filtros__form .boton--secundario {
    border-color: var(--color-crema);
}

/* --- Tags: los filtros de CATEGORÍA (§5.3.1) --------------------------------
 * Un filtro de lista cerrada no necesita un <select> con "Todos": necesita
 * ver de un vistazo qué valores hay y cuál está puesto. Cada tag es un <a>
 * —navega, no envía el formulario— así que funciona sin JavaScript y la URL
 * resultante se puede compartir igual que cualquier otro filtro.
 *
 * Ocupa el ancho entero del panel: los grupos son de largo variable (dos
 * estados, ocho sucursales) y encajarlos en una celda del grid los partía. */
.filtros__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.9rem;
    /* Renglón propio: los grupos son de largo variable (dos estados, ocho
     * sucursales) y meterlos en la fila de los campos los partía al medio. */
    flex-basis: 100%;
}

.tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
}

/* El nombre del grupo ("Tipo", "Estado"): mismo peso y tamaño que
 * `.filtros__etiqueta`, porque es lo mismo —la etiqueta de un filtro— solo
 * que en línea con sus valores en vez de arriba de un control. */
.tags__titulo {
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--color-crema);
}

/* Apagado: contorno crema sobre el azul (9,66:1). Encendido: se invierte a
 * crema plena con texto azul, que es la misma inversión que usa
 * `.paginacion__pagina--actual` para decir "estás acá" — el color no es la
 * única señal, el tag activo lleva además `aria-current` en la plantilla. */
/* Chicos a propósito: son una NUBE, no una fila de botones, y la zona entera
 * tiene que caber sin empujar la tabla fuera de la primera pantalla. 28 px de
 * alto es el piso que se puede bajar sin romper nada: el objetivo táctil de
 * 44 px del §7 vale para una acción suelta, mientras que el mínimo duro de un
 * control en línea dentro de un grupo es el 24 × 24 del SC 2.5.8, y 28 lo pasa
 * con aire. Más chico que esto sí habría que discutirlo. */
.tag {
    display: inline-flex;
    align-items: center;
    min-height: 1.75rem;
    padding: 0 0.6rem;
    border: 1px solid var(--color-crema);
    border-radius: 999px;
    color: var(--color-crema);
    font-size: 0.85rem;
    text-decoration: none;
    transition: background-color 120ms ease, color 120ms ease;
}

.tag:hover {
    background: var(--color-crema-velo);
}

.tag--activo {
    background: var(--color-crema);
    color: var(--color-primario);
    font-weight: 600;
}

.tag--activo:hover {
    background: var(--color-crema-suave);
}

@media (min-width: 40rem) {
    /* Las acciones vuelven a pegarse a la derecha: en una barra ancha el
     * sobrante está entre los campos y el borde, y ahí «Filtrar» ancla. */
    .filtros__acciones {
        flex-basis: auto;
        margin-left: auto;
    }
}

/* Celular: nube de tags -----------------------------------------------------
 * El nombre del grupo ("Tipo", "Estado") se oculta VISUALMENTE. A 360 px cada
 * título empujaba a sus tags a un renglón propio —cinco grupos, cinco
 * renglones— y el panel se comía la pantalla antes de la primera fila de
 * datos. Sin ellos los tags fluyen como una nube y entran de a dos o tres por
 * renglón; la separación mayor entre grupos (el `gap` de `.filtros__tags`,
 * contra el `gap` chico de `.tags`) sigue dejando ver dónde termina uno.
 *
 * Oculto y no borrado: el nombre del grupo sigue anunciándose por el
 * `aria-label` del `role="group"` (`partials/_filtros.html`), y `clip-path`
 * —no `display: none`— es lo que garantiza que el texto siga en el árbol de
 * accesibilidad si un lector prefiere leerlo desde ahí. */
@media (max-width: 39.999rem) {
    .tags__titulo {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* --- Tabla (§5.4) --------------------------------------------------------- */

/* En celular la tabla no se convierte en tarjetas: desliza de lado adentro de
 * su envoltorio (§5.4). Lo que faltaba era decir que se puede deslizar — una
 * tabla cortada al ras del borde se lee como una tabla que termina ahí.
 *
 * Las cuatro capas son el truco de sombras de scroll, sin JavaScript: las dos
 * primeras (`local`) viajan con el contenido y tapan a las otras dos cuando el
 * deslizamiento llega al tope, así que la sombra aparece solo del lado donde
 * queda tabla por ver. Para que se vean, `.tabla` no lleva fondo propio: el
 * blanco lo pone el envoltorio, debajo. */
.tabla-envoltorio {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    margin-bottom: var(--espacio);
    background:
        linear-gradient(to right, var(--color-superficie), transparent) 0 0 / 1.5rem 100% no-repeat local,
        linear-gradient(to left, var(--color-superficie), transparent) 100% 0 / 1.5rem 100% no-repeat local,
        linear-gradient(to right, var(--color-borde), transparent) 0 0 / 0.75rem 100% no-repeat scroll,
        linear-gradient(to left, var(--color-borde), transparent) 100% 0 / 0.75rem 100% no-repeat scroll,
        var(--color-superficie);
    border: 1px solid var(--color-borde);
    border-radius: var(--radio);
}

.tabla {
    width: 100%;
    border-collapse: collapse;
    min-width: 480px;
}

.tabla th,
.tabla td {
    padding: 0.6rem 0.75rem;
    text-align: left;
    border-bottom: 1px solid var(--color-borde);
    white-space: nowrap;
}

/* Encabezado sobre el azul de marca desde el 2026-09-05 (antes era
 * `--color-fondo`, el mismo gris de la página). Selector `thead th` a
 * propósito y no `.tabla th`: la primera columna del CUERPO también es un
 * `th` (identifica la fila, §5.4) y tiene que seguir viéndose como una celda
 * más, no como parte del encabezado — por eso este selector no la toca.
 * El `border-bottom` de `.tabla th` (heredado, ver arriba) queda tal cual:
 * `--color-borde` casi no se ve sobre el azul, pero no hace falta que se
 * vea — el salto de azul a blanco entre encabezado y cuerpo YA es el límite,
 * el filete es un extra sobre fondo claro, no la única señal acá. */
.tabla thead th {
    background: var(--color-superficie-marca);
    color: var(--color-crema);
    font-weight: 600;
    font-size: 0.9rem;
}

.tabla tbody th { font-weight: 600; }

/* La fila entera se ilumina al pasar por encima. En una tabla de siete
 * columnas sin nada que las una, el ojo pierde el renglón entre el
 * identificador y el estado, y eso es leer mal la fila de otro activo. Solo
 * con puntero: en celular no hay hover y una fila teñida sin motivo confunde. */
@media (hover: hover) {
    .tabla tbody tr { transition: background-color 120ms ease; }
    .tabla tbody tr:hover { background: var(--color-fondo); }
}

.tabla tbody tr:last-child td,
.tabla tbody tr:last-child th { border-bottom: 0; }

/* El pie (`<tfoot>`, `partials/_tabla.html`, #214): un total no es una fila
 * más. Antes, cuando cada pantalla escribía la tabla a mano, la única marca
 * era un `<strong>` puntual en las celdas que no fueran ya un `<th>` de
 * cuerpo (que sale en negrita por la regla de arriba) — con el pie como
 * componente compartido esa marca es una regla, no una etiqueta que cada
 * plantilla se acuerde de poner. El borde superior separa el total del
 * cuerpo, que sin él quedaba pegado a la última fila. */
.tabla tfoot td {
    font-weight: 600;
    border-top: 2px solid var(--color-borde);
}

/* Los montos y cualquier columna numérica van a la derecha, en Mono tabular.
 * El selector lleva `.tabla` delante para ganarle a `.tabla th`. */
.tabla .tabla__numero { text-align: right; }

/* Celda de prosa (un mensaje de error, una observación): es la única que
 * puede envolver, si no la tabla se hace más ancha que la pantalla. */
.tabla .tabla__texto { white-space: normal; min-width: 22rem; }

/* --- Celular: la fila se dibuja como tarjeta (§5.4) -----------------------
 * Hasta el 2026-09-06 la tabla NO se reflow-eaba: deslizaba de lado dentro de
 * su envoltorio, para no perder la comparación entre filas. A 360 px eso
 * dejaba fuera de la pantalla todo lo que no fuera la primera columna —el
 * precio cortado a la mitad, los botones de acción inalcanzables sin
 * deslizar—, y en un catálogo de productos lo que se hace desde el teléfono
 * es mirar UNA ficha y actuar sobre ella, no comparar diez. La comparación
 * sigue estando donde de verdad se usa, que es el escritorio.
 *
 * Es puro CSS sobre el mismo marcado: no hay una segunda plantilla de
 * tarjetas que mantener en paralelo (§9). Lo que el marcado sí aporta son dos
 * cosas que el CSS no puede inventar: `data-etiqueta` en cada `<td>` (el
 * nombre del dato, que sin `<thead>` a la vista no está en ningún lado) y los
 * `role` explícitos del `<table>` — cambiarle el `display` a una tabla le
 * borra la semántica al lector de pantalla, y los roles la reponen.
 *
 * El `<thead>` se esconde VISUALMENTE (no `display: none`): sigue en el árbol
 * de accesibilidad, así que quien navega por celdas con un lector escucha el
 * encabezado de la columna como en escritorio. Mismo patrón que
 * `.tags__titulo`. */
@media (max-width: 47.999rem) {
    /* El envoltorio deja de ser una caja con scroll: las tarjetas son las que
     * llevan borde y fondo, y las sombras de deslizamiento no tienen nada que
     * anunciar cuando ya no hay nada que deslizar. */
    .tabla-envoltorio {
        overflow-x: visible;
        background: none;
        border: 0;
        border-radius: 0;
    }

    .tabla {
        display: block;
        min-width: 0;
    }

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

    .tabla tbody,
    .tabla tfoot {
        display: block;
    }

    /* La tarjeta es una reja de dos columnas y no un bloque, para que el
     * título y los botones de acción compartan el renglón de arriba: son las
     * dos cosas que se buscan al abrir la lista —qué es y qué le hago—, y con
     * las acciones al pie había que pasar por encima de todos los datos para
     * llegar. El resto de las celdas ocupa el ancho entero (`1 / -1`) y cae
     * sola en los renglones de abajo, en el orden del marcado.
     *
     * `column-gap: 0` a propósito: el filete que separa el encabezado del
     * cuerpo lo dibujan las dos celdas de arriba con su `border-bottom`, y
     * con un `gap` la línea aparecería cortada justo al medio. La separación
     * entre el título y los botones la pone el `padding` de la celda de
     * acciones. */
    .tabla tbody tr,
    .tabla tfoot tr {
        display: grid;
        grid-template-columns: 1fr auto;
        column-gap: 0;
        margin-bottom: var(--espacio-sm);
        padding: 0.75rem;
        background: var(--color-superficie);
        border: 1px solid var(--color-borde);
        border-radius: var(--radio);
    }

    /* Cada dato es una línea "nombre → valor". `data-etiqueta` a la izquierda
     * en gris suave, el valor a la derecha: la misma lectura que daba la
     * columna, girada 90°. Y acá SÍ se permite envolver —lo que en una tabla
     * rompe la comparación, en una tarjeta solo hace la tarjeta más alta—. */
    .tabla tbody td,
    .tabla tfoot td {
        grid-column: 1 / -1;
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 1rem;
        padding: 0.25rem 0;
        border-bottom: 0;
        white-space: normal;
    }

    .tabla tbody td::before {
        content: attr(data-etiqueta);
        flex: 0 0 auto;
        color: var(--color-texto-suave);
        font-size: 0.85rem;
    }

    /* El identificador de la fila es el título de la tarjeta: ocupa el ancho
     * entero, sin etiqueta al lado (su nombre es el dato) y separado del
     * resto por un filete. */
    .tabla tbody th[scope="row"] {
        grid-column: 1;
        grid-row: 1;
        /* Flex para centrar el título contra unos botones que son más altos
         * que él, y NO `align-self: center` sobre la celda: eso encogía su
         * caja al alto del texto y dejaba su `border-bottom` a media altura,
         * o sea el filete del encabezado partido en dos tramos a distinto
         * nivel. Estirada (el `stretch` de fábrica), las dos celdas terminan
         * en la misma línea y el filete sale entero. */
        display: flex;
        align-items: center;
        padding: 0 0 0.5rem;
        margin-bottom: 0.4rem;
        border-bottom: 1px solid var(--color-borde);
        font-size: 1.05rem;
        white-space: normal;
    }

    /* La fila entera se ilumina en escritorio para no perder el renglón; en
     * una tarjeta el renglón no se pierde y el fondo teñido solo ensucia. */
    .tabla tbody tr:hover {
        background: var(--color-superficie);
    }

    /* Las acciones van ARRIBA, al lado del título, y comparten con él el
     * filete que cierra el encabezado de la tarjeta. Acá sí pueden ser flex:
     * fuera del layout de tabla no hay ancho de columna que compartir con
     * las demás filas (el motivo del comentario de `listado.css`). */
    .tabla tbody .tabla__acciones {
        grid-column: 2;
        grid-row: 1;
        display: flex;
        align-items: center;
        gap: 0.4rem;
        padding: 0 0 0.5rem 0.75rem;
        margin-bottom: 0.4rem;
        border-bottom: 1px solid var(--color-borde);
    }

    .tabla__acciones::before { content: none; }

    /* La ÚLTIMA fila de la tabla no lleva filete de cierre —eso es correcto en
     * escritorio, donde el borde del envoltorio ya cierra la tabla— y esa
     * regla le ganaba por especificidad al filete que separa el encabezado de
     * la tarjeta del resto de sus datos: la última tarjeta de la lista salía
     * sin línea y con el título pegado al precio. Acá la última tarjeta es una
     * tarjeta más y su encabezado se cierra igual que el de cualquier otra. */
    .tabla tbody tr:last-child th[scope="row"],
    .tabla tbody tr:last-child .tabla__acciones {
        border-bottom: 1px solid var(--color-borde);
    }

    .tabla__acciones .boton-icono + .boton-icono { margin-left: 0; }

    /* El total del pie no es una tarjeta más: se distingue por el borde
     * grueso que ya lo distinguía como `<tfoot>`. */
    .tabla tfoot tr {
        border-top-width: 2px;
    }

    .tabla tfoot td { font-weight: 600; }
}

/* Tabla de líneas de un documento (venta, compra): cada celda lleva un control
 * y no un dato. Sin piso por columna, en celular el `width: 100%` de los
 * campos los dejaba en dos centímetros —imposible escribir una cantidad— y el
 * error de la línea salía apretado contra el borde. Con el piso, la tabla
 * desliza como cualquier otra y cada campo se puede usar. */
.tabla--edicion { min-width: 100%; }
.tabla--edicion td { min-width: 9rem; white-space: normal; }
.tabla--edicion input,
.tabla--edicion select { min-width: 8rem; }

/* "Contar" (#22): conteo ciego por defecto -- la columna de stock del
   sistema se oculta y el toggle de arriba la muestra. Sin JS: `:has()` lee
   la casilla desmarcada y esconde la columna en el mismo formulario. */
.conteo-toggle { display: block; margin-bottom: 1rem; }
.formulario:has(.conteo-toggle__casilla:not(:checked)) .conteo-col-sistema { display: none; }

/* Carta Gantt de mantención (#55): cada semana es una columna angosta de
 * ancho fijo -- sin piso, dieciséis columnas de fecha se aprietan contra el
 * nombre del activo y la grilla se vuelve ilegible. La semana de hoy se
 * marca con un fondo (no es el único indicador: el encabezado además
 * escribe "(hoy)" en texto, marca-frontend.md §5.2). */
.gantt__semana { min-width: 4.5rem; text-align: center; vertical-align: top; }
.gantt__semana--hoy { background: var(--color-fondo); }

/* Una semana puede traer más de una orden (reprogramada, doble visita):
 * cada badge en su propia línea y no todos apretados en la celda. */
.gantt__ordenes { display: flex; flex-direction: column; gap: 0.25rem; align-items: center; }
.gantt__ordenes a { text-decoration: none; }

/* --- Panel de cifras ------------------------------------------------------ */

.cifras {
    display: grid;
    gap: var(--espacio-sm);
    /* 12rem de piso y no 9rem: la columna tiene que dar el ancho de un monto
       en pesos ("$12.596.700" en mono), no solo el de un conteo de dos
       dígitos. Con 9rem los montos del panel se cortaban contra el borde.
       Y 18rem de techo, que es lo que faltaba: `1fr` reparte todo el sobrante
       entre las columnas que hay, así que una sección de dos cifras armaba dos
       tarjetas de 500 px con el número perdido en la esquina. Con el techo la
       tarjeta mide lo mismo tenga la sección dos cifras o cinco, y lo que
       sobra queda como aire al final de la fila. */
    grid-template-columns: repeat(auto-fit, minmax(12rem, 18rem));
    margin: 0 0 var(--espacio);
}

.cifra {
    /* Columna con el valor abajo: las etiquetas no miden lo mismo ("Clientes"
       contra "Comprado en el mes (neto)") y sin esto los números de una misma
       fila quedaban a distinta altura según cuántas líneas ocupara su rótulo. */
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    margin: 0;
    padding: var(--espacio);
    background: var(--color-superficie);
    border: 1px solid var(--color-borde);
    border-radius: var(--radio);
    /* Una cifra con `url` es un `<a>` (ver `_cifras.html`): sin esto hereda
       el azul y el subrayado del enlace genérico y deja de leerse como el
       resto de las tarjetas del panel. */
    color: inherit;
    text-decoration: none;
}

/* En versalitas y chica: el rótulo dice qué se está mirando y el número es lo
 * que se mira. Con los dos en la misma escala la tarjeta no tenía jerarquía y
 * el ojo entraba por el texto en vez de por la cifra, que es al revés de para
 * qué existe el panel. */
.cifra__etiqueta {
    margin: 0;
    color: var(--color-texto-suave);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.cifra__valor {
    margin: 0.5rem 0 0;
    font-family: var(--fuente-mono);
    font-variant-numeric: tabular-nums;
    font-size: 1.75rem;
    line-height: 1.1;
}

.cifra--atrasados .cifra__valor { color: var(--color-error); }
.cifra--al-dia .cifra__valor { color: var(--color-exito); }

/* --- Tarjetas de un hub --------------------------------------------------- *
 * La portada de un hub (hoy Administración, mañana el que venga): una tarjeta
 * por apartado, con el nombre y una línea que dice qué se hace ahí. El marcado
 * es `templates/partials/_apartados.html` y no es de ningún módulo.
 * Misma reja que `.cifras` con un techo más ancho, porque acá lo que manda el
 * ancho es la descripción y no un número. Es una tarjeta y no un `.boton` como
 * los de `.accesos`: un botón sin espacio para la línea explicativa obliga a
 * adivinar qué hay detrás de cada palabra. */
.apartados {
    display: grid;
    gap: var(--espacio);
    grid-template-columns: repeat(auto-fit, minmax(14rem, 24rem));
    margin: 0 0 var(--espacio);
}

.apartado {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--espacio);
    background: var(--color-superficie);
    border: 1px solid var(--color-borde);
    border-radius: var(--radio);
    color: inherit;
    text-decoration: none;
}

.apartado:hover { border-color: var(--color-primario); }

.apartado__texto { font-weight: 600; }

.apartado__descripcion {
    color: var(--color-texto-suave);
    font-size: 0.875rem;
}

/* --- Lista de definiciones (fichas de detalle) ---------------------------- */

.ficha {
    display: grid;
    gap: 0 var(--espacio);
    margin: 0 0 var(--espacio);
    padding: var(--espacio);
    background: var(--color-superficie);
    border: 1px solid var(--color-borde);
    border-radius: var(--radio);
}

.ficha dt {
    color: var(--color-texto-suave);
    font-size: 0.9rem;
    padding-top: var(--espacio-sm);
}

.ficha dd {
    margin: 0 0 var(--espacio-sm);
    padding-bottom: var(--espacio-sm);
    border-bottom: 1px solid var(--color-borde);
}

.ficha > div:last-child dd,
.ficha dd:last-child { border-bottom: 0; margin-bottom: 0; }

@media (min-width: 40rem) {
    /* `minmax(0, 1fr)` y no `1fr`: el mínimo automático de una columna `1fr` es
     * el de su contenido, así que un correo largo en Mono —que no tiene dónde
     * cortar— ensanchaba la columna y con ella la ficha entera. Medido a 768
     * px, que es donde la columna del valor es más angosta: la página
     * deslizaba 12 px de lado. Con el cero, la columna cede y el texto corta. */
    .ficha { grid-template-columns: 14rem minmax(0, 1fr); align-items: baseline; }
    .ficha dt { padding-top: var(--espacio-sm); }
}

/* --- Paginación ----------------------------------------------------------- */

.paginacion {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--espacio-sm);
    margin: var(--espacio) 0;
}

.paginacion__posicion { color: var(--color-texto-suave); font-size: 0.9rem; }

/* --- Paginación con números -------------------------------------------------
 * Alternativa a "Página N de M" cuando la vista arma la lista de números:
 * mismo `<nav class="paginacion">`, con `.paginacion__paginas` en vez de (o
 * junto a) `.paginacion__posicion`. Cada número es un `<a>`; el `…` entre dos
 * saltos es `.paginacion__elipsis`, sin enlace.
 *
 * 2.25rem y no `--toque-minimo` (44px): es un renglón de números, no una
 * fila de acciones — a 44px por número, un total de diez páginas ya no entra
 * en un celular de 360px sin apretarse entre sí. 2.25rem (36px) es lo que
 * pide el §7 para un objetivo secundario y denso; el número actual, que es
 * el único con algo que decir, sigue midiendo lo mismo pero se distingue por
 * COLOR y PESO, no por tamaño. */
.paginacion__paginas {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
}

.paginacion__pagina {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    min-height: 2.25rem;
    padding: 0 0.4rem;
    border-radius: var(--radio);
    color: var(--color-texto);
    text-decoration: none;
    font-size: 0.9rem;
    transition: background-color 120ms ease, color 120ms ease;
}

.paginacion__pagina:hover { background: var(--color-fondo); }

/* La página actual va en el azul de marca, mismo criterio que el `thead` y
 * `.filtros`: crema sobre `--color-superficie-marca` da 9,66:1. No es la
 * única señal —la plantilla la marca además con `aria-current="page"`
 * (§7)—, pero es la que se ve de un vistazo entre una fila de números
 * iguales. El hover pasa a `--color-primario-hover`, el mismo oscurecido
 * que usa `.boton--primario:hover`, y no al gris de `--color-fondo` que usa
 * el resto de los números: ese gris encima del azul apenas se notaría. */
.paginacion__pagina--actual {
    background: var(--color-superficie-marca);
    color: var(--color-crema);
    font-weight: 600;
}

.paginacion__pagina--actual:hover { background: var(--color-primario-hover); }

.paginacion__elipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    min-height: 2.25rem;
    color: var(--color-texto-suave);
}

/* --- Estado vacío (§5.6) --------------------------------------------------
 * Texto plano y una acción. Sin ilustración ni frase simpática. */

.estado-vacio {
    text-align: center;
    padding: 2.5rem var(--espacio);
    color: var(--color-texto-suave);
    background: var(--color-superficie);
    border: 1px solid var(--color-borde);
    border-radius: var(--radio);
}

.estado-vacio p { margin: 0 0 var(--espacio); }

/* --- Calendario de la agenda (issue #10) ----------------------------------
 * Modificador de `.tabla`: mismo envoltorio con scroll horizontal en celular
 * (no hay versión "tarjeta" para esta tabla tampoco, mismo criterio del
 * §5.4), pero las celdas envuelven texto en vez de cortarlo — una celda es
 * un día con varias citas, no un dato de una columna. `table-layout: fixed`
 * + 1/7 por columna para que las siete queden parejas sea cual sea el largo
 * del contenido de cada una. */

.tabla--calendario { table-layout: fixed; min-width: 700px; }

.tabla--calendario th,
.tabla--calendario td {
    width: 14.28%;
    white-space: normal;
    vertical-align: top;
}

.calendario__celda { height: 6rem; }

/* Días del mes vecino que completan la primera y la última semana: se leen,
 * no se ocultan —siguen enlazando a su ficha si tienen algo— pero en gris
 * para que la cuadrícula lea "esta semana pertenece a otro mes" de un
 * vistazo. */
.calendario__celda--fuera { color: var(--color-texto-suave); background: var(--color-fondo); }

.calendario__celda--hoy { background: var(--color-info-fondo); }

.calendario__fecha { margin: 0 0 0.3rem; font-weight: 600; }

/* El día de hoy no se distingue solo por el fondo azul: el texto "Hoy" es
 * la señal que le queda a quien no distingue ese azul del resto (§8). */
.calendario__etiqueta-hoy {
    margin-left: 0.3rem;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--color-info);
    text-transform: uppercase;
}

.calendario__items { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }

.calendario__item { font-size: 0.85rem; line-height: 1.3; }
.calendario__item a { display: block; }
.calendario__item .badge { margin-top: 0.15rem; }

/* --- Selector de modo de la agenda (lista/semana/mes, issue #10) --------- */

.modo-agenda {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espacio-sm) var(--espacio);
    align-items: center;
    margin-bottom: var(--espacio);
}

.modo-agenda__modos,
.modo-agenda__rango { display: flex; flex-wrap: wrap; gap: var(--espacio-sm); align-items: center; }

/* Los botones del selector no son la acción primaria de la pantalla: sin el
 * piso de 9.5 rem de `.boton` caben los tres en una fila también en celular. */
.modo-agenda__modos .boton,
.modo-agenda__rango .boton { min-width: auto; padding: 0.5rem 0.9rem; }

/* El modo activo no se marca solo con `aria-current`: el fondo y el peso de
 * la letra son la señal visual, y las dos —no solo el color— para quien no
 * distingue el gris de fondo del blanco de la página. */
.modo-agenda__activo { background: var(--color-fondo); font-weight: 700; border-color: var(--color-borde-campo); }

.modo-agenda__titulo { margin: 0; font-weight: 600; }

/* --- Movimiento ----------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
