/* Sistema tipográfico oficial "Clara y cálida". Las fuentes viven dentro de la aplicación:
   recepción no pierde legibilidad si se corta Internet y ninguna pantalla depende de un CDN.

   En WOFF2 y no en TTF, que es como estaban hasta el 03-08. El mismo archivo pesa entre un 60 %
   y un 74 % menos —2,1 MB de tipografías quedaron en 744 KB— porque WOFF2 trae la compresión
   Brotli adentro, y los ejes variables sobreviven intactos. El formato es de 2018 y no hay
   navegador en uso que no lo entienda, así que no queda un TTF de respaldo: sería peso muerto
   en el repositorio y una segunda copia que mantener sincronizada.

   Por venir ya comprimidos, estos archivos quedan fuera de `server.compression`: volver a
   comprimirlos gasta CPU del servidor para no ahorrar un byte. */
@font-face {
    font-family: "Nunito Sans";
    src: url("/fonts/nunito-sans-variable-abc455272596a9f52975ebdc73338d1c.woff2") format("woff2");
    font-style: normal;
    font-weight: 200 1000;
    font-stretch: 75% 125%;
    font-display: swap;
}

@font-face {
    font-family: "Nunito Sans";
    src: url("/fonts/nunito-sans-italic-variable-034231bc8baa7bdae0bf1aa829d88693.woff2") format("woff2");
    font-style: italic;
    font-weight: 200 1000;
    font-stretch: 75% 125%;
    font-display: swap;
}

@font-face {
    font-family: "Source Sans 3";
    src: url("/fonts/source-sans-3-variable-61db60cc19bc3d38ccc670fda4139fa4.woff2") format("woff2");
    font-style: normal;
    font-weight: 200 900;
    font-display: swap;
}

@font-face {
    font-family: "Source Sans 3";
    src: url("/fonts/source-sans-3-italic-variable-4419d63052fc56006f68d8aa299aa52d.woff2") format("woff2");
    font-style: italic;
    font-weight: 200 900;
    font-display: swap;
}

:root {
    /* Paleta oficial "Menta amable". Ver docs/identidad-visual.md. */
    color-scheme: light;
    --turquesa: #2aae91;
    --turquesa-profundo: #16745f;
    --sobre-turquesa: #102f2b;
    --rojo: #d95663;
    --rojo-profundo: #9d2f3e;
    --texto: #244643;
    --texto-tenue: #58716d;
    --fondo: #f7fbf5;
    --superficie: #ffffff;
    --barra: #ffffff;
    --campo: #ffffff;
    --menta-suave: #e1f2e8;
    --rojo-suave: #fceced;
    /* Ámbar de "revisar": lo que hay que mirar pero no es urgencia. Nació con la caja cerrada
       con diferencia —un faltante que el admin tiene que pescar entre las cuadradas verdes, y que
       el gris de "anulado" escondía—. Va entre el verde de "en orden" y el rojo de "ahora". Ver
       `.etiqueta.revisar`. */
    --ambar-suave: #fbeccb;
    --ambar-profundo: #8a5a12;
    --borde: #caddd8;
    --blanco: #ffffff;

    /* Tinta sobre papel. **No se redefinen en Grafito clínico, y eso es justamente el punto**: el
       membrete de una hoja se dibuja sobre blanco en los dos temas porque es lo único de la
       aplicación cuyo destino es la impresora. Ver `.hoja .membrete`. */
    --tinta-papel: #244643;
    --tinta-papel-tenue: #58716d;

    --fuente-marca: "Nunito Sans", system-ui, sans-serif;
    --fuente-interfaz: "Source Sans 3", system-ui, sans-serif;

    /* Tintas del símbolo. Van aparte de los tokens de interfaz a propósito: el logo no
       sigue al color de un botón, sigue a la marca. Ver "Uso en la aplicación" en
       docs/identidad-visual.md. */
    --marca-turquesa: #2aae91;
    --marca-tinta: #244643;
    --marca-rojo: #d95663;

    --sombra: 0 1px 2px rgba(36, 70, 67, 0.06);
}

/* "Grafito clínico": modo oscuro oficial. Puede forzarse con data-tema="oscuro". */
:root[data-tema="oscuro"] {
    color-scheme: dark;
    --turquesa: #55cbb2;
    --turquesa-profundo: #70dbc3;
    --sobre-turquesa: #102e29;
    --rojo: #f2737d;
    --rojo-profundo: #ff9ba4;
    --texto: #f1f6f4;
    --texto-tenue: #b4c3bf;
    --fondo: #171c1c;
    --superficie: #222b2a;
    --barra: #1b2322;
    --campo: #1b2423;
    --menta-suave: #293b37;
    --rojo-suave: #3a2529;
    --ambar-suave: #3a3120;
    --ambar-profundo: #f2c879;
    --borde: #3a4947;

    --marca-turquesa: #55cbb2;
    --marca-tinta: #f1f6f4;
    --marca-rojo: #f2737d;

    --sombra: none;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-tema="claro"]) {
        color-scheme: dark;
        --turquesa: #55cbb2;
        --turquesa-profundo: #70dbc3;
        --sobre-turquesa: #102e29;
        --rojo: #f2737d;
        --rojo-profundo: #ff9ba4;
        --texto: #f1f6f4;
        --texto-tenue: #b4c3bf;
        --fondo: #171c1c;
        --superficie: #222b2a;
        --barra: #1b2322;
        --campo: #1b2423;
        --menta-suave: #293b37;
        --rojo-suave: #3a2529;
        --ambar-suave: #3a3120;
        --ambar-profundo: #f2c879;
        --borde: #3a4947;

        --marca-turquesa: #55cbb2;
        --marca-tinta: #f1f6f4;
        --marca-rojo: #f2737d;

        --sombra: none;
    }
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--fuente-interfaz);
    font-optical-sizing: auto;
    color: var(--texto);
    background: var(--fondo);
    line-height: 1.5;
}

main {
    max-width: 48rem;
    margin: 0 auto;
    padding: 2rem 1.25rem;
}

h1,
h2,
h3 {
    font-family: var(--fuente-marca);
}

h1 {
    color: var(--turquesa-profundo);
    margin-bottom: 0.25rem;
}

section {
    margin-top: 2rem;
    padding: 1.25rem;
    background: var(--superficie);
    border-radius: 0.5rem;
    border: 1px solid var(--borde);
    box-shadow: var(--sombra);
}

/* El margen propio del h2 se suma al padding de la sección y deja más aire arriba que a los
   lados. Se nota sobre todo cuando hay varias secciones seguidas, que es casi siempre. */
section > :first-child {
    margin-top: 0;
}

section > :last-child {
    margin-bottom: 0;
}

main.angosto {
    max-width: 24rem;
}

/* Los enlaces del contenido: sin esto quedan en el azul del navegador, que no es de la
   paleta y sobre Grafito clínico casi no se lee. */
a {
    color: var(--turquesa-profundo);
}

/* Un foco visible es lo que permite operar la pantalla con el teclado, y recepción cobra
   con las dos manos ocupadas. El anillo se dibuja separado del borde del campo para que
   se note también sobre los inputs. */
:focus-visible {
    outline: 2px solid var(--turquesa);
    outline-offset: 2px;
    border-radius: 0.2rem;
}

/* Nace fuera de la pantalla y baja al recibir el foco: quien navega con teclado lo
   encuentra en el primer tabulador, y quien usa el mouse no lo ve nunca. */
.saltar {
    position: absolute;
    top: -3rem;
    left: 0.75rem;
    z-index: 20;
    padding: 0.5rem 0.9rem;
    border-radius: 0 0 0.3rem 0.3rem;
    background: var(--turquesa);
    color: var(--sobre-turquesa);
    text-decoration: none;
    transition: top 0.12s ease;
}

.saltar:focus {
    top: 0;
}

/* El <main> recibe el foco al saltar, pero es un contenedor: dibujarle el anillo alrededor
   de toda la pantalla no señala nada. */
main:focus-visible {
    outline: none;
}

.barra {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 0.6rem 1.25rem;
    background: var(--barra);
    border-bottom: 1px solid var(--borde);
    font-size: 0.9rem;
}

/* El símbolo y el nombre son un solo enlace al inicio. Nunito Sans compone el nombre como
   logotipo tipográfico mientras se definen las composiciones horizontal y vertical. */
.marca {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--fuente-marca);
    font-weight: 700;
    letter-spacing: 0.01em;
}

.marca svg {
    width: 1.75rem;
    height: 1.75rem;
    flex: none;
}

.barra .grupo {
    display: flex;
    align-items: center;
    gap: 1rem;
}

/* En una pantalla angosta el nombre largo se parte en tres líneas y estira la barra sobre
   el contenido. Lo que se va es el rol: se deduce de lo que la pantalla deja hacer, mientras
   que el nombre es lo único que distingue una sesión de otra en un computador compartido. */
@media screen and (max-width: 40rem) {
    .barra {
        flex-wrap: wrap;
        row-gap: 0.4rem;
    }

    .barra .sesion {
        white-space: nowrap;
    }

    .barra .rol {
        display: none;
    }
}

label {
    display: block;
    margin-top: 0.9rem;
    font-size: 0.9rem;
}

/* En el computador compartido del mesón la contraseña puede comprobarse sin quedar expuesta
   después: el ojo vive dentro del campo y solo la muestra mientras el control sigue presionado.
   Sin prefijo `.login` a propósito: el mismo componente viste el login, los tres campos de
   "cambiar mi contraseña", la contraseña inicial del alta y el reseteo —la razón del mesón
   compartido vale igual en todos—, y el JS engancha por clase, no por pantalla. */
.contrasena-momentanea {
    position: relative;
}

.contrasena-momentanea input {
    padding-right: 3rem;
}

/* Sin fondo ni borde: heredado de `button`, el turquesa dibujaba un cuadrado que pesaba más que
   el campo mismo y competía con «Entrar», que es el único botón que la pantalla quiere ofrecer.
   La caja de 2.15rem se conserva aunque no se vea, porque es la que se puede apretar con el
   pulgar. */
.revelar-contrasena {
    position: absolute;
    right: 0.25rem;
    bottom: 0.25rem;
    display: grid;
    width: 2.15rem;
    height: 2.15rem;
    margin: 0;
    padding: 0.45rem;
    place-items: center;
    background: none;
    touch-action: manipulation;
    user-select: none;
}

.revelar-contrasena[hidden] {
    display: none;
}

/* El PNG entregado es blanco. Usarlo como máscara deja que el ojo tome la tinta del texto y no
   la suya, que es lo que lo mantiene legible sobre el campo en Menta amable y en Grafito
   clínico. Va en la tinta tenue: acompaña al campo, no lo encabeza. */
.icono-cateye {
    width: 100%;
    height: 100%;
    background: var(--texto-tenue);
    -webkit-mask: url("/img/interfaz/cateye.png") center / contain no-repeat;
    mask: url("/img/interfaz/cateye.png") center / contain no-repeat;
    pointer-events: none;
    transition: background-color 0.12s ease;
}

/* Sin fondo que oscurecer, el `brightness()` que `button` aplica al pasar y al apretar no cambia
   nada —y sobre el campo claro aclararía el ojo, que es al revés de lo que un hover debe hacer.
   La respuesta pasa a ser el color de la tinta. */
.revelar-contrasena:hover,
.revelar-contrasena:active {
    filter: none;
}

.revelar-contrasena:hover .icono-cateye {
    background: var(--texto);
}

/* Mientras la contraseña está a la vista el ojo se pone turquesa. Se cuelga del `type` del campo
   y no de `:active` a propósito: es el mismo estado que ve quien la reveló con la barra
   espaciadora, y `:active` con el teclado no es fiable entre navegadores. */
.contrasena-momentanea input[type="text"] + .revelar-contrasena .icono-cateye {
    background: var(--turquesa-profundo);
}

button {
    margin-top: 1.25rem;
    padding: 0.55rem 1.1rem;
    border: 0;
    border-radius: 0.3rem;
    background: var(--turquesa);
    color: var(--sobre-turquesa);
    font: inherit;
    cursor: pointer;
    transition: filter 0.12s ease, background-color 0.12s ease;
}

button:hover {
    filter: brightness(1.06);
}

button:active {
    filter: brightness(0.94);
}

/* Un botón apagado que se ve igual que uno vivo se clickea igual, y el usuario concluye que
   la pantalla no responde. Los hay: los formularios dentro de un fieldset deshabilitado. */
button:disabled,
button:disabled:hover {
    filter: none;
    opacity: 0.5;
    cursor: not-allowed;
}

button.enlace {
    margin: 0;
    padding: 0;
    background: none;
    color: var(--turquesa-profundo);
    text-decoration: underline;
}

/* brightness() sobre una superficie casi blanca o sobre nada no cambia nada: el botón
   secundario y el de texto necesitan su propio hover o quedan sin respuesta al pasar. */
button.secundario:hover {
    filter: none;
    background: var(--menta-suave);
}

button.enlace:hover {
    filter: none;
    color: var(--texto);
}

/* Anular es irreversible. Va en rojo desde antes de tocarlo, no después en un mensaje. */
button.enlace.riesgo {
    color: var(--rojo-profundo);
}

/* "Salir" es un botón porque cerrar sesión es un POST, pero en la barra es un enlace más:
   sin esto queda turquesa y subrayado entre cuatro enlaces que no lo están. */
.barra button.enlace {
    color: var(--texto);
    text-decoration: none;
}

