/* =====================================================================
   SP_BERACA — Hoja de estilos única
   Seguridad Privada Beraca SA de CV

   MÓVIL PRIMERO. El caso de uso que manda: un supervisor parado en la
   caseta de un cliente, con el celular en una mano, a pleno sol y con
   datos móviles. De ahí:
     - Objetivos táctiles de 48px (mínimo 44px), nunca enlaces diminutos.
     - Contraste alto: texto casi negro sobre blanco, sin grises lavados.
     - Tipografía de sistema: no se descarga ni una fuente.
     - Cero dependencias, cero CDN, un solo archivo.

   Índice
     01. Variables (design tokens)
     02. Base y reinicio
     03. Estructura
     04. Tipografía
     05. Botones
     06. Formularios
     07. Barra y navegación (sistema)
     08. Pestañas de sección
     09. Tarjetas y cifras
     10. Tablas
     11. Listas de operación
     12. Avisos y etiquetas
     13. Pantalla de acceso
     14. Sitio público
     15. Utilidades
     16. Pre-nómina y reportes
     17. Pase de lista (captura de asistencia)
     18. Rejilla empleado × día
     19. Marcaje, bitácora y piezas sueltas
     20. Modal de alta rápida
     21. Impresión
   ===================================================================== */

/* =====================================================================
   01. VARIABLES (design tokens)

   Todo valor que se repita vive aquí. Si un color, un espacio, un radio
   o un tamaño de letra aparece más de una vez en el archivo, es un token
   — no un número suelto. Así el sistema se ve como una sola pieza y se
   reafina desde un solo lugar.
   ===================================================================== */
:root {
    /* -------- Color de acción ----------------------------------------
       Antes esta escala era azul marino y se llamaba --azul-*. Se cambió
       a la familia del oro —y con ella el nombre, porque un token que se
       llama «azul» y pinta bronce es una trampa para el que venga— para
       que el cuerpo de la aplicación pertenezca al mismo mundo que el
       encabezado: escudo de oro sobre negro.

       Reparto de la escala, que NO es arbitrario:
         050–200  fondos y bordes tenues (fila apuntada, etiquetas)
         600–800  TEXTO sobre fondo claro: enlaces y sus estados
         900      cifras grandes del tablero

       Los 700/800 son bronce y no oro brillante a propósito: el oro del
       escudo (#d99413) sobre blanco da 2.4:1 y es ilegible como texto.
       El oro se usa RELLENO, con letra negra encima — ver --relleno-*. */
    --accion-900: #33240a;
    --accion-800: #5c3f0b;
    --accion-700: #7a5510;   /* enlace: 6.7:1 sobre blanco */
    --accion-600: #8a6414;
    --accion-200: #e2cd9c;
    --accion-100: #f4ead1;
    --accion-050: #faf5e9;

    /* -------- Marca --------------------------------------------------
       El escudo de la empresa es un león de oro sobre un disco negro. El
       cromo del sistema —barra, menú, pie, portada y acceso— se pinta con
       ese mismo negro para que el escudo se pare sobre lo suyo.

       Con la escala de acción ya en oro, el sistema entero se lee como
       una sola pieza: negro de marco, oro de acción, y el verde y el rojo
       reservados para lo único que debe gritar (asistencia y faltas). */
    --marca-900: #101013;   /* barra superior, pie, portada, acceso */
    --marca-800: #1b1b21;   /* segundo nivel: la fila del menú */
    --marca-700: #2b2b33;   /* filetes y separadores sobre negro */

    /* Acento ámbar: alto contraste bajo el sol, lenguaje de seguridad.
       Es también el oro del escudo: acento y marca son el mismo color. */
    --acento-700: #92610f;
    --acento-600: #b8790f;
    --acento-500: #d99413;
    --acento-200: #f6dcaa;
    --acento-100: #fdf1d8;

    /* Semánticos. El tono 700 es el que se usa sobre fondo claro:
       todos pasan AA a 15px, que es el tamaño de las tablas densas. */
    --exito-800: #0e4d2b;
    --exito-700: #14663a;
    --exito-600: #17804a;
    --exito-200: #a9dcc2;
    --exito-100: #dcf5e7;
    --error-800: #7a1414;
    --error-700: #9b1c1c;
    --error-600: #c02626;
    --error-200: #f0b9b9;
    --error-100: #fde5e5;
    /* La alerta se corrió de ámbar a NARANJA. En ámbar era prácticamente
       el mismo color que el oro de la marca, y desde que el oro es el
       color de acción, una advertencia y un botón se veían igual. */
    --alerta-800: #7c3208;
    --alerta-700: #9a3d0a;
    --alerta-600: #c2540c;
    --alerta-200: #f4cfa8;
    --alerta-100: #fdf0e4;
    --info-800:  #10345f;
    --info-700:  #17457f;
    --info-200:  #b9d0ee;
    --info-100:  #dfeaf9;

    /* Neutros TIBIOS (arena), no grises azulados: al lado del oro un gris
       frío se ve sucio, y era la razón principal de que el cuerpo de la
       página no pareciera del mismo sistema que el encabezado. Se
       conservaron las mismas luminosidades, así que todos los contrastes
       que ya pasaban siguen pasando.

       El texto es casi negro a propósito.
       --tinta-3 es el gris MÁS claro permitido para texto (5.6:1).
       Por debajo de eso no se lee a pleno sol: no lo aclares. */
    --tinta:      #1c1a15;
    --tinta-2:    #4d4740;
    --tinta-3:    #6e675d;
    --tinta-4:    #7d766b;   /* solo marcadores de posición e iconos */
    --linea:      #d8d2c6;
    --linea-suave:#ebe6dc;
    --fondo:      #faf8f4;
    --superficie: #ffffff;
    --superficie-2: #fbf9f5;
    --superficie-3: #f2eee6;

    /* Alias que algunos componentes ya referenciaban con respaldo. */
    --tinta-suave:  var(--tinta-3);
    --exito-suave:  var(--exito-100);

    /* -------- Rellenos sólidos ---------------------------------------
       LEER ANTES DE TOCAR EL MODO OSCURO.

       Las escalas 600–900 tienen DOS oficios en este archivo:

         a) color de TEXTO sobre un fondo tenue
            (.etiqueta--exito: fondo --exito-100, letra --exito-700)
         b) RELLENO de un botón sólido, siempre con letra blanca
            (.pase__boton--presente: fondo --exito-700, letra #fff)

       En claro conviven porque el mismo verde oscuro sirve de letra sobre
       verde pálido y de fondo bajo letra blanca. En oscuro se pelean: el
       texto tiene que ACLARARSE y el relleno tiene que seguir OSCURO, o el
       botón «Presente» queda verde pastel con letra blanca encima.

       Por eso el relleno se saca a sus propios tokens. Aquí valen
       exactamente lo mismo que antes —el modo claro no cambia ni un pixel—
       y en el bloque oscuro cada uno toma su rumbo. */
    --relleno-accion:   #c8890f;   /* oro macizo; letra NEGRA encima */
    --relleno-accion-2: #dda12a;   /* apuntado: el oro SUBE de brillo.
                                     Oscurecerlo bajaba el contraste con
                                     la letra negra a 4.3:1. */

    /* Letra sobre los rellenos de oro. El oro es claro: encima va negro,
       nunca blanco (blanco sobre #c8890f da 2.3:1 y no se lee). */
    --sobre-accion: #21160a;

    /* Relleno «fuerte»: carbón con letra blanca. Es lo que marca la
       pestaña abierta. En oro competiría con el botón principal, y la
       pestaña activa no es una acción: es un estado. */
    --relleno-fuerte:   #2a2a33;
    --relleno-fuerte-2: #3d3d48;   /* apuntada */
    --sobre-fuerte:     #ffffff;
    --relleno-exito:   var(--exito-700);
    --relleno-exito-2: var(--exito-800);
    --relleno-error:   var(--error-700);

    /* Letra sobre `.boton--claro`, que tiene el blanco escrito a mano y
       por eso sigue siendo blanco en oscuro: su texto no puede aclararse. */
    --sobre-claro:     var(--accion-900);

    /* -------- Formas ------------------------------------------------ */
    --radio-xs: 4px;
    --radio-sm: 7px;
    --radio:    10px;
    --radio-lg: 14px;
    --radio-xl: 20px;
    --radio-pilula: 999px;

    /* -------- Bordes ------------------------------------------------ */
    --borde:        1px solid var(--linea-suave);
    --borde-fuerte: 1px solid var(--linea);
    --borde-campo:  2px solid var(--linea);

    /* -------- Sombras -----------------------------------------------
       Discretas: profundidad, no maquillaje. La escala sube con la
       altura real del elemento sobre la página. */
    --sombra-0: 0 1px 1px rgba(40, 32, 16, .05);
    --sombra-1: 0 1px 2px rgba(40, 32, 16, .08), 0 1px 3px rgba(40, 32, 16, .06);
    --sombra-2: 0 2px 6px rgba(40, 32, 16, .10), 0 6px 16px rgba(40, 32, 16, .08);
    --sombra-3: 0 12px 28px rgba(40, 32, 16, .18), 0 4px 10px rgba(40, 32, 16, .09);
    --sombra-borde: inset 0 0 0 1px var(--linea-suave);

    /* -------- Espaciado ---------------------------------------------
       Escala única, base 4px. Ningún margen o padding del archivo debe
       llevar un valor inventado: siempre uno de estos siete pasos. */
    --esp-0: .125rem;   /*  2px */
    --esp-1: .25rem;    /*  4px */
    --esp-2: .5rem;     /*  8px */
    --esp-3: .75rem;    /* 12px */
    --esp-4: 1rem;      /* 16px */
    --esp-5: 1.5rem;    /* 24px */
    --esp-6: 2rem;      /* 32px */
    --esp-7: 3rem;      /* 48px */
    --esp-8: 4rem;      /* 64px */

    /* -------- Tipografía --------------------------------------------
       Ocho pasos y ni uno más. 1rem es el piso de todo control de
       formulario: por debajo iOS hace zoom al enfocar. */
    --t-xs:   .75rem;     /* 12px  encabezado de tabla, antetítulos */
    --t-sm:   .8125rem;   /* 13px  metadatos, etiquetas */
    --t-md:   .875rem;    /* 14px  ayudas y notas */
    --t-base: .9375rem;   /* 15px  tablas densas */
    --t-lg:   1rem;       /* 16px  cuerpo y controles */
    --t-xl:   1.125rem;   /* 18px  títulos de tarjeta */
    --t-2xl:  1.375rem;   /* 22px  título de página en móvil */
    --t-3xl:  1.75rem;    /* 28px  título de página en escritorio */
    --t-4xl:  2.25rem;    /* 36px  cifras grandes */

    --peso-normal: 400;
    --peso-medio:  550;
    --peso-fuerte: 650;
    --peso-negro:  750;

    --interlinea-apretada: 1.2;
    --interlinea-titulo:   1.25;
    --interlinea-cuerpo:   1.55;

    --rastreo-mayusculas: .05em;   /* letter-spacing de textos en caja alta */

    --tipografia: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
                  Arial, "Noto Sans", sans-serif;
    --tipografia-num: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                      "Liberation Mono", monospace;

    /* -------- Movimiento --------------------------------------------
       Corto y sin rebotes. Esto es una herramienta de trabajo. */
    --transicion-rapida: .12s ease;
    --transicion:        .18s ease;

    /* -------- Objetivos táctiles ------------------------------------
       48px es el mínimo con guante puesto. 44px solo para acciones
       secundarias dentro de una fila de tabla en escritorio. */
    --tactil: 48px;
    --tactil-sm: 44px;
    --tactil-lg: 60px;

    /* -------- Foco ---------------------------------------------------
       Ámbar sobre azul y sobre blanco: se ve en ambos fondos. */
    --anillo-foco: 3px solid var(--acento-500);
    --anillo-campo: 0 0 0 3px rgba(200, 137, 15, .22);
    --anillo-campo-error: 0 0 0 3px var(--error-100);

    /* Texto sobre el cromo negro (barra, menú, pie, portada, acceso).
       Neutros TIBIOS, no azulados: el acento de esas superficies es el oro
       del escudo, y un gris azulado al lado del oro se ve sucio. Todos
       pasan AA sobre --marca-900. */
    --sobre-negro:   #e9e6e0;
    --sobre-negro-2: #aca79e;
    --sobre-negro-3: #8d887f;

    /* Nombres viejos. Cuando el cromo era azul marino se llamaban así y
       medio archivo los referencia; se dejan apuntando a los de arriba en
       lugar de renombrar ocho reglas y arriesgar una errata. */
    --sobre-azul:    var(--sobre-negro);
    --sobre-azul-2:  var(--sobre-negro-2);
    --sobre-azul-3:  var(--sobre-negro-3);

    --ancho-maximo: 1180px;
    --barra-alto: 60px;   /* alto de la barra superior fija */
}

/* El sistema se diseñó para leerse a pleno sol. El modo claro sigue siendo
   el punto de partida y el que se sirve si el JavaScript no corre. */
:root { color-scheme: light; }

/* =====================================================================
   01b. MODO OSCURO

   CÓMO SE ENCIENDE. Un solo interruptor: el atributo `data-tema` en la
   etiqueta <html>. Lo pone un script diminuto y SÍNCRONO en el <head>
   —antes de que el navegador pinte nada— leyendo, en este orden:

     1. lo que el usuario haya elegido con el botón de la barra
        (guardado en localStorage bajo 'beraca-tema'),
     2. y si nunca eligió, la preferencia del sistema operativo.

   Va en el <head> y sin `defer` a propósito: si se cargara con el resto
   del JavaScript, la página se pintaría en claro y saltaría a oscuro un
   instante después. Ese parpadeo blanco es justo lo que más molesta de
   un modo oscuro mal puesto.

   POR QUÉ NO HAY UN BLOQUE @media (prefers-color-scheme: dark). Podría
   haberlo, pero entonces cada token habría que escribirlo DOS veces —una
   en el @media y otra en `[data-tema="oscuro"]`, porque una media query y
   un atributo no se pueden juntar en el mismo selector— y dos listas de
   setenta colores se desincronizan a la primera. Como el script ya
   traduce la preferencia del sistema a un `data-tema`, con una lista
   basta. El precio: sin JavaScript siempre sale claro. Para esta
   aplicación es el precio correcto, porque claro es lo que se lee en la
   caseta a mediodía.

   EL CROMO NO CAMBIA. Barra, menú, pie, portada y acceso ya son negros en
   modo claro, porque son el fondo del escudo. En oscuro se quedan igual:
   lo que cambia es el CUERPO de la página, que pasa de gris claro a
   carbón. Por eso aquí no aparece ningún token --marca-*.
   ===================================================================== */