.barra button.enlace:hover {
    color: var(--turquesa-profundo);
    text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Mensajes del sistema
   --------------------------------------------------------------------------- */

/* "Guardado." como línea suelta de texto se pierde: después de un POST la pantalla se
   vuelve a dibujar entera y el ojo va al formulario, no a un párrafo del mismo tamaño y
   peso que todo lo demás. El mensaje va en un cuadro con icono para que se lea primero.

   El icono se dibuja con una máscara y no con un <img> ni un <svg> en la plantilla: así
   toma el color del mensaje —turquesa o rojo, claro u oscuro— sin repetir el mismo bloque
   de marcado en las veintitantas pantallas que muestran uno.

   El relleno izquierdo lo hace `padding` y no `flex`, a propósito: varios mensajes traen
   un <strong> adentro, y en un contenedor flex ese <strong> se convertiría en columna
   aparte en vez de seguir el párrafo. */
.aviso,
.error {
    position: relative;
    margin: 1rem 0;
    padding: 0.7rem 0.9rem 0.7rem 2.5rem;
    border: 1px solid;
    border-radius: 0.5rem;
    animation: mensaje-aparece 0.2s ease-out;
}

.aviso::before,
.error::before {
    content: "";
    position: absolute;
    top: 0.78rem;
    left: 0.85rem;
    width: 1.15rem;
    height: 1.15rem;
    background-color: currentColor;
    -webkit-mask: var(--icono) center / contain no-repeat;
    mask: var(--icono) center / contain no-repeat;
}

.aviso {
    color: var(--turquesa-profundo);
    background: var(--menta-suave);
    border-color: var(--turquesa);
    --icono: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.2'/%3E%3Cpath d='m7.7 12.3 3 3 5.6-6.3'/%3E%3C/svg%3E");
}

.error {
    color: var(--rojo-profundo);
    background: var(--rojo-suave);
    border-color: var(--rojo);
    --icono: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.2'/%3E%3Cpath d='M12 7.3v5.3'/%3E%3Ccircle cx='12' cy='16.4' r='1.05' fill='black' stroke='none'/%3E%3C/svg%3E");
}

/* Mismo tono que el aviso, otro icono. La paleta oficial tiene dos colores de señal —menta
   y rojo— y un tercero inventado para "ojo con esto" diría menos de lo que ensuciaría; el
   visto y la i separan "quedó hecho" de "hay algo que saber" sin agregar color. */
.aviso.info {
    --icono: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.2'/%3E%3Cpath d='M12 11.3v5.3'/%3E%3Ccircle cx='12' cy='7.6' r='1.05' fill='black' stroke='none'/%3E%3C/svg%3E");
}

/* El mensaje llega con la página ya cargada, no aparece mientras se mira: sin el
   desplazamiento corto no hay nada que delate que algo cambió en la pantalla. */
@keyframes mensaje-aparece {
    from {
        opacity: 0;
        transform: translateY(-0.3rem);
    }
}

.tenue {
    color: var(--texto-tenue);
}

/* Microchips, RUT, folios y códigos: se leen dígito a dígito y se comparan con lo que trae
   el tutor en un papel. Antes iban en la monoespaciada del sistema, que es una cuarta
   familia fuera del sistema tipográfico oficial; Source Sans 3 con cifras tabulares alinea
   igual las columnas y el espaciado extra basta para no perder el dígito. */
.mono {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
}

/* La fila envuelve, y hasta F3 no lo hacía. Medida en un teléfono de 375 px, la barra de ADMIN
   ya se salía —526 px de enlaces— y **arrastraba la página entera de lado**: no era que la barra
   se viera apretada, era que toda la pantalla se corría al deslizar. El séptimo enlace lo llevó a
   579 px y lo hizo evidente al medirlo. Dos renglones de barra es lo que el `flex-wrap` de
   `.barra` ya hacía con el nombre de la sesión; esto es lo mismo para los enlaces. */
.barra nav {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
}

.barra a {
    color: var(--texto);
    text-decoration: none;
}

.barra a:hover {
    color: var(--turquesa-profundo);
}

form.en-linea,
.barra form {
    display: inline;
}

/* El campo, en una sola regla y por descarte.

   Antes eran dos listas de tipos —`text, search, tel, date, number` acá, `email, password` cien
   líneas más arriba con las mismas nueve declaraciones copiadas— y había que acordarse de agregar
   el tipo nuevo a la correcta. Nadie se acordó: `time` no estaba en ninguna de las dos, y el campo
   de hora de la agenda salía a la pantalla como lo dibuja el navegador —74×24 px contra los 192×42
   de sus vecinos, borde gris de 2 px, monoespaciada de 13 px y un fondo que no está en la paleta—,
   justo al lado del campo de día, que sí estaba en la lista.

   Por descarte falla del lado correcto: el tipo que se use mañana queda vestido sin que nadie lo
   agregue, y lo que no es una caja de escribir —las casillas, las opciones, el archivo, que tienen
   sus propias reglas— se nombra una vez acá. Los `submit` y `reset` no existen hoy en ninguna
   plantilla —todo botón es un `<button>`— y se descartan igual, porque el día que aparezca uno el
   error sería mudo: se vería como un campo en vez de como un botón.

   La lista va dentro de un solo `:not()` a propósito. Escrita como `:not(a):not(b)…` la regla sube
   a especificidad 0,4,1 y le gana a `.cantidad input`, que es la que angosta a 4,5rem la cantidad
   dentro de la tabla del presupuesto; con la lista adentro, `:not()` pesa lo que su argumento más
   específico y el conjunto se queda en 0,1,1, que es lo que pesaban las dos listas viejas. */
input:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"], [type="submit"], [type="reset"], [type="button"], [type="image"], [type="range"], [type="color"]),
select,
textarea {
    width: 100%;
    padding: 0.5rem;
    margin-top: 0.25rem;
    border: 1px solid var(--borde);
    color: var(--texto);
    background: var(--campo);
    border-radius: 0.3rem;
    font: inherit;
}

textarea {
    resize: vertical;
}

/* El campo tiene que decir dónde está el cursor y cuál está bajo el puntero. Sin esto, un
   formulario largo —la ficha nueva son catorce campos— es una grilla de rectángulos
   iguales, y el anillo de foco es la única pista. */
input:hover:not(:disabled),
select:hover:not(:disabled),
textarea:hover:not(:disabled) {
    border-color: var(--texto-tenue);
}

input:focus,
select:focus,
textarea:focus {
    border-color: var(--turquesa);
}

/* La marca de una casilla y el punto de una opción los dibuja el sistema, y sin decirle nada los
   dibuja en el azul de Windows: el único color que quedaba en pantalla sin salir de la paleta.
   Estaba resuelto solo en `.casilla`, que es la del receptor del cobro y una de ocho —las otras
   siete son los tres tipos de preventivo, los roles del usuario, el estado de la ficha, los
   inactivos de los dos listados, y el exento y el pago previo del servicio—, así que la aplicación
   tenía una casilla verde y siete grupos azules.

   Va en todos los controles y no solo en las casillas por el panel del calendario y el del reloj.
   Ese panel **no es parte de la página**: lo dibuja el navegador aparte, y ni su fondo gris, ni sus
   esquinas rectas, ni su tipografía, ni los enlaces «Borrar» y «Hoy» se pueden alcanzar desde acá
   —no existe pseudo-elemento para ellos—. Las dos únicas cosas que cruzan esa frontera son
   `color-scheme`, que es lo que hace que el panel salga oscuro y no blanco, y `accent-color`.

   **`accent-color` NO llega al día seleccionado dentro del panel, comprobado a mano el 17-08**: la
   propiedad llega a los campos —turquesa en los dos temas, se ve en las casillas y los radios— pero
   Chromium dibuja el día resaltado de su date-picker en su azul y no la respeta ahí adentro. Se deja
   puesta igual, porque es la única palanca que existe y no cuesta nada, y porque el día que Chromium
   la respete el turquesa aparece solo. El panel no tiene otro arreglo desde CSS; el único camino
   sería un calendario propio en JavaScript, descartado por la decisión de controles nativos del
   14-08.

   **Y no todo Chromium la respeta igual, comprobado el 25-08 durante la §25**: en Chrome el radio
   marcado sale turquesa y en Opera sale oscuro, con la misma hoja y el mismo tema. No se arregla
   desde acá —pintar el control propio exige `appearance: none` y volver a dibujar el punto, que es
   la decisión de controles nativos otra vez—, y no es un defecto de la pantalla: queda anotado para
   que la próxima vez que alguien vea un radio apagado sepa que lo primero que hay que preguntar es
   en qué navegador está mirando. */
input,
select,
textarea {
    accent-color: var(--turquesa);
}

/* ---------------------------------------------------------------------------
   Lo que el navegador sigue dibujando adentro del campo

   Vestir la caja no alcanza. Dentro de un `date`, un `time`, un `number` y un `search` el navegador
   pone controles propios —el calendario, el reloj, las flechitas de subir y bajar, la equis de
   limpiar— y los dibuja como el sistema operativo: gris de Windows, del tamaño que él quiera,
   con su propia idea de dónde ponerlos. Es el mismo problema que tenía el campo de archivo antes
   de vestirlo, y se resuelve con el mismo criterio: se viste lo que se puede vestir, y solo se
   apaga lo que no le quita nada a nadie.
   --------------------------------------------------------------------------- */

/* El calendario y el reloj, dibujados con una máscara igual que el ojo del login y la flecha del
   campo de archivo: así toman la tinta del tema —tenue en Menta amable, tenue en Grafito clínico—
   en vez del gris del sistema, que en oscuro quedaba casi invisible sobre el campo.

   El `background` va en atajo y no en `background-color` a propósito: el atajo borra de paso el
   `background-image` con el que el navegador pinta su propio glifo, que si no se cuela por debajo
   de la máscara y se ven los dos dibujos encima. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    margin-left: 0.35rem;
    background: var(--texto-tenue);
    cursor: pointer;
    transition: background-color 0.12s ease;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover {
    background: var(--texto);
}

input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    --icono-calendario: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.4' y='5.4' width='17.2' height='15.2' rx='2.2'/%3E%3Cpath d='M3.4 10.2h17.2'/%3E%3Cpath d='M8.2 3.4v3.6'/%3E%3Cpath d='M15.8 3.4v3.6'/%3E%3C/svg%3E");
    -webkit-mask: var(--icono-calendario) center / contain no-repeat;
    mask: var(--icono-calendario) center / contain no-repeat;
}

input[type="time"]::-webkit-calendar-picker-indicator {
    --icono-reloj: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.6'/%3E%3Cpath d='M12 7.4V12l3.2 1.9'/%3E%3C/svg%3E");
    -webkit-mask: var(--icono-reloj) center / contain no-repeat;
    mask: var(--icono-reloj) center / contain no-repeat;
}

/* Los dos píxeles del campo de fecha. Medido: día y hora salían de 44 px de alto contra los 42 de
   todos los demás campos, y en la fila «Día · Hora» del formulario de la agenda eso deja los dos
   controles desalineados respecto de «Duración», que va al lado. No es el icono ni la tipografía
   —quitando el indicador el campo seguía en 44—: es el relleno vertical de un píxel que el
   navegador le pone por dentro a la caja donde viven los trozos editables. A cero quedan los 42
   exactos y el contenido no se recorta. */
input::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
}

/* El trozo que se está editando —el día, el mes, la hora— lo resalta el navegador con el azul de
   selección del sistema. Es lo primero que se ve al tabular hasta el campo, y era lo más ajeno de
   la pantalla: un rectángulo azul dentro de un formulario que no tiene azul en ninguna parte.

   No apaga ningún foco: el resalte *es* el indicador de qué trozo recibe las teclas, y el campo
   entero además cambia su borde a turquesa con `input:focus`. Solo se le cambia el color. */
input::-webkit-datetime-edit-year-field:focus,
input::-webkit-datetime-edit-month-field:focus,
input::-webkit-datetime-edit-day-field:focus,
input::-webkit-datetime-edit-hour-field:focus,
input::-webkit-datetime-edit-minute-field:focus,
input::-webkit-datetime-edit-second-field:focus,
input::-webkit-datetime-edit-ampm-field:focus {
    border-radius: 0.15rem;
    background: var(--turquesa);
    color: var(--sobre-turquesa);
}

/* Las flechitas del campo numérico. Es lo único de este bloque que se apaga en vez de vestirse, y
   no por pereza: son un control que el navegador no deja redibujar —se le puede cambiar el alto y
   poco más— así que vestirlo es imposible y dejarlo es tener un objeto de Windows dentro del
   campo.

   Apagarlo no le quita el paso a nadie. Las flechas del teclado siguen subiendo y bajando el valor
   igual que antes, que es el camino que usa quien no puede apuntar con el mouse; el cuadrito de
   siete píxeles era justamente el que no servía ni con el dedo en el mesón. Y ninguno de los
   números de la aplicación se llega apretando: la duración va de 5 a 720, el precio y la cantidad
   se escriben, y el peso tiene tres decimales. */
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}

/* La equis que limpia la búsqueda, con la misma máscara y la misma tinta que el calendario. Se
   viste y no se apaga porque acá sí hace falta: los siete buscadores de la aplicación —fichas,
   usuarios, catálogo, la cola de cobros, la ficha de la hora, el detalle del presupuesto y el
   correo en el registro de accesos— se usan escribiendo, borrando y volviendo a escribir, y vaciar
   el campo de un clic es más rápido que dejar apretado el retroceso. */
input[type="search"]::-webkit-search-cancel-button {
    --icono-limpiar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.1' stroke-linecap='round'%3E%3Cpath d='M6.8 6.8 17.2 17.2'/%3E%3Cpath d='M17.2 6.8 6.8 17.2'/%3E%3C/svg%3E");
    -webkit-appearance: none;
    appearance: none;
    width: 0.95rem;
    height: 0.95rem;
    margin-left: 0.35rem;
    background: var(--texto-tenue);
    -webkit-mask: var(--icono-limpiar) center / contain no-repeat;
    mask: var(--icono-limpiar) center / contain no-repeat;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

input[type="search"]::-webkit-search-cancel-button:hover {
    background: var(--texto);
}

/* El gris por defecto del navegador no está en la paleta y en varios se lee casi tanto como
   el valor escrito, que es justo lo que un ejemplo no debe hacer. */
::placeholder {
    color: var(--texto-tenue);
    opacity: 1;
}

fieldset {
    border: 0;
    padding: 0;
    margin: 0;
    min-width: 0;
}

fieldset:disabled {
    opacity: 0.75;
}

.campos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0 1rem;
}

.campos .ancho {
    grid-column: 1 / -1;
}

.acciones {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 1.5rem;
}

/* Los botones traen su propio margen superior para cuando cierran un formulario. Dentro de
   una fila de acciones ese margen los baja respecto de los enlaces que tienen al lado, y la
   fila queda escalonada. */
.acciones > button,
.acciones > a.boton {
    margin-top: 0;
}

/* El formato de papel acompaña al botón porque modifica exactamente la impresión que ese botón
   abre. Es un control nativo y compacto: los cuatro nombres caben sin inventar otro componente,
   y su valor se comparte entre las hojas desde `imprimir.js`. */
.selector-papel {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0;
    color: var(--texto-tenue);
    font-size: 0.9rem;
}

.selector-papel select {
    width: auto;
    min-width: 8.5rem;
    margin-top: 0;
}

.titulo-con-accion {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
}

/* Una fila de acciones que acompaña a un título no arrastra el margen que lleva cuando cierra
   un formulario: acá va a la altura del h2 y no debajo. */
.titulo-con-accion .acciones,
.titulo-con-accion .acciones button {
    margin-top: 0;
}

/* En un teléfono el título y sus botones no caben en la misma fila: "Vacunas y
   desparasitaciones" ya ocupa medio ancho y "Desparasitación" se salía del borde de la tarjeta.
   Bajo 40rem se apilan —título arriba, acciones debajo con el ancho entero— y los botones vuelven
   a caber en una fila que envuelve sola en vez de aplastarse en una columna a la derecha. */
@media screen and (max-width: 40rem) {
    .titulo-con-accion {
        flex-direction: column;
        align-items: stretch;
    }

    /* Apilado, los botones quedan pegados al `dl` que sigue: un `dl` no trae margen propio —en la
       fila de escritorio el aire salía del margen inferior del h2 que tenía al lado, y apilado ese
       margen queda arriba de los botones, no debajo—. Solo le pasa al `dl`: al resto de las
       tarjetas lo que sigue al título es un `<p>` o una tabla, que traen su propio aire. */
    .titulo-con-accion + dl {
        margin-top: 1rem;
    }
}

button.secundario {
    background: var(--superficie);
    color: var(--turquesa-profundo);
    border: 1px solid var(--turquesa);
}

a.boton {
    display: inline-block;
    margin-top: 0.75rem;
    padding: 0.55rem 1.1rem;
    border-radius: 0.3rem;
    background: var(--turquesa);
    color: var(--sobre-turquesa);
    text-decoration: none;
    transition: filter 0.12s ease;
}

/* Se ve como un botón: tiene que responder como un botón. */
a.boton:hover {
    filter: brightness(1.06);
}

a.boton:active {
    filter: brightness(0.94);
}

/* ---------------------------------------------------------------------------
   El campo de archivo

   Es el único control que el navegador dibuja entero a su manera: un botón gris de sistema y un
   "Sin archivos seleccionados" con la tipografía del sistema operativo, dos cosas que no se
   parecen a nada del resto de la aplicación. Sale en tres pantallas —el logo de la marca, y la
   firma y el timbre en la cuenta propia y en la ficha del usuario— y en las tres es lo primero
   que se ve de la sección, así que es lo que fija el tono.

   La caja exterior imita al campo de texto —mismo borde, mismo fondo, mismo hover, mismo foco—
   porque en el formulario ocupa el lugar de uno.

   Adentro se dibuja en dos capas. Sin JavaScript se viste el botón nativo con
   `::file-selector-button`: conserva el foco, el teclado y el anuncio del lector de pantalla sin
   una línea de código, y lo único que no se le puede cambiar son las palabras. Con JavaScript,
   archivo-elegido.js esconde el control y muestra el `label`, que sí dice "Elegir imagen" y
   escribe al lado el nombre y el peso de lo elegido. El peso importa: el tope son 30 MB
   (`application.yml`) y la clínica sube por la conexión que tiene, así que verlo antes de apretar
   "Subir" evita la espera larga que termina en un error.
   --------------------------------------------------------------------------- */
.campo-archivo {
    /* Lo pide el input escondido: `.visualmente-oculto` es absoluto, y sin un ancestro que lo
       sujete se va al origen de la página. Ahí pasan dos cosas, y la segunda es la grave:
       tabular hasta el campo salta el scroll al encabezado, y el globo de "Completa este
       campo" —el input es `required`— sale colgando del inicio de la página en vez de del
       campo que falta. Recortado a 1px sigue siendo enfocable, que es lo que permite que el
       navegador lo muestre; escondido con `display: none` Chrome no lo muestra en ninguna
       parte y el formulario se queda sin enviar sin decir por qué. */
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.25rem;
    padding: 0.35rem;
    border: 1px solid var(--borde);
    border-radius: 0.3rem;
    background: var(--campo);
}

/* Lo mismo que `input:hover` e `input:focus` hacen con los demás campos, escrito aparte porque
   acá el campo es un <div> y quien recibe el foco es un hijo suyo. */
.campo-archivo:hover {
    border-color: var(--texto-tenue);
}

.campo-archivo:focus-within {
    border-color: var(--turquesa);
}