:root[data-tema="oscuro"] {
    color-scheme: dark;

    /* -------- Superficies --------------------------------------------
       Carbón, no negro puro: sobre negro absoluto las sombras no existen
       y las tarjetas se despegan del fondo con un borde y nada más. */
    --fondo:        #131317;
    --superficie:   #1c1c22;
    --superficie-2: #212129;
    --superficie-3: #292932;
    --linea:        #3d3d48;
    --linea-suave:  #2e2e38;

    /* -------- Tinta ---------------------------------------------------
       Blanco roto, nunca #fff: el blanco puro sobre carbón vibra y cansa
       en una tabla de 58 renglones. --tinta-3 conserva sus 6:1. */
    --tinta:   #eceae5;
    --tinta-2: #c5c1b9;
    --tinta-3: #a09b93;
    --tinta-4: #847f77;

    /* -------- Acción (oro) ---------------------------------------------
       Se invierte la escala: los 050–200 eran fondos pálidos y ahora son
       tintes oscuros; los 700–900 eran bronce de texto y ahora son oro
       claro, que es lo que se lee sobre carbón. El relleno de los botones
       NO va aquí: el oro macizo no cambia entre temas (ver --relleno-*). */
    --accion-050: #241c0e;
    --accion-100: #33260f;
    --accion-200: #5e4a1e;
    --accion-600: #d9a648;
    --accion-700: #e8bd6a;   /* enlace: 9.9:1 sobre la tarjeta */
    --accion-800: #f2d193;
    --accion-900: #f7e2b8;

    /* -------- Oro -----------------------------------------------------
       Es el color de la marca y se mantiene brillante: --acento-500/600
       siguen siendo relleno con letra oscura encima (.boton--acento) y
       siguen siendo el filete del encabezado. */
    --acento-100: #33260f;
    --acento-200: #5e4a1e;
    --acento-500: #e0a53a;
    --acento-600: #c98f1c;
    --acento-700: #eec276;

    /* -------- Semánticos ----------------------------------------------
       Los 100 son el tinte de la etiqueta, los 200 su borde, los 600 el
       relleno que lleva blanco encima y los 700/800 la letra sobre el
       tinte. Todos los pares verificados a AA. */
    --exito-100: #122c1e;
    --exito-200: #2f5a41;
    --exito-600: #1f9457;
    --exito-700: #74d99f;
    --exito-800: #9ce8bd;

    --error-100: #35161a;
    --error-200: #6b2b2f;
    --error-600: #c0392f;
    --error-700: #f28b8b;
    --error-800: #f7b0b0;

    /* naranja, para no confundirse con el oro de acción */
    --alerta-100: #331a0d;
    --alerta-200: #653418;
    --alerta-600: #d15a10;
    --alerta-700: #f2a06a;
    --alerta-800: #f7bf95;

    --info-100: #16283d;
    --info-200: #2e4c6e;
    --info-700: #7fb8f0;
    --info-800: #a5cef5;

    /* -------- Rellenos sólidos ----------------------------------------
       Aquí es donde se paga el desdoble de tokens. Siguen OSCUROS y con
       letra blanca: «Presente» tiene que ser un bloque verde macizo,
       no una pastilla clara. */
    --relleno-exito:   #17804a;
    --relleno-exito-2: #12603a;
    --relleno-error:   #a12626;

    /* El oro macizo y su letra negra NO cambian: un botón de oro se ve
       igual de bien sobre carbón que sobre blanco, y mantenerlo idéntico
       hace que la acción principal se reconozca en los dos temas.
       Por eso --relleno-accion y --sobre-accion no aparecen aquí.

       La pestaña activa sí sube de tono: el carbón del modo claro se
       confundiría con --superficie-3, que ya es carbón. */
    --relleno-fuerte:   #4a4a56;
    --relleno-fuerte-2: #585866;

    /* .boton--claro sigue siendo blanco (vive sobre el cromo negro), así
       que su letra sigue teniendo que ser oscura. */
    --sobre-claro: #33240a;

    /* -------- Sombras --------------------------------------------------
       Una sombra azulada al 7% no se ve sobre carbón. Se vuelven negras y
       bastante más densas para que la tarjeta siga despegando. */
    --sombra-0: 0 1px 1px rgba(0, 0, 0, .35);
    --sombra-1: 0 1px 2px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .35);
    --sombra-2: 0 2px 6px rgba(0, 0, 0, .50), 0 6px 16px rgba(0, 0, 0, .40);
    --sombra-3: 0 12px 28px rgba(0, 0, 0, .62), 0 4px 10px rgba(0, 0, 0, .45);

    /* Halos de foco: el azul-100 de arriba es un tinte oscuro y no se
       vería como halo, así que van en color translúcido. */
    --anillo-campo:       0 0 0 3px rgba(200, 137, 15, .32);
    --anillo-campo-error: 0 0 0 3px rgba(242, 139, 139, .26);
}

/* ---------------------------------------------------------------------
   PARCHES DEL MODO OSCURO

   Todo lo de abajo existe porque tiene un color ESCRITO A MANO que no
   pasa por un token, y por lo tanto no se entera de que cambió el tema.
   Si algún día se sustituyen por tokens, estas reglas se borran.
   --------------------------------------------------------------------- */

/* Campos y tarjetas en error: el rosa pálido se vuelve vino apagado. */
:root[data-tema="oscuro"] .campo__control[aria-invalid="true"],
:root[data-tema="oscuro"] .campo--error .campo__control { background: #2a1518; }
:root[data-tema="oscuro"] .tarjeta--error { background: #231619; }

/* Veladuras pensadas para caer sobre blanco: se invierten. */
:root[data-tema="oscuro"] .aviso__cerrar:hover { background: rgba(255, 255, 255, .10); }
:root[data-tema="oscuro"] .pestanas__conteo   { background: rgba(255, 255, 255, .14); }
:root[data-tema="oscuro"] .etiqueta           { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .10); }
:root[data-tema="oscuro"] .etiqueta-color     { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .30); }
:root[data-tema="oscuro"] .etiqueta--claro    { box-shadow: none; }

/* =====================================================================
   02. BASE Y REINICIO
   ===================================================================== */
*,
*::before,
*::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    font-family: var(--tipografia);
    font-size: var(--t-lg);     /* 16px: por debajo de esto iOS hace zoom. */
    line-height: var(--interlinea-cuerpo);
    color: var(--tinta);
    background: var(--fondo);
    -webkit-font-smoothing: antialiased;
}

img, svg { max-width: 100%; }
img { height: auto; display: block; }

/* ---------------------------------------------------------------------
   ENLACES

   Esto es sobre todo un sistema de tablas. La pantalla de empleados trae
   CUATRO enlaces por renglón —nombre, sitio, puesto y turno— por 58
   renglones: 232 enlaces en una pantalla. Si no se le dice nada al
   navegador, subraya los 232 y la tabla se lee como un rayado. Un
   subrayado que está en todos lados no señala nada.

   La regla del sistema, entonces:

     - En INTERFAZ (tablas, fichas, listas) el enlace se distingue por su
       color y por el lugar donde está. El subrayado aparece al apuntarlo
       o al enfocarlo con el teclado, que es cuando sirve de confirmación.
     - En TEXTO CORRIDO el subrayado va desde el principio. Ahí sí hace
       falta: el color solo no separa el enlace de la frase que lo rodea,
       y menos con un daltonismo o una pantalla lavada por el sol.

   OJO al tocar esto: el subrayado al apuntar se aplica SOLO en los
   contextos listados más abajo, nunca en `a:hover` a secas. Botones,
   pestañas, celdas de la rejilla y tarjetas-enlace son enlaces también,
   y a esos el subrayado los rompe.
   --------------------------------------------------------------------- */
a {
    color: var(--accion-700);
    text-decoration: none;
}
a:hover { color: var(--accion-800); }

/* Texto corrido: subrayado siempre. */
:is(.parrafo, .tarjeta__texto, .tarjeta__aclaracion, .aviso__texto,
    .campo__ayuda, .grupo-campos__ayuda, .seccion__entrada, .punto__texto,
    .contacto__texto, .marcaje__nota, .acceso__pie, .bitacora__nota) a {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
:is(.parrafo, .tarjeta__texto, .tarjeta__aclaracion, .aviso__texto,
    .campo__ayuda, .grupo-campos__ayuda, .seccion__entrada, .punto__texto,
    .contacto__texto, .marcaje__nota, .acceso__pie, .bitacora__nota) a:hover {
    text-decoration-thickness: 2px;
}

/* Dato enlazado dentro de una tabla o de una ficha: se subraya al
   apuntarlo, como confirmación de que el renglón lleva a algún lado. */
:is(.tabla td, .datos__par dd, .lista-sitios, .lista-personas,
    .contacto__lista) a:hover,
:is(.tabla td, .datos__par dd, .lista-sitios, .lista-personas,
    .contacto__lista) a:focus-visible {
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

/* Foco siempre visible: se usa mucho con teclado en escritorio (RH).
   El doble anillo (ámbar + halo blanco) se distingue tanto sobre la
   barra azul oscuro como sobre las tarjetas blancas. */
:focus-visible {
    outline: var(--anillo-foco);
    outline-offset: 2px;
    border-radius: var(--radio-xs);
}

button, input, select, textarea { font: inherit; color: inherit; }

code, kbd, samp, pre {
    font-family: var(--tipografia-num);
    font-size: .9em;
}

code {
    padding: .1em .35em;
    border-radius: var(--radio-xs);
    background: var(--superficie-3);
    color: var(--accion-800);
}

hr {
    border: 0;
    border-top: 1px solid var(--linea-suave);
    margin: var(--esp-5) 0;
}

::selection { background: var(--accion-200); color: var(--tinta); }

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

/* =====================================================================
   03. ESTRUCTURA
   ===================================================================== */
.app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

.contenedor {
    width: 100%;
    max-width: var(--ancho-maximo);
    margin-inline: auto;
    padding-inline: var(--esp-4);
}

main.contenedor {
    flex: 1 0 auto;
    padding-top: var(--esp-5);
    padding-bottom: var(--esp-7);
}

/* Ritmo vertical del contenido de página. Los bloques hermanos se
   separan siempre igual; el encabezado de página respira un poco más
   para que se lea como cabecera y no como una tarjeta más. */
main.contenedor > * + * { margin-top: var(--esp-5); }
main.contenedor > .encabezado-pagina + * { margin-top: var(--esp-4); }
main.contenedor > .avisos + * { margin-top: var(--esp-4); }
/* Las pestañas mandan sobre el bloque que las sigue: van pegadas a él. */
main.contenedor > .pestanas + * { margin-top: var(--esp-4); }

.pie {
    flex-shrink: 0;
    background: var(--marca-900);
    color: var(--sobre-azul);
    border-top: 2px solid var(--acento-600);
    padding-block: var(--esp-5);
    font-size: var(--t-md);
}
.pie__interior { display: grid; gap: var(--esp-1); }
.pie__empresa { margin: 0; font-weight: 600; color: #fff; letter-spacing: .01em; }
.pie__nota    { margin: 0; color: var(--sobre-azul-2); }

@media (min-width: 640px) {
    .contenedor { padding-inline: var(--esp-5); }
    .pie__interior {
        grid-auto-flow: column;
        justify-content: space-between;
        align-items: center;
    }
}

/* =====================================================================
   04. TIPOGRAFÍA

   JERARQUÍA. Cuatro niveles y nada más, cada uno separado del anterior
   por tamaño Y por color o peso — no solo por tamaño, que a un metro de
   distancia no se nota:

     1. .titulo-pagina    22/28px, negro, tracking cerrado   "¿dónde estoy?"
     2. .titulo-seccion   18px, con filete ámbar             "bloque nuevo"
     3. .tarjeta__titulo  18px, negro                        "esta tarjeta"
     4. .subseccion__titulo 15px caja alta, gris             "sub-bloque"

   Los encabezados de tabla y las etiquetas de cifra viven por debajo de
   todo esto: 12–13px en caja alta y gris. Así, aunque la pantalla esté
   llena de datos, la vista cae primero en el título.
   ===================================================================== */
h1, h2, h3, h4 {
    margin: 0;
    line-height: var(--interlinea-titulo);
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--tinta);
    text-wrap: balance;
}

/* --- Nivel 1: título de página ------------------------------------- */
.titulo-pagina {
    font-size: var(--t-2xl);
    letter-spacing: -.02em;
}

.subtitulo-pagina {
    margin: var(--esp-1) 0 0;
    color: var(--tinta-3);
    font-size: var(--t-base);
    font-weight: var(--peso-normal);
    max-width: 60ch;
}

.parrafo { margin: 0 0 var(--esp-4); color: var(--tinta-2); max-width: 72ch; }

.encabezado-pagina {
    display: flex;
    flex-direction: column;
    gap: var(--esp-4);
    padding-bottom: var(--esp-4);
    border-bottom: 2px solid var(--linea-suave);
}

/* --- Nivel 2: título de sección dentro de la página ----------------- */
/* Lleva un filete ámbar corto a la izquierda: marca el arranque de un
   bloque sin gritar, y se distingue del título de tarjeta de un vistazo. */
.titulo-seccion {
    display: flex;
    align-items: center;
    gap: var(--esp-3);
    margin: 0 0 var(--esp-4);
    font-size: var(--t-xl);
    letter-spacing: -.01em;
}
.titulo-seccion::before {
    content: "";
    flex: 0 0 auto;
    width: 4px;
    align-self: stretch;
    min-height: 1.1em;
    border-radius: var(--radio-pilula);
    background: var(--acento-600);
}

/* --- Nivel 4: subsección ------------------------------------------- */
.subseccion { margin-top: var(--esp-5); }

.subseccion__titulo {
    margin: 0 0 var(--esp-3);
    font-size: var(--t-base);
    font-weight: var(--peso-negro);
    text-transform: uppercase;
    letter-spacing: var(--rastreo-mayusculas);
    color: var(--tinta-3);
}

@media (min-width: 640px) {
    .titulo-pagina { font-size: var(--t-3xl); }
    .encabezado-pagina {
        flex-direction: row;
        align-items: flex-end;
        justify-content: space-between;
    }
}

/* Cifras: siempre alineadas a la derecha y con dígitos de ancho fijo,
   para que las columnas de pesos y horas se comparen de un vistazo. */
.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-family: var(--tipografia-num);
    font-feature-settings: "tnum" 1;
    white-space: nowrap;
}

/* =====================================================================
   05. BOTONES
   Todo botón mide al menos 48px de alto: se toca con guante y con el
   celular en movimiento.
   ===================================================================== */
.boton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--esp-2);
    min-height: var(--tactil);
    padding: .7rem 1.25rem;
    border: 2px solid transparent;
    border-radius: var(--radio);
    background: var(--accion-050);
    color: var(--accion-800);
    font-size: var(--t-lg);
    font-weight: var(--peso-fuerte);
    line-height: var(--interlinea-apretada);
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--transicion-rapida),
                border-color var(--transicion-rapida),
                color var(--transicion-rapida),
                box-shadow var(--transicion-rapida);
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    /* Un botón nunca se parte en dos renglones por una palabra larga. */
    overflow-wrap: break-word;
}

.boton:hover  { background: var(--accion-100); color: var(--accion-800); }

/* El hundido al presionar es la única confirmación táctil que hay con
   guante puesto: sin él no se sabe si el toque entró. */
.boton:active { transform: translateY(1px); }

.boton--principal {
    background: var(--relleno-accion);
    border-color: var(--relleno-accion);
    color: var(--sobre-accion);
    box-shadow: var(--sombra-1);
}
.boton--principal:hover { background: var(--relleno-accion-2); border-color: var(--relleno-accion-2); color: var(--sobre-accion); }
.boton--principal:active { box-shadow: none; }