/* Mientras el archivo viene arrastrado encima, el campo se enciende: sin respuesta no hay cómo
   saber si soltarlo ahí sirve de algo, y el que suelta al lado pierde el archivo en una pestaña
   nueva. */
.campo-archivo.arrastrando {
    border-color: var(--turquesa);
    background: var(--menta-suave);
}

/* Contorno y no relleno turquesa, por lo mismo que `.boton-tabla`: debajo está "Subir el logo" o
   "Reemplazar", que es el que manda el formulario. Dos botones sólidos seguidos compiten y el ojo
   elige mal —elegir un archivo no sube nada—. */
.boton-archivo {
    display: inline-flex;
    align-items: center;
    flex: none;
    gap: 0.45rem;
    margin: 0;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--turquesa);
    border-radius: 0.25rem;
    background: var(--superficie);
    color: var(--turquesa-profundo);
    font: inherit;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

/* Nace oculto y lo muestra archivo-elegido.js, igual que el ojo del login: sin ese script el
   control nativo sigue trayendo su propio botón y se verían dos para lo mismo. El `display`
   propio obliga a apagarlo a mano. */
.boton-archivo[hidden] {
    display: none;
}

.boton-archivo:hover {
    background: var(--menta-suave);
}

/* El foco lo tiene el input, que está escondido: sin esto, tabular hasta acá no se ve. */
.campo-archivo:focus-within .boton-archivo {
    outline: 2px solid var(--turquesa);
    outline-offset: 2px;
}

/* La flecha se dibuja con una máscara, igual que el icono de los mensajes: así toma la tinta del
   botón —turquesa en Menta amable, más clara en Grafito clínico— sin repetir un <svg> en cada
   plantilla. El negro del trazo no se pinta nunca, solo define la forma. */
.boton-archivo::before {
    --icono-subir: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15.2V4.3'/%3E%3Cpath d='m7.8 8.5 4.2-4.2 4.2 4.2'/%3E%3Cpath d='M4.6 15.2v3.4c0 .9.7 1.6 1.6 1.6h11.6c.9 0 1.6-.7 1.6-1.6v-3.4'/%3E%3C/svg%3E");
    content: "";
    flex: none;
    width: 1.05rem;
    height: 1.05rem;
    background-color: currentColor;
    -webkit-mask: var(--icono-subir) center / contain no-repeat;
    mask: var(--icono-subir) center / contain no-repeat;
}

/* El nombre del archivo se recorta con puntos suspensivos y no parte la caja en dos líneas: un
   escaneo se llama "WhatsApp Image 2026-08-12 at 19.44.31.jpeg" y estiraría el campo a lo alto
   cada vez. */
.archivo-elegido {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--texto-tenue);
    font-size: 0.9rem;
}

/* Mientras no hay nada elegido el texto es una indicación; con archivo pasa a ser el dato del
   formulario y se lee a tinta entera. */
.archivo-elegido.elegido {
    color: var(--texto);
}

/* La capa sin JavaScript: el control nativo, ya dentro de la caja, aportando solo su botón y el
   nombre que escribe el navegador. Sin borde ni fondo propios porque la caja ya los puso. */
.campo-archivo input[type="file"] {
    flex: 1;
    min-width: 0;
    border: 0;
    background: none;
    color: var(--texto-tenue);
    font: inherit;
    font-size: 0.9rem;
}

/* Va en su propia regla y no junto a `.boton-archivo` aunque se vistan igual: un navegador que no
   entienda `::file-selector-button` descarta la regla entera, y de paso se llevaría el botón que
   sí se dibuja siempre. */
.campo-archivo input[type="file"]::file-selector-button {
    margin-right: 0.75rem;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--turquesa);
    border-radius: 0.25rem;
    background: var(--superficie);
    color: var(--turquesa-profundo);
    font: inherit;
    /* El input va en 0.9rem porque ahí escribe el navegador el nombre del archivo, que es
       secundario; el botón vuelve al cuerpo normal para pesar lo mismo que `.boton-archivo`. */
    font-size: 1rem;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.campo-archivo input[type="file"]::file-selector-button:hover {
    background: var(--menta-suave);
}

/* ---------------------------------------------------------------------------
   Una fecha opcional que se puede quitar de un golpe

   El control nativo no ofrece cómo vaciarse: hay que borrar el día, el mes y el año por separado
   y quedarse mirando si el campo volvió a "dd-mm-aaaa" o si quedó a medias. Salió probando el
   próximo control de la receta, que se fija y se corre seguido, y donde equivocarse en silencio
   manda al tutor de vuelta un día que no es.

   El botón vive al lado del campo y solo se ve cuando hay una fecha puesta, así que su ausencia
   ya dice que no quedó nada. Lo muestra `fecha-borrable.js`: sin JavaScript el campo conserva su
   conducta normal y no aparece un botón que no haría nada.
   --------------------------------------------------------------------------- */
.fecha-borrable {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.5rem;
}

/* El campo manda y el botón toma lo suyo: todo input de la aplicación es `width: 100%`, y sin
   esto empuja al botón fuera de la línea apenas la pantalla se angosta. */
.fecha-borrable input {
    flex: 1 1 10rem;
    min-width: 0;
}

/* Los botones nacen con `margin-top: 1.25rem`, que es el aire con que se separan de un formulario
   entero. Este acompaña a un campo y tiene que quedar a la altura del control, que arranca en el
   0.25rem de todo input. */
.fecha-borrable button {
    flex: none;
    margin-top: 0.25rem;
}

/* El aviso de que se quitó toma la fila entera debajo, y no una tercera columna que angostaría el
   campo cada vez que aparece. */
.fecha-borrable .fecha-quitada {
    flex: 1 0 100%;
    font-size: 0.9rem;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 0.75rem;
}

th, td {
    text-align: left;
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid var(--borde);
    vertical-align: top;
}

th {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--texto-tenue);
}

/* El `th` de una fila identifica, no encabeza. La regla de arriba —versalita, chica, apagada—
   es del encabezado de columna, y heredada por la celda del nombre deja "SALA CANINA" gritando
   en mitad del listado. Las tres tablas que ya tenían una celda así se escribieron esta misma
   corrección cada una por su cuenta; está acá para que la cuarta no tenga que descubrirla. */
th[scope="row"] {
    color: var(--texto);
    font-size: 1rem;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
}

/* ---------------------------------------------------------------------------
   La cola de cobro

   Seis columnas y la última es un control, así que el ancho está justo. Repartirlo a mano por
   porcentaje deja "Emitido" en dos líneas incluso cuando cabía de sobra: la fila se ve alta y
   desordenada todo el tiempo para cubrir el caso raro. Acá reparte la tabla y solo se sujeta
   lo que de verdad se desborda.
   --------------------------------------------------------------------------- */

/* Con seis columnas el relleno lateral se lleva por sí solo casi una palabra de cada nombre.
   Algo más apretada que el resto de las tablas es lo que deja la fila entera en una línea. */
.tabla-cola-cobros th,
.tabla-cola-cobros td {
    padding-left: 0.45rem;
    padding-right: 0.45rem;
}

/* Paciente, tutor y responsable los escribe la clínica y no tienen largo controlado. En el
   escritorio la tabla no tiene a dónde desbordarse, así que un nombre de una sola palabra
   larguísima se parte antes que empujar el saldo fuera de la sección.

   Solo en el escritorio: bajo 40rem la tabla ya se desplaza dentro de su sección, y ahí
   `anywhere` deja de proteger nada y pasa a hacer daño —le dice al navegador que la columna
   puede medir una letra, y "María González" baja partida en "María Gonz / ález". */
@media (min-width: 40rem) {
    .tabla-cola-cobros td:nth-child(2),
    .tabla-cola-cobros td:nth-child(3),
    .tabla-cola-cobros td:nth-child(4) {
        overflow-wrap: anywhere;
    }
}

/* Un saldo de la cola no pasa de seis cifras. Fijarle el ancho es lo que impide que la línea
   del abono, que es larga, se lleve un tercio de la tabla y deje a los nombres sin dónde caber. */
.tabla-cola-cobros .columna-saldo {
    width: 6.5rem;
}

/* El abono explica el saldo, no es otra cifra que se compare hacia abajo: si no cabe se apila
   en dos líneas cortas en vez de ensanchar la columna. Cada mitad se mantiene entera, porque
   partir "$200.000" entre el signo y el número es justo lo que `.numero` evita. */
.tabla-cola-cobros .abono-en-cola {
    white-space: normal;
}

.tabla-cola-cobros .abono-en-cola span {
    white-space: nowrap;
}

/* `width: 1%` con `nowrap` es cómo se le pide a una tabla automática que una columna se quede
   con lo justo de su contenido: el ancho que sobra va a las tres de texto, que es donde varía.
   El alto lo pone el botón, así que la celda aporta menos relleno propio para que la fila no
   crezca respecto de las demás pantallas. */
.tabla-cola-cobros .columna-accion {
    width: 1%;
    padding: 0.25rem 0.35rem;
    white-space: nowrap;
    vertical-align: middle;
}

dl {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.35rem 1rem;
    margin: 0;
}

dt {
    color: var(--texto-tenue);
    font-size: 0.9rem;
}

dd {
    margin: 0;
}

.folio {
    font-size: 0.9rem;
    font-weight: normal;
    color: var(--texto-tenue);
}

/* Los datos que identifican el presupuesto son una sola fila de lectura. Cada pieza comparte
   el mismo eje vertical y, si falta ancho, salta completa en vez de cortar un nombre o una fecha. */
ul.metadatos-presupuesto {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.25rem 0.45rem;
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
}

/* El `gap` no es aire de más: un contenedor flex descarta el espacio en blanco entre sus
   hijos, así que "ficha <span>10</span>" se leía "ficha10". Es el único dato de la fila que
   lleva palabra y valor en piezas separadas, y era el único que salía pegado. */
.metadatos-presupuesto > li {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    min-height: 1.5rem;
    white-space: nowrap;
}

/* Descontado el `gap` que ahora separa a los hijos, para que la separación tras el punto
   quede donde estaba. */
.metadatos-presupuesto > li + li::before {
    content: "·";
    margin-right: 0.2rem;
    color: var(--texto-tenue);
}

/* La etiqueta es una sola pieza en todo el sistema —el estado de un presupuesto, la línea
   exenta, el paciente extraviado, el tutor principal— y por eso el alto lo pone ella y no su
   contenido: dos etiquetas seguidas miden igual aunque una diga "exenta" y la otra "nota de
   crédito pendiente". Lo que se ganó al subir esto desde la del presupuesto, que era la única
   que lo tenía, es que dejaran de sentarse medio renglón más abajo que el texto que acompañan. */
.etiqueta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 1.5rem;
    padding: 0.15rem 0.55rem;
    border-radius: 0.8rem;
    background: var(--menta-suave);
    color: var(--turquesa-profundo);
    font-size: 0.75rem;
    line-height: 1;
    vertical-align: middle;
}