.boton--acento {
    background: var(--acento-600);
    border-color: var(--acento-600);
    color: #21160a;
}
.boton--acento:hover { background: var(--acento-500); border-color: var(--acento-500); color: #21160a; }

.boton--contorno {
    background: transparent;
    border-color: var(--linea);
    color: var(--accion-800);
}
.boton--contorno:hover { background: var(--accion-050); border-color: var(--accion-600); }

.boton--contorno-claro {
    background: transparent;
    border-color: rgba(255, 255, 255, .55);
    color: #fff;
}
.boton--contorno-claro:hover { background: rgba(255, 255, 255, .12); color: #fff; }

.boton--claro {
    background: #fff;
    border-color: #fff;
    color: var(--sobre-claro);
}
/* Este botón vive sobre el cromo negro (portada y barra pública), así que
   es blanco en los dos temas. Por eso su fondo de apuntado va escrito a
   mano y no con --accion-050: ese token se oscurece en modo oscuro y dejaría
   letra oscura sobre fondo oscuro. */
.boton--claro:hover { background: #e4ecf5; border-color: #e4ecf5; color: var(--sobre-claro); }

.boton--peligro {
    background: var(--error-600);
    border-color: var(--error-600);
    color: #fff;
}
.boton--peligro:hover { background: var(--relleno-error); border-color: var(--relleno-error); color: #fff; }

/* Terciario: acción de escape (Cancelar, Volver). Sin caja, para que no
   compita con el botón que sí queremos que se presione — pero conserva
   los 48px de alto, que el área táctil no se negocia. */
.boton--texto {
    background: transparent;
    border-color: transparent;
    color: var(--accion-700);
    font-weight: var(--peso-medio);
    padding-inline: var(--esp-3);
}
.boton--texto:hover {
    background: var(--accion-050);
    border-color: transparent;
    color: var(--accion-800);
    text-decoration: underline;
}

.boton--compacto {
    min-height: var(--tactil-sm);
    padding: .45rem .9rem;
    font-size: var(--t-base);
}

.boton--bloque { width: 100%; }

.boton--grande {
    min-height: var(--tactil-lg);
    font-size: var(--t-xl);
    padding: .9rem 1.5rem;
}

/* Solo para acciones secundarias dentro de una fila de tabla en
   escritorio, donde el ratón apunta fino. Nunca en la vista móvil. */
.boton--sm {
    min-height: 2.25rem;
    padding: .35rem .7rem;
    font-size: var(--t-sm);
    border-width: 1px;
}

.boton[disabled],
.boton[aria-disabled="true"],
.boton:disabled {
    background: var(--superficie-3);
    border-color: var(--linea-suave);
    color: var(--tinta-4);
    box-shadow: none;
    cursor: not-allowed;
    pointer-events: none;
}

/* Icono en línea dentro de un botón: hereda el color del texto. */
.boton svg { width: 1.15em; height: 1.15em; flex: 0 0 auto; fill: none;
             stroke: currentColor; stroke-width: 2; stroke-linecap: round;
             stroke-linejoin: round; }

/* Fila de botones que en móvil se apilan a ancho completo. */
.acciones {
    display: flex;
    flex-direction: column;
    gap: var(--esp-3);
}
@media (min-width: 560px) {
    .acciones { flex-direction: row; flex-wrap: wrap; align-items: center; }
}

/* =====================================================================
   06. FORMULARIOS
   ===================================================================== */
/* Jerarquía de un campo, de arriba abajo:
     etiqueta   15px  peso 650  tinta-2   — qué es
     control    16px  normal    tinta     — el dato
     ayuda      14px  normal    tinta-3   — cómo llenarlo
     error      14px  peso 650  error-700 + icono — qué falló
   El error pesa más que la ayuda a propósito: es lo que hay que atender. */
.campo { margin-bottom: var(--esp-4); }

.campo__etiqueta {
    display: block;
    margin-bottom: var(--esp-2);
    font-size: var(--t-base);
    font-weight: var(--peso-fuerte);
    color: var(--tinta-2);
    line-height: 1.35;
}

/* "(opcional)" en la etiqueta: se lee como aclaración, no como parte
   del nombre del campo. */
.campo__opcional {
    font-weight: var(--peso-normal);
    color: var(--tinta-3);
    font-size: .85em;
}

.campo__control {
    width: 100%;
    min-height: var(--tactil);
    padding: .7rem .85rem;
    border: var(--borde-campo);
    border-radius: var(--radio);
    background: var(--superficie);
    color: var(--tinta);
    font-size: var(--t-lg);   /* No bajar: iOS hace zoom con menos de 16px. */
    box-shadow: var(--sombra-0);
    transition: border-color var(--transicion-rapida),
                box-shadow var(--transicion-rapida),
                background-color var(--transicion-rapida);
}

.campo__control:hover { border-color: var(--tinta-4); }

.campo__control:focus,
.campo__control:focus-visible {
    outline: none;
    border-color: var(--accion-600);
    box-shadow: var(--anillo-campo);
}

/* Error: borde rojo grueso + halo. Se reconoce sin leer nada. */
.campo__control[aria-invalid="true"],
.campo--error .campo__control {
    border-color: var(--error-600);
    background: #fffafa;
}
.campo__control[aria-invalid="true"]:focus,
.campo--error .campo__control:focus {
    border-color: var(--error-600);
    box-shadow: var(--anillo-campo-error);
}

/* Deshabilitado: se ve apagado y hundido, nunca "casi igual". */
.campo__control:disabled,
.campo__control[readonly] {
    background: var(--superficie-3);
    border-color: var(--linea-suave);
    color: var(--tinta-3);
    box-shadow: none;
    cursor: not-allowed;
}
.campo__control[readonly] { cursor: default; }

.campo__control::placeholder { color: var(--tinta-4); opacity: 1; }

select.campo__control {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%235f6d80' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m5 8 5 5 5-5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .7rem center;
    background-size: 1.15rem;
    padding-right: 2.5rem;
    cursor: pointer;
}
select.campo__control:disabled { cursor: not-allowed; }

textarea.campo__control { min-height: 7rem; resize: vertical; line-height: 1.5; }

/* Los campos de hora del pase de lista se teclean mucho: dígitos
   monoespaciados y un poco más grandes. */
input[type="time"].campo__control,
input[type="date"].campo__control {
    font-family: var(--tipografia-num);
    font-variant-numeric: tabular-nums;
}

.campo__ayuda {
    margin: var(--esp-2) 0 0;
    font-size: var(--t-md);
    color: var(--tinta-3);
    line-height: 1.45;
}

.campo__error {
    display: flex;
    align-items: flex-start;
    gap: var(--esp-2);
    margin: var(--esp-2) 0 0;
    font-size: var(--t-md);
    color: var(--error-700);
    font-weight: var(--peso-fuerte);
    line-height: 1.4;
}
/* Triángulo de advertencia dibujado con CSS: ni una petición extra. */
.campo__error::before {
    content: "!";
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: .1rem;
    border-radius: var(--radio-pilula);
    background: var(--error-600);
    color: #fff;
    font-size: var(--t-xs);
    font-weight: var(--peso-negro);
}

/* El campo con error se marca también en el contenedor: así el bloque
   entero salta a la vista al recorrer un formulario largo. */
.campo--error .campo__etiqueta { color: var(--error-700); }

.campo__grupo { display: flex; gap: var(--esp-2); }
.campo__grupo .campo__control { flex: 1 1 auto; min-width: 0; }

.campo__boton {
    flex: 0 0 auto;
    min-width: 64px;
    min-height: var(--tactil);
    padding: 0 .9rem;
    border: var(--borde-campo);
    border-radius: var(--radio);
    background: var(--superficie-2);
    color: var(--accion-800);
    font-weight: var(--peso-fuerte);
    cursor: pointer;
    touch-action: manipulation;
    transition: background-color var(--transicion-rapida),
                border-color var(--transicion-rapida);
}
.campo__boton:hover { background: var(--accion-050); border-color: var(--accion-600); }
.campo__boton:active { transform: translateY(1px); }

/* Campo cuyo "control" es en realidad un botón (filtros): se alinea
   con los campos de al lado aunque no tenga etiqueta visible. */
.campo--accion {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}

/* Casillas y radios: el área de toque es toda la fila, no el cuadrito.
   Con guante puesto apuntar a 24px es imposible. */
.opcion {
    display: flex;
    align-items: center;
    gap: var(--esp-3);
    min-height: var(--tactil);
    padding: var(--esp-2) var(--esp-2);
    margin-inline: calc(var(--esp-2) * -1);
    border-radius: var(--radio-sm);
    cursor: pointer;
    transition: background-color var(--transicion-rapida);
}
.opcion:hover { background: var(--accion-050); }
.opcion:has(input:checked) { background: var(--accion-050); font-weight: var(--peso-medio); }
.opcion:has(input:disabled) { color: var(--tinta-3); cursor: not-allowed; }

.opcion input[type="checkbox"],
.opcion input[type="radio"] {
    width: 24px;
    height: 24px;
    accent-color: var(--accion-700);
    flex: 0 0 auto;
    cursor: inherit;
}

/* Rejilla de formulario: una columna en móvil, dos en escritorio. */
.forma-rejilla { display: grid; gap: 0 var(--esp-4); }
@media (min-width: 720px) {
    .forma-rejilla { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .forma-rejilla .campo--ancho { grid-column: 1 / -1; }
}

/* Formulario suelto y su versión dentro del modal de alta rápida. */
.forma { margin: 0; }

.forma--rapida .campo:last-of-type { margin-bottom: 0; }

/* Barra de acciones al pie de un formulario. En móvil se apila y la
   acción principal queda ARRIBA: es la que alcanza el pulgar primero
   cuando el teclado ocupa media pantalla. */
.forma__acciones {
    display: flex;
    flex-direction: column;
    gap: var(--esp-2);
    margin-top: var(--esp-5);
    padding-top: var(--esp-4);
    border-top: var(--borde);
}
.forma__acciones .boton { width: 100%; }

@media (min-width: 560px) {
    .forma__acciones {
        flex-direction: row-reverse;
        justify-content: flex-start;
        flex-wrap: wrap;
        align-items: center;
    }
    .forma__acciones .boton { width: auto; }
}

/* =====================================================================
   07. BARRA Y NAVEGACIÓN (sistema interno)

   OJO: `.barra` está ocupada dos veces en las páginas. Es la barra
   superior fija (<header class="barra">) y también la barrita de
   proporción del reporte por sitio (<span class="barra"> con un
   `.barra__relleno` adentro). Sin separarlas, esa barrita heredaba
   `position: sticky` y fondo azul marino dentro de una celda de tabla.

   Aquí se separan por elemento: header = navegación, span/con relleno =
   medidor. Los nombres de clase no se tocan porque las páginas PHP ya
   los usan y no son mías.
   ===================================================================== */
/* El <header> envuelve la fila de marca Y la fila del menú, así que el
   filete de oro del borde inferior cierra las dos: el encabezado se lee
   como un solo bloque negro rematado en oro, igual que el escudo. */
.barra {
    position: sticky;
    top: 0;
    z-index: 30;
    background: var(--marca-900);
    color: #fff;
    border-bottom: 2px solid var(--acento-600);
    box-shadow: var(--sombra-2);
    padding-top: env(safe-area-inset-top);
}

/* Barrita de proporción: cualquier `.barra` que contenga un relleno, o
   que sea un span en línea, NO es la barra de navegación. */
span.barra,
.barra:has(> .barra__relleno) {
    position: static;
    z-index: auto;
    display: inline-block;
    vertical-align: middle;
    width: 100%;
    min-width: 3rem;
    max-width: 8rem;
    height: .5rem;
    margin-right: var(--esp-2);
    padding-top: 0;
    /* La barra de navegación lleva filete de oro abajo; la barrita de
       proporción no es esa barra y tiene que soltarlo. */
    border-bottom: 0;
    border-radius: var(--radio-pilula);
    background: var(--superficie-3);
    box-shadow: inset 0 0 0 1px var(--linea-suave);
    overflow: hidden;
}

.barra__relleno {
    display: block;
    height: 100%;
    border-radius: var(--radio-pilula);
    background: var(--accion-600);
}

/* Bajo 90% de cumplimiento la barra se pone ámbar y bajo 75% roja.
   Como el porcentaje se pinta en línea desde PHP, el tono se decide
   aquí con el ancho declarado en el estilo del propio elemento. */
.barra__relleno--alerta { background: var(--alerta-600); }
.barra__relleno--error  { background: var(--error-600); }
.barra__relleno--exito  { background: var(--exito-600); }

.barra__interior {
    display: flex;
    align-items: center;
    gap: var(--esp-3);
    width: 100%;
    max-width: var(--ancho-maximo);
    margin-inline: auto;
    padding: var(--esp-3) var(--esp-4);
    min-height: var(--barra-alto);
}

.barra__marca {
    display: inline-flex;
    align-items: center;
    gap: var(--esp-2);
    color: #fff;
    text-decoration: none;
    font-weight: 700;
    letter-spacing: .02em;
    margin-right: auto;
}
/* El escudo es una imagen con sus propios colores (oro y negro), no un
   glifo que herede currentColor: aquí solo se le da tamaño. */
.barra__escudo { width: 32px; height: 32px; flex: 0 0 auto; }
.barra__nombre { font-size: var(--t-xl); text-transform: uppercase; letter-spacing: .06em; }

.barra__usuario { display: none; }

.barra__menu,
.barra__tema {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tactil);
    height: var(--tactil);
    border: 0;
    border-radius: var(--radio-sm);
    background: rgba(255, 255, 255, .1);
    color: #fff;
    cursor: pointer;
}
.barra__menu:hover,
.barra__tema:hover { background: rgba(255, 255, 255, .18); }

/* Botón de tema. Va en la barra, que es negra en los dos modos, así que
   el icono es blanco siempre. Se enseña el icono del tema al que se VA,
   no el que ya se está viendo: en claro una luna, en oscuro un sol. Es la
   convención y evita la duda de "¿esto me dice dónde estoy o a dónde voy?". */
.barra__tema { flex: 0 0 auto; touch-action: manipulation; }
.barra__tema-icono { width: 22px; height: 22px; }

.barra__tema-icono--sol { display: none; }
:root[data-tema="oscuro"] .barra__tema-icono--luna { display: none; }
:root[data-tema="oscuro"] .barra__tema-icono--sol  { display: block; }

.barra__hamburguesa,
.barra__hamburguesa::before,
.barra__hamburguesa::after {
    display: block;
    width: 20px;
    height: 2px;
    background: currentColor;
    border-radius: 2px;
    content: "";
    position: relative;
}
.barra__hamburguesa::before { top: -6px; }
.barra__hamburguesa::after  { top: 4px; }

.barra__menu[aria-expanded="true"] .barra__hamburguesa { background: transparent; }
.barra__menu[aria-expanded="true"] .barra__hamburguesa::before { top: 0; transform: rotate(45deg); }
.barra__menu[aria-expanded="true"] .barra__hamburguesa::after  { top: -2px; transform: rotate(-45deg); }

/* Menú: cajón desplegable en móvil. Un punto más claro que la barra:
   se lee como segundo nivel del mismo encabezado, no como otra pieza. */
.nav {
    display: none;
    background: var(--marca-800);
    border-top: 1px solid var(--marca-700);
}
.nav.esta-abierto { display: block; }

.nav__lista {
    list-style: none;
    margin: 0;
    padding: var(--esp-2) var(--esp-3) var(--esp-3);
    max-width: var(--ancho-maximo);
    margin-inline: auto;
}

.nav__enlace {
    display: flex;
    align-items: center;
    min-height: var(--tactil);
    padding: var(--esp-2) var(--esp-3);
    border-radius: var(--radio-sm);
    color: var(--sobre-azul);
    font-weight: 600;
    text-decoration: none;
}
.nav__enlace:hover { background: rgba(255, 255, 255, .1); color: #fff; }

.nav__enlace--activo {
    background: rgba(255, 255, 255, .14);
    color: #fff;
    box-shadow: inset 3px 0 0 var(--acento-500);
}

.nav__enlace--salir { color: #f3c9c9; }
.nav__enlace--salir:hover { background: rgba(192, 38, 38, .3); color: #fff; }

@media (min-width: 900px) {
    .barra__menu { display: none; }

    .barra__usuario {
        display: flex;
        align-items: center;
        gap: var(--esp-2);
        font-size: var(--t-base);
    }
    .barra__usuario-nombre { font-weight: 600; color: #fff; }

    .nav { display: block; background: var(--marca-800); }
    .nav__lista {
        display: flex;
        align-items: center;
        gap: var(--esp-1);
        padding: 0 var(--esp-5);
        overflow-x: auto;
        scrollbar-width: none;
    }
    .nav__lista::-webkit-scrollbar { display: none; }

    .nav__elemento { flex: 0 0 auto; }
    .nav__elemento--fin { margin-left: auto; }

    .nav__enlace {
        min-height: 46px;
        border-radius: 0;
        white-space: nowrap;
    }
    .nav__enlace--activo {
        background: transparent;
        box-shadow: inset 0 -3px 0 var(--acento-500);
    }
}

/* =====================================================================
   08. PESTAÑAS DE SECCIÓN

   Son navegación real: cada pestaña es un <a> a otra URL. Por eso NO
   llevan role="tablist" (eso mentiría al lector de pantalla, que
   esperaría paneles que se alternan sin recargar). El estado activo se
   anuncia con aria-current="page", que es lo correcto para un enlace
   que apunta a la página en la que ya estás.

   Se dibuja como un control segmentado, no como pestañitas subrayadas:
   el activo va RELLENO de azul marino. A pleno sol un subrayado de 3px
   se pierde; un bloque de color sólido no.

   Genérico a propósito: sirve para Catálogos, Configuración y para
   cualquier página que venga después.
   ===================================================================== */
.pestanas {
    display: flex;
    gap: var(--esp-1);
    padding: var(--esp-1);
    background: var(--superficie-3);
    border: var(--borde-fuerte);
    border-radius: var(--radio);

    /* En móvil se desliza en horizontal sin barra de scroll visible y
       sin empujar el ancho de la página. */
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    /* Deja respirar la última pestaña contra el borde al deslizar. */
    scroll-padding-inline: var(--esp-1);
}
.pestanas::-webkit-scrollbar { display: none; }

.pestanas__enlace {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    scroll-snap-align: start;

    min-height: var(--tactil-sm);
    padding: var(--esp-2) var(--esp-4);
    border-radius: var(--radio-sm);

    color: var(--tinta-2);
    font-size: var(--t-base);
    font-weight: var(--peso-fuerte);
    line-height: var(--interlinea-apretada);
    text-decoration: none;
    white-space: nowrap;

    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background-color var(--transicion-rapida),
                color var(--transicion-rapida),
                box-shadow var(--transicion-rapida);
}

.pestanas__enlace:hover {
    background: var(--superficie);
    color: var(--accion-800);
    box-shadow: var(--sombra-0);
}

/* Activa: relleno sólido. Se marca por clase Y por aria-current, para
   que las dos formas de escribirlo en PHP funcionen igual.

   Las dos terminaciones valen a propósito: catalogos.php escribe
   `--activo` y configuracion.php `--activa`. Hoy las dos se pintan bien
   porque las dos páginas ponen también aria-current, que es quien las
   salva; la clase masculina se acepta aquí para que la pestaña no
   dependa de que nadie olvide el atributo. */
.pestanas__enlace--activa,
.pestanas__enlace--activo,
.pestanas__enlace[aria-current="page"] {
    background: var(--relleno-fuerte);
    color: var(--sobre-fuerte);
    box-shadow: var(--sombra-1);
}
.pestanas__enlace--activa:hover,
.pestanas__enlace--activo:hover,
.pestanas__enlace[aria-current="page"]:hover {
    background: var(--relleno-fuerte-2);
    color: var(--sobre-fuerte);
}

/* Contador opcional dentro de una pestaña: "Sitios 11". */
.pestanas__conteo {
    margin-left: var(--esp-2);
    padding: 0 .4rem;
    border-radius: var(--radio-pilula);
    background: rgba(19, 28, 40, .1);
    font-size: var(--t-xs);
    font-variant-numeric: tabular-nums;
}
.pestanas__enlace--activa .pestanas__conteo,
.pestanas__enlace--activo .pestanas__conteo,
.pestanas__enlace[aria-current="page"] .pestanas__conteo {
    background: rgba(255, 255, 255, .22);
}

/* En escritorio no se estira a todo lo ancho: un control segmentado que
   ocupa 1180px se ve mal. Se ajusta a su contenido. */
@media (min-width: 640px) {
    .pestanas { display: inline-flex; max-width: 100%; }
    .pestanas__enlace { min-height: var(--tactil-sm); padding-inline: var(--esp-5); }
}

/* Variante a ancho completo, por si una página la quiere así. */
.pestanas--completas { display: flex; }
.pestanas--completas .pestanas__enlace { flex: 1 1 0; }

/* =====================================================================
   09. TARJETAS Y CIFRAS
   ===================================================================== */
.tarjeta {
    background: var(--superficie);
    border: var(--borde);
    border-radius: var(--radio-lg);
    box-shadow: var(--sombra-1);
    padding: var(--esp-4);
}

@media (min-width: 640px) {
    .tarjeta { padding: var(--esp-5); }
}

/* El encabezado de tarjeta es el nivel 3 de la jerarquía: se separa del
   cuerpo con una línea, y la nota de la derecha baja a 13px grises para
   que no compita con el título. */
.tarjeta__encabezado {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--esp-3);
    flex-wrap: wrap;
    margin-bottom: var(--esp-4);
    padding-bottom: var(--esp-3);
    border-bottom: var(--borde);
}
.tarjeta__titulo {
    font-size: var(--t-xl);
    letter-spacing: -.01em;
}
.tarjeta__nota {
    color: var(--tinta-3);
    font-size: var(--t-sm);
    font-weight: var(--peso-medio);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.tarjeta__texto  { margin: 0; color: var(--tinta-2); }

/* Nota de contexto dentro de la tarjeta: explica una regla de negocio
   (cómo se calcula algo, qué pasa si falta un dato). */
.tarjeta__aclaracion {
    margin: 0 0 var(--esp-4);
    padding: var(--esp-3) var(--esp-4);
    background: var(--accion-050);
    border-left: 3px solid var(--accion-600);
    border-radius: 0 var(--radio-sm) var(--radio-sm) 0;
    font-size: var(--t-md);
    line-height: 1.5;
    color: var(--tinta-2);
}

.tarjeta--centrada { text-align: center; }
.tarjeta--error    { border-color: var(--error-200); background: #fffcfc; }

/* Tarjeta con un dato que exige atención (turnos descubiertos). */
.tarjeta--alerta { border-color: var(--error-200); }

/* Tarjetas de cifra: lo primero que se ve al entrar. */
.tarjetas-cifra {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--esp-3);
}

@media (min-width: 900px) {
    .tarjetas-cifra { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--esp-4); }
}

.cifra {
    display: flex;
    flex-direction: column;
    gap: var(--esp-1);
    padding: var(--esp-4);
    background: var(--superficie);
    border: 1px solid var(--linea-suave);
    border-left: 4px solid var(--accion-700);
    border-radius: var(--radio);
    box-shadow: var(--sombra-1);
}

.cifra__valor {
    font-size: var(--t-4xl);
    font-weight: var(--peso-negro);
    line-height: 1.05;
    color: var(--accion-900);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}

.cifra__etiqueta {
    font-size: var(--t-sm);
    font-weight: var(--peso-fuerte);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tinta-3);
    line-height: 1.3;
}

.cifra--alerta { border-left-color: var(--error-600); }
.cifra--alerta .cifra__valor { color: var(--error-700); }

.cifra--exito { border-left-color: var(--exito-600); }
.cifra--exito .cifra__valor { color: var(--exito-700); }

/* ---------------------------------------------------------------------
   CIFRA QUE ADEMÁS ES ENLACE

   En el tablero cuatro de las cifras llevan al detalle ("3 turnos
   descubiertos" abre la asistencia de hoy). El marcado ya las pintaba
   como <a class="cifra cifra--enlace">, pero la clase no existía en la
   hoja: salían subrayadas de punta a punta y sin ninguna seña de ser
   pulsables. Aquí se les da el trato de tarjeta pulsable: la cifra
   conserva su color, la tarjeta se levanta al apuntarla y una comilla
   angular a la derecha avisa que hay algo más que ver.
   --------------------------------------------------------------------- */
a.cifra {
    position: relative;
    color: inherit;
    text-decoration: none;
    transition: border-color var(--transicion-rapida),
                box-shadow var(--transicion-rapida),
                transform var(--transicion-rapida);
}

a.cifra:hover {
    border-color: var(--accion-600);
    border-left-color: var(--accion-700);
    box-shadow: var(--sombra-2);
    transform: translateY(-1px);
}
a.cifra--alerta:hover { border-left-color: var(--error-600); }
a.cifra--exito:hover  { border-left-color: var(--exito-600); }

a.cifra:active { transform: none; box-shadow: var(--sombra-1); }

/* Hueco reservado para la comilla angular: una cifra larga
   ("$123,456.78") no debe pasarle por debajo. */
a.cifra { padding-right: var(--esp-6); }

a.cifra::after {
    content: "›";
    position: absolute;
    top: var(--esp-3);
    right: var(--esp-3);
    font-size: var(--t-xl);
    line-height: 1;
    color: var(--tinta-4);
    transition: color var(--transicion-rapida), transform var(--transicion-rapida);
}
a.cifra:hover::after { color: var(--accion-700); transform: translateX(2px); }

@media (prefers-reduced-motion: reduce) {
    a.cifra:hover { transform: none; }
    a.cifra:hover::after { transform: none; }
}

/* ---------------------------------------------------------------------
   Métricas del resumen de asistencia

   Mismo papel que .cifra pero en tarjeta completa: cumplimiento, turnos
   programados, cubiertos y descubiertos. Se apilan de 2 en 2 en móvil
   —a 360px cuatro columnas dejarían los números ilegibles— y de 5 en 5
   en escritorio.
   --------------------------------------------------------------------- */
.tarjetas-resumen {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--esp-3);
}

@media (min-width: 700px) {
    .tarjetas-resumen { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1000px) {
    .tarjetas-resumen {
        grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
        gap: var(--esp-4);
    }
}

.tarjeta--metrica {
    display: flex;
    flex-direction: column;
    gap: var(--esp-1);
    padding: var(--esp-4);
    border-top: 3px solid var(--accion-700);
}

.metrica__valor {
    font-size: var(--t-4xl);
    font-weight: var(--peso-negro);
    line-height: 1.05;
    letter-spacing: -.02em;
    color: var(--accion-900);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

.metrica__etiqueta {
    font-size: var(--t-sm);
    font-weight: var(--peso-fuerte);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tinta-3);
    line-height: 1.3;
}

/* La métrica en rojo es la única que se pinta distinto: un turno
   descubierto es un guardia que no llegó a un cliente. */
.tarjeta--metrica.tarjeta--alerta { border-top-color: var(--error-600); }
.tarjeta--metrica.tarjeta--alerta .metrica__valor { color: var(--error-700); }

/* Lista de definición para fichas de datos. */
.datos { margin: 0; display: grid; gap: var(--esp-3); }
.datos__par {
    display: grid;
    grid-template-columns: 8.5rem 1fr;
    gap: var(--esp-3);
    align-items: baseline;
}
.datos__par dt {
    font-size: var(--t-md);
    font-weight: 650;
    color: var(--tinta-3);
    text-transform: uppercase;
    letter-spacing: .03em;
}
.datos__par dd { margin: 0; color: var(--tinta); font-weight: 500; }

@media (max-width: 479px) {
    .datos__par { grid-template-columns: 1fr; gap: var(--esp-1); }
}

/* ---------------------------------------------------------------------
   ESTADO VACÍO

   Un "no hay nada" que solo dice "no hay nada" obliga a adivinar el
   siguiente paso. Aquí el bloque lleva tres cosas: un glifo que lo
   identifica de lejos, el mensaje, y —cuando la página incluye un
   enlace dentro— la acción sugerida convertida en botón.

   El marcado real es siempre <p class="vacio">, así que todo se resuelve
   con ::before y con el estilo del <a> que venga adentro. Las páginas no
   tienen que cambiar nada para verse mejor.
   --------------------------------------------------------------------- */
.vacio {
    margin: 0;
    padding: var(--esp-6) var(--esp-4);
    text-align: center;
    text-wrap: pretty;
    color: var(--tinta-2);
    font-size: var(--t-base);
    line-height: 1.5;
    background: var(--superficie-2);
    border: 1px dashed var(--linea);
    border-radius: var(--radio);
}

/* Carpeta vacía, en SVG embebido: sin peticiones y sin fuente de iconos.
   Se deja en bloque (no flex) porque el contenido real de estos
   párrafos es texto suelto con enlaces y puntuación intercalada. */
.vacio::before {
    content: "";
    display: block;
    width: 34px;
    height: 34px;
    margin: 0 auto var(--esp-3);
    background: no-repeat center / contain
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236d7b8d' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'/%3E%3Cpath d='M3 11h18'/%3E%3C/svg%3E");
}

/* El enlace que la página ya trae en el texto es la acción sugerida:
   se resalta y se le da altura de toque sin sacarlo del renglón, para
   que la puntuación que lo sigue no quede colgando sola. */
.vacio a {
    display: inline-block;
    min-height: var(--tactil-sm);
    padding: .6rem .25rem;
    color: var(--accion-700);
    font-weight: var(--peso-fuerte);
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}
.vacio a:hover { color: var(--accion-800); }

/* Acción explícita del estado vacío: botón completo. Para páginas que
   quieran ofrecer el siguiente paso como tal ("Generar periodos"). */
.vacio__accion {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--tactil);
    margin-top: var(--esp-4);
    padding: var(--esp-2) var(--esp-5);
    border: 2px solid var(--accion-700);
    border-radius: var(--radio);
    background: var(--superficie);
    color: var(--accion-800);
    font-weight: var(--peso-fuerte);
    text-decoration: none;
    touch-action: manipulation;
    transition: background-color var(--transicion-rapida), color var(--transicion-rapida);
}
.vacio__accion:hover { background: var(--relleno-accion); color: var(--sobre-accion); }

/* Vacío que es una BUENA noticia: "ya no falta nadie por capturar".
   Verde, sólido y con palomita — se lee como logro, no como error. */
.vacio--exito {
    color: var(--exito-800);
    background: var(--exito-100);
    border-color: var(--exito-200);
    border-style: solid;
    font-weight: var(--peso-fuerte);
}
.vacio--exito::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314663a' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8 12.5 2.5 2.5L16 9.5'/%3E%3C/svg%3E");
}

/* Vacío que hay que atender: falta un dato para que el sistema opere. */
.vacio--atencion {
    color: var(--alerta-800);
    background: var(--alerta-100);
    border-color: var(--alerta-200);
    border-style: solid;
}
.vacio--atencion::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2392510b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4 2.5 20h19z'/%3E%3Cpath d='M12 10v4'/%3E%3Cpath d='M12 17.5v.01'/%3E%3C/svg%3E");
}

/* Vacío discreto: dentro de una celda o de un bloque chico, donde el
   recuadro punteado completo sería más ruido que ayuda. */
.vacio--compacto {
    flex-direction: row;
    justify-content: flex-start;
    text-align: left;
    padding: var(--esp-3) var(--esp-4);
    font-size: var(--t-md);
}
.vacio--compacto::before { width: 20px; height: 20px; }

/* Título opcional del estado vacío, si la página lo agrega. */
.vacio__titulo {
    display: block;
    font-size: var(--t-lg);
    font-weight: var(--peso-fuerte);
    color: var(--tinta);
}

/* =====================================================================
   10. TABLAS

   Dos escenarios distintos y una sola tabla:

   ESCRITORIO (RH, 58 empleados). Hay que barrer la columna con la vista
   sin perder el renglón. De ahí: encabezado que se queda fijo al hacer
   scroll dentro del contenedor, franjas alternas muy tenues, una línea
   de guía al pasar el cursor y números tabulares a la derecha.

   MÓVIL (supervisor). `tabla--apilable` convierte cada renglón en una
   ficha con su etiqueta al lado del dato. Nada de scroll horizontal
   peleado con el pulgar.

   `.tabla-envoltura` y `.tabla-contenedor` son el mismo componente: las
   páginas usan las dos y ninguna se puede renombrar, así que las dos
   valen. `--contenedor` además fija alto máximo para que el encabezado
   pegajoso tenga contra qué pegarse.
   ===================================================================== */
.tabla-envoltura,
.tabla-contenedor {
    position: relative;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    /* El borde vive en el contenedor y no en la tabla: así las esquinas
       redondeadas recortan bien el encabezado fijo. */
    border: var(--borde);
    border-radius: var(--radio);
    background: var(--superficie);
    overscroll-behavior-x: contain;
}

/* Listado largo: se limita el alto para que el encabezado se quede
   arriba mientras se recorren los 58 renglones. */
@media (min-width: 768px) {
    .tabla-contenedor { max-height: 70vh; }
    .tabla-envoltura--alto { max-height: 70vh; }
}

/* Barra de scroll visible y gruesa en escritorio: si la tabla se
   desplaza en horizontal hay que enterarse. */
.tabla-envoltura::-webkit-scrollbar,
.tabla-contenedor::-webkit-scrollbar { height: 10px; width: 10px; }
.tabla-envoltura::-webkit-scrollbar-thumb,
.tabla-contenedor::-webkit-scrollbar-thumb {
    background: var(--linea);
    border-radius: var(--radio-pilula);
}
.tabla-envoltura::-webkit-scrollbar-thumb:hover,
.tabla-contenedor::-webkit-scrollbar-thumb:hover { background: var(--tinta-4); }

.tabla {
    width: 100%;
    border-collapse: separate;   /* separate: requisito del thead pegajoso */
    border-spacing: 0;
    font-size: var(--t-base);
}

.tabla th,
.tabla td {
    padding: var(--esp-3);
    text-align: left;
    border-bottom: 1px solid var(--linea-suave);
    vertical-align: middle;
}

/* Encabezado fijo. Con border-collapse: separate los bordes del <th> no
   viajan al hacer scroll, así que la línea inferior se dibuja con
   box-shadow. */
.tabla thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--superficie-3);
    color: var(--tinta-2);
    font-size: var(--t-xs);
    font-weight: var(--peso-negro);
    text-transform: uppercase;
    letter-spacing: var(--rastreo-mayusculas);
    white-space: nowrap;
    border-bottom: 0;
    box-shadow: inset 0 -2px 0 var(--linea);
}

/* Franjas: 2% de tinte, lo justo para no perder el renglón en una
   tabla ancha sin que la tabla parezca un mantel. */
.tabla tbody tr:nth-child(even) { background: var(--superficie-2); }

/* Guía de lectura: además del tinte, un filete azul a la izquierda
   marca el renglón activo sin desplazar nada. */
.tabla tbody tr:hover {
    background: var(--accion-050);
    box-shadow: inset 3px 0 0 var(--accion-600);
}
.tabla tbody tr:last-child td { border-bottom: 0; }

/* Números: a la derecha, monoespaciados y un punto más oscuros que el
   texto normal, porque son el dato que se compara. */
.tabla td.num { color: var(--tinta); font-variant-numeric: tabular-nums; }
.tabla th.num { text-align: right; }

/* Primera columna: es el identificador (nombre del empleado o sitio).
   Va en seminegrita para anclar la vista al recorrer la fila. */
.tabla tbody td:first-child { font-weight: var(--peso-medio); color: var(--tinta); }

/* Enlaces dentro de tabla.

   Dos papeles distintos y por eso dos colores:

   - La PRIMERA columna es la identidad del renglón (el nombre del
     empleado, el sitio). Va en tinta, como el texto que sería si no
     fuera enlace: es el título de la fila, no una acción. Pintarla de
     azul con otras tres columnas azules deja la tabla sin ancla.
   - Las DEMÁS son filtros y saltos ("ver todos los de este puesto").
     Esas sí en azul, que es el color de lo accionable.

   Las dos se subrayan al apuntarlas; de eso se encarga la regla de
   enlaces de la sección 02. */
.tabla td a { font-weight: var(--peso-medio); }

.tabla tbody td:first-child a { color: var(--tinta); }
.tabla tbody td:first-child a:hover { color: var(--accion-800); }

@media (max-width: 767px) {
    /* En fichas ya no hay contenedor con borde: cada renglón trae el
       suyo. Se le quita el marco al envoltorio para no duplicarlo. */
    .tabla-envoltura:has(.tabla--apilable),
    .tabla-contenedor:has(.tabla--apilable) {
        border: 0;
        border-radius: 0;
        background: transparent;
        overflow: visible;
        max-height: none;
    }

    .tabla--apilable thead {
        position: absolute;
        width: 1px; height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .tabla--apilable tbody tr {
        display: block;
        margin-bottom: var(--esp-3);
        border: var(--borde-fuerte);
        border-radius: var(--radio);
        background: var(--superficie);
        box-shadow: var(--sombra-1);
        overflow: hidden;
    }
    .tabla--apilable tbody tr:nth-child(even) { background: var(--superficie); }
    .tabla--apilable tbody tr:hover { background: var(--superficie); box-shadow: var(--sombra-1); }

    /* El primer dato de la ficha hace de título: más grande, sin
       etiqueta al lado y sobre fondo tenue. Así cada ficha se lee
       "nombre → datos" y no como una lista de pares sin jerarquía. */
    .tabla--apilable tbody td:first-child {
        display: block;
        padding: var(--esp-3) var(--esp-4);
        background: var(--superficie-2);
        border-bottom: 1px solid var(--linea);
        font-size: var(--t-lg);
        font-weight: var(--peso-fuerte);
        text-align: left;
    }
    .tabla--apilable tbody td:first-child::before { display: none; }

    .tabla--apilable td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: var(--esp-4);
        min-height: var(--tactil-sm);
        padding: var(--esp-2) var(--esp-4);
        text-align: right;
        border-bottom: 1px solid var(--linea-suave);
    }
    .tabla--apilable td:last-child { border-bottom: 0; }

    .tabla--apilable td::before {
        content: attr(data-etiqueta);
        flex: 0 0 auto;
        max-width: 45%;
        font-size: var(--t-xs);
        font-weight: var(--peso-negro);
        text-transform: uppercase;
        letter-spacing: .04em;
        color: var(--tinta-3);
        text-align: left;
        line-height: 1.3;
    }

    .tabla--apilable td:empty { display: none; }
    .tabla--apilable td.num { justify-content: space-between; font-size: var(--t-lg); }

    /* La celda de acciones ocupa la ficha completa: los botones se
       reparten a lo ancho y quedan al alcance del pulgar. */
    .tabla--apilable td:has(.acciones-fila) {
        display: block;
        padding: var(--esp-3) var(--esp-4);
        background: var(--superficie-2);
    }
    .tabla--apilable td:has(.acciones-fila)::before { display: block; margin-bottom: var(--esp-2); max-width: none; }
    .tabla--apilable td .acciones-fila { justify-content: stretch; }
    .tabla--apilable td .acciones-fila > * { flex: 1 1 auto; }
    .tabla--apilable td .acciones-fila .boton { width: 100%; min-height: var(--tactil); }

    /* El pie de totales también se vuelve ficha, y se marca en azul
       para que no se confunda con un empleado más. */
    .tabla--apilable tfoot tr {
        display: block;
        border: 2px solid var(--accion-600);
        border-radius: var(--radio);
        background: var(--accion-050);
        overflow: hidden;
    }
    .tabla--apilable tfoot td,
    .tabla--apilable tfoot th {
        display: flex;
        justify-content: space-between;
        gap: var(--esp-4);
        padding: var(--esp-2) var(--esp-4);
        text-align: right;
    }
    .tabla--apilable tfoot td::before {
        content: attr(data-etiqueta);
        font-size: var(--t-xs);
        font-weight: var(--peso-negro);
        text-transform: uppercase;
        letter-spacing: .04em;
        color: var(--accion-800);
        text-align: left;
    }
}

/* =====================================================================
   11. LISTAS DE OPERACIÓN
   Sitios y personal: filas altas, pensadas para el dedo.
   ===================================================================== */
.lista-sitios,
.lista-personas {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--esp-2);
}

.lista-sitios__item,
.lista-personas__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--esp-3);
    min-height: 64px;
    padding: var(--esp-3);
    background: var(--superficie-2);
    border: 1px solid var(--linea-suave);
    border-radius: var(--radio);
}

.lista-sitios__datos,
.lista-personas__datos { display: grid; gap: 2px; min-width: 0; }

.lista-sitios__nombre,
.lista-personas__nombre {
    font-weight: 650;
    color: var(--tinta);
    overflow-wrap: anywhere;
}

.lista-sitios__cliente,
.lista-personas__meta,
.lista-sitios__conteo {
    font-size: var(--t-md);
    color: var(--tinta-3);
}

.lista-personas__item { border-left: 4px solid var(--alerta-600); }

/* =====================================================================
   12. AVISOS Y ETIQUETAS
   ===================================================================== */
.avisos { display: grid; gap: var(--esp-3); }

/* El aviso lleva un glifo a la izquierda además del color: el color
   solo no basta ni para daltonismo ni para una pantalla lavada por el
   sol. El glifo se dibuja con ::before, sin imágenes externas. */
.aviso {
    display: flex;
    align-items: flex-start;
    gap: var(--esp-3);
    padding: var(--esp-3) var(--esp-4);
    border: 1px solid var(--info-200);
    border-left-width: 4px;
    border-left-color: var(--info-700);
    border-radius: var(--radio);
    background: var(--info-100);
    box-shadow: var(--sombra-0);
}