/* Casilla y su texto en la misma línea, sin el display:block de los demás label. */
label.opcion {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

label.opcion input {
    width: auto;
    margin: 0;
}

/* El dinero se lee comparando columnas, y para eso tiene que estar alineado a la derecha
   y con cifras de ancho fijo: si no, $9.000 y $130.000 no se pueden comparar de un vistazo.

   `nowrap` es por los negativos: en una columna angosta el navegador parte "−$25.000" entre
   el signo y el peso, y el menos queda huérfano al final de una línea. Justo lo que enPesos
   existe para evitar. */
.numero {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* La acción que se repite en cada fila: abrir el presupuesto, editar al tutor, agregar el
   servicio del catálogo. Es un botón y no un enlace suelto porque en una columna de "abrir"
   idénticos la palabra sola no dice dónde termina un objetivo y empieza el siguiente, y en el
   mesón se apunta con el dedo sobre una pantalla táctil: 2.25rem es lo que hace que se acierte
   sin mirar.

   Ancho completo de su celda para que todas midan igual. Contorno y no relleno turquesa: hay
   una por fila, y una columna de botones sólidos pesa más que los datos que acompaña.

   Vale para `button` y para `a` —abrir es un GET y agregar del catálogo un POST— y por eso la
   caja se declara entera acá en vez de heredarla: un `a` no toma nada de la regla de `button`.
   El centrado va con flex porque `min-height` sobre un `a` estira la caja y deja el texto
   pegado arriba, mientras el `button` lo centraba solo; sin esto los dos no coincidían. */
button.boton-tabla,
a.boton-tabla {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 2.25rem;
    margin: 0;
    padding: 0.35rem 0.7rem;
    border: 1px solid var(--turquesa);
    border-radius: 0.3rem;
    background: var(--superficie);
    color: var(--turquesa-profundo);
    font: inherit;
    font-weight: 400;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
}

button.boton-tabla:hover,
a.boton-tabla:hover {
    filter: none;
    background: var(--menta-suave);
}

button.boton-tabla:active,
a.boton-tabla:active {
    filter: none;
    background: var(--borde);
}

/* Las columnas que no son datos sino señales: el estado, la acción y la de "abierta" en las
   atenciones en curso. Se encogen a su contenido —`width: 1%` en una tabla al 100% deja que el
   sobrante lo repartan las columnas de datos— y no parten su contenido en dos líneas.

   El `nowrap` no es cosmético en ninguna: sin él el botón se estiraba hasta donde llegara la
   celda y cada tabla tenía un "abrir" de otro ancho; la etiqueta "en curso" se rompía por el
   espacio y quedaba de dos renglones dentro de una píldora pensada para uno; y "hace 21 días"
   se partía en "hace 21" / "días". Al encogerse estas señales, el ancho sobrante lo absorben
   Tutor y Veterinario, que sí pueden ir en varias líneas. */
.columna-accion,
.columna-estado,
.columna-abierta {
    width: 1%;
    white-space: nowrap;
}

/* Dos acciones en la misma celda —"abrir" y "receta" en el historial de la ficha—, una al lado de
   la otra.

   Estuvieron apiladas exactamente un día, y el motivo de apilarlas era cierto: mientras el
   historial metía el motivo y el diagnóstico en columna propia, la columna de acciones pasaba de
   ~6,5rem a ~11rem y lo que se comprimía era el texto, con la fecha partida en cuatro líneas. Ese
   motivo se fue con la prosa: en seis columnas de dato corto sobran 4rem de ancho, y apiladas la
   fila mide 4,8rem de alto para mostrar una sola línea de datos.

   El contenedor hace falta igual: `a.boton-tabla` trae `margin-top` propio para cerrar formularios
   y dentro de una celda eso deja las dos separadas de forma despareja. Acá el `gap` lo decide esta
   regla. */
.acciones-de-fila {
    display: flex;
    gap: 0.35rem;
}

/* Los mismos dos botones cuando bajan a la fila propia del registro: hoy, la tabla de comisiones.

   `a.boton-tabla` nace al 100 % de su celda, y eso es lo correcto mientras la celda sea una columna
   angosta —así todas las tablas tienen un "abrir" del mismo ancho—. Cuando la celda cruza la tabla
   entera, ese mismo 100 % reparte dos botones de más de 20rem cada uno, y para un acto que no se
   deshace ése es justo el tamaño que no hay que darle: un blanco enorme se aprieta sin apuntar. */
.acciones-del-registro > .boton-tabla {
    width: auto;
}

/* ---------------------------------------------------------------------------
   El registro que ocupa dos filas

   Dos listados de la ficha llegaron acá por el mismo camino: pedían más ancho del que tiene la
   sección —43rem— y el navegador lo resolvía comprimiendo, que es lo peor que puede hacerle a una
   columna de fechas.

   El **historial** es el único listado con prosa adentro: el motivo y el diagnóstico los escribe el
   veterinario y no tienen largo controlado —un diagnóstico de tres líneas es lo normal—. Con los dos
   en columna propia eran ocho columnas y la tabla pedía 60rem de mínimo: primero se dibujaba encima
   del borde y, envuelta en `.tabla-ancha` para que no lo hiciera, lo que se fue de la pantalla
   fueron los botones de cada fila. El **carnet** no tiene prosa y llegó igual por cantidad: siete
   columnas de dato corto, dos de ellas cargando una etiqueta al lado, y lo que se partía en dos
   renglones era "01-01-2026", la fecha que ordena el listado entero (16-08).

   La corrección no es repartir mejor el ancho, es **sacar de la retícula lo que no se compara hacia
   abajo**. Cada registro es un `<tbody>` de dos filas: arriba las columnas cortas —las que se
   recorren con la vista— y abajo, a lo ancho de la tabla, lo que solo se lee cuando ya se eligió una
   fila. El historial queda en seis columnas que suman 39rem y el carnet en cinco, así que las dos
   caben enteras y el desplazamiento lateral desaparece en el escritorio. Bajo 40rem sigue habiendo:
   ni cinco ni seis columnas caben en un teléfono, y ahí vale lo mismo que para todos los listados.

   Lo que baja no es lo mismo en las dos —relato en una, procedencia en la otra— y por eso vive en el
   bloque de cada tabla. Acá está solo la mecánica de que un registro sean dos filas.
   --------------------------------------------------------------------------- */

/* La línea separa registros, no filas. Entre los datos de uno y su continuación no hay nada que
   separar —son el mismo registro—, así que el borde pasa de la celda al grupo.

   Escrito apagando el borde de **todas** las celdas y no solo el de la fila de datos, porque con un
   `<tbody>` por registro la regla general `tbody tr:last-child` —que existe para que la última fila
   no cierre por dos lados, contra el borde de la sección— pasa a alcanzar la última fila de cada
   grupo y dejaría el listado entero sin una sola línea. */
.registro-en-dos-filas tbody > tr > * {
    border-bottom: 0;
}

.registro-en-dos-filas tbody:not(:last-child) {
    border-bottom: 1px solid var(--borde);
}

/* El resaltado también es del grupo: apuntar la línea de datos e iluminar medio registro delata que
   por dentro son dos filas, que es justo lo que estas tablas no quieren decir. Nombra `th` además de
   `td` porque la fecha encabeza su fila y la regla general solo cubre celdas de dato. */
.registro-en-dos-filas tbody:hover th,
.registro-en-dos-filas tbody:hover td {
    background: var(--menta-suave);
}

/* La segunda fila cuelga de la primera, sin relleno propio arriba: es la continuación del mismo
   registro y no un bloque aparte.

   Por posición y no por una clase, porque cada tabla le da un nombre distinto a lo que baja y lo
   único que esta regla necesita saber es que no es la primera. */
.registro-en-dos-filas tbody > tr + tr > td {
    padding-top: 0;
}

/* Las columnas que son un dato y no prosa: las fechas de las dos tablas y las medidas del historial.

   El navegador partía "12-08-2026 17:31" en cada guion y separaba el número de su unidad: "4,2" en
   una línea y "kg" en la siguiente, "38,3" y "°C". **Un valor partido de su unidad no es un valor
   comprimido, es un valor que hay que rearmar leyendo**, y en la columna donde se sigue la evolución
   del peso de un paciente eso es justo lo que no puede costar trabajo. La fecha va sin tabla que la
   acote porque el problema no es de ninguna en particular: una fecha partida por un guion deja de
   leerse de un vistazo en cualquier listado que la use.

   No se usa `.numero` —que también trae `nowrap`— porque alinea a la derecha, y estas columnas son
   angostas y con el encabezado a la izquierda: el número se despegaría de su título. */
.columna-fecha,
.tabla-historial .columna-medida {
    white-space: nowrap;
}

/* Motivo y diagnóstico con su nombre al lado, en la misma retícula con la que la sección de arriba
   muestra los datos del paciente: es el `dl` del sistema, con el interlineado apretado porque acá
   las dos filas son prosa y no dos datos sueltos.

   El `minmax(0, 1fr)` es lo único que no se hereda y hace falta: con `1fr` a secas una palabra larga
   —"antiparasitario"— fija el ancho mínimo de la celda, y como la celda cruza las seis columnas eso
   vuelve a empujar la tabla fuera de la sección, que es el problema que este bloque vino a terminar. */
.relato-atencion {
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.15rem 0.9rem;
}

/* Lo que baja en el carnet: de qué frasco salió y quién la puso, en una sola línea.

   En `flex` y no en el `dl` del sistema que usa el relato, porque son dos datos cortos y no dos
   bloques de prosa: apilados ocupaban dos o tres renglones para decir "Lote A1234, aplicó María", y
   en un carnet de veinte filas eso es media pantalla de aire. El `wrap` es lo que lo salva en un
   teléfono sin una consulta de medios propia: cuando no cabe en una línea, baja el pedazo que sobra
   en vez de estirar la tabla. */
.procedencia {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1rem;
    font-size: 0.9rem;
}

/* Lo que el veterinario anotó de esa aplicación, en su propio renglón: es lo único del carnet sin
   largo controlado, y como un elemento más de la línea de arriba empujaba "Aplicó" fuera de la
   vista. Conserva el color del texto —no va en `.tenue`— porque "reaccionó a la anterior" no es un
   dato de respaldo: es lo que hay que leer antes de poner la siguiente. */
.observacion-preventiva {
    display: block;
    margin-top: 0.2rem;
    font-size: 0.9rem;
}

/* ---------------------------------------------------------------------------
   El catálogo del presupuesto

   Servicio, precio y acción forman una retícula estable. La confirmación ocupa el mismo lugar
   que el botón: el resultado del clic se ve donde se actuó, pero no ensancha la columna ni mueve
   el siguiente objetivo bajo el puntero.
   --------------------------------------------------------------------------- */
.catalogo-servicios {
    table-layout: fixed;
}

.catalogo-servicios th,
.catalogo-servicios td {
    padding-top: 0.35rem;
    padding-bottom: 0.35rem;
    vertical-align: middle;
}

/* Lo demás que esta celda corregía —el tamaño, el peso, la versalita— lo hace ahora
   `th[scope="row"]` para todas. */
.catalogo-servicios .servicio-catalogo {
    width: auto;
}

.catalogo-servicios .precio-catalogo {
    width: 7rem;
    color: var(--texto);
    font-weight: 400;
}

.catalogo-servicios .agregar-catalogo {
    width: 7.75rem;
    text-align: right;
}

.catalogo-servicios tbody tr:last-child > * {
    border-bottom: 0;
}

.catalogo-servicios tbody tr:hover > * {
    background: var(--menta-suave);
}

.control-catalogo {
    position: relative;
    display: inline-block;
    width: 6.5rem;
}

.control-catalogo form {
    display: block;
}

.confirmacion {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    border: 1px solid var(--turquesa);
    border-radius: 0.3rem;
    background: var(--menta-suave);
    color: var(--turquesa-profundo);
    font-size: 0.85rem;
    font-weight: 600;
    opacity: 0;
    pointer-events: none;
}

.confirmacion::before {
    content: "✓";
    font-weight: 700;
}

.confirmacion.visible {
    animation: confirmar 1.6s ease-out;
}

@keyframes confirmar {
    0%   { opacity: 0; }
    15%  { opacity: 1; }
    70%  { opacity: 1; }
    100% { opacity: 0; }
}

/* Quien pidió no ver animaciones igual necesita la señal: se queda quieta y se va sin
   transición. */
@media (prefers-reduced-motion: reduce) {
    .confirmacion.visible {
        animation: confirmar 1.6s step-end;
    }
}

h2.categoria {
    margin: 1.75rem 0 0;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--texto-tenue);
}

h2.categoria + table {
    margin-top: 0.25rem;
}

@media screen and (max-width: 32rem) {
    .catalogo-servicios,
    .catalogo-servicios tbody {
        display: block;
        overflow: visible;
    }

    .catalogo-servicios tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "servicio precio"
            "servicio accion";
        gap: 0.35rem 1rem;
        padding: 0.7rem 0.4rem;
        border-bottom: 1px solid var(--borde);
    }

    .catalogo-servicios th,
    .catalogo-servicios td {
        width: auto;
        padding: 0;
        border-bottom: 0;
    }

    .catalogo-servicios .servicio-catalogo {
        grid-area: servicio;
        align-self: center;
    }

    .catalogo-servicios .precio-catalogo {
        grid-area: precio;
    }

    .catalogo-servicios .agregar-catalogo {
        grid-area: accion;
    }

    .catalogo-servicios tbody tr:last-child {
        border-bottom: 0;
    }
}

/* El `th[scope="row"]` va con los `td`: es la celda del nombre, y una fila retirada con el
   nombre a plena tinta y el resto apagado se lee como si estuviera a medio cargar.

   El `tbody` está por la misma razón, un nivel más arriba: donde un registro son dos filas —el
   carnet—, apagar solo la primera dejaba el lote y quién la puso a plena tinta debajo de una
   línea gris, que es el mismo efecto de "a medio cargar" que esta regla vino a evitar. */
tr.inactiva td,
tr.inactiva th[scope="row"],
tbody.inactiva td,
tbody.inactiva th[scope="row"] {
    color: var(--texto-tenue);
}

/* La cantidad de una línea se edita dentro de la tabla del presupuesto, así que el input no
   puede llevar el ancho completo ni el margen que tiene en un formulario. */