.aviso::before {
    content: "";
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin-top: .15rem;
    background: no-repeat center / contain
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2317457f' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5'/%3E%3Cpath d='M12 8v.01'/%3E%3C/svg%3E");
}

.aviso__texto {
    margin: 0;
    flex: 1 1 auto;
    min-width: 0;
    font-weight: var(--peso-medio);
    color: var(--tinta);
    line-height: 1.45;
}

.aviso__cerrar {
    flex: 0 0 auto;
    /* 32px de caja visible pero 44px de área real: el pseudo-elemento
       extiende el objetivo táctil sin engordar el aviso. */
    position: relative;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: var(--radio-sm);
    background: transparent;
    color: var(--tinta-2);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    touch-action: manipulation;
}
.aviso__cerrar::after {
    content: "";
    position: absolute;
    inset: -6px;
}
.aviso__cerrar:hover { background: rgba(0, 0, 0, .07); color: var(--tinta); }

.aviso--exito {
    background: var(--exito-100);
    border-color: var(--exito-200);
    border-left-color: var(--exito-700);
}
.aviso--exito::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314663a' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8 12.5 2.5 2.5L16 9.5'/%3E%3C/svg%3E");
}

.aviso--error {
    background: var(--error-100);
    border-color: var(--error-200);
    border-left-color: var(--error-700);
}
.aviso--error::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239b1c1c' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m9 9 6 6'/%3E%3Cpath d='m15 9-6 6'/%3E%3C/svg%3E");
}

.aviso--advertencia {
    background: var(--alerta-100);
    border-color: var(--alerta-200);
    border-left-color: var(--alerta-700);
}
.aviso--advertencia::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2392510b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4 2.5 20h19z'/%3E%3Cpath d='M12 10v4'/%3E%3Cpath d='M12 17.5v.01'/%3E%3C/svg%3E");
}

.aviso--info {
    background: var(--info-100);
    border-color: var(--info-200);
    border-left-color: var(--info-700);
}

/* ---------------------------------------------------------------------
   ETIQUETAS DE ESTADO

   Píldoras. Llevan un punto de color al inicio: en una tabla de estados
   el punto se distingue antes de leer la palabra.
   --------------------------------------------------------------------- */
.etiqueta {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .6rem;
    border-radius: var(--radio-pilula);
    background: var(--accion-100);
    color: var(--accion-800);
    font-size: var(--t-sm);
    font-weight: var(--peso-fuerte);
    line-height: 1.5;
    white-space: nowrap;
    box-shadow: inset 0 0 0 1px rgba(19, 28, 40, .07);
}

.etiqueta::before {
    content: "";
    flex: 0 0 auto;
    width: .45rem;
    height: .45rem;
    border-radius: var(--radio-pilula);
    background: currentColor;
    opacity: .85;
}

.etiqueta--exito      { background: var(--exito-100); color: var(--exito-700); }
.etiqueta--pendiente  { background: var(--alerta-100); color: var(--alerta-700); }
.etiqueta--error      { background: var(--error-100); color: var(--error-700); }
.etiqueta--incidencia { background: var(--alerta-100); color: var(--alerta-700); }
.etiqueta--claro      { background: rgba(255, 255, 255, .16); color: #fff; box-shadow: none; }

/* Estados de periodo */
.etiqueta--abierto   { background: var(--exito-100); color: var(--exito-700); }
.etiqueta--calculado { background: var(--info-100);  color: var(--info-700); }
.etiqueta--cerrado   { background: var(--superficie-3); color: var(--tinta-2); }

/* Cuadrito del color configurable de cada tipo de incidencia. El color
   llega en un style en línea desde PHP; aquí solo va la forma. */
.etiqueta-color {
    display: inline-block;
    width: .7rem;
    height: .7rem;
    margin-right: var(--esp-1);
    vertical-align: baseline;
    border-radius: var(--radio-xs);
    background: var(--tinta-4);
    box-shadow: inset 0 0 0 1px rgba(19, 28, 40, .25);
}

/* =====================================================================
   13. PANTALLA DE ACCESO
   ===================================================================== */
.acceso {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    min-height: 100dvh;
    padding: var(--esp-5) var(--esp-4);
    /* Negro de la marca con un halo de oro muy apagado detrás de la caja:
       el mismo par de colores del escudo, sin competir con el formulario. */
    background:
        radial-gradient(1100px 540px at 50% -12%, rgba(217, 148, 19, .16) 0%, transparent 62%),
        var(--marca-900);
}

.acceso__caja {
    width: 100%;
    max-width: 25rem;
    padding: var(--esp-6) var(--esp-5);
    background: var(--superficie);
    border-radius: var(--radio-lg);
    border-top: 3px solid var(--acento-600);
    box-shadow: var(--sombra-3);
}

.acceso__encabezado { text-align: center; margin-bottom: var(--esp-5); }
.acceso__escudo { width: 56px; height: 56px; margin: 0 auto var(--esp-3); }
.acceso__titulo { font-size: var(--t-2xl); letter-spacing: .01em; }
.acceso__subtitulo { margin: var(--esp-1) 0 0; color: var(--tinta-3); font-size: var(--t-base); }

.acceso__forma { margin-top: var(--esp-4); }
.acceso__forma .aviso { margin-bottom: var(--esp-4); }

.acceso__pie {
    margin: var(--esp-5) 0 0;
    text-align: center;
    font-size: var(--t-base);
}

.acceso__empresa {
    margin: var(--esp-5) 0 0;
    color: var(--sobre-azul-2);
    font-size: var(--t-sm);
    text-align: center;
    letter-spacing: .04em;
}

.acceso .aviso { margin-bottom: var(--esp-4); }

/* =====================================================================
   14. SITIO PÚBLICO
   Misma paleta y mismas reglas táctiles. Sobrio: es la cara de una
   empresa de seguridad, no una promoción.
   ===================================================================== */
.publico { background: var(--superficie); }

.salta-contenido {
    position: absolute;
    left: -9999px;
    top: 0;
    padding: var(--esp-3);
    background: #fff;
    z-index: 100;
}
.salta-contenido:focus { left: var(--esp-3); top: var(--esp-3); }

.publico__barra {
    position: sticky;
    top: 0;
    z-index: 30;
    background: var(--marca-900);
    color: #fff;
    border-bottom: 1px solid var(--marca-700);
    padding-top: env(safe-area-inset-top);
}

.publico__barra-interior {
    display: flex;
    align-items: center;
    gap: var(--esp-3);
    max-width: var(--ancho-maximo);
    margin-inline: auto;
    padding: var(--esp-3) var(--esp-4);
    min-height: 64px;
}

.publico__marca {
    display: inline-flex;
    align-items: center;
    gap: var(--esp-2);
    margin-right: auto;
    color: #fff;
    text-decoration: none;
}
.publico__escudo { width: 38px; height: 38px; flex: 0 0 auto; }
.publico__marca-texto { display: grid; line-height: 1.15; }
.publico__marca-texto strong { font-size: var(--t-xl); letter-spacing: .04em; text-transform: uppercase; }
.publico__marca-texto small  { font-size: var(--t-xs); color: var(--sobre-azul-2); letter-spacing: .06em; text-transform: uppercase; }

.publico__nav { display: none; }

@media (min-width: 860px) {
    .publico__nav { display: flex; gap: var(--esp-2); margin-right: var(--esp-4); }
    .publico__nav a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding: 0 var(--esp-3);
        border-radius: var(--radio-sm);
        color: var(--sobre-azul);
        font-weight: 600;
        text-decoration: none;
    }
    .publico__nav a:hover { background: rgba(255, 255, 255, .1); color: #fff; }
}

/* Portada. Negro con una vuelta de azul marino a media altura: da fondo
   sin aclarar la parte de arriba, donde va el escudo. */
.portada {
    background: linear-gradient(158deg,
        var(--marca-900) 0%,
        #16243a 52%,
        var(--marca-900) 100%);
    color: #fff;
    border-bottom: 4px solid var(--acento-600);
}

.portada__interior { padding-block: var(--esp-7); max-width: 56rem; }

.portada__antetitulo {
    margin: 0 0 var(--esp-3);
    color: var(--acento-500);
    font-size: var(--t-sm);
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
}

.portada__titulo {
    margin: 0;
    font-size: var(--t-3xl);
    line-height: 1.2;
    color: #fff;
    letter-spacing: -.015em;
}

.portada__texto {
    margin: var(--esp-4) 0 0;
    max-width: 42rem;
    color: var(--sobre-azul);
    font-size: var(--t-xl);
}

.portada__acciones {
    display: flex;
    flex-direction: column;
    gap: var(--esp-3);
    margin-top: var(--esp-5);
}

@media (min-width: 560px) {
    .portada__acciones { flex-direction: row; flex-wrap: wrap; }
}
@media (min-width: 900px) {
    .portada__interior { padding-block: 4.5rem; }
    .portada__titulo { font-size: 2.5rem; }
}

/* Secciones */
.seccion { padding-block: var(--esp-7); }
.seccion--alterna { background: var(--fondo); border-block: 1px solid var(--linea-suave); }

.seccion__titulo {
    font-size: var(--t-2xl);
    margin-bottom: var(--esp-3);
    position: relative;
    padding-bottom: var(--esp-3);
}
.seccion__titulo::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 48px;
    height: 3px;
    background: var(--acento-600);
    border-radius: 2px;
}

.seccion__entrada {
    margin: 0 0 var(--esp-5);
    max-width: 46rem;
    color: var(--tinta-2);
    font-size: var(--t-xl);
}

.rejilla { display: grid; gap: var(--esp-4); }

@media (min-width: 700px) {
    .rejilla--servicios { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .rejilla--operacion { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1000px) {
    .rejilla--servicios { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .rejilla--operacion { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.tarjeta--servicio {
    border-top: 3px solid var(--accion-700);
}
.tarjeta--servicio .tarjeta__titulo { margin-bottom: var(--esp-2); }

.icono-servicio {
    width: 34px;
    height: 34px;
    margin-bottom: var(--esp-3);
    color: var(--accion-700);
}

.punto {
    padding-left: var(--esp-4);
    border-left: 3px solid var(--acento-600);
}
.punto__titulo { font-size: var(--t-xl); margin-bottom: var(--esp-2); }
.punto__texto  { margin: 0; color: var(--tinta-2); }

/* Contacto */
.contacto { display: grid; gap: var(--esp-5); }

@media (min-width: 860px) {
    .contacto { grid-template-columns: 1.1fr .9fr; gap: var(--esp-7); align-items: start; }
}

.contacto__lista {
    list-style: none;
    margin: 0;
    padding: var(--esp-4);
    display: grid;
    gap: var(--esp-2);
    background: var(--fondo);
    border: 1px solid var(--linea-suave);
    border-radius: var(--radio-lg);
}

.contacto__item {
    display: grid;
    gap: 2px;
    min-height: var(--tactil);
    padding: var(--esp-2) 0;
    border-bottom: 1px solid var(--linea-suave);
}
.contacto__item:last-child { border-bottom: 0; }

.contacto__etiqueta {
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--tinta-3);
}

.contacto__valor {
    font-size: var(--t-xl);
    font-weight: 600;
    color: var(--tinta);
    overflow-wrap: anywhere;
}
a.contacto__valor { color: var(--accion-700); }

/* Marcador visible de dato que el cliente todavía debe capturar. */
.contacto__valor--pendiente {
    display: inline-block;
    padding: .15rem .5rem;
    border: 1px dashed var(--alerta-700);
    border-radius: var(--radio-sm);
    background: var(--alerta-100);
    color: var(--alerta-700);
    font-family: var(--tipografia-num);
    font-size: var(--t-base);
}

/* Pie público */
.publico__pie {
    background: var(--marca-900);
    color: var(--sobre-azul-2);
    border-top: 2px solid var(--acento-600);
    padding-block: var(--esp-5);
    font-size: var(--t-md);
}
.publico__pie-interior { display: grid; gap: var(--esp-2); }
.publico__pie-empresa { margin: 0; color: #fff; font-weight: 600; }
.publico__pie-nota    { margin: 0; }
.publico__pie-acceso  {
    color: var(--acento-500);
    font-weight: 650;
    text-decoration: none;
    justify-self: start;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
}
.publico__pie-acceso:hover { color: #fff; text-decoration: underline; }

@media (min-width: 860px) {
    .publico__pie-interior {
        grid-auto-flow: column;
        justify-content: space-between;
        align-items: center;
    }
}

/* =====================================================================
   15. UTILIDADES
   ===================================================================== */
.visualmente-oculto {
    position: absolute !important;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.oculto { display: none !important; }
.texto-centro { text-align: center; }
.texto-derecha { text-align: right; }
.texto-tenue { color: var(--tinta-3); }
.texto-fuerte { font-weight: 700; }
.sin-margen { margin: 0 !important; }
.margen-arriba { margin-top: var(--esp-4); }
.ancho-completo { width: 100%; }

/* Dato que exige atención dentro de una tabla: turnos descubiertos,
   faltas. Rojo Y negrita — el color solo no se ve a pleno sol. */
.texto--alerta { color: var(--error-700); font-weight: var(--peso-negro); }
.texto--exito  { color: var(--exito-700); font-weight: var(--peso-fuerte); }
.texto--tenue  { color: var(--tinta-3); }

/* Cero que no importa: se apaga para que los números que sí importan
   destaquen en una columna larga. */
.texto--cero { color: var(--tinta-4); font-weight: var(--peso-normal); }

.error__detalle {
    margin-top: var(--esp-4);
    padding: var(--esp-3);
    background: #1b2432;
    color: #e6edf5;
    border-radius: var(--radio);
    font-size: var(--t-sm);
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Estado "enviando" de los formularios (lo pone app.js). */
.esta-enviando { opacity: .7; pointer-events: none; }

/* =====================================================================
   16. PRE-NÓMINA Y REPORTES
   Piezas que solo usan las pantallas de pre-nómina y reportes. Todo con
   las mismas variables de color: ni una dependencia nueva.
   ===================================================================== */

/* ---------------------------------------------------------------------
   BARRA DE FILTROS

   Se apila en móvil y se alinea en escritorio. Va sobre fondo tenue y
   con marco propio: así se lee como "controles de esta vista" y no como
   un formulario de captura más, que es de lo que se quejaba la pantalla
   de reportes.
   --------------------------------------------------------------------- */
.filtros {
    display: grid;
    gap: var(--esp-3);
}

.filtros__campo { display: grid; gap: var(--esp-2); align-content: end; }

.filtros__etiqueta {
    font-size: var(--t-sm);
    font-weight: var(--peso-negro);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tinta-3);
}

.filtros__campo--acciones { display: flex; flex-wrap: wrap; gap: var(--esp-2); align-items: flex-end; }

/* Rejilla de campos de filtro dentro de una tarjeta de filtros. */
.filtros__campos {
    display: grid;
    gap: 0 var(--esp-4);
}
.filtros__campos .campo { margin-bottom: var(--esp-3); }

/* Botonera del filtro: a ancho completo en móvil, a la derecha en
   escritorio. Aplicar es la acción principal y va primero. */
.filtros__acciones {
    display: flex;
    flex-direction: column;
    gap: var(--esp-2);
    margin-top: var(--esp-2);
}
.filtros__acciones .boton { width: 100%; }

@media (min-width: 560px) {
    .filtros__acciones { flex-direction: row; flex-wrap: wrap; align-items: center; }
    .filtros__acciones .boton { width: auto; }
}

@media (min-width: 720px) {
    .filtros { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); align-items: end; }
    .filtros__campos { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); align-items: end; }
}

/* Botones y formularios que van dentro de una celda de tabla. */
.acciones-fila { display: flex; flex-wrap: wrap; gap: var(--esp-2); align-items: center; }
.forma-en-linea { display: inline-block; margin: 0; }

/* Pie de tabla: los totales tienen que saltar a la vista. */
.tabla tfoot td,
.tabla tfoot th {
    background: var(--superficie-2);
    font-weight: 750;
    border-top: 2px solid var(--linea);
    border-bottom: 0;
}
.tabla tfoot tr:hover { background: var(--superficie-2); }

/* Renglones especiales del recibo y de la pre-nómina. */
.fila--seccion td {
    background: var(--accion-050);
    color: var(--accion-800);
    text-transform: uppercase;
    font-size: var(--t-xs);
    letter-spacing: .05em;
}
.fila--subtotal td { background: var(--superficie-2); }
.fila--manual td   { box-shadow: inset 3px 0 0 var(--acento-600); }
.fila--estimada td { box-shadow: inset 3px 0 0 var(--alerta-600); }

.etiqueta--manual { background: var(--acento-100); color: var(--acento-700); }

/* Barra de proporción hecha con CSS: cumplimiento, ausentismo, costo. */
.medida { display: flex; align-items: center; gap: var(--esp-2); }
.medida__valor {
    font-variant-numeric: tabular-nums;
    font-family: var(--tipografia-num);
    font-size: var(--t-md);
    white-space: nowrap;
}

.barra-medida {
    display: block;
    flex: 1 1 auto;
    min-width: 4rem;
    height: .55rem;
    border-radius: 999px;
    background: var(--linea-suave);
    overflow: hidden;
}
.barra-medida__relleno {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--accion-600);
}
.barra-medida__relleno--exito  { background: var(--exito-600); }
.barra-medida__relleno--alerta { background: var(--alerta-600); }
.barra-medida__relleno--error  { background: var(--error-600); }

/* Avisos del cálculo, agrupados por tipo.
   Se pinta el encabezado siempre visible con su conteo; el detalle se
   despliega. Un aviso escondido es un aviso que nadie atiende. */
.avisos-grupo { display: grid; gap: var(--esp-2); }

.aviso-detalle {
    padding: var(--esp-2) var(--esp-3);
    border: 1px solid #f0d29a;
    border-left: 4px solid var(--alerta-700);
    border-radius: var(--radio);
    background: var(--alerta-100);
}
.aviso-detalle--error { background: var(--error-100); border-color: var(--error-200); border-left-color: var(--error-700); }
.aviso-detalle--info  { background: var(--info-100);  border-color: var(--info-200); border-left-color: var(--info-700); }

.aviso-detalle__resumen {
    display: flex;
    align-items: center;
    gap: var(--esp-2);
    min-height: 44px;
    font-weight: 650;
    color: var(--tinta);
    cursor: pointer;
    list-style: none;
}
.aviso-detalle__resumen::-webkit-details-marker { display: none; }
.aviso-detalle__resumen::after {
    content: "▸";
    margin-left: auto;
    color: var(--tinta-3);
}
.aviso-detalle[open] > .aviso-detalle__resumen::after { content: "▾"; }

.aviso-detalle__lista {
    display: grid;
    gap: var(--esp-1);
    margin: var(--esp-2) 0 var(--esp-2);
    padding-left: var(--esp-5);
    font-size: var(--t-md);
    color: var(--tinta-2);
}

/* =====================================================================
   17. PASE DE LISTA (captura de asistencia)

   ESTA ES LA PANTALLA. El supervisor está parado en la caseta de un
   cliente, a pleno sol, con el celular en una mano y a veces con
   guantes. Va a tocar "Presente" o "Falta" cincuenta y ocho veces.

   Decisiones que salen de ahí y no de ningún gusto:

   - Cada empleado es una FICHA con marco propio, no un renglón de
     lista. Con una ficha no se equivoca de persona al tocar.
   - Los dos botones de decisión miden 56px de alto y ocupan media
     ficha cada uno. Son los objetivos más grandes del sistema.
   - "Presente" es verde relleno y "Falta" es rojo con contorno: la
     acción destructiva NO va rellena, para que no se toque de más.
   - El estado ya capturado se ve en el color del filete izquierdo de
     la ficha ANTES de leer nada. Verde = listo, rojo = falta, ámbar =
     incidencia, gris = sin capturar.
   - La incidencia va dentro de un <details> cerrado: es el caso raro y
     no debe estorbar al caso común.
   ===================================================================== */
.pase {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--esp-3);
}

.pase__item {
    background: var(--superficie);
    border: var(--borde-fuerte);
    border-left: 5px solid var(--linea);
    border-radius: var(--radio);
    box-shadow: var(--sombra-1);
    overflow: hidden;
}

/* Estado capturado: el filete izquierdo lo canta de lejos.
   Las claves son las que devuelve Asistencia::estadoDe():
   pendiente, descanso, presente, por_autorizar, falta e incidencia. */
.pase__item--presente      { border-left-color: var(--exito-600); }
.pase__item--falta         { border-left-color: var(--error-600); }
.pase__item--incidencia    { border-left-color: var(--alerta-600); }
.pase__item--por_autorizar { border-left-color: var(--acento-500); }
.pase__item--cobertura     { border-left-color: var(--accion-600); }
.pase__item--descanso      { border-left-color: var(--linea); background: var(--superficie-2); }
.pase__item--pendiente,
.pase__item--sin_capturar  { border-left-color: var(--tinta-4); }

.pase__forma { margin: 0; padding: var(--esp-3) var(--esp-4) var(--esp-4); }

.pase__cabeza {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--esp-3);
    margin-bottom: var(--esp-3);
}

.pase__quien { display: grid; gap: var(--esp-0); min-width: 0; }

/* El nombre es lo más grande de la ficha: hay que verificar que la
   persona que tienes enfrente es la que dice la pantalla. */
.pase__nombre {
    font-size: var(--t-xl);
    font-weight: var(--peso-fuerte);
    line-height: 1.25;
    color: var(--tinta);
    overflow-wrap: anywhere;
}

.pase__meta {
    font-size: var(--t-md);
    color: var(--tinta-3);
    line-height: 1.35;
}

/* Píldora de estado actual, a la derecha del nombre. */
.pase__estado {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .25rem .65rem;
    border-radius: var(--radio-pilula);
    background: var(--superficie-3);
    color: var(--tinta-2);
    font-size: var(--t-sm);
    font-weight: var(--peso-negro);
    text-transform: uppercase;
    letter-spacing: .03em;
    white-space: nowrap;
}
.pase__estado::before {
    content: "";
    width: .45rem;
    height: .45rem;
    border-radius: var(--radio-pilula);
    background: currentColor;
}
.pase__estado--presente      { background: var(--exito-100); color: var(--exito-700); }
.pase__estado--falta         { background: var(--error-100); color: var(--error-700); }
.pase__estado--incidencia    { background: var(--alerta-100); color: var(--alerta-700); }
.pase__estado--por_autorizar { background: var(--acento-100); color: var(--acento-700); }
.pase__estado--cobertura     { background: var(--info-100); color: var(--info-700); }
.pase__estado--descanso      { background: var(--superficie-3); color: var(--tinta-3); }

/* Sin capturar: es el estado que hay que ATENDER, no un estado neutro.
   Va en ámbar con borde, para que en una lista de 58 se vea cuáles
   faltan sin tener que leer. */
.pase__estado--pendiente {
    background: var(--superficie);
    color: var(--tinta-2);
    box-shadow: inset 0 0 0 2px var(--linea);
}

/* Horas de entrada y salida, lado a lado. Caben en 360px. */
.pase__horas {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--esp-2);
    margin-bottom: var(--esp-3);
}

.pase__hora { display: grid; gap: var(--esp-1); min-width: 0; }

.pase__hora-etiqueta {
    display: flex;
    align-items: center;
    gap: var(--esp-2);
    font-size: var(--t-xs);
    font-weight: var(--peso-negro);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tinta-3);
}