.cantidad input {
    width: 4.5rem;
    margin: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* El detalle repite la misma lógica visual del catálogo: la descripción absorbe el ancho
   disponible y las cifras y la acción conservan una columna estable para poder compararlas. */
.detalle-presupuesto {
    table-layout: fixed;
}

.detalle-presupuesto th,
.detalle-presupuesto td {
    vertical-align: middle;
}

/* La descripción no lleva ancho: absorbe lo que dejan las cinco columnas fijas. Cuando esas
   sumaban casi toda la tabla le quedaban ~4rem, "Esterilización canina hembra" caía en tres
   renglones y la palabra larga se salía de la celda hasta pisar el selector de "Quién lo hizo".
   Se le devolvió aire recortando las cifras y la acción a lo que miden sus datos, y por si un
   nombre de servicio aún no cabe, que la celda parta la palabra antes que invadir la vecina. */
.detalle-presupuesto .item-detalle {
    overflow-wrap: break-word;
}

.detalle-presupuesto .cantidad-detalle {
    width: 6rem;
}

/* Quién ejecutó la línea. El select va al ancho de la columna —con `table-layout: fixed` un
   nombre largo la desbordaría— y sin el margen que tiene en un formulario, igual que el input
   de cantidad. */
.detalle-presupuesto .ejecutor-detalle {
    width: 11rem;
}

.detalle-presupuesto .ejecutor-detalle select {
    width: 100%;
    margin: 0;
}

/* Ajustadas a lo que mide un monto de esta clínica —"$130.000", y con margen para siete cifras—
   en vez de las 7rem holgadas de antes: el sobrante se le devolvió a la descripción, que era la
   que se quedaba sin ancho. `.numero` las mantiene sin cortar. */
.detalle-presupuesto .precio-detalle,
.detalle-presupuesto .monto-detalle {
    width: 6rem;
}

/* La acción solo lleva el botón "Quitar"; su piso lo pone el botón, no un ancho elegido, así que
   se apretó hasta ahí para no robarle columna a la descripción. */
.detalle-presupuesto .accion-detalle {
    width: 6rem;
    text-align: right;
}

.detalle-presupuesto .accion-detalle form {
    display: inline-block;
    width: 4.75rem;
}

/* El total cierra la tabla: sin la línea de abajo, que ya no separa nada, y sin el gris de
   encabezado que lo dejaría más apagado que las líneas que suma. */
tfoot th,
tfoot td {
    border-bottom: 0;
    color: var(--texto);
    font-size: 1rem;
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
}

/* ---------------------------------------------------------------------------
   El buscador de fichas

   Mismo criterio que el catálogo del presupuesto, y por el mismo motivo: el listado se vuelve a
   dibujar con cada tecla, así que con anchos automáticos las columnas se recalculan a mitad de
   la búsqueda y lo que se estaba leyendo se corre de sitio. Folio, especie y microchip miden lo
   que miden sus datos —el microchip son quince dígitos y no crece—, y paciente y tutor se
   reparten lo que sobra.

   Van dentro de una consulta de ancho porque solo aplican donde hay ancho que repartir: bajo
   40rem la tabla ya se desplaza sola dentro de su sección, y ahí imponerle veinte rem de mínimo
   entre tres columnas es garantizarle el desplazamiento lateral en vez de dejarla apretarse.
   --------------------------------------------------------------------------- */
@media (min-width: 40rem) {
    .tabla-fichas {
        table-layout: fixed;
    }

    .tabla-fichas .folio-ficha {
        width: 4rem;
    }

    .tabla-fichas .especie-ficha {
        width: 5rem;
    }

    .tabla-fichas .microchip-ficha {
        width: 9.5rem;
    }

    /* La columna del botón existe solo cuando esta tabla la abre una hora de la agenda que viene a
       buscar su ficha. Lleva ancho propio porque con `table-layout: fixed` el `width: 1%` con el
       que `.columna-accion` se encoge en las tablas automáticas se toma al pie de la letra: el
       botón se salía de su celda y quedaba encima de la columna del tutor. */
    .tabla-fichas .columna-accion {
        width: 6.5rem;
    }
}

/* La que manda el alto de la fila es la del tutor, que lleva dos líneas. Las demás se centran
   contra ella en vez de colgar del borde de arriba: así ficha, paciente, marca, especie y
   microchip se leen sobre un mismo eje y no escalonados. */
.tabla-fichas td {
    vertical-align: middle;
}

/* Nombre y marca, en ese orden y sin posibilidad de que la marca baje: es una fila flex que no
   envuelve, así que un nombre que no quepa se parte él y la marca se queda a su derecha. Corrido
   como texto suelto, era el largo del nombre el que decidía dónde caía la marca. */
.con-marca {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.con-marca > .etiqueta {
    flex: none;
}

/* El tutor sí va en dos líneas: el RUT se compara dígito a dígito contra el papel que trae el
   tutor, y detrás del nombre el navegador lo partía por la mitad. Debajo empieza siempre en el
   mismo punto y se puede recorrer la columna con la vista. */
.apilado {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
}

/* ---------------------------------------------------------------------------
   Cambio de tema
   --------------------------------------------------------------------------- */

/* Nace oculto y lo muestra tema.js: sin JavaScript el botón no haría nada, y un control
   muerto en la barra es peor que no tenerlo. */
.tema[hidden] {
    display: none;
}

.tema {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 0.35rem;
    border-radius: 0.3rem;
    background: none;
    color: var(--texto-tenue);
    cursor: pointer;
}

.tema:hover {
    filter: none;
    background: var(--menta-suave);
    color: var(--turquesa-profundo);
}

.tema svg {
    width: 1.15rem;
    height: 1.15rem;
    display: none;
}

/* Solo se dibuja el icono del estado actual: automático, claro u oscuro. */
.tema[data-estado="sistema"] .icono-sistema,
.tema[data-estado="claro"] .icono-claro,
.tema[data-estado="oscuro"] .icono-oscuro {
    display: block;
}

/* Para lectores de pantalla: el estado del tema se anuncia, pero en la barra lo dice el
   icono. */
.visualmente-oculto {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* El caption de una tabla es la única excepción: se oculta igual, pero sin salirse del flujo.
   Con `table-layout: fixed` y sin `<colgroup>`, los anchos de columna salen de las celdas de la
   primera fila, y un caption absoluto se los hace perder: el navegador reparte la tabla en
   partes iguales y los `width` declarados no se aplican nunca. Eso dejaba el catálogo del
   presupuesto con tres columnas de 229px —"Esterilización canina hasta 15 kg" partido en dos
   líneas con 220px libres al lado— en vez de las de 450, 112 y 124 que pide el bloque del
   catálogo. Las tablas que sí declaran `<colgroup>` —usuarios y servicios— nunca dependieron de
   la primera fila y miden igual con o sin esto. Recortado a 1px ocupa lo mismo quieto que
   absoluto, y el lector de pantalla lo sigue anunciando. */
caption.visualmente-oculto {
    position: static;
}

/* La sección de la barra donde está parado el usuario. */
.barra a.actual {
    color: var(--turquesa-profundo);
    font-weight: 600;
}

tbody tr:hover td {
    background: var(--menta-suave);
}

/* La última línea de la tabla no separa nada de lo que viene abajo: es el borde de la
   sección el que cierra. */
table:not(:has(tfoot)) tbody tr:last-child td {
    border-bottom: 0;
}

/* En pantalla angosta las cuatro columnas de una tabla de montos no caben, y la página
   entera termina desplazándose de lado. Primero se recupera ancho apretando los rellenos;
   la tabla que aun así no quepa se desplaza sola dentro de su sección, que es lo que evita
   que el encabezado y el resto del contenido se vayan con ella. */
@media screen and (max-width: 40rem) {
    main {
        padding: 1.5rem 0.9rem;
    }

    section {
        padding: 1rem;
    }

    th,
    td {
        padding: 0.5rem 0.4rem;
    }

    /* Ojo con el selector: **ningún listado de la aplicación vive dentro de un `section`** —en la
       cola, en fichas y en usuarios el `</section>` cierra antes de la tabla—, así que esta regla
       no alcanza a ninguno y `.tabla-ancha` es la que hace el trabajo ahí. Ésta cubre las tablas
       de detalle y de formulario, que sí están adentro. */
    section table,
    form table {
        /* `relative` no coloca nada: encierra. Los títulos que solo oye un lector de pantalla
           —el `caption` de una tabla agrupada, el "Acción" de la columna del botón— van en
           `position: absolute`, y sin un ancestro posicionado se sitúan respecto de la página.
           Quedan a la derecha de la tabla desplazada, fuera del ancho del teléfono, y arrastran
           de lado justo lo que este bloque existe para no mover. */
        position: relative;
        display: block;
        overflow-x: auto;
    }
}

/* Un listado que no cabe se desplaza dentro de su marco en vez de arrastrar la página con él.

   Se descubrió el 11-08 en la bandeja de problemas: con la espera del outbox metida en la celda,
   su tabla se salía 189 px en un teléfono y lo que se movía de lado era el documento entero, con
   el encabezado y la barra incluidos. La regla de arriba dice hacer exactamente esto y no la
   alcanzaba, porque pide un `section` que ningún listado tiene.

   Envoltorio, y no `display: block` sobre la tabla: así conserva su reparto de columnas, y los
   títulos que solo oye un lector de pantalla siguen dentro de un ancestro que los contiene. Va sin
   `@media` porque `auto` no dibuja nada mientras la tabla quepa, y sin `tabindex`: los navegadores
   actuales dan foco solos a una zona que se puede desplazar, y un tabulador muerto en cada
   listado ancho costaría más de lo que arregla. */
.tabla-ancha {
    overflow-x: auto;
}

/* La portada del login: el símbolo hace de encabezado y el nombre va debajo. */
.portada {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 1.5rem;
    text-align: center;
}

.portada svg {
    width: 4rem;
    height: 4rem;
}

.portada h1 {
    margin: 0;
    font-size: 1.6rem;
    letter-spacing: 0.01em;
}

.portada p {
    margin: 0;
    font-size: 0.9rem;
}

/* En el login no hay barra donde colgar el cambio de tema, y sigue haciendo falta: el
   computador de recepción se configura una vez, desde la pantalla de entrada. */
.tema-esquina {
    position: absolute;
    top: 0.6rem;
    right: 0.75rem;
}

/* El desglose tributario: pocas cifras, alineadas a la derecha y sin competir con el total
   de la tabla, que es el número que se cobra. */
dl.desglose {
    grid-template-columns: max-content 7rem;
    justify-content: end;
    margin-top: 0.75rem;
    font-size: 0.9rem;
}

dl.desglose dd {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* En el mesón los cuatro importes se comparan antes de cobrar. Cantidad conserva su ancho y
   precio, monto y desgloses comparten una guía de siete rem, sin inmovilizar toda la tabla. */
.tabla-cobro .cantidad-cobro {
    width: 6rem;
}

.tabla-cobro .precio-cobro,
.tabla-cobro .monto-cobro {
    width: 7rem;
}

/* Un cobro mixto se lee como una lista corta de comprobantes, no como una sola grilla que
   accidentalmente ganó más columnas. El borde conserva juntas las tres piezas de cada pago
   cuando hay varios y sigue usando las mismas superficies en tema claro y oscuro. */
.lista-medios-cobro {
    display: grid;
    gap: 1rem;
}

.medio-cobro {
    padding: 1rem;
    border: 1px solid var(--borde);
    border-radius: 0.55rem;
    background: var(--superficie);
}

.medio-cobro legend {
    padding: 0 0.4rem;
    color: var(--texto);
    font-family: var(--fuente-marca);
    font-weight: 700;
}

.acciones-del-medio {
    justify-content: flex-end;
    margin-top: 0.35rem;
}

.nota-codigo-cobro {
    margin-bottom: 0;
}

/* La factura es del cobro entero y no de un medio, así que va fuera de la lista de medios y con
   la misma superficie: dentro de `.lista-medios-cobro` se leía como un cuarto comprobante y en
   un cobro mixto invitaba a creer que se factura solo esa porción. */
.factura-cobro {
    margin-top: 1rem;
    padding: 1rem;
    border: 1px solid var(--borde);
    border-radius: 0.55rem;
    background: var(--superficie);
}

.factura-cobro legend {
    padding: 0 0.4rem;
}

/* La casilla y su texto forman un solo blanco de clic. En el mesón se marca al vuelo mientras el
   tutor dicta, y un cuadrito de un centímetro obliga a apuntar con el mouse. */
.casilla {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--texto);
    font-family: var(--fuente-marca);
    font-weight: 700;
    cursor: pointer;
}

/* Un poco más grande que la de fábrica: esta casilla abre los cinco datos del receptor y se marca
   mientras el tutor los dicta. El `accent-color` que estaba acá se subió al selector general de
   casillas y opciones, donde alcanza también a las otras ocho de la aplicación. */
.casilla input[type="checkbox"] {
    width: 1rem;
    height: 1rem;
}

/* Los cinco datos del receptor separados de la casilla que los abre: sin el aire, el bloque
   aparecía pegado al borde del fieldset y parecía parte de la etiqueta. */
#datos-del-receptor {
    margin-top: 0.75rem;
}

/* El comprobante lleva el texto más largo y necesita más aire que el monto. En ancho estrecho
   vuelve a mandar la grilla adaptable de `.campos`, que apila sin comprimir los controles. */
@media (min-width: 40rem) {
    .campos-del-medio {
        grid-template-columns: minmax(8rem, 0.85fr) minmax(10rem, 1fr) minmax(14rem, 1.3fr);
    }
}

/* Agregar un medio y comprobar el saldo son el mismo cierre del reparto del pago. La línea los
   reúne sin convertirlos en otra tarjeta dentro de la sección. */
.pie-medios-cobro {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: 1rem 2rem;
    margin-top: 0.85rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--borde);
}

.acciones-medios-cobro {
    margin-top: 0;
}

dl.resumen-del-cobro {
    margin-top: 0;
}

@media screen and (max-width: 40rem) {
    .pie-medios-cobro {
        grid-template-columns: 1fr;
        align-items: start;
    }

    dl.resumen-del-cobro {
        width: 100%;
    }
}

.error-en-linea {
    color: var(--rojo-profundo);
    font-weight: 700;
}

/* El estado del presupuesto se busca de un vistazo desde recepción, y para eso el color
   tiene que decir algo antes que la palabra. */
.etiqueta.estado-emitido {
    background: var(--turquesa);
    color: var(--sobre-turquesa);
}

/* `apagada` es el estado del paciente que cerró su historial. Comparte el gris con el
   presupuesto anulado porque dice lo mismo —esto ya no está en curso— y tiene nombre propio
   porque no es lo mismo: una ficha no se anula. */
.etiqueta.estado-anulado,
.etiqueta.apagada {
    background: var(--borde);
    color: var(--texto-tenue);
}

/* Ámbar de "revisar": algo que hay que mirar y que ya ocurrió —no es urgencia—. La caja cerrada
   con diferencia es el caso que lo trajo: un faltante es la fila que el admin tiene que pescar
   entre todas las verdes, y antes reusaba el gris de `estado-anulado` —el de lo muerto, un
   presupuesto anulado—, que la hacía recederse justo cuando tiene que saltar. No va en rojo porque
   el rojo de la paleta es "hay que hacer algo ahora" —frasco vencido, paciente extraviado, boleta
   en duda— y una caja ya cerrada, con su faltante registrado, no lo es. */
.etiqueta.revisar {
    background: var(--ambar-suave);
    color: var(--ambar-profundo);
}

/* El día que se acabó sin que nadie declarara el arqueo, en el historial de cierres. Ámbar como
   `revisar` porque pide lo mismo —mirar algo que ya ocurrió, sin urgencia—, y con contorno en vez
   de relleno por lo mismo que `sin-salida`: las dos filas que el historial tiene que hacer saltar
   son esta y la cerrada con diferencia, y con el relleno ámbar en las dos se leían como la misma
   cosa. No lo son —en una hay un número escrito que se investiga, en la otra no hay arqueo y
   alguien tiene que ir a contar— y el historial existe justo para separarlas. */
.etiqueta.sin-arqueo {
    background: none;
    border: 1px solid var(--ambar-profundo);
    color: var(--ambar-profundo);
}

/* El refuerzo atrasado y el frasco vencido son las dos cosas del carnet que hay que ver sin
   leer. Van en el rojo de la paleta —que es color de señal oficial, no uno inventado para la
   ocasión— y no en menta, porque menta acá diría "todo en orden".

   `alerta` es el mismo rojo para el paciente extraviado, y por el mismo motivo: es la única
   marca de la ficha que pide hacer algo ahora —llamar al tutor del perro que acaban de traer—.
   Van juntas para que nadie tenga que acordarse de cambiar dos rojos. */
.etiqueta.atrasada,
.etiqueta.alerta {
    background: var(--rojo-suave);
    color: var(--rojo-profundo);
}

/* El reverso de un pago no es un estado muerto como "anulado": es un movimiento que ocurrió
   y que mueve plata. Va con contorno y no con relleno para que se distinga de un vistazo de
   la fila anulada que tiene justo encima, que era el problema: dos etiquetas grises con
   nombres casi iguales se leían como la misma cosa repetida. */
.etiqueta.reverso {
    background: none;
    border: 1px solid var(--texto-tenue);
    color: var(--texto-tenue);
}

/* El trabajo que salió y no sabemos si ocurrió, en la bandeja de problemas. Es el mismo truco
   del reverso y por la misma razón, esta vez en rojo: "esperando a una persona" y "en duda" son
   las dos filas que piden atención, y con el rojo relleno de `alerta` en las dos se leían como
   la misma cosa. No lo son —una se arregla apretando "reintentar" y la otra no se puede
   reintentar nunca, que es el ADR-021 entero— y el contorno es lo que dice que acá no hay botón
   que apretar. */
.etiqueta.sin-salida {
    background: none;
    border: 1px solid var(--rojo-profundo);
    color: var(--rojo-profundo);
}

/* En qué está el intento, debajo del problema tributario que explica. Separado del texto de
   arriba porque son dos cosas distintas —qué le falta a la venta y qué está haciendo Vetika al
   respecto— y pegados se leían como una sola frase larga. */
.espera {
    /* Sin `margin-bottom`, y por eso va explícito: no hay reset de `p` en este archivo, así que el
       1em que pone el navegador dejaba un renglón muerto al pie de la celda y descolgaba el monto
       de la fila de al lado. */
    margin: 0.4rem 0 0;
}

/* ---------------------------------------------------------------------------
   Confirmar lo ejecutado, al cerrar la atención
   --------------------------------------------------------------------------- */

/* Un bloque por línea de servicio y no una tabla. Cada línea pregunta cuatro cosas —qué pasó,
   cuándo, quién y a veces por qué— y en 48rem eso son seis columnas que aprietan el nombre del
   servicio, que es lo único que se busca con la vista, hasta partirlo en tres renglones. */
.lista-por-confirmar {
    display: grid;
    gap: 1rem;
}

/* Un grupo de campos que se despega de la superficie que lo contiene. Es el mismo bloque que
   `.medio-cobro` y `.factura-cobro`, escrito acá **sin nombre de pantalla** porque es el tercero
   igual; las dos del cobro quedan pendientes de doblarse a esta la próxima vez que se toque esa
   pantalla, como `.tabla-usuarios .fila-rol` con `.fila-grupo`. */
.bloque-de-campos {
    padding: 1rem;
    border: 1px solid var(--borde);
    border-radius: 0.55rem;
    background: var(--superficie);
}

/* `>` y no a secas: adentro hay otro fieldset con su propio legend —las tres opciones— y con el
   selector suelto ese «Qué pasó» salía en Nunito Sans en negrita, compitiendo con el nombre del
   servicio que encabeza el bloque. */
.bloque-de-campos > legend {
    padding: 0 0.4rem;
    color: var(--texto);
    font-family: var(--fuente-marca);
    font-weight: 700;
}

/* El monto y el folio de la línea. Sin esto, el 1em que el navegador le pone a un `p` deja dos
   renglones muertos entre el nombre del servicio y su primera pregunta. */
.bloque-de-campos p {
    margin: 0.3rem 0;
}

/* El fieldset de las opciones no dibuja caja: la caja ya la pone el bloque de la línea, y una
   segunda adentro se lee como si fueran dos servicios. */
.gesto-del-servicio {
    margin: 0.7rem 0 0.2rem;
    padding: 0;
    border: 0;
}

.gesto-del-servicio legend {
    padding: 0;
}

/* Las tres en fila mientras quepan, apiladas cuando no. Apiladas siempre eran tres renglones por
   servicio: una atención de cinco líneas se iba a dos pantallas de alto, justo en el momento en
   que hay alguien esperando en el box. */
.opciones-del-gesto {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem 1.25rem;
    margin-top: 0.2rem;
}

/* El motivo nace visible y lo esconde `motivo-del-gesto.js` mientras no se marque «no se realizó».
   Sin `hidden` propio en el CSS: el atributo del HTML basta porque esta regla no fija `display`,
   y así el campo sigue a la vista cuando el script no corre, que es la degradación segura. */
.motivo-del-gesto {
    margin-top: 0.6rem;
}

/* El reparto entre varias personas (K5). El `fieldset` no dibuja caja —la regla general de arriba
   ya lo deja sin ella, que es lo que corresponde acá por lo mismo que en `.gesto-del-servicio`: la
   caja la pone el bloque de la línea, y una segunda adentro se lee como si fueran dos servicios—.
   Lo único suyo es el margen de arriba, y su porqué está dos párrafos más abajo.

   «Quién lo hizo» es la misma pregunta que «Cuándo se hizo», que está en la columna de al lado, y
   tiene que pesar igual. Con el estilo que el navegador le da a un `legend` salía más grande y
   medio renglón más arriba, y las dos preguntas dejaban de leerse como pareja.

   **El desnivel se empuja desde el `fieldset` y no desde el `legend`**: el navegador no le aplica
   el margen de arriba a un `legend`, así que la regla lo pedía y no movía nada —la pregunta de la
   derecha quedaba los mismos 0,9rem más arriba que la de la izquierda, y el selector no coincidía
   con el campo de fecha—. El `fieldset` sí es un elemento de la grilla, como el `div` de al lado,
   y empujarlo a él baja la pregunta y su selector juntos. Lo encontró la §25 el 25-08 mirando la
   pantalla: es exactamente lo que ninguna prueba automática puede decir. */
.reparto {
    margin-top: 0.9rem;
}

.reparto > legend {
    padding: 0;
    margin-top: 0;
    font-size: 0.9rem;
}

/* Persona y porcentaje en una fila. Envuelve en vez de apretar: en el teléfono, un selector de
   nombres comprimido a dos letras es justo el dato que hay que comprobar antes de acreditar plata.

   El margen es el mismo `0.25rem` que todo campo lleva bajo su etiqueta, y no uno propio: es lo que
   deja el selector a la altura exacta del campo de fecha de la columna de al lado. Con 0,35 sobraba
   un pelo y las dos columnas volvían a leerse desparejas después de emparejar las preguntas. */
.fila-del-reparto {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem;
}

/* Cada persona como un bloque, y no como una línea más, cuando el reparto tiene varias.

   La fila se envuelve en cuanto lleva rol y porcentaje, así que el porcentaje, el monto y «Quitar»
   caen a un segundo renglón, y ese renglón quedaba a la misma distancia de la persona de arriba que
   de la de abajo: mirando la pantalla no se podía decir a cuál pertenecía «Quitar» (30-08). No es un
   problema de estética —es el botón que saca a alguien de la plata de una cirugía—.

   **El aire se compara contra el `row-gap` de la propia fila, que son 0,5rem, y no contra el margen
   que había antes.** El primer intento del 30-08 subió el margen de 0,25 a 0,55rem creyendo que con
   eso separaba «más», y en pantalla las dos distancias quedaron iguales: el `gap: 0.5rem` de
   `.fila-del-reparto` gobierna **también** el salto entre sus dos renglones cuando envuelve, y
   0,55rem no le gana a 0,5rem. Con 1rem, dos personas quedan al doble de lo que separa a una fila de
   su propia continuación, que es la comparación que el ojo hace de verdad.

   La barra y el sangrado acompañan, y por eso no hay caja: adentro ya hay dos —la del registro y la
   del `fieldset`— y una tercera se leería como otro servicio, que es lo mismo que decide
   `.gesto-del-servicio` dos bloques más arriba.

   **3px y no 2, que es el filete del sistema** (`.hecho-vigente`). **Y neutro y no turquesa**,
   aunque aquél lo use: dentro de esta misma caja el turquesa ya significa *«esto es lo que hay que
   leer antes de tocar nada»*, y repetirlo por persona le quitaría a esa frase lo único que la hace
   saltar. */
.fila-del-reparto.de-varias {
    margin-top: 1rem;
    padding-left: 0.6rem;
    border-left: 3px solid var(--borde);
}

/* El selector se lleva lo que sobre. `min-width: 0` porque sin él un nombre largo estira la fila y
   saca la pantalla de sus 48rem, que es lo que el `main` no puede hacer. */
.fila-del-reparto select {
    flex: 1 1 8rem;
    min-width: 0;
    margin-top: 0;
}

/* El nombre de la persona pesa más que el rol, y el primer `select` de la fila es siempre él.
   Con el reparto de K5 —un selector, un número y un botón— los 8rem alcanzaban. L5 le agregó a la
   fila **el selector del rol y un segundo botón**, los dos selectores se repartieron el sobrante
   por igual, y el que se cortó fue el largo: en la corrida del 28-08 se leía «Vania Veterinaria»
   sin la última letra y «Amador Adminis», mientras «Cirujano» sobraba de ancho. Se corta el que
   hay que leer antes de acreditar plata, y los nombres de rol son cortos por naturaleza —el tope
   de 40 caracteres es una guarda contra el dedazo, no un ancho de trabajo—. */
.fila-del-reparto select:first-of-type {
    flex: 2 1 11rem;
}

.peso-del-reparto {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

/* Ancho fijo y chico: son tres dígitos y dos decimales. Suelto dentro del flex se estiraba a media
   fila y el porcentaje parecía otro campo de texto. */
.peso-del-reparto input {
    width: 6rem;
    margin-top: 0;
}

/* Los botones del reparto no cierran ningún formulario, así que no llevan el margen de arriba que
   `button` trae para eso: acá los dejaba descolgados media línea bajo el selector de al lado. */
.fila-del-reparto button,
.acciones-del-reparto button {
    margin-top: 0;
    padding: 0.5rem 0.8rem;
    font-size: 0.9rem;
}

.acciones-del-reparto {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.75rem;
    margin-top: 0.55rem;
}

/* «No se le acredita a nadie» es una respuesta y no un hueco, así que ocupa el lugar donde
   estarían las filas: el bloque en blanco no distingue esa decisión de que esto se haya roto. */
.sin-reparto {
    margin: 0.5rem 0 0;
}

/* Lo que la fila devenga, al final de la fila (L5, ADR-035 §7). Ancho mínimo y `margin-left: auto`
   para que los montos de las dos o tres filas queden alineados entre sí y contra el borde derecho:
   es el único dato de la fila que se compara de arriba abajo, y en el resto de la aplicación la
   plata ya se busca a la derecha. **Escrito de una y todavía sin ver en pantalla**: la pasada
   manual de L es la que dice si con dos participantes y un nombre largo esto se sostiene. */
.costo-de-la-fila {
    min-width: 4.5rem;
    margin-left: auto;
    color: var(--texto-tenue);
    font-size: 0.9rem;
}

/* El total del reparto, sobre el fondo de la página y no suelto como las notas de al lado: es la
   respuesta a lo que se está decidiendo —cuánto sale este reparto— y no un pie de página, así que
   se le da superficie propia en vez de dejarlo en `.tenue` con el resto de los avisos. Es una
   decisión de escritorio, no un ajuste después de mirarla. */
.costo-del-reparto {
    margin-top: 0.6rem;
    padding: 0.5rem 0.65rem;
    border-radius: 0.3rem;
    background: var(--fondo);
}

.costo-del-reparto p {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin: 0;
    font-size: 0.9rem;
}

/* Los motivos van en lista y no corridos: son de cero a cuatro frases completas que vienen del
   mismo fragmento que usa el cierre del periodo, y una detrás de otra en un párrafo se leerían como
   una sola frase mal puntuada. */
.costo-del-reparto ul {
    margin: 0.35rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.85rem;
}

/* ---------------------------------------------------------------------------
   Corregir lo ya confirmado (K4)
   --------------------------------------------------------------------------- */

/* Lo que el sistema afirma hoy sobre la línea, encima de un formulario que llega con esos mismos
   valores puestos. Va con fondo y no en `.tenue` como el resto de las notas: es la única frase de la
   pantalla que hay que leer antes de tocar nada, y en gris se confundía con el monto y el folio de
   arriba, que sí son contexto. */
.hecho-vigente {
    margin: 0.5rem 0 0;
    padding: 0.5rem 0.7rem;
    border-left: 3px solid var(--turquesa);
    border-radius: 0.3rem;
    background: var(--menta-suave);
    color: var(--turquesa-profundo);
}

/* El de la corrección se ve siempre, así que no hereda de `.motivo-del-gesto`: ese lo esconde un
   script mientras no se marque «no se realizó», y compartir la clase habría bastado para que la
   próxima vuelta de ese archivo escondiera un campo obligatorio de otra pantalla. */
.motivo-de-la-correccion {
    margin-top: 0.6rem;
}

/* Las constancias anteriores. Numerada y compacta: son pocas —una línea corregida dos veces ya es
   raro— y lo que se busca en ellas es la fecha y el nombre, no leerlas como prosa. */
.historial-del-hecho {
    margin-top: 0.7rem;
    font-size: 0.9rem;
}

.historial-del-hecho ol {
    margin: 0.4rem 0 0;
    padding-left: 1.2rem;
    display: grid;
    gap: 0.3rem;
}

/* Sin esto, `.mono` con su `letter-spacing` pega la hora contra el punto medio que la sigue. */
.historial-del-hecho .mono {
    margin-right: 0.15rem;
}

/* ---------------------------------------------------------------------------
   Usuarios
   --------------------------------------------------------------------------- */

.panel-usuarios {
    margin-top: 1.5rem;
}

.controles-usuarios {
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--borde);
}

.controles-usuarios > label:first-child {
    margin-top: 0;
}

label.filtro-inactivos {
    margin-top: 0.75rem;
}

.resultados-usuarios {
    margin-top: 0.75rem;
}

.tabla-usuarios {
    table-layout: fixed;
    margin-top: 0;
}

.tabla-usuarios .columna-persona {
    width: 31%;
}

.tabla-usuarios .columna-correo {
    width: 43%;
}

.tabla-usuarios .columna-estado {
    width: 26%;
}

.tabla-usuarios th,
.tabla-usuarios td {
    vertical-align: middle;
}

.tabla-usuarios .correo-usuario {
    overflow-wrap: anywhere;
}

.tabla-usuarios .estado-usuario {
    text-align: right;
}

.estado-usuario-contenido {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.estado-usuario-contenido .etiqueta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 1.5rem;
    line-height: 1;
}

.etiqueta.inicial {
    border: 1px solid var(--borde);
    background: transparent;
    color: var(--texto-tenue);
}

/* El encabezado de un grupo dentro de una tabla, sin tabla que lo acote: lo usa el listado de
   tasas de comisión, agrupado por a qué alcanza cada una. Va sin prefijo a propósito, porque el
   patrón no es de ninguna tabla en particular —lo mismo que ya pasó con `.columna-fecha`—.

   `.tabla-usuarios .fila-rol`, acá abajo, es este mismo bloque con otro nombre y con scope. No se
   unificaron dentro de este cambio para no tocar una pantalla ya verificada por algo que no es
   suyo: la próxima vez que se toque el listado de usuarios, esa clase se dobla en esta.

   La segunda línea va tenue y sin versalita: dice por qué ese nivel le gana a los de abajo, y esa
   es la única parte de la cascada que no se deduce mirando las filas. */
.fila-grupo th {
    padding: 1.25rem 0.6rem 0.35rem;
    border-bottom: 0;
    color: var(--texto-tenue);
    font-size: 0.8rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

tbody:first-of-type .fila-grupo th {
    padding-top: 0.75rem;
}

.fila-grupo th .tenue {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.75rem;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
}

.tabla-usuarios .fila-rol th {
    padding: 1.25rem 0.6rem 0.35rem;
    border-bottom: 0;
    color: var(--texto-tenue);
    font-size: 0.8rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.tabla-usuarios tbody:first-of-type .fila-rol th {
    padding-top: 0.75rem;
}

.cantidad-rol {
    margin-left: 0.45rem;
    font-size: 0.75rem;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
}

.tabla-usuarios tbody tr:last-child > * {
    border-bottom: 0;
}

.tabla-usuarios tr:not(.fila-rol):hover > * {
    background: var(--menta-suave);
}

.tabla-usuarios tr.inactiva .persona-usuario a {
    color: var(--texto-tenue);
}

.resumen-usuarios {
    margin: 1rem 0 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.vacio-usuarios {
    margin: 0;
    padding: 1.5rem 0;
    text-align: center;
}

@media screen and (max-width: 40rem) {
    .tabla-usuarios,
    .tabla-usuarios tbody {
        display: block;
        overflow: visible;
    }

    .tabla-usuarios colgroup,
    .tabla-usuarios thead {
        display: none;
    }

    .tabla-usuarios .fila-rol {
        display: block;
    }

    .tabla-usuarios .fila-rol th {
        display: block;
        width: auto;
        padding-right: 0.4rem;
        padding-left: 0.4rem;
    }

    .tabla-usuarios tr:not(.fila-rol) {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "persona estado"
            "correo estado";
        gap: 0.15rem 1rem;
        padding: 0.7rem 0.4rem;
        border-bottom: 1px solid var(--borde);
    }

    .tabla-usuarios tr:not(.fila-rol) > * {
        width: auto;
        padding: 0;
        border-bottom: 0;
    }

    .tabla-usuarios tr:not(.fila-rol) .persona-usuario {
        grid-area: persona;
    }

    .tabla-usuarios tr:not(.fila-rol) .correo-usuario {
        grid-area: correo;
    }

    .tabla-usuarios tr:not(.fila-rol) .estado-usuario {
        grid-area: estado;
        align-self: center;
    }
}

/* ---------------------------------------------------------------------------
   Servicios
   --------------------------------------------------------------------------- */

.panel-servicios {
    margin-top: 1.5rem;
}

.controles-servicios {
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--borde);
}

.controles-servicios > label:first-child {
    margin-top: 0;
}

label.filtro-inactivos-servicios {
    margin-top: 0.75rem;
}

.resultados-servicios {
    margin-top: 0.75rem;
}

.tabla-servicios {
    table-layout: fixed;
    margin-top: 0;
}

.tabla-servicios .columna-servicio {
    width: 46%;
}

.tabla-servicios .columna-codigo {
    width: 18%;
}

.tabla-servicios .columna-precio {
    width: 17%;
}

.tabla-servicios .columna-estado-servicio {
    width: 19%;
}

.tabla-servicios th,
.tabla-servicios td {
    vertical-align: middle;
}

.descripcion-servicio {
    display: block;
    margin-top: 0.1rem;
    font-size: 0.85rem;
    font-weight: 400;
}

.tabla-servicios .codigo-servicio {
    overflow-wrap: anywhere;
}

/* De qué es el precio, debajo del precio y solo cuando no es «unidad». En su propia línea
   porque la columna es angosta y "$300 por mL" corrido se partía entre el número y la unidad,
   que es justo lo que no se puede separar: el precio del mililitro leído sin su unidad parece
   un servicio de $300. Hereda el `tabular-nums` de `.numero` sin arrastrar su `nowrap`. */
.unidad-servicio {
    display: block;
    margin-top: 0.1rem;
    font-size: 0.85rem;
    font-weight: 400;
}

.tabla-servicios .estado-servicio {
    text-align: right;
}

.estado-servicio-contenido {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.estado-servicio-contenido .etiqueta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 1.5rem;
    line-height: 1;
}

.etiqueta.exenta {
    border: 1px solid var(--borde);
    background: transparent;
    color: var(--texto-tenue);
}

.tabla-servicios .fila-categoria-servicio th {
    padding: 1.25rem 0.6rem 0.35rem;
    border-bottom: 0;
    color: var(--texto-tenue);
    font-size: 0.8rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.tabla-servicios tbody:first-of-type .fila-categoria-servicio th {
    padding-top: 0.75rem;
}

.cantidad-categoria {
    margin-left: 0.45rem;
    font-size: 0.75rem;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
}

.tabla-servicios tbody tr:last-child > * {
    border-bottom: 0;
}

.tabla-servicios tr:not(.fila-categoria-servicio):hover > * {
    background: var(--menta-suave);
}

.tabla-servicios tr.inactiva .nombre-servicio,
.tabla-servicios tr.inactiva .nombre-servicio a {
    color: var(--texto-tenue);
}

.resumen-servicios {
    margin: 1rem 0 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.vacio-servicios {
    margin: 0;
    padding: 1.5rem 0;
    text-align: center;
}

.vacio-catalogo > :first-child {
    margin-top: 0;
}

@media screen and (max-width: 40rem) {
    .tabla-servicios,
    .tabla-servicios tbody {
        display: block;
        overflow: visible;
    }

    .tabla-servicios colgroup,
    .tabla-servicios thead {
        display: none;
    }

    .tabla-servicios .fila-categoria-servicio {
        display: block;
    }

    .tabla-servicios .fila-categoria-servicio th {
        display: block;
        width: auto;
        padding-right: 0.4rem;
        padding-left: 0.4rem;
    }

    .tabla-servicios tr:not(.fila-categoria-servicio) {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "servicio precio"
            "codigo estado";
        gap: 0.3rem 1rem;
        padding: 0.7rem 0.4rem;
        border-bottom: 1px solid var(--borde);
    }

    .tabla-servicios tr:not(.fila-categoria-servicio) > * {
        width: auto;
        padding: 0;
        border-bottom: 0;
    }

    .tabla-servicios tr:not(.fila-categoria-servicio) .nombre-servicio {
        grid-area: servicio;
    }

    .tabla-servicios tr:not(.fila-categoria-servicio) .codigo-servicio {
        grid-area: codigo;
    }

    .tabla-servicios tr:not(.fila-categoria-servicio) .precio-servicio {
        grid-area: precio;
    }

    .tabla-servicios tr:not(.fila-categoria-servicio) .estado-servicio {
        grid-area: estado;
    }
}

/* ---------------------------------------------------------------------------
   Auditoría
   --------------------------------------------------------------------------- */

/* Cambios y accesos son dos pantallas de la misma sección. La barra de arriba marca la
   sección; esto marca en cuál de las dos se está parado. */
nav.sub {
    display: flex;
    gap: 1.25rem;
    margin: 0.5rem 0 1rem;
    border-bottom: 1px solid var(--borde);
}

nav.sub a {
    padding: 0.35rem 0;
    margin-bottom: -1px;
    border-bottom: 2px solid transparent;
    text-decoration: none;
}

nav.sub a.actual {
    color: var(--turquesa-profundo);
    font-weight: 600;
    border-bottom-color: var(--turquesa);
}

/* El diff vive dentro de una celda: sin viñetas ni sangría, porque compite con el resto de
   la fila y lo que tiene que leerse primero es el valor nuevo. */
ul.diff {
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.9rem;
}

ul.diff li + li {
    margin-top: 0.15rem;
}

/* De dónde venía el valor: presente para poder comparar, apagado para no disputarle la
   atención a en qué quedó. */
.antes {
    color: var(--texto-tenue);
}

/* Una celda de auditoría puede traer ocho campos; sin esto, una nota larga estira la
   columna y desplaza la tabla entera de lado. */
ul.diff li {
    overflow-wrap: anywhere;
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
    }
}

/* ---------------------------------------------------------------------------
   La agenda del día

   Una tabla y no una retícula dibujada a mano: la pregunta que trae a alguien acá —"¿a qué hora
   tengo libre la sala felina?"— se contesta bajando por una columna, y una tabla con encabezados
   es lo único que también se puede recorrer con un lector de pantalla.

   Todo lo que sigue existe para que se lea como un día y no como un listado: alto de fila
   constante, la media hora más callada que la hora en punto, y el hueco comportándose como lo que
   es, un control del tamaño de la celda.
   --------------------------------------------------------------------------- */
.agenda-dia .columna-hora {
    width: 4.5rem;
}

/* Un ancho mínimo por recurso, y no un reparto elástico: con cinco columnas el nombre del tutor
   se parte en cuatro renglones y las filas dejan de medir lo mismo, que es de lo único que
   depende poder leer el día. Cuando no caben, la tabla se desplaza dentro de su sección —eso ya
   lo hace el bloque de pantallas angostas— en vez de apretarse hasta ser ilegible. */
.agenda-dia thead th:not(.columna-hora) {
    min-width: 7rem;
}

/* El alto fijo es lo que convierte la tabla en una escala de tiempo. Sin él, media hora vacía
   mide dos milímetros y media hora con una consulta mide tres centímetros: la columna deja de
   poder recorrerse de un vistazo y las horas dejan de estar donde uno las va a buscar. */
.agenda-dia th,
.agenda-dia td {
    height: 2.5rem;
    padding: 0.25rem 0.4rem;
}

/* La hora en punto manda y la media hora acompaña: rotularlas igual llena la columna de la
   izquierda de cifras que compiten entre ellas. */
.agenda-dia tr.media th[scope="row"] {
    color: var(--texto-tenue);
    font-size: 0.85rem;
}

/* La única línea fuerte es la del cambio de hora; las divisiones de adentro se callan. Qué fila
   cierra la hora depende de en cuántos bloques se divida el día —con medias horas es la de las
   09:30 y con cuartos la de las 09:45—, y por eso la clase la pone Kotlin: el CSS no sabe cuánto
   dura un bloque. Sin esto, un día en cuartos se lee como cuatro líneas iguales por hora y deja de
   distinguirse dónde empieza cada una. */
.agenda-dia tbody tr:not(.cierra-hora) > * {
    border-bottom-color: transparent;
}

/* El hover de fila del resto de las tablas acá estorba: pinta de menta las celdas libres, que es
   justo el color con el que se distingue lo ocupado. Lo que se resalta es la celda que está bajo
   el cursor, que además es la única que se puede pinchar. */
.agenda-dia tbody tr:hover td {
    background: none;
}

.agenda-dia td.hueco {
    padding: 0;
}

/* El hueco es un enlace del tamaño de la celda: en el mesón se pincha con el dedo y sin dar en el
   blanco a la primera. El "+" aparece al apuntarlo —dibujarlo siempre llena la grilla de cruces y
   el día deja de leerse— y también al recibir el foco, porque quien tabula necesita ver dónde
   está parado. */
.agenda-dia td.hueco a {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    min-height: 2rem;
    color: transparent;
    text-decoration: none;
}

.agenda-dia td.hueco a:hover,
.agenda-dia td.hueco a:focus-visible {
    background: var(--menta-suave);
    color: var(--turquesa-profundo);
}

/* La hora tomada se lee como un bloque macizo con el canto marcado: es lo que hace que una
   cirugía de tres horas se vea como una y no como seis medias horas seguidas. */
.agenda-dia td.tomada {
    background: var(--menta-suave);
    border-left: 3px solid var(--turquesa);
}

.agenda-dia .hora-tomada a {
    font-weight: 600;
}

.agenda-dia .hora-tomada small {
    display: block;
}

/* Lo que ya ocurrió —atendida, no asistió— sigue ocupando su franja, que es el registro de lo que
   se reservó, pero deja de pedir algo. */
.agenda-dia .hora-tomada.cerrada {
    color: var(--texto-tenue);
}

/* Dos horas cortas caídas en la misma media hora comparten celda, porque no hay dos columnas
   donde ponerlas. La línea las separa para que no se lean como una sola reserva. */
.agenda-dia .hora-tomada + .hora-tomada {
    margin-top: 0.3rem;
    padding-top: 0.3rem;
    border-top: 1px solid var(--borde);
}

/* Los minutos que sobran dentro de un bloque tomado. La línea de puntos es la que dice que esto
   no es otra reserva sino lo que queda entre medio: con la línea entera de arriba se leía como
   una segunda hora tomada, que es exactamente lo contrario de lo que ofrece.

   `block` y no en línea porque comparte celda con el nombre del tutor, que ya ocupa su renglón, y
   corrido detrás quedaba partido entre dos líneas. Más chico y más apagado que la cita: lo que se
   recorre buscando en esta pantalla son las horas comprometidas, no los ratos sueltos. */
.agenda-dia .hueco-parcial {
    display: block;
    margin-top: 0.3rem;
    padding-top: 0.3rem;
    border-top: 1px dashed var(--borde);
    font-size: 0.85rem;
    text-decoration: none;
}

/* Y cuando caen antes de la cita, la línea va abajo: separa del mismo modo y deja los minutos
   libres arriba, que es donde ocurren. */
.agenda-dia .hueco-parcial:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
    margin-bottom: 0.3rem;
    padding-bottom: 0.3rem;
    border-bottom: 1px dashed var(--borde);
}

.agenda-dia .hueco-parcial:hover,
.agenda-dia .hueco-parcial:focus-visible {
    text-decoration: underline;
}

/* Los dos campos que dependen del tipo de atención viven dentro de un `div` para que htmx pueda
   reemplazarlos de una pieza. Sin esto, ese envoltorio se lleva **una** celda de la grilla de
   campos y los dos quedan uno debajo del otro y angostos; con `contents` el envoltorio existe
   para htmx y desaparece para el diseño. */
#propuesta {
    display: contents;
}

/* ---------------------------------------------------------------------------
   Mis comisiones: lo propio, en la mano (N2)

   Es la única pantalla del sistema dibujada a 360–390 px desde el primer trazo, y no porque N
   adelante la pasada angosta de nadie: su enunciado *es* el teléfono —«este mes llevo 600 lucas»,
   dicho por alguien que no va a abrir un computador para saberlo—. De ahí sale todo lo de acá
   abajo: ninguna tabla que desplazar de lado, el número grande primero y el detalle como lista.
   --------------------------------------------------------------------------- */

/* El monto es la pregunta entera de la pantalla, y por eso es lo único acá con tamaño de titular.
   `clamp` y no una medida fija: seis cifras a 2.6rem se salen del borde en un teléfono de 360 px,
   y 2rem en el escritorio no se distingue de un `h2` cualquiera. */
.monto-del-periodo {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.6rem;
    margin: 0 0 0.4rem;
}

.monto-del-periodo strong {
    color: var(--turquesa-profundo);
    font-family: var(--fuente-marca);
    font-size: clamp(2rem, 10vw, 2.6rem);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

/* El rango va pegado al monto porque es lo que lo hace verdadero: «$63.400» sin fechas no dice
   nada. Con el `1em` que el navegador le pone a un `p`, el periodo flotaba lejos de la cifra que
   acota y en el teléfono quedaba abajo del pliegue. */
.periodo-del-monto {
    margin: 0 0 0.4rem;
}

/* Los dos saltos, uno en cada extremo. `space-between` con un solo enlace lo deja a la izquierda,
   que es donde tiene que quedar «Periodo anterior» cuando el siguiente todavía no ha empezado. */
.paso-de-periodo {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin-top: 0.9rem;
}

/* El detalle es una lista y no una tabla. Sin `list-style: none` el bullet se come, a 360 px, el
   ancho que necesita el monto para no bajar de línea. */
.mis-operaciones {
    display: grid;
    gap: 0.6rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* La caja de una operación. Es el mismo bloque que `.bloque-de-campos` —relleno, borde, radio y
   superficie— y no es esa clase: aquella viste un `fieldset` de formulario y su nombre lo dice.
   Con `.medio-cobro` y `.factura-cobro` ya son cuatro iguales, y queda anotado acá como está
   anotado allá: conviene doblarlos en uno la próxima vez que se toque el cobro. */
.mi-operacion {
    padding: 0.7rem 0.8rem;
    border: 1px solid var(--borde);
    border-radius: 0.55rem;
    background: var(--superficie);
}

/* El nombre a la izquierda y el monto a la derecha, con `wrap` para que en el teléfono el monto
   baje entero en vez de comprimir el nombre hasta partirlo por sílabas. */
.encabezado-de-operacion {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.2rem 0.8rem;
}

/* El que cede es el nombre y no el monto: el número es lo que se viene a mirar. Sin `min-width: 0`
   un nombre de servicio largo fija el mínimo del elemento y empuja el monto fuera de la caja. */
.encabezado-de-operacion .apilado {
    flex: 1 1 12rem;
    min-width: 0;
}

/* Los párrafos de adentro sin el `1em` del navegador: son dos renglones muertos por operación, y
   en una lista de doce eso es media pantalla de teléfono. Es lo mismo que hace `.bloque-de-campos`
   con los suyos, por la misma razón. */
.mi-operacion p {
    margin: 0.35rem 0 0;
}

/* La cuenta que produjo la comisión va más chica y debajo: se lee cuando alguien pregunta por qué
   ese número, que no es cada vez que abre la pantalla. Es lo que el anexo pone en su segunda fila,
   escrito para una pantalla angosta en vez de para una tabla de seis columnas. */
.cuenta-de-la-operacion {
    font-size: 0.85rem;
}

/* ---------------------------------------------------------------------------
   La hoja: la epicrisis, que es lo único que se imprime

   En pantalla se ve como el resto del sistema —hereda `main`— pero está pensada para el
   papel. Todo lo que acá se ajusta responde a algo que se rompe al imprimir: los fondos de
   sección que se comen el tóner, la consulta que queda partida entre dos páginas, y el
   encabezado de la clínica, que en pantalla no existe en ninguna otra pantalla.
   --------------------------------------------------------------------------- */
/* El membrete: lo único de la aplicación que se dibuja para el papel y no para la pantalla, y
   desde F3 tiene que probarlo. Los colores de la clínica se comprueban contra blanco —el único
   fondo que la impresora garantiza— y su logo es un PNG con las tintas que eligió su diseñador.
   Sobre Grafito clínico las dos cosas se caen: un color que pasa 4,5:1 sobre blanco queda casi
   negro contra el gris de la superficie, y un logo de tinta oscura desaparece. Así que acá gana
   el papel en los dos temas, que además es lo que se espera de una vista previa.

   Los tokens se redefinen para el subárbol en vez de escribir el color en cada regla: así el
   `.tenue` de adentro también queda en tinta de papel. Eso arregla de paso algo que se imprimía
   mal desde antes —en tema oscuro el RUT salía en gris claro sobre blanco, casi invisible—, pero
   solo dentro del membrete: el resto de la hoja sigue como estaba. */
.hoja .membrete {
    --texto: var(--tinta-papel);
    --texto-tenue: var(--tinta-papel-tenue);
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1rem;
    border-radius: 0.5rem 0.5rem 0 0;
    background: var(--blanco);
    color: var(--tinta-papel);
    /* El color principal de la clínica cuando lo hay; si no, la paleta del sistema, que es como
       se imprimía hasta F3. Lo declara la hoja entera —`th:style` en el `<main>`— porque el otro
       color de la marca cierra el pie, que está fuera de este bloque. */
    border-bottom: 2px solid var(--marca-principal, var(--turquesa));
}

.hoja .membrete .tenue {
    display: block;
    font-size: 0.85rem;
}

/* Alto y no ancho: los logos llegan cuadrados, apaisados o verticales, y limitar el ancho dejaría
   uno alto ocupando media hoja. 3,5rem son unos 15 mm impresos, la altura de un membrete de
   carta. El ancho máximo es la guarda del caso raro —un logo muy apaisado— y `contain` impide que
   cualquiera de los dos topes lo deforme. */
.logo-clinica {
    max-height: 3.5rem;
    max-width: 12rem;
    width: auto;
    object-fit: contain;
}

/* El nombre de la clínica en su color. Es lo que hace que una clínica sin logo igual tenga
   membrete propio, y es la razón por la que a este color se le exige el más alto de los dos
   umbrales de contraste sobre blanco: acá es texto y no adorno (ver ColorDeMarca, que además
   dice por qué esos umbrales ya no son los de WCAG AA). */
.nombre-clinica {
    color: var(--marca-principal, inherit);
}

/* En el teléfono el logo se va arriba y el texto abajo. Al lado, un logo de 12rem deja el RUT en
   una columna de tres palabras por línea. */
@media screen and (max-width: 30rem) {
    .hoja .membrete {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.6rem;
    }
}

/* Una atención se lee como bloque: dos columnas de etiqueta y contenido, con el texto largo
   —anamnesis, tratamiento— respetando los saltos de línea que escribió el veterinario. */
.hoja .consulta dd {
    white-space: pre-wrap;
}

/* La línea que cierra la hoja, y el segundo de los dos colores de la marca. Un membrete de verdad
   abre arriba y cierra abajo; acá además es lo que le da un lugar al color de acento sin ponerlo
   debajo de ningún texto, que es lo que permite exigirle 3:1 y no 4,5:1 (ver ColorDeMarca).

   La línea la tenían tres de las cuatro hojas —vivía en `.pie-documento`— y la epicrisis no. Se
   subió acá para que la marca cierre las cuatro igual; la epicrisis gana una línea sobre su pie,
   que es lo que ya hacían el presupuesto, la boleta y la receta. */
.hoja .pie {
    margin-top: 2.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--marca-acento, var(--borde));
}

/* Contra el margen derecho, que es donde se firma un papel. Pegada al izquierdo quedaba justo
   debajo del texto del pie y se leía como un párrafo más —un renglón en blanco con una raya—;
   a la derecha el espacio vacío a su izquierda es lo que la anuncia como lugar para firmar.
   Lo decidimos nosotros: no es exigencia de ninguna norma y vale para todas las clínicas.

   `width` y no `max-width` porque el margen automático es lo que la empuja: con el ancho en
   `auto` los márgenes automáticos se resuelven en cero y la caja se queda a la izquierda. */
.hoja .firma {
    width: min(18rem, 100%);
    margin-top: 3rem;
    margin-left: auto;
    /* El rótulo centrado bajo la línea, igual que en la receta: son el mismo objeto en dos
       hojas distintas y no tienen por qué verse distinto. */
    text-align: center;
}

/* La línea sobre la que se firma. Va con borde y no con guiones bajos para que no se corte
   entre dos líneas cuando el pie cae justo al final de la página. */
.hoja .firma span {
    display: block;
    border-top: 1px solid var(--texto);
}

.hoja .firma p {
    margin: 0.25rem 0 0;
}

/* Quién emitió la hoja y cuándo, contra el mismo borde que la firma. Pegada a la izquierda le
   quedaba enfrentada: una esquina abajo a la izquierda y otra abajo a la derecha, y el pie se
   leía como dos cierres distintos de la misma hoja. Con las dos al mismo lado el papel cierra
   por uno solo, que es además como cierra la receta, y las dos se entregan en el mismo mesón.

   Es la única línea de pie que se mueve. La del presupuesto y la de la boleta se quedan a la
   izquierda porque esas hojas no llevan firma: ahí no hay nada enfrente con qué chocar.
   Lo decidimos nosotros: no lo manda ninguna norma. */
.hoja .emitido-por {
    text-align: right;
}

@media print {
    /* Los botones y el enlace de volver no son parte del documento. */
    .sin-imprimir {
        display: none !important;
    }

    /* Sobre papel manda el papel: aunque la pantalla esté en Grafito clínico, lo que sale de
       la impresora es tinta sobre blanco. Sin esto, el tema oscuro imprime un rectángulo
       gris de tinta por cada sección.

       **Y una sola tinta, en dos niveles.** Es la regla de la hoja impresa entera: el cuerpo en
       `--tinta-papel`, y lo secundario —etiquetas, encabezados de tabla, el folio, el pie— en
       `--tinta-papel-tenue`. Nada más, aparte de los dos colores que configura la clínica, que
       tocan el membrete y las dos líneas y ninguna otra cosa.

       Hasta el 16-08-2026 eran tres tintas y ninguna decidida: `black` acá, `var(--texto)` en las
       reglas que declaran su color y `var(--texto-tenue)` en las apagadas. Las dos primeras hacen
       exactamente el mismo trabajo —texto a plena tinta— y salían distintas, con el agravante de
       que el membrete ya se imprimía en `--tinta-papel`: el encabezado y el cuerpo de la misma hoja
       en dos negros que no eran el mismo. Medido sobre una factura: cuerpo `#000000`, Total
       `#244643`, etiquetas `#58716d`.

       Se eligió la tinta de papel y no el negro puro justamente por el membrete: al revés habría
       que repintarlo a él, y eso ya no es legibilidad sino identidad. Con 10,33:1 sobre blanco
       —y 5,25:1 el nivel apagado— las dos siguen siendo texto legible, no gris decorativo. */
    body {
        background: white;
        color: var(--tinta-papel);
    }

    /* Y los tokens con él, que es la otra mitad del mismo problema: `color` se hereda, pero toda
       regla que **declare** `color: var(--texto)` se lleva el valor de la pantalla a la impresora.
       En Grafito eso es `#f1f6f4` sobre blanco, o sea nada, y así salió la fila del **Total** de la
       primera factura que se imprimió, mientras el detalle que esa fila suma se leía a plena tinta.

       Redefinir los dos tokens en vez de pintar cada regla es lo que ya hace `.hoja .membrete`
       —desde que el RUT salía gris claro por esto mismo—, y acá tiene además una garantía exacta:
       valen lo mismo que `--texto` y `--texto-tenue` del tema claro, así que imprimir desde Grafito
       produce la misma hoja que imprimir desde Menta. Antes eran dos hojas y solo una servía. */
    .hoja {
        --texto: var(--tinta-papel);
        --texto-tenue: var(--tinta-papel-tenue);
        max-width: none;
        padding: 0;
    }

    .hoja section {
        background: none;
        border: none;
        box-shadow: none;
        padding: 0;
        margin-top: 1.25rem;
        /* Que una consulta no quede partida entre dos hojas: leerla a caballo entre dos
           páginas es exactamente lo que hace que un resumen clínico se malinterprete. */
        break-inside: avoid;
    }

    /* El `h1` sí necesita su regla: fuera de la impresora se pinta con `--turquesa-profundo`, que
       es marca de Vetika y no tiene nada que hacer en el papel que una clínica le entrega a un
       tutor. Lo que cambió el 16-08-2026 es a qué se lo repinta: antes a `black`, que era la
       tercera tinta entrando por la puerta de atrás. */
    .hoja h1,
    .hoja h2 {
        color: var(--tinta-papel);
    }

    .hoja .etiqueta {
        border: 1px solid var(--tinta-papel);
        background: none;
        color: var(--tinta-papel);
    }
}

/* ---------------------------------------------------------------------------
   Documentos impresos: presupuesto, DTE y receta

   Comparten la gramática de la epicrisis —membrete, hoja blanca y controles que desaparecen—
   pero no su estructura clínica. La jerarquía acá es emisor → documento → detalle → firma o
   advertencia, que es la lectura natural de una hoja que se entrega en el mesón.
   --------------------------------------------------------------------------- */
.hoja-documento {
    max-width: 58rem;
}

.cabecera-documento {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 2rem;
    margin-top: 2rem;
}

.cabecera-documento h1 {
    margin-bottom: 0.15rem;
}

.folio-documento {
    margin: 0;
    color: var(--texto-tenue);
    font-variant-numeric: tabular-nums;
}

/* Se lee como un sello de documento, no como un estado interactivo de la aplicación. */
.sello-no-boleta {
    display: inline-block;
    flex: 0 0 auto;
    padding: 0.55rem 0.75rem;
    border: 2px solid var(--rojo);
    color: var(--rojo-profundo);
    font-size: 0.85rem;
    line-height: 1.2;
    text-align: center;
}

/* El mismo sello, en el ámbar de "hay que mirarlo y ya ocurrió": el anexo de un periodo que
   todavía no se cierra puede crecer, y entregado sin decirlo se lee como definitivo. No va en el
   rojo del "no válido como boleta" porque ahí el papel afirma algo que no es —esto no es una
   boleta— y acá el papel es correcto, solo que todavía no está completo. */
.sello-provisorio {
    display: inline-block;
    flex: 0 0 auto;
    padding: 0.55rem 0.75rem;
    border: 2px solid var(--ambar-profundo);
    color: var(--ambar-profundo);
    font-size: 0.85rem;
    line-height: 1.2;
    text-align: center;
}

.advertencia-impresa {
    margin-top: 1.25rem;
    padding: 0.85rem 1rem;
    border: 1px solid var(--rojo);
    background: var(--rojo-suave);
    color: var(--rojo-profundo);
}

.advertencia-impresa strong {
    display: block;
    margin-bottom: 0.15rem;
}

.hoja-documento .datos-documento dl {
    grid-template-columns: minmax(5rem, max-content) 1fr;
}

.tabla-documento tbody tr,
.tabla-documento tfoot tr {
    break-inside: avoid;
}

/* ---- El anexo del art. 54 bis: la comisión y la cuenta que la produjo, en dos filas ----

   El art. 54 bis inc. 3.º pide la operación **y la forma empleada para su cálculo**. Son cinco
   pasos —bruto, insumo, neto, participación, tasa— y en una columna más no caben: se leen como una
   frase, así que van en una fila propia debajo de su operación.

   Sin quitarle el borde a la de arriba, las dos se ven como dos operaciones distintas y la hoja
   parece tener el doble de filas de las que tiene. */
tr:has(+ .calculo-de-la-comision) td {
    border-bottom: none;
}

.calculo-de-la-comision td {
    padding-top: 0;
    font-size: 0.85rem;
}

/* Cada operación es su propio `tbody` justamente para esto: que la comisión y su explicación no
   queden a caballo entre dos hojas. Una cifra cuyo cálculo pasó a la página siguiente es lo que
   estas dos hojas existen para evitar: el anexo porque la ley pide la forma del cálculo pegada a
   su operación, y la del externo porque ese total es el que alguien va a boletear. */
@media print {
    .hoja-anexo .tabla-documento tbody,
    .hoja-del-externo .tabla-documento tbody {
        break-inside: avoid;
    }
}

.desglose-impreso {
    break-inside: avoid;
}

.texto-documento {
    max-width: 75ch;
    white-space: pre-wrap;
}

.bloque-receta .texto-documento {
    min-height: 4rem;
    font-size: 1.05rem;
    line-height: 1.65;
}

/* Si la firma de una receta pasa completa a otra página, esta identificación pasa con ella. Es
   deliberadamente compacta: no compite con el membrete cuando ambas quedan en la misma página. */
.identidad-firma {
    margin: 0;
    color: var(--texto-tenue);
    font-size: 0.85rem;
    text-align: right;
}

/* La clase sigue existiendo aunque ya no traiga borde ni relleno propios —los subió `.hoja .pie`
   cuando el color de acento necesitó estar en las cuatro hojas—: es el gancho con el que
   `@media print` mantiene el pie entero en la misma página. */

/* A la derecha por lo mismo que `.hoja .firma`, y con ella tiene que moverse: la receta y la
   epicrisis se entregan en el mismo mesón y una hoja firma abajo a la izquierda y la otra abajo
   a la derecha se ve como dos sistemas. */
.firma-profesional {
    width: min(20rem, 100%);
    margin-top: 5rem;
    margin-left: auto;
    text-align: center;
}

.firma-profesional > span {
    display: block;
    border-top: 1px solid var(--texto);
}

.firma-profesional p {
    margin: 0.25rem 0 0;
}

/* La firma y el timbre descansan sobre la raya, no flotan encima: alineados abajo, para que la
   imagen termine donde empieza la línea igual que una firma de puño.

   4rem son unos 17 mm impresos, que es lo que ocupa una firma sobre una receta de la clínica. El
   `min-height` mantiene la raya en el mismo lugar cuando hay una sola de las dos imágenes, o
   ninguna todavía cargada, para que la hoja no cambie de forma según quién atendió. */
.credencial-aplicada {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 1rem;
    min-height: 4rem;
}

.credencial-aplicada img,
.muestra-de-credencial img {
    max-height: 4rem;
    max-width: 45%;
    object-fit: contain;
}

/* Una firma escaneada es tinta oscura sobre transparencia, así que sobre Grafito clínico
   desaparece. Es exactamente el defecto que F3 encontró con el logo y resolvió dándole papel al
   membrete, y acá vale por la misma razón: esto se dibuja para la impresora.

   El papel se pone **solo cuando hay imagen** —de ahí la clase aparte—: sin credencial este bloque
   es una raya y dos líneas de texto que se leen perfecto en los dos temas, y una tarjeta blanca en
   medio de una hoja oscura sería peor que lo que arregla.

   `.muestra-de-credencial` es lo mismo en la pantalla de la cuenta propia, donde alguien está
   mirando precisamente si la imagen que subió se ve. */
.firma-profesional.con-credencial,
.muestra-de-credencial {
    --texto: var(--tinta-papel);
    --texto-tenue: var(--tinta-papel-tenue);
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    background: var(--blanco);
    color: var(--tinta-papel);
}

/* En la cuenta propia la muestra es una caja chica dentro de la sección, no un pie de hoja. */
.muestra-de-credencial {
    width: min(20rem, 100%);
    margin: 0.5rem 0;
    border: 1px solid var(--borde);
    text-align: center;
}

/* Cada bloque —la firma, el timbre— se separa del anterior sin necesitar una sección propia: son
   dos formularios cortos que se leen como una sola pregunta. */
.credencial + .credencial {
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--borde);
}

/* La vista previa de la pantalla de marca: la misma hoja, en chico y dentro de una sección.

   Va sobre papel entero y no solo en el membrete, porque acá lo que se está mirando es
   precisamente si los dos colores funcionan sobre blanco. Es la única parte de la aplicación
   —fuera del membrete— que no sigue al tema, y por la misma razón: se está previendo una
   impresión, no una pantalla. */
.vista-previa {
    --texto-tenue: var(--tinta-papel-tenue);
    padding: 0 1rem 1rem;
    background: var(--blanco);
    color: var(--tinta-papel);
    border: 1px solid var(--borde);
    border-radius: 0.5rem;
}

/* El membrete llega al borde de la hoja, como en el papel. Los márgenes negativos son lo que
   deja que la banda blanca ocupe el ancho completo sin sacar al resto del relleno. */
.vista-previa .membrete {
    margin: 0 -1rem;
}

/* El mensaje de cobro escrito tal cual sale: de ejemplo en la pantalla de datos de transferencia,
   y de verdad en la de cobro cuando el tutor no tiene teléfono con el que armar el enlace y hay
   que copiarlo a mano.

   Es un `pre` y no un párrafo porque los saltos de línea son parte del mensaje: el tutor copia el
   número de cuenta de una línea suya hacia la aplicación de su banco, y un párrafo los colapsa
   dejando los cinco datos corridos. Lo que hay que apagarle es la monoespaciada que el navegador
   le pone de fábrica —en este sistema esa familia no existe— y el `pre` a secas, que en un mensaje
   de sesenta caracteres obligaría a desplazar de lado.

   Angosto a propósito, y esta sí sigue al tema, al revés que la vista previa del membrete: el
   mensaje se lee en la pantalla de un teléfono y no se imprime. A 48rem un mensaje de seis líneas
   se ve como un párrafo suelto y no como lo que le va a llegar al tutor. */
.mensaje-de-cobro {
    font-family: inherit;
    font-size: 1rem;
    line-height: 1.6;
    white-space: pre-wrap;
    max-width: 26rem;
    margin: 0;
    padding: 1rem;
    background: var(--campo);
    color: var(--texto);
    border: 1px solid var(--borde);
    border-radius: 0.75rem;
}

@media screen and (max-width: 640px) {
    .cabecera-documento {
        flex-direction: column;
        gap: 1rem;
    }
}

@media print {
    .hoja-documento {
        font-size: 10.5pt;
    }

    /* Una tabla larga puede continuar en la hoja siguiente; cada fila se mantiene unida. */
    .hoja-documento section {
        break-inside: auto;
    }

    .hoja-documento .datos-documento,
    .hoja-documento .pie-documento {
        break-inside: avoid;
    }

    /* Una indicación corta queda junta por sus viudas y huérfanas; una larga tiene que poder
       continuar en la página siguiente. Declarar el bloque entero indivisible dejaba casi vacía
       la primera media carta cuando el texto ya no cabía completo. */
    .hoja-receta .bloque-receta {
        break-inside: auto;
    }

    .hoja-receta .bloque-receta h2 {
        break-after: avoid;
    }

    .hoja-receta .texto-documento {
        orphans: 3;
        widows: 3;
    }

    .sello-no-boleta,
    .advertencia-impresa {
        border-color: var(--tinta-papel);
        background: none;
        color: var(--tinta-papel);
    }

    /* Sobre papel, la banda del membrete ya no tiene por qué dibujarse: el papel es el fondo.
       Lo que sí queda es su línea, y en el color de la clínica cuando lo hay.

       Sin marca la línea sale **en la tinta del papel y no en turquesa**, que es un cambio respecto
       de antes de F3: el turquesa es la marca de Vetika, y no corresponde en el papel que una
       clínica le entrega a un tutor. La epicrisis lo venía imprimiendo así porque nunca tuvo esta
       regla. La alternativa era `black` hasta el 16-08-2026, y la cambió la regla de una sola
       tinta: una clínica sin marca configurada tenía sus dos líneas en un negro que no era el de
       ninguna otra cosa de la hoja. */
    .hoja .membrete {
        padding: 0 0 0.75rem;
        border-radius: 0;
        background: none;
        border-bottom-color: var(--marca-principal, var(--tinta-papel));
    }

    .hoja .pie {
        border-top-color: var(--marca-acento, var(--tinta-papel));
    }

    /* La línea sobre la que se firma, en tinta y no en el color del tema. La receta ya lo hacía;
       la epicrisis no, y por eso imprimir desde Grafito clínico le dejaba la raya casi blanca
       sobre el papel: `var(--texto)` sigue al tema y la impresora no. Se ve al mover las dos
       firmas juntas y es la misma regla, así que va en el mismo lugar.

       `.firma-profesional` necesita nombrarse aparte y no le basta con heredar: vive fuera de
       `.hoja` en la vista previa de `/marca`, donde redefine los tokens por su cuenta. */
    .firma-profesional > span,
    .hoja .firma span {
        border-color: var(--tinta-papel);
    }

    /* A5 y media carta no son una versión encogida de Carta: conservan la lectura, pero recuperan
       el espacio que en una hoja corta se pierde en rellenos, separaciones y la reserva de firma.
       Los selectores dependen del formato elegido, no del ancho calculado por el navegador; así
       una impresora con otra resolución no activa por accidente el diseño de teléfono. */
    html[data-papel="a5"] .hoja,
    html[data-papel="media-carta"] .hoja {
        font-size: 9.25pt;
    }

    html[data-papel="a5"] .hoja h1,
    html[data-papel="media-carta"] .hoja h1 {
        font-size: 17pt;
    }

    html[data-papel="a5"] .hoja h2,
    html[data-papel="media-carta"] .hoja h2 {
        font-size: 11.5pt;
    }

    html[data-papel="a5"] .hoja .membrete,
    html[data-papel="media-carta"] .hoja .membrete {
        gap: 0.65rem;
        padding-bottom: 0.55rem;
    }

    html[data-papel="a5"] .hoja .membrete .tenue,
    html[data-papel="media-carta"] .hoja .membrete .tenue {
        font-size: 8.25pt;
    }

    html[data-papel="a5"] .logo-clinica,
    html[data-papel="media-carta"] .logo-clinica {
        max-height: 2.8rem;
        max-width: 9rem;
    }

    html[data-papel="a5"] .cabecera-documento,
    html[data-papel="media-carta"] .cabecera-documento {
        gap: 1rem;
        margin-top: 1.1rem;
    }

    html[data-papel="a5"] .hoja section,
    html[data-papel="media-carta"] .hoja section {
        margin-top: 0.85rem;
    }

    html[data-papel="a5"] .hoja th,
    html[data-papel="a5"] .hoja td,
    html[data-papel="media-carta"] .hoja th,
    html[data-papel="media-carta"] .hoja td {
        padding: 0.35rem 0.3rem;
    }

    html[data-papel="a5"] .advertencia-impresa,
    html[data-papel="media-carta"] .advertencia-impresa {
        margin-top: 0.85rem;
        padding: 0.55rem 0.65rem;
    }

    html[data-papel="a5"] .bloque-receta .texto-documento,
    html[data-papel="media-carta"] .bloque-receta .texto-documento {
        min-height: 0;
        font-size: 9.75pt;
        line-height: 1.5;
    }

    html[data-papel="a5"] .hoja .pie,
    html[data-papel="media-carta"] .hoja .pie {
        margin-top: 1.4rem;
        padding-top: 0.7rem;
    }

    html[data-papel="a5"] .firma-profesional,
    html[data-papel="media-carta"] .firma-profesional {
        margin-top: 2.5rem;
    }

    html[data-papel="a5"] .credencial-aplicada,
    html[data-papel="media-carta"] .credencial-aplicada {
        min-height: 3rem;
    }

    html[data-papel="a5"] .credencial-aplicada img,
    html[data-papel="media-carta"] .credencial-aplicada img {
        max-height: 3rem;
    }

    html[data-papel="a5"] .hoja .firma,
    html[data-papel="media-carta"] .hoja .firma {
        margin-top: 2rem;
    }
}