/* "+1 día" en los turnos de 24x24 que cruzan la medianoche. Es la causa
   número uno de una hora mal capturada, así que se marca en ámbar. */
.pase__marca-dia {
    padding: .05rem .35rem;
    border-radius: var(--radio-xs);
    background: var(--acento-100);
    color: var(--acento-700);
    font-size: var(--t-xs);
    letter-spacing: 0;
    text-transform: none;
}

/* --- Los dos botones grandes -------------------------------------- */
.pase__botones {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--esp-2);
}

.pase__boton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--esp-2);
    min-height: 56px;          /* más que el mínimo: se toca con guante */
    padding: var(--esp-2) var(--esp-3);
    border: 2px solid transparent;
    border-radius: var(--radio);
    font-size: var(--t-lg);
    font-weight: var(--peso-negro);
    line-height: var(--interlinea-apretada);
    text-align: center;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background-color var(--transicion-rapida),
                border-color var(--transicion-rapida),
                color var(--transicion-rapida);
}
.pase__boton:active { transform: translateY(1px); }

/* Presente: relleno verde. Es lo que se toca el 90% de las veces. */
.pase__boton--presente {
    background: var(--relleno-exito);
    border-color: var(--relleno-exito);
    color: #fff;
}
.pase__boton--presente:hover { background: var(--relleno-exito-2); border-color: var(--relleno-exito-2); }

/* Falta: contorno rojo, no relleno. Marcar falta le pega al sueldo de
   alguien; el botón no debe invitar al toque distraído. */
.pase__boton--falta {
    background: var(--superficie);
    border-color: var(--error-600);
    color: var(--error-700);
}
.pase__boton--falta:hover { background: var(--error-100); }

/* Incidencia: es el <summary> del bloque desplegable, ocupa el ancho
   completo debajo de los dos botones. */
.pase__boton--incidencia {
    grid-column: 1 / -1;
    width: 100%;
    min-height: var(--tactil);
    margin-top: var(--esp-2);
    background: var(--superficie-2);
    border-color: var(--linea);
    color: var(--tinta-2);
    font-size: var(--t-base);
    font-weight: var(--peso-fuerte);
    list-style: none;
}
.pase__boton--incidencia::-webkit-details-marker { display: none; }
.pase__boton--incidencia::after {
    content: "▾";
    color: var(--tinta-3);
    font-size: var(--t-md);
}
.pase__boton--incidencia:hover { background: var(--accion-050); border-color: var(--accion-600); color: var(--accion-800); }

.pase__extra { margin-top: var(--esp-2); }
.pase__extra[open] > .pase__boton--incidencia {
    background: var(--acento-100);
    border-color: var(--acento-600);
    color: var(--acento-700);
    border-radius: var(--radio) var(--radio) 0 0;
}
.pase__extra[open] > .pase__boton--incidencia::after { content: "▴"; }

.pase__extra-cuerpo {
    display: grid;
    gap: var(--esp-2);
    padding: var(--esp-3);
    border: 2px solid var(--acento-600);
    border-top: 0;
    border-radius: 0 0 var(--radio) var(--radio);
    background: var(--superficie-2);
}
.pase__extra-cuerpo .campo__etiqueta { margin-bottom: 0; }

.pase__extra-acciones { display: grid; gap: var(--esp-2); margin-top: var(--esp-2); }

/* Acción sobre todos los que faltan por capturar. Va arriba de la lista
   y se marca como caja aparte para que no parezca parte de una ficha. */
.pase__masivo {
    margin: 0 0 var(--esp-4);
    padding: var(--esp-3) var(--esp-4);
    border: 1px dashed var(--linea);
    border-radius: var(--radio);
    background: var(--superficie-2);
}
.pase__masivo .boton { width: 100%; }

@media (min-width: 560px) {
    .pase__masivo {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--esp-3);
        flex-wrap: wrap;
    }
    .pase__masivo .boton { width: auto; }
}

/* Zona donde el JS confirma "Guardado" sin recargar. Sin JS queda
   vacía y no ocupa espacio: la página recarga y ya. */
.pase__resultado:empty { display: none; }
.pase__resultado {
    margin: var(--esp-2) 0 0;
    padding: var(--esp-2) var(--esp-3);
    border-radius: var(--radio-sm);
    background: var(--exito-100);
    color: var(--exito-800);
    font-size: var(--t-md);
    font-weight: var(--peso-fuerte);
}

/* En tableta y escritorio la lista se acomoda en dos columnas: RH
   revisa el pase de un sitio completo sin tanto scroll. */
@media (min-width: 900px) {
    .pase { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--esp-4); }
}

/* =====================================================================
   18. REJILLA EMPLEADO × DÍA

   Concentrado del mes: un renglón por empleado, una columna por día.
   Con 58 empleados y 31 días no hay forma de evitar el desplazamiento
   horizontal, así que se hace navegable:

   - La columna del nombre se congela a la izquierda al desplazar.
   - El encabezado de días se congela arriba.
   - Cada celda es un cuadro de 30px con símbolo Y color: ✓ verde,
     ✗ rojo, + azul, I ámbar. El símbolo sirve cuando el color no se
     alcanza a distinguir, que es justo lo que pasa bajo el sol.
   ===================================================================== */
.tabla-contenedor--rejilla {
    max-height: 75vh;
    /* Sombra que aparece al desplazar: avisa que hay más columnas. */
    background:
        linear-gradient(90deg, var(--superficie) 30%, rgba(255, 255, 255, 0)) left / 24px 100% no-repeat,
        linear-gradient(90deg, rgba(15, 41, 66, .12), rgba(15, 41, 66, 0)) left / 8px 100% no-repeat,
        var(--superficie);
    background-attachment: local, scroll, scroll;
}

.tabla--rejilla { font-size: var(--t-sm); }
.tabla--rejilla th,
.tabla--rejilla td { padding: var(--esp-1); text-align: center; }

/* Columna del empleado: congelada a la izquierda. */
.rejilla__nombre {
    position: sticky;
    left: 0;
    z-index: 1;
    min-width: 12rem;
    max-width: 16rem;
    padding: var(--esp-2) var(--esp-3) !important;
    text-align: left !important;
    background: var(--superficie);
    box-shadow: 2px 0 0 var(--linea-suave);
}
/* La esquina superior izquierda se cruza con el thead pegajoso: tiene
   que quedar por encima de las dos capas. */
.tabla--rejilla thead .rejilla__nombre {
    z-index: 3;
    background: var(--superficie-3);
    box-shadow: 2px 0 0 var(--linea), inset 0 -2px 0 var(--linea);
}
.tabla--rejilla tbody tr:nth-child(even) .rejilla__nombre { background: var(--superficie-2); }
.tabla--rejilla tbody tr:hover .rejilla__nombre { background: var(--accion-050); }

.rejilla__nombre a {
    font-weight: var(--peso-fuerte);
    color: var(--tinta);
    text-decoration: none;
}
.rejilla__nombre a:hover { color: var(--accion-700); text-decoration: underline; }

.rejilla__meta {
    display: block;
    margin-top: var(--esp-0);
    font-size: var(--t-xs);
    font-weight: var(--peso-normal);
    color: var(--tinta-3);
    line-height: 1.3;
}

/* Encabezado de día: letra arriba, número abajo. */
.rejilla__dia {
    width: 2.25rem;
    min-width: 2.25rem;
    padding: var(--esp-2) var(--esp-0) !important;
    line-height: 1.15;
}
.rejilla__dia-letra {
    display: block;
    font-size: var(--t-xs);
    font-weight: var(--peso-negro);
    color: var(--tinta-3);
}
.rejilla__dia-num {
    display: block;
    font-size: var(--t-sm);
    font-weight: var(--peso-fuerte);
    color: var(--tinta-2);
    font-variant-numeric: tabular-nums;
}
/* Fin de semana: fondo distinto. Ayuda a contar días sin leer números. */
.rejilla__dia--finde { background: var(--accion-050) !important; }
.rejilla__dia--finde .rejilla__dia-letra,
.rejilla__dia--finde .rejilla__dia-num { color: var(--accion-800); }

/* --- La celda ------------------------------------------------------ */
.rejilla__celda {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: var(--radio-xs);
    background: var(--superficie-2);
    color: var(--tinta-4);
    font-size: var(--t-sm);
    font-weight: var(--peso-negro);
    line-height: 1;
    text-decoration: none;
    box-shadow: inset 0 0 0 1px var(--linea-suave);
    transition: transform var(--transicion-rapida), box-shadow var(--transicion-rapida);
}
a.rejilla__celda:hover {
    transform: scale(1.15);
    box-shadow: inset 0 0 0 1px var(--linea-suave), var(--sombra-2);
    z-index: 1;
    position: relative;
}

.rejilla__celda--ok {
    background: var(--exito-100);
    color: var(--exito-800);
    box-shadow: inset 0 0 0 1px var(--exito-200);
}
.rejilla__celda--hueco {
    background: var(--error-100);
    color: var(--error-800);
    box-shadow: inset 0 0 0 1px var(--error-200);
}
.rejilla__celda--incidencia {
    background: var(--alerta-100);
    color: var(--alerta-800);
    box-shadow: inset 0 0 0 1px var(--alerta-200);
}
.rejilla__celda--cobertura {
    background: var(--info-100);
    color: var(--info-700);
    box-shadow: inset 0 0 0 1px var(--info-200);
}

/* Leyenda de la rejilla: se lee antes de la tabla, así que va compacta
   y en línea, con los mismos cuadros que aparecen abajo. */
.leyenda {
    display: flex;
    flex-wrap: wrap;
    gap: var(--esp-2) var(--esp-4);
    margin: 0 0 var(--esp-3);
    padding: var(--esp-3);
    border-radius: var(--radio);
    background: var(--superficie-2);
    border: var(--borde);
}

.leyenda__item {
    display: inline-flex;
    align-items: center;
    gap: var(--esp-2);
    font-size: var(--t-md);
    color: var(--tinta-2);
}
.leyenda__item .rejilla__celda { width: 1.5rem; height: 1.5rem; font-size: var(--t-xs); }

/* Rol de turnos que el motor proyecta hacia adelante. */
.rol-previo {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--esp-1);
}

.rol-previo__dia {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--esp-3);
    min-height: var(--tactil-sm);
    padding: var(--esp-2) var(--esp-3);
    border-radius: var(--radio-sm);
    background: var(--superficie-2);
    border-left: 3px solid var(--linea);
    font-size: var(--t-base);
}
.rol-previo__dia--trabaja {
    background: var(--exito-100);
    border-left-color: var(--exito-600);
    font-weight: var(--peso-fuerte);
}
.rol-previo__fecha {
    font-family: var(--tipografia-num);
    font-variant-numeric: tabular-nums;
    color: var(--tinta-2);
}
.rol-previo__nota { color: var(--tinta-3); font-size: var(--t-md); text-align: right; }
.rol-previo__dia--trabaja .rol-previo__nota { color: var(--exito-700); }

/* =====================================================================
   19. MARCAJE, BITÁCORA Y PIEZAS SUELTAS
   ===================================================================== */

/* --- Mi marcaje ----------------------------------------------------
   El guardia entra a esta pantalla a hacer UNA cosa. Un solo botón,
   enorme, centrado, imposible de fallar con el pulgar. */
.marcaje {
    display: grid;
    gap: var(--esp-4);
    max-width: 30rem;
    margin-inline: auto;
    padding: var(--esp-5) var(--esp-4);
    background: var(--superficie);
    border: var(--borde);
    border-radius: var(--radio-lg);
    box-shadow: var(--sombra-1);
    text-align: center;
}

.marcaje__estado {
    margin: 0;
    padding: var(--esp-3) var(--esp-4);
    border-radius: var(--radio);
    background: var(--superficie-3);
    color: var(--tinta-2);
    font-size: var(--t-xl);
    font-weight: var(--peso-fuerte);
    line-height: 1.35;
}
.marcaje__estado--dentro  { background: var(--exito-100); color: var(--exito-800); }
.marcaje__estado--cerrado { background: var(--accion-100);  color: var(--accion-900); }

.marcaje__nota {
    margin: 0;
    font-size: var(--t-md);
    color: var(--tinta-3);
    line-height: 1.45;
}

.marcaje__boton {
    width: 100%;
    min-height: 72px;          /* el objetivo táctil más grande del sistema */
    padding: var(--esp-4);
    border: 2px solid transparent;
    border-radius: var(--radio);
    background: var(--relleno-accion);
    color: var(--sobre-accion);
    font-size: var(--t-xl);
    font-weight: var(--peso-negro);
    letter-spacing: .02em;
    line-height: var(--interlinea-apretada);
    cursor: pointer;
    box-shadow: var(--sombra-2);
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background-color var(--transicion-rapida);
}
.marcaje__boton:active { transform: translateY(1px); box-shadow: var(--sombra-1); }
.marcaje__boton--entrada { background: var(--relleno-exito); color: #fff; }
.marcaje__boton--entrada:hover { background: var(--relleno-exito-2); }
.marcaje__boton--salida  { background: var(--relleno-accion); }
.marcaje__boton--salida:hover { background: var(--relleno-accion-2); }

/* --- Bitácora de cambios ------------------------------------------
   Quién tocó qué y cuándo. Es evidencia: se lee como registro, con
   tipografía monoespaciada en los valores. */
.bitacora {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--esp-2);
}

.bitacora__item {
    padding: var(--esp-3);
    border: var(--borde);
    border-left: 3px solid var(--tinta-4);
    border-radius: var(--radio-sm);
    background: var(--superficie-2);
    font-size: var(--t-md);
}
.bitacora__item--crear    { border-left-color: var(--exito-600); }
.bitacora__item--editar   { border-left-color: var(--alerta-600); }
.bitacora__item--eliminar { border-left-color: var(--error-600); }

.bitacora__cabeza {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--esp-2);
    margin-bottom: var(--esp-1);
}

.bitacora__accion {
    padding: .1rem .45rem;
    border-radius: var(--radio-xs);
    background: var(--superficie-3);
    font-size: var(--t-xs);
    font-weight: var(--peso-negro);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tinta-2);
}
.bitacora__item--crear    .bitacora__accion { background: var(--exito-100); color: var(--exito-700); }
.bitacora__item--editar   .bitacora__accion { background: var(--alerta-100); color: var(--alerta-700); }
.bitacora__item--eliminar .bitacora__accion { background: var(--error-100); color: var(--error-700); }

.bitacora__quien  { font-weight: var(--peso-fuerte); color: var(--tinta); }
.bitacora__cuando {
    margin-left: auto;
    font-family: var(--tipografia-num);
    font-size: var(--t-xs);
    color: var(--tinta-3);
    white-space: nowrap;
}

.bitacora__nota { margin: 0; color: var(--tinta-3); font-style: italic; }

.bitacora__cambios {
    margin: var(--esp-1) 0 0;
    padding-left: var(--esp-4);
    display: grid;
    gap: var(--esp-0);
    color: var(--tinta-2);
}

.bitacora__antes {
    padding: .05rem .3rem;
    border-radius: var(--radio-xs);
    background: var(--error-100);
    color: var(--error-800);
    font-family: var(--tipografia-num);
    text-decoration: line-through;
}
.bitacora__despues {
    padding: .05rem .3rem;
    border-radius: var(--radio-xs);
    background: var(--exito-100);
    color: var(--exito-800);
    font-family: var(--tipografia-num);
    font-weight: var(--peso-fuerte);
}

/* --- Fichas de variables de fórmula (configuración) ---------------- */
.chips {
    list-style: none;
    margin: 0 0 var(--esp-3);
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--esp-2);
}

.chip {
    display: inline-flex;
    align-items: center;
    min-height: var(--tactil-sm);
    padding: var(--esp-1) var(--esp-3);
    border: 1px solid var(--accion-200);
    border-radius: var(--radio-sm);
    background: var(--accion-050);
    color: var(--accion-800);
    font-family: var(--tipografia-num);
    font-size: var(--t-md);
    cursor: pointer;
    touch-action: manipulation;
    transition: background-color var(--transicion-rapida), border-color var(--transicion-rapida);
}
.chip:hover { background: var(--accion-100); border-color: var(--accion-600); }
.chip:active { transform: translateY(1px); }

/* --- Paginación ---------------------------------------------------- */
.paginacion {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--esp-3);
    margin-top: var(--esp-4);
    padding-top: var(--esp-4);
    border-top: var(--borde);
}

.paginacion__texto {
    font-size: var(--t-md);
    font-weight: var(--peso-fuerte);
    color: var(--tinta-3);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

/* --- Tabla ordenable (mejora de app.js) ----------------------------
   El encabezado se vuelve botón. Sin JS sigue siendo un <th> normal y
   la tabla se lee igual: el orden que manda es el del servidor. */
.tabla th[aria-sort],
.tabla th.es-ordenable {
    cursor: pointer;
    user-select: none;
    padding-right: 1.5rem;
    position: relative;
}
.tabla th.es-ordenable:hover { background: var(--accion-100); color: var(--accion-800); }
.tabla th.es-ordenable::after {
    content: "";
    position: absolute;
    right: var(--esp-2);
    top: 50%;
    width: 0;
    height: 0;
    margin-top: -2px;
    border-inline: 4px solid transparent;
    border-bottom: 5px solid var(--tinta-4);
    opacity: .35;
}
.tabla th[aria-sort="ascending"]::after  { opacity: 1; border-bottom-color: var(--accion-700); }
.tabla th[aria-sort="descending"]::after {
    opacity: 1;
    border-bottom: 0;
    border-top: 5px solid var(--accion-700);
}

/* --- Buscador en vivo de tabla (mejora de app.js) ------------------ */
.buscador-tabla { position: relative; margin-bottom: var(--esp-3); }

.buscador-tabla__conteo {
    margin: var(--esp-2) 0 0;
    font-size: var(--t-md);
    color: var(--tinta-3);
    font-variant-numeric: tabular-nums;
}

/* Fila escondida por el filtro. Es solo presentación: el HTML completo
   sigue en la página y se imprime entero. */
.esta-filtrada { display: none !important; }

/* --- Contador de caracteres (mejora de app.js) --------------------- */
.contador-campo {
    display: block;
    margin: var(--esp-1) 0 0;
    font-size: var(--t-sm);
    color: var(--tinta-3);
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.contador-campo--cerca { color: var(--alerta-700); font-weight: var(--peso-fuerte); }
.contador-campo--tope  { color: var(--error-700); font-weight: var(--peso-negro); }

/* =====================================================================
   21. IMPRESIÓN
   RH imprime concentrados y recibos de pre-nómina.
   ===================================================================== */
@media print {
    .barra, .nav, .pie, .avisos, .boton, .publico__barra, .publico__pie,
    .filtros, .acciones, .acciones-fila { display: none !important; }

    /* Los avisos del cálculo se imprimen abiertos: en papel no hay clic. */
    .aviso-detalle__lista { display: grid !important; }
    .aviso-detalle__resumen::after { content: none; }

    body { background: #fff; color: #000; font-size: 11pt; }
    main.contenedor { padding: 0; max-width: none; }
    .tarjeta { border: 1px solid #999; box-shadow: none; break-inside: avoid; page-break-inside: avoid; }
    .tabla { font-size: 10pt; }
    .tabla thead th { background: #eee !important; color: #000; }
    .tabla--apilable thead { position: static; width: auto; height: auto; clip: auto; }
    .tabla--apilable td::before { content: none; }
    a[href]::after { content: ""; }
}

/* =====================================================================
   20. MODAL DE ALTA RÁPIDA

   <dialog> nativo. El navegador se encarga del foco atrapado, de Escape
   y de volver inerte el fondo; aquí solo va la apariencia.

   En móvil sube desde abajo y ocupa el ancho completo: es como se
   comportan las hojas de acción del sistema y es donde el pulgar llega.
   ===================================================================== */

.modal {
    border: 0;
    padding: 0;
    background: transparent;
    max-width: 100%;
    max-height: 100%;
    width: 100%;
    height: 100%;
    margin: 0;
    color: var(--tinta);
}

.modal::backdrop {
    background: rgba(9, 17, 28, .55);
    backdrop-filter: blur(2px);
}

.modal__caja {
    background: var(--superficie);
    border-radius: var(--radio-lg) var(--radio-lg) 0 0;
    box-shadow: var(--sombra-3);
    width: 100%;
    max-width: 34rem;
    margin: auto auto 0;
    position: absolute;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    max-height: 92vh;
    overflow-y: auto;
    animation: modal-subir .18s ease-out;
}

@keyframes modal-subir {
    from { transform: translate(-50%, 12px); opacity: .6; }
    to   { transform: translate(-50%, 0);    opacity: 1; }
}

.modal__encabezado {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--esp-4);
    padding: var(--esp-4) var(--esp-4) var(--esp-3);
    border-bottom: var(--borde);
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--superficie);
}

.modal__titulo {
    font-size: var(--t-xl);
    font-weight: var(--peso-fuerte);
    margin: 0;
}

.modal__cerrar {
    border: 0;
    background: transparent;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--tinta-suave);
    cursor: pointer;
    width: var(--tactil);
    height: var(--tactil);
    border-radius: var(--radio-sm);
    flex: 0 0 auto;
    touch-action: manipulation;
}

.modal__cerrar:hover { background: var(--superficie-3); color: var(--tinta); }

.modal__cuerpo { padding: var(--esp-4); }

.modal__cuerpo .titulo-pagina { display: none; }

/* Barra de agarre de la hoja móvil: seña de que se puede cerrar. */
@media (max-width: 39.99rem) {
    .modal__encabezado::before {
        content: "";
        position: absolute;
        top: var(--esp-2);
        left: 50%;
        width: 2.25rem;
        height: 4px;
        margin-left: -1.125rem;
        border-radius: var(--radio-pilula);
        background: var(--linea);
    }
    .modal__encabezado { padding-top: var(--esp-5); }
}

.cargando {
    color: var(--tinta-suave);
    text-align: center;
    padding: var(--esp-6) 0;
}

/* En pantalla grande el modal se centra, como una ventana. */
@media (min-width: 40rem) {
    .modal__caja {
        border-radius: var(--radio-lg);
        bottom: auto;
        top: 50%;
        transform: translate(-50%, -50%);
        box-shadow: var(--sombra-3);
    }

    @keyframes modal-subir {
        from { transform: translate(-50%, calc(-50% + 10px)); opacity: .6; }
        to   { transform: translate(-50%, -50%);              opacity: 1; }
    }
}

@media (prefers-reduced-motion: reduce) {
    .modal__caja { animation: none; }
}

/* Registro recién creado: se resalta un momento para que se vea dónde
   quedó, sobre todo en una lista larga de sitios. */
.opcion--nueva {
    animation: destacar 1.6s ease-out;
}

@keyframes destacar {
    0%   { background: var(--exito-suave); }
    100% { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
    .opcion--nueva { animation: none; }
}

/* Encabezado de bloque con acción a la derecha: "Sitios  [+ Nuevo]" */
.subseccion__encabezado {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--esp-3);
    flex-wrap: wrap;
    margin-bottom: var(--esp-3);
}


/* =====================================================================
   COMPONENTES PENDIENTES DE LAS PANTALLAS REDISEÑADAS

   Las páginas ya usaban estas clases; aquí se les da forma con los
   mismos tokens del sistema. Sin valores inventados.
   ===================================================================== */

/* ---- Tabla compacta -------------------------------------------------
   Para listas largas (58 empleados, catálogos). Baja el aire vertical
   sin bajar el tamaño de letra: se escanea más rápido pero se sigue
   leyendo. El renglón sigue siendo tocable con el pulgar. */
.tabla--compacta th,
.tabla--compacta td {
    padding-block: var(--esp-2);
}

.tabla--compacta td {
    font-size: .92rem;
}

/* Registro dado de baja o desactivado. No basta con el color: se
   antepone un guion largo para que también se note sin distinguir
   tonos. */
.fila--inactiva {
    color: var(--tinta-3);
    background: var(--superficie-2);
}

.fila--inactiva td:first-child::before {
    content: "— ";
    color: var(--tinta-4);
}

/* ---- Buscador dentro de un bloque de filtros ---------------------- */
.filtros__busqueda {
    flex: 1 1 16rem;
    min-width: 0;
}

/* ---- Filtros activos ------------------------------------------------
   Muestra qué está filtrando ahora mismo y permite quitarlo de uno en
   uno. Sin esto, un filtro olvidado hace pensar que faltan empleados. */
.filtros-activos {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--esp-2);
    margin-top: var(--esp-3);
    padding-top: var(--esp-3);
    border-top: var(--borde);
}

.filtros-activos__titulo {
    font-size: .82rem;
    font-weight: 600;
    color: var(--tinta-3);
    margin: 0;
}

.filtros-activos__chip {
    display: inline-flex;
    align-items: center;
    gap: var(--esp-1);
    background: var(--accion-050);
    border: 1px solid var(--accion-200);
    color: var(--accion-800);
    border-radius: var(--radio-pilula);
    padding: var(--esp-1) var(--esp-2) var(--esp-1) var(--esp-3);
    font-size: .85rem;
    line-height: 1.4;
}

.filtros-activos__llave {
    color: var(--tinta-3);
    font-weight: 600;
}

/* Quitar un filtro: área táctil real aunque la equis se vea chica. */
.filtros-activos__quitar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--radio-pilula);
    color: var(--accion-700);
    text-decoration: none;
    font-size: 1.1rem;
    line-height: 1;
    flex: 0 0 auto;
}

.filtros-activos__quitar:hover,
.filtros-activos__quitar:focus-visible {
    background: var(--accion-200);
    color: var(--accion-900);
}

/* ---- Pestañas: texto y conteo --------------------------------------
   El texto se separa del conteo para que el número no se subraye
   junto con el nombre al pasar el puntero. */
.pestanas__texto {
    white-space: nowrap;
}

/* ---- Formulario plegable -------------------------------------------
   El alta de un catálogo va replegada: la pantalla existe sobre todo
   para consultar y editar lo que ya hay, no para dar de alta.
   <details>/<summary> nativo: funciona sin JavaScript. */
.forma-plegable {
    border: var(--borde);
    border-radius: var(--radio);
    background: var(--superficie-2);
    margin-bottom: var(--esp-5);
}

.forma-plegable[open] {
    background: var(--superficie);
    box-shadow: var(--sombra-1);
}

.forma-plegable__resumen {
    cursor: pointer;
    list-style: none;
    padding: var(--esp-3) var(--esp-4);
    min-height: var(--tactil);
    display: flex;
    align-items: center;
    gap: var(--esp-2);
    font-weight: 600;
    color: var(--accion-800);
    border-radius: var(--radio);
}

.forma-plegable__resumen::-webkit-details-marker { display: none; }

/* Triángulo propio, que gira al abrir. */
.forma-plegable__resumen::before {
    content: "";
    width: 0;
    height: 0;
    border-left: 6px solid currentColor;
    border-block: 5px solid transparent;
    transition: transform .15s ease;
    flex: 0 0 auto;
}

.forma-plegable[open] .forma-plegable__resumen::before {
    transform: rotate(90deg);
}

.forma-plegable__resumen:hover,
.forma-plegable__resumen:focus-visible {
    background: var(--accion-050);
}

.forma-plegable > .forma,
.forma-plegable > form {
    padding: 0 var(--esp-4) var(--esp-4);
}

@media (prefers-reduced-motion: reduce) {
    .forma-plegable__resumen::before { transition: none; }
}

/* ---- Grupo de campos ------------------------------------------------
   Agrupa campos relacionados dentro de un formulario largo (por
   ejemplo la geocerca de un sitio, o las horas de un turno). */
.grupo-campos {
    border: var(--borde);
    border-radius: var(--radio-sm);
    padding: var(--esp-4);
    margin-block: var(--esp-4);
}

.grupo-campos__titulo {
    font-size: .85rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tinta-3);
    margin: 0 0 var(--esp-1);
}

.grupo-campos__ayuda {
    font-size: .88rem;
    color: var(--tinta-3);
    margin: 0 0 var(--esp-3);
    line-height: 1.5;
}

/* ---- Nota al pie de una cifra del tablero --------------------------- */
.cifra__nota {
    display: block;
    font-size: .8rem;
    color: var(--tinta-3);
    margin-top: var(--esp-1);
    line-height: 1.4;
}

/* ---- Texto de contacto de la página pública ------------------------- */
.contacto__texto {
    color: var(--tinta-2);
    line-height: 1.6;
    margin: 0;
}

/* Iconos SVG incrustados: el color del trazo va escrito dentro del
   data: URI, así que no hay token que lo cambie. Se repone la misma
   figura con el trazo claro que le toca a cada tinte. Sin esto, la
   palomita del aviso de éxito es verde oscuro sobre verde oscuro. */
:root[data-tema="oscuro"] select.campo__control { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23a09b93' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m5 8 5 5 5-5'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .vacio::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a09b93' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'/%3E%3Cpath d='M3 11h18'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .vacio--exito::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2374d99f' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8 12.5 2.5 2.5L16 9.5'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .vacio--atencion::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f0c88c' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4 2.5 20h19z'/%3E%3Cpath d='M12 10v4'/%3E%3Cpath d='M12 17.5v.01'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .aviso::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237fb8f0' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5'/%3E%3Cpath d='M12 8v.01'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .aviso--exito::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2374d99f' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8 12.5 2.5 2.5L16 9.5'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .aviso--error::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f28b8b' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m9 9 6 6'/%3E%3Cpath d='m15 9-6 6'/%3E%3C/svg%3E"); }
:root[data-tema="oscuro"] .aviso--advertencia::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f0c88c' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4 2.5 20h19z'/%3E%3Cpath d='M12 10v4'/%3E%3Cpath d='M12 17.5v.01'/%3E%3C/svg%3E"); }
