/* ASSURE — capa de aplicación sobre tokens.css y app-glass.css.
   Acá va lo que el sistema de diseño define en las capturas pero no trae
   como clase: barra de filtros, paginación, panel de detalle, pantalla de
   carga en campo, estados y comportamiento responsivo. */

/* ---------- paleta por función ----------------------------------------

   Un color, una cosa.

   La regla anterior era «ok / red / amb / blue»: nombres de color, no de
   función, y por eso cada uno terminó diciendo tres cosas distintas. El
   verde era «cerrado» y también «terminado» —la torta de Indicadores los
   separaba en verde y azul, y el badge de la tabla los pintaba iguales—.
   El azul era «en carga», «hecho sin aceptar», los enlaces y el anillo de
   foco. El rojo era «rechazado» y «categoría A». Mirando la pantalla no se
   podía saber qué estaba diciendo un color.

   Acá hay tres escalas, y cada una contesta una pregunta distinta:

   1. AVANCE (verde agua, el de la marca). Cuánto de la obra está hecho.
      Anillos, barras, el logo. Nunca un estado, nunca una categoría. Es el
      único verde agua de la aplicación.

   2. CICLO DE VIDA (gris → azul → verde). Dónde está un protocolo. Es una
      rampa porque los estados están ordenados: sin empezar, en curso, hecho
      sin firmar, firmado. «En curso» y «hecho» son el mismo azul a dos
      intensidades, justamente porque son la misma clase de cosa a dos
      distancias del final. El verde aparece sólo al final: en toda la
      aplicación, verde quiere decir «firmado».

   3. ATENCIÓN (ámbar → rojo). Esto necesita a una persona. Ámbar: trabado,
      espera a alguien. Rojo: rechazado, o punch de categoría A. Los dos
      significan «frena, y alguien tiene que ir», y nunca comparten columna.

   Afuera de las tres queda la especialidad (I/E/P/M), que es identidad sin
   orden: violeta, rosa, terracota y arena, lejos de las tres escalas. El
   naranja de Electrical se corrió a terracota porque el ámbar y él se
   confundían en una tabla.

   Cada familia tiene tres papeles, porque el mismo valor no sirve para los
   tres tamaños:

     --f-x        texto, íconos, puntos de 8 px, bordes. 4,5:1 contra el
                  panel en los dos temas.
     --f-x-fill   áreas grandes: porciones de torta, tramos de barra. Lleva
                  el mismo croma que la tinta —la misma saturación, al punto
                  decimal— y se diferencia sólo en la luz. Esa es toda la
                  diferencia que hace falta: el croma es lo que se ve como
                  «color», y la luz es lo que separa una porción de la de al
                  lado. Antes el `-fill` bajaba las dos cosas a la vez y las
                  tortas quedaban polvorientas al lado de las tarjetas.
     --f-x-soft   fondos teñidos de chips y tarjetas.

   Las porciones de la torta están separadas además en luminancia, no sólo
   en tono. Sin eso, en escala de grises —o para un ojo que no distingue
   rojo de verde— la torta es un círculo liso. Los cuatro `-fill` del ciclo
   se calcularon para eso: en oscuro cada par adyacente da 1,49 / 1,48 / 1,47
   y 3,26, y ninguna porción baja de 3:1 contra el panel; en claro, 2,08 /
   1,84 / 2,20 / 1,95. «Pendientes» en claro es la excepción a propósito
   —gris pálido, 1,34:1 contra el panel—: lo que todavía no empezó no tiene
   que pesar como lo demás. Y el gris no gana nada con más croma, así que es
   la única familia que quedó donde estaba.

   Los `--ag-ok/red/amb/blue` siguen existiendo como alias de las familias:
   app-glass.css los usa y no se toca, y las pantallas que no se tocaron
   —la ingesta, entre otras— cambian de color solas, sin editarlas. */

.ag-root {
  /* 1 · avance: la obra moviéndose */
  --f-avance: #2ff0d6;  --f-avance-fill: #0fcbb1;  --f-avance-soft: rgba(79, 216, 196, .14);
  --f-avance-ink: #04201d;    /* lo que se escribe ARRIBA del verde agua */
  /* 2 · ciclo de vida de un protocolo */
  --f-sin:     #a8aeb4; --f-sin-fill:     #5e666d; --f-sin-soft:     rgba(162, 167, 172, .13);
  --f-curso:   #5ec2ff; --f-curso-fill:   #1ba8ff; --f-curso-soft:   rgba(143, 192, 236, .14);
  --f-hecho:   #8098ff; --f-hecho-fill:   #8198ff; --f-hecho-soft:   rgba(125, 156, 255, .15);
  --f-firmado: #2fe87e; --f-firmado-fill: #19de6d; --f-firmado-soft: rgba(85, 217, 138, .14);
  --f-firmado-ink: #07240f;   /* lo que se escribe ARRIBA del verde */
  /* 3 · atención: alguien tiene que ir */
  --f-espera:  #ffd21f; --f-espera-fill:  #d8ad00; --f-espera-soft:  rgba(248, 203, 70, .14);
  --f-rechazo: #ff5168; --f-rechazo-fill: #ff0526; --f-rechazo-soft: rgba(255, 107, 125, .15);

  /* El foco no es un estado: tenía el mismo azul que «en carga», y un
     anillo de teclado sobre una fila en carga no se distinguía de la fila. */
  /* `--ag-neon` pinta las barras de avance y el azulejo del logo: es un
     área, no una tinta, así que va al `-fill` de avance y no a `--f-avance`.
     En claro la tinta de avance es un verde oscuro y arriba del azulejo va
     escrito #04201d: dos oscuros, ilegible. El `-fill` sirve para los dos
     temas. Va declarado en los dos bloques por lo mismo que los alias de
     abajo: app-glass lo declara también en `.ag-root.ag-light`. */
  --ag-neon: var(--f-avance-fill);
  --ag-accent: var(--f-avance);
  --f-foco: #8fb8ff;
  /* Electrical, corrido del naranja a terracota para no pisar el ámbar. */
  --esp-e: #ff8a5a;

  /* alias de compatibilidad: app-glass y las vistas sin tocar */
  --ag-ok: var(--f-firmado);  --ag-red:  var(--f-rechazo);
  --ag-amb: var(--f-espera);  --ag-blue: var(--f-hecho);
}

/* En claro no es la misma paleta aclarada: es la misma familia resuelta de
   nuevo. Sobre papel blanco el color que lee es el oscuro, y el que pinta
   áreas es el claro —al revés que sobre fondo negro—, así que `-fill` acá
   es más claro que su `--f-x` y en oscuro es más oscuro. */
.ag-root.ag-light {
  --f-avance: #00756a;  --f-avance-fill: #00c1ae;  --f-avance-soft: #dff4f1;
  --f-avance-ink: #ffffff;
  --f-sin:     #5a6066; --f-sin-fill:     #cdd0d2; --f-sin-soft:     #e9ebec;
  --f-curso:   #0a67a8; --f-curso-fill:   #71bff7; --f-curso-soft:   #e2eef7;
  --f-hecho:   #2338d6; --f-hecho-fill:   #3b4edf; --f-hecho-soft:   #e4e7fb;
  --f-firmado: #0b7a36; --f-firmado-fill: #0fab4b; --f-firmado-soft: #dcf3e3;
  --f-firmado-ink: #ffffff;
  --f-espera:  #8f5a00; --f-espera-fill:  #f29900; --f-espera-soft:  #fbefd3;
  --f-rechazo: #cf0b2a; --f-rechazo-fill: #f64c67; --f-rechazo-soft: #fce1e4;
  --f-foco: #2f45d6;
  --esp-e: #b8400c;
  --ag-neon: var(--f-avance-fill);
  /* `--ag-accent` va repetido por lo mismo que los cuatro alias de abajo.
     Hoy el valor de app-glass para el tema claro coincide con el de la
     familia, así que no se ve; el día que avance cambie de verde, el acento
     del tema claro se quedaría en el viejo sin que nadie lo note. */
  --ag-accent: var(--f-avance);
  /* Los cuatro alias van repetidos acá: app-glass los declara en
     `.ag-root.ag-light`, dos clases, y contra eso la redirección de arriba
     —`.ag-root` pelado— pierde por especificidad por más que esta hoja
     cargue después. Sin esto el tema claro se queda con los colores de
     antes de la paleta en todo lo que pase por un alias. */
  --ag-ok: var(--f-firmado);  --ag-red:  var(--f-rechazo);
  --ag-amb: var(--f-espera);  --ag-blue: var(--f-hecho);
}

/* ---------- base ---------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* hidden tiene que ganarle a cualquier display: con un .stack o un .fila encima,
   el atributo no alcanza y el bloque sigue a la vista. */
[hidden] { display: none !important; }
body { font-family: var(--font-sans); background: var(--ag-page, #050505); }

/* El diseño es un lienzo fijo; la app tiene que poder scrollear. */
.ag-root { overflow: visible; min-height: 100vh; }
.ag-bg, .ag-shade { position: fixed; z-index: 0; }
.ag-bg { inset: -20px; }
.ag-shade { inset: 0; }
.ag-app { position: relative; z-index: 1; }

a { color: var(--ag-link); }
a:hover { text-decoration: underline; }
h1, h2, h3, h4 { margin: 0; }
::selection { background: var(--ag-chip-on); }

:focus-visible { outline: 2px solid var(--f-foco); outline-offset: 2px; border-radius: 4px; }
/* app-glass le pone a estos cuatro el azul de «en carga»; acá se los lleva
   al color de foco, que no significa ningún estado. */
.ag-btn:focus-visible, .ag-seg button:focus-visible, .ag-nav a:focus-visible,
.ag-icon:focus-visible { outline-color: var(--f-foco); }

.num { font-variant-numeric: tabular-nums; }
.ag-mono { font-variant-numeric: tabular-nums; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Saltar la barra: antes del contenido hay hasta dieciséis controles —el
   menú, la marca, el proyecto, el tema, el buscador, el usuario, la salida y
   las nueve secciones—. Con mouse no se notan; con teclado se recorren en
   cada pantalla. Sólo aparece cuando el foco lo alcanza. */
.saltar { position: fixed; top: 12px; left: 50%; transform: translate(-50%, -200%);
  z-index: 70; padding: 11px 18px; border-radius: 12px; font-weight: 650; font-size: 14px;
  background: var(--ag-pri); color: var(--ag-pri-ink); text-decoration: none;
  box-shadow: var(--ag-sombra); transition: transform .16s ease; }
/* `:focus` y no `:focus-visible`: a este enlace sólo se llega con el teclado,
   y `:focus-visible` no se enciende cuando el foco lo mueve un script. */
.saltar:focus { transform: translate(-50%, 0); }
.saltar:hover { text-decoration: none; }
/* El destino es toda la zona de contenido: un anillo de 2 px alrededor de la
   pantalla entera no dice nada, y el salto ya se ve porque la página se mueve. */
.ag-main:focus, .ag-main:focus-visible { outline: none; }

/* ---------- barra superior ------------------------------------------ */
/* La barra de arriba es pegajosa, así que necesita fondo propio: sin él,
   lo que scrollea se le ve por atrás. Pero no lleva su propio redondeo.

   Lo llevaba, y con el mismo radio que la cáscara —28 px—, sólo que la
   cáscara tiene 1 px de borde, así que la barra arranca 1 px adentro. Dos
   curvas del mismo radio con los centros corridos 1 px no encajan: en cada
   esquina de arriba quedaba una medialuna del fondo de la cáscara asomando
   por afuera de la barra, y como la barra pinta --ag-app encima de la
   --ag-app de la cáscara, esa medialuna se veía más oscura. Eran las dos
   muescas de las esquinas.

   Ahora la cáscara recorta a sus hijos y la barra no tiene que saber nada
   del redondeo. `clip` y no `hidden` a propósito: `hidden` la convertiría
   en contenedor de scroll y la propia barra dejaría de ser pegajosa. */
.ag-app { overflow: clip; }
.ag-top { position: sticky; top: 0; z-index: 30; background: var(--ag-app); backdrop-filter: blur(18px); }
.ag-brand { flex: none; }
.ag-brand small { display: block; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ag-muted); font-weight: 650; }
.ag-word { font-family: "Michroma", var(--font-sans); font-size: 17px; letter-spacing: .06em; line-height: 1; }
.ag-top .right { display: flex; align-items: center; gap: 8px; margin-left: auto; flex: none; }
.ag-proj { cursor: pointer; }
.ag-proj .cc { font-weight: 600; }
.ag-proj .dotg { width: 8px; height: 8px; border-radius: 50%; background: var(--ag-ok); flex: none; }
.ag-nav { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; max-width: 100%; }
.ag-nav::-webkit-scrollbar { display: none; }
.ag-nav a { white-space: nowrap; font-size: 15px; }
.menu-btn { display: none; }

/* menú lateral en pantallas chicas */
.sheet { position: fixed; inset: 0; z-index: 60; display: none; }
.sheet.on { display: block; }
.sheet .velo { position: absolute; inset: 0; background: rgba(0,0,0,.55); }
.sheet .caja { position: absolute; inset: 0 auto 0 0; width: min(300px, 86vw); background: var(--ag-page); border-right: 1px solid var(--ag-line); padding: 18px; display: flex; flex-direction: column; gap: 4px; overflow-y: auto; }
.sheet .caja a, .sheet .caja .ag-link-boton { display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 14px; border-radius: 12px; color: var(--ag-ink); text-decoration: none; font-weight: 600; }
.sheet .caja .ag-link-boton { background: none; border: 0; font: inherit; font-weight: 600; cursor: pointer; text-align: left; width: 100%; }
.sheet .caja a.on { background: var(--ag-ctl-on); }
.sheet .caja .grupo { padding: 16px 14px 6px; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ag-muted); font-weight: 650; }

/* ---------- encabezado de página ------------------------------------ */
.ag-main { padding: 26px 30px 36px; }
.ag-head { margin-bottom: 2px; }
.ag-title { font-size: 40px; }
.ag-title .sub { font-weight: 400; }
.ag-acts { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* ---------- rejillas -------------------------------------------------- */
.cols { display: grid; gap: 18px; align-items: start; }
.cols.c2 { grid-template-columns: minmax(0,1fr) minmax(0,1fr); }
.cols.c3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.cols.c4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.cols.sub-detalle { grid-template-columns: 300px minmax(0,1fr); }
.stack { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.fila { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fila.entre { justify-content: space-between; }
.crece { flex: 1; min-width: 0; }

/* ---------- KPI ------------------------------------------------------ */
.kpis { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(205px, 100%), 1fr)); }
.kpi { background: var(--ag-panel); border: 1px solid var(--ag-panel-line); backdrop-filter: blur(22px); border-radius: 18px;
       padding: 16px 18px 16px; min-width: 0; box-sizing: border-box; display: flex; flex-direction: column; --c: var(--ag-ink); --cf: var(--ag-ink); }
a.kpi { text-decoration: none; color: inherit; }
.kpi .et { display: flex; align-items: center; gap: 9px; font-size: 14px; color: var(--ag-muted); min-height: 42px; }
/* El ícono no informa nada que el título no diga: sirve para encontrar la
   tarjeta de un vistazo, y el color del chip es el mismo que el de la barra
   y el del número cuando el número es malo. */
.kpi .chip { flex: none; width: 32px; height: 32px; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center;
             color: var(--c); background: color-mix(in srgb, var(--c) 15%, transparent); }
.kpi .v { font-size: 42px; line-height: 1.05; font-weight: 600; letter-spacing: -.02em; font-variant-numeric: tabular-nums; margin-top: 12px; }
.kpi .v small { font-size: 20px; font-weight: 500; color: var(--ag-muted); }
.kpi .pie { font-size: 13px; color: var(--ag-muted); margin-top: 6px; }
/* La barra dice una proporción, así que sólo va donde hay denominador: un
   promedio diario o una cuenta de punch no tienen escala contra la cual
   dibujarla, y una barra sin escala es adorno. `margin-top:auto` la deja
   siempre contra el piso para que las seis tarjetas cierren parejas. */
.kpi .bar { margin-top: auto; padding-top: 16px; }
.kpi .bar > span {
  display: block; height: 10px; border-radius: 2px;
  background: linear-gradient(90deg, var(--c) 0 var(--p, 0%), var(--ag-track) var(--p, 0%) 100%);
  -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px);
          mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px);
}
/* Las seis salían de los alias `--ag-*`, y en el tema claro los alias nunca
   llegaron a las familias: app-glass los declara en `.ag-root.ag-light`, dos
   clases, y la redirección de acá vive en `.ag-root`, una sola. Con la misma
   hoja ganando por orden pero perdiendo por especificidad, las tarjetas del
   tema claro siguieron todo este tiempo con los colores de antes de la
   paleta. Apuntan derecho a la familia y el problema desaparece. */
.kpi.tono-signal { --c: var(--f-avance);  --cf: var(--f-avance-fill); }
.kpi.tono-ok     { --c: var(--f-firmado); --cf: var(--f-firmado-fill); }
.kpi.tono-info   { --c: var(--f-hecho);   --cf: var(--f-hecho-fill); }
.kpi.tono-red    { --c: var(--f-rechazo); --cf: var(--f-rechazo-fill); }
.kpi.tono-amb    { --c: var(--f-espera);  --cf: var(--f-espera-fill); }
.kpi.tono-neutro { --c: var(--f-sin);     --cf: var(--f-sin-fill); }



/* ---------- anillo de avance ----------------------------------------- */
.ring { position: relative; display: inline-grid; place-items: center; flex: none; }
.ring svg { transform: rotate(-90deg); display: block; }
.ring .pista { stroke: var(--ag-track); fill: none; }
.ring .arco { stroke: var(--ag-neon); fill: none; stroke-linecap: round; transition: stroke-dasharray .5s ease; }
.ring .arco.ok { stroke: var(--ag-ok); }
.ring .txt { position: absolute; display: flex; align-items: baseline; font-weight: 600; font-variant-numeric: tabular-nums; }
.ring .txt small { font-size: .5em; color: var(--ag-muted); margin-left: 1px; }

/* ---------- barra de filtros ----------------------------------------- */
.filtros { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
/* El mínimo es para que el buscador no quede ridículo cuando sobra lugar.
   Cuando no sobra, un mínimo rígido desborda la caja en vez de achicarse:
   `min(220px, 100%)` pide lo mismo mientras entre y cede cuando no. */
.buscar { position: relative; flex: 1; min-width: min(220px, 100%); }
.buscar svg { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--ag-muted); pointer-events: none; }
.inp, .sel, .ta { width: 100%; height: 42px; padding: 0 14px; border-radius: 11px; border: 1px solid var(--ag-ctl-line); background: var(--ag-field); color: var(--ag-ink); font: inherit; }
.buscar .inp { padding-left: 40px; }
.ta { height: auto; min-height: 92px; padding: 12px 14px; resize: vertical; line-height: 1.45; }
.inp::placeholder, .ta::placeholder { color: var(--ag-muted); }
.inp:focus, .sel:focus, .ta:focus { outline: none; border-color: var(--ag-blue); box-shadow: 0 0 0 1px var(--ag-blue); }
.inp.mal, .sel.mal { border-color: var(--ag-red); }
.sel { appearance: none; padding-right: 36px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 19px, calc(100% - 13px) 19px; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
.campo { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.campo > span { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ag-muted); font-weight: 650; }
.campo .ayuda { font-size: 13px; color: var(--ag-muted); text-transform: none; letter-spacing: 0; font-weight: 400; }
.campo .err { font-size: 13px; color: var(--ag-red); text-transform: none; letter-spacing: 0; font-weight: 500; }
.cuenta { font-size: 14px; color: var(--ag-muted); white-space: nowrap; }

/* chip-filtro con valor aplicado */
.ag-chip.quitar { padding-right: 6px; }
.ag-chip .x { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 6px; color: var(--ag-muted); text-decoration: none; }
.ag-chip .x:hover { background: var(--ag-hover); color: var(--ag-ink); text-decoration: none; }
.selcap { position: relative; display: inline-flex; }
.selcap .sel { height: 34px; padding: 0 34px 0 12px; border-radius: 10px; font-size: 14px; font-weight: 600; background: var(--ag-ctl); background-position: calc(100% - 16px) 15px, calc(100% - 11px) 15px; }
.selcap .sel.on { background-color: var(--ag-chip-on); border-color: var(--ag-chip-on-line); }

/* chip-filtro de sí/no: el mismo alto y el mismo tono de «activo» que los
   selects, pero con un checkbox de verdad adentro, así anda con el teclado y
   el lector de pantalla lo anuncia como lo que es. */
.chipf { cursor: pointer; user-select: none; }
.chipf:hover { background: var(--ag-hover); }
.chipf .ag-check { width: 16px; height: 16px; flex: none; margin: 0; }

/* ---------- tabla ----------------------------------------------------- */
.tabla-caja { overflow-x: auto; margin: 0 -22px; padding: 0 22px; }
.ag-table { min-width: 100%; }
/* .tabla-caja tiene overflow-x:auto, y eso la convierte en contenedor de scroll
   en los dos ejes. Una cabecera sticky con top:76px se corre 76 px DENTRO del
   contenedor y tapa las primeras filas. Con top:0 se queda donde corresponde. */
.ag-table th { position: sticky; top: 0; z-index: 5;
  background: linear-gradient(var(--ag-panel), var(--ag-panel)), var(--ag-page); }
.ag-table td.n { font-variant-numeric: tabular-nums; }
.ag-table tr.sel td { background: var(--ag-sel); }
.ag-table a.fila-link { color: inherit; text-decoration: none; font-weight: 600; }
.ag-table a.fila-link:hover { text-decoration: underline; }
.ag-table td.acc { text-align: right; }
.ag-table .vacio-cel { height: 220px; text-align: center; color: var(--ag-muted); }

.barra-sel { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 14px; border-radius: 14px; background: var(--ag-ctl-on); border: 1px solid var(--ag-ctl-line); }
.barra-sel b { font-variant-numeric: tabular-nums; }

.pag { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; padding-top: 16px; margin-top: 4px; border-top: 1px solid var(--ag-line); }
.pag .nums { display: flex; gap: 6px; align-items: center; }
.pag a, .pag span.n { display: inline-flex; align-items: center; justify-content: center; min-width: 40px; height: 40px; padding: 0 10px; border-radius: 11px; border: 1px solid var(--ag-ctl-line); background: var(--ag-ctl); color: var(--ag-ink); text-decoration: none; font-variant-numeric: tabular-nums; font-weight: 600; }
.pag a:hover { background: var(--ag-hover); text-decoration: none; }
.pag .n.on { background: var(--ag-ctl-on); border-color: var(--ag-chip-on-line); }
.pag .n.off { opacity: .4; }
.pag .elipsis { color: var(--ag-muted); padding: 0 2px; }

/* ---------- estados --------------------------------------------------- */
.estado { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; font-weight: 600; }
.estado::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
/* La rampa del ciclo de vida. «Terminado» era verde igual que «Cerrado»,
   y son cosas distintas: terminado es llenado completo sin firma, cerrado
   es firmado. La torta de Indicadores ya los separaba —«Aceptados» verde,
   «Hechos sin aceptar» azul—, así que la tabla y el gráfico decían dos
   cosas distintas del mismo protocolo. Verde ahora es sólo «firmado». */
.e-pendiente { color: var(--f-sin); }
.e-encarga   { color: var(--f-curso); }
.e-terminado { color: var(--f-hecho); }
.e-aprobado  { color: var(--f-firmado); }
.e-cerrado   { color: var(--f-firmado); }
.e-rechazado { color: var(--f-rechazo); }
.e-bloqueado { color: var(--f-espera); }
/* Devuelto por el inspector: hay que corregirlo, pero todavía no es un
   rechazo. Ámbar de «espera», no el rojo del rechazo. */
.e-devuelto  { color: var(--f-espera); }

/* La barra de avance es avance, y avance es verde agua: al 100 % se ponía
   verde, y verde ahora quiere decir «firmado». En la lista de protocolos eso
   dejaba una barra verde entera en la fila de uno rechazado, que es lo
   contrario de lo que pasó. El 100 % ya lo dice el ancho; no hace falta que
   además lo diga un color que significa otra cosa. */
.ag-bar > span.ok { background: var(--ag-neon); }

/* El botón «Firmar». El verde está bien —es la acción que firma—, pero la
   tinta de arriba estaba fija en #0b1a08 para los dos temas: sobre el verde
   oscuro del tema claro daba 2,38:1, abajo de cualquier mínimo legible.
   Ahora la tinta es del tema: 9,2:1 en oscuro y 5,4:1 en claro. */
.ag-btn.ok { background: var(--f-firmado); border-color: var(--f-firmado); color: var(--f-firmado-ink); }

.aviso { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border-radius: 14px; border: 1px solid var(--ag-ctl-line); background: var(--ag-card); }
.aviso .ico { flex: none; margin-top: 1px; }
.aviso b { display: block; }

.vacio { display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 56px 20px; text-align: center; color: var(--ag-muted); }
.vacio .ico { width: 52px; height: 52px; border-radius: 16px; display: grid; place-items: center; background: var(--ag-ctl); border: 1px solid var(--ag-ctl-line); color: var(--ag-ink); }
.vacio h3 { color: var(--ag-ink); font-size: 18px; }

.esq { border-radius: 8px; background: var(--ag-track); animation: late 1.3s ease-in-out infinite; }
@keyframes late { 0%, 100% { opacity: .55; } 50% { opacity: .95; } }
@media (prefers-reduced-motion: reduce) { .esq { animation: none; } * { transition: none !important; } }

.hilando { display: inline-block; width: 16px; height: 16px; border-radius: 50%; border: 2px solid currentColor; border-top-color: transparent; animation: gira .7s linear infinite; }
@keyframes gira { to { transform: rotate(360deg); } }
.ag-btn[aria-busy="true"] { pointer-events: none; }
/* Apagar un botón con opacity le arruina el texto: el "Faltan 3 controles
   por resolver" quedaba en 1,5:1 sobre su propio verde, y justo ese es el
   que hay que poder leer. Se apaga cambiándole el color, no la opacidad. */
/* Lleva .ag-root adelante para ganarle a `.ag-light .ag-btn.ok`, que fija el
   texto en blanco: sin eso el botón apagado quedaba blanco sobre blanco. */
.ag-root .ag-btn:disabled, .ag-root .ag-btn[aria-disabled="true"] {
  background: var(--ag-ctl); border-color: var(--ag-ctl-line); color: var(--ag-muted);
  pointer-events: none; cursor: not-allowed;
}

/* ---------- tarjeta de proyecto ---------------------------------------- */
.proys { display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(min(380px, 100%), 1fr)); }
.proy { display: flex; flex-direction: column; gap: 14px; text-decoration: none; color: inherit; }
.proy:hover { text-decoration: none; }
.proy .cab { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.proy .nom { font-size: 19px; font-weight: 650; line-height: 1.2; }
.proy .mini { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 10px; }
.proy .mini div { background: var(--ag-card); border: 1px solid var(--ag-card-line); border-radius: 12px; padding: 10px 12px; }
.proy .mini b { display: block; font-family: var(--font-mono); font-size: 17px; font-weight: 500; }
.proy .mini span { font-size: 13px; color: var(--ag-muted); }
.proy .pie { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid var(--ag-line); font-size: 13px; color: var(--ag-muted); }

/* ---------- lista de subsistemas --------------------------------------- */
.ss { display: flex; flex-direction: column; gap: 2px; }
.ss a { display: block; padding: 12px 14px; border-radius: 12px; text-decoration: none; color: inherit; }
.ss a:hover { background: var(--ag-hover); text-decoration: none; }
.ss a.on { background: var(--ag-ctl-on); box-shadow: inset 3px 0 0 var(--ag-ink); }
.ss .l1 { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.ss .l1 b { font-family: var(--font-mono); font-weight: 600; }
.ss .l1 em { font-style: normal; color: var(--ag-muted); }
.ss .l2 { font-size: 13px; color: var(--ag-muted); margin-top: 4px; }

/* confianza en barras */
.conf { display: inline-flex; align-items: center; gap: 8px; }
.conf .b { display: flex; gap: 2px; }
.conf .b i { width: 6px; height: 15px; border-radius: 2px; background: var(--ag-track); }
.conf.alta .b i:nth-child(-n+5) { background: var(--ag-ok); }
.conf.media .b i:nth-child(-n+3) { background: var(--ag-amb); }
.conf.baja .b i:nth-child(-n+2) { background: var(--ag-red); }

/* ---------- diálogo ----------------------------------------------------- */
dialog.dlg { border: 1px solid var(--ag-panel-line); background: var(--ag-page); color: var(--ag-ink); border-radius: 20px; padding: 0; width: min(520px, 92vw); box-shadow: var(--shadow-overlay); }
dialog.dlg::backdrop { background: rgba(0,0,0,.6); backdrop-filter: blur(3px); }
dialog.dlg .cuerpo { padding: 24px; display: flex; flex-direction: column; gap: 16px; }
dialog.dlg h2 { font-size: 22px; font-weight: 650; }
dialog.dlg .acc { display: flex; gap: 10px; justify-content: flex-end; }

/* ---------- barra inferior en celular ----------------------------------- */
.tabbar { display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); background: rgba(10,11,12,.86); backdrop-filter: blur(18px); border-top: 1px solid var(--ag-line); }
.ag-light .tabbar { background: rgba(246,247,248,.9); }
.tabbar nav { display: flex; gap: 4px; }
.tabbar a { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; min-height: 56px; justify-content: center; border-radius: 12px; color: var(--ag-nav); text-decoration: none; font-size: 11px; font-weight: 600; }
.tabbar a.on { background: var(--ag-ctl-on); color: var(--ag-ink); }

/* ---------- responsivo -------------------------------------------------- */
/* ---------- agregados de la capa de aplicación ---------------------- */
/* Botón de icono en rojo: el mismo cuadrado que .ag-icon, pero avisando.
   La acción destructiva nunca va con el color de marca. */
.ag-icon.danger { border-color: var(--ag-red); color: var(--ag-red); }
.ag-icon.danger:hover { background: color-mix(in srgb, var(--ag-red) 12%, transparent); }
.ag-icon.sm { width: 36px; height: 36px; border-radius: 10px; }

/* display:flex le gana al atributo hidden, así que hay que decirlo. */
.barra-sel[hidden] { display: none; }

/* ---------- correcciones de contraste ------------------------------- */
/* Van acá y no en app-glass.css a propósito: ese archivo es el sistema de
   diseño Assure tal como llegó, y conviene poder reemplazarlo entero el día
   que haya una versión nueva.

   Acá estaban los cuatro colores de estado del tema claro, corregidos uno
   por uno porque no llegaban a 4,5:1. Ahora el contraste es condición de
   entrada de la paleta por función, que está arriba: las dieciocho tintas
   se midieron contra los dos extremos que compone el vidrio —rgb(238,238,239)
   con la foto clara debajo y rgb(249,249,249) con la oscura— y la más
   ajustada, el verde de «firmado», da 4,69:1. Lo que queda acá es el verde
   agua, que no es un estado y por eso no vive con las familias. */
.ag-root.ag-light { --ag-accent: var(--f-avance); }

/* El botón primario invierte el fondo en los dos temas: oscuro en claro y
   claro en oscuro. El gris apagado de la página queda ilegible encima en
   cualquiera de los dos, así que se apaga la tinta propia del botón. */
.ag-btn.pri .ag-muted { color: color-mix(in srgb, var(--ag-pri-ink) 68%, transparent); }

/* ---------- Hoy: estado de la obra, un cuadro por dato -------------- */
/* Era un solo panel con un anillo y cuatro cifras separadas por una línea
   de 1 px. La línea no alcanzaba: las cuatro cifras se leían como un
   bloque de texto y no como cuatro datos distintos. Ahora cada una es una
   tarjeta igual a las de Indicadores —chip, número, pie— y el anillo es la
   suya. Siguen en una sola franja; lo que cambia es que ahora se ve dónde
   termina una y empieza la otra.

   Sin barra: la barra dice una proporción, y acá la proporción la dice el
   anillo. Cuatro barras al lado de un anillo de 132 px compiten con él y
   no agregan nada que el pie no diga. */
.estado-obra { display: flex; align-items: stretch; gap: 14px; flex-wrap: wrap; }
.estado-obra .anillo { display: flex; align-items: center; gap: 18px; flex: 1 1 320px; max-width: 440px; }
.estado-obra .cifras { flex: 3 1 540px; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(158px, 100%), 1fr)); }

/* ---------- Inicio, repartido para que entre en una pantalla ---------
   La pantalla medía 1.526 px —dos pantallas de alto en un portátil— y lo
   que más se notaba no era el largo sino el hueco: las cuatro tarjetas de
   «Requiere atención» pedían 320 px de mínimo y en 1.192 px de ancho útil
   entran tres, así que caían tres y una. El agujero al lado de la cuarta
   era lo primero que veías al entrar.

   El reparto va por escalones de cuatro, dos y una columna, y no por
   `auto-fit`. Con `auto-fit` y 250 px de mínimo la cuenta da cuatro
   columnas en 1.280 px pero TRES entre 900 y 1.100, y con cuatro avisos
   tres columnas es el mismo tres y uno de antes, sólo que más angosto.
   Yendo por potencias de dos, cuatro avisos cierran rectángulo en todos
   los anchos. Lo que no arregla ningún reparto es un número impar de
   avisos, y la cantidad no está acotada: hay uno por cada subsistema
   trabado por punch A.

   El resto es aire. Cada panel, cada tarjeta y cada fila de la lista
   ceden unos milímetros; nada cambia de lugar, así que no hay que
   reaprender dónde está nada. Va todo colgado de `.inicio` porque los
   mismos `.ag-panel` y `.kpi` los usan Indicadores, Punch y Firmas, y
   ahí el aire está bien donde está. */
.inicio { gap: 14px; }
.inicio .ag-panel { padding: 16px 18px; }
.inicio .kpi { padding: 12px 14px; }
.inicio .kpi .v { margin-top: 8px; }
.inicio .fila-pend { padding: 9px 10px; }
.aten-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
@media (min-width: 1180px) { .aten-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 680px)  { .aten-cols { grid-template-columns: minmax(0, 1fr); } }
.aten-cols .aviso { padding: 11px 13px; }

/* ---------- Inicio: la lista de "por dónde seguir" ------------------- */
.pend { display: flex; flex-direction: column; }
.fila-pend {
  display: grid; align-items: center; gap: 14px; padding: 12px 10px;
  grid-template-columns: 30px minmax(0, 1fr) 110px auto auto;
  border-top: 1px solid var(--ag-line); text-decoration: none; color: inherit; border-radius: 10px;
}
.fila-pend:first-child { border-top: 0; }
.fila-pend:hover { background: var(--ag-ctl); }
.fila-pend .qu { min-width: 0; display: flex; flex-direction: column; }
.fila-pend .qu b { font-weight: 600; }
.fila-pend .qu span { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fila-pend .ss { font-size: 13px; }
.fila-pend .av { display: flex; align-items: center; gap: 10px; width: 170px; }
.fila-pend .av .ag-mono { font-size: 13px; white-space: nowrap; }
/* El verbo de la fila, no un botón: la fila entera ya es el enlace, y ocho
   botones rojos en vertical eran puro ruido. */
.fila-pend .ir {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font-size: 14px; font-weight: 600; color: var(--ag-muted); min-width: 92px; justify-content: flex-end;
}
.fila-pend:hover .ir { color: var(--ag-ink); }

@media (max-width: 900px) {
  .fila-pend { grid-template-columns: 30px minmax(0, 1fr) auto; }
  .fila-pend .ss, .fila-pend .av { display: none; }
}

/* Cabecera del protocolo: queda arriba mientras se recorren los controles. */
.fld-cab {
  position: sticky; top: 76px; z-index: 6;
  padding: 14px 18px; display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}
.fld-cab .volver, .fld-cab .ag-esp { flex: none; }
.fld-cab .quien { min-width: 0; flex: 1 1 260px; }
.fld-cab .quien .l1 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fld-cab .quien .tag { font-size: 21px; font-weight: 600; }
.fld-cab .quien .nom { font-weight: 600; }
.fld-cab .quien .l2 { font-size: 14px; margin-top: 2px; }
.fld-cab .avance { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex: none; }
.fld-cab .avance .ag-bar { width: 170px; flex: none; height: 6px; }
.fld-cab .salidas { display: flex; gap: 8px; flex: none; }
/* Barra de cierre: queda abajo, siempre a mano. Va opaca a propósito: si es
   translúcida, las filas de controles se leen por detrás y no se entiende nada. */
.fld-pie-fijo {
  position: sticky; bottom: 14px; z-index: 6;
  background: linear-gradient(var(--ag-panel), var(--ag-panel)), var(--ag-page);
  border-color: var(--ag-panel-line);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
}
/* Lo mismo para la cabecera, que también se queda fija sobre los controles. */
.fld-cab { background: linear-gradient(var(--ag-panel), var(--ag-panel)), var(--ag-page); }

/* ---------- portada del ITR ----------------------------------------- */
/* El encabezado del formulario en papel: los datos que identifican el equipo,
   arriba de todo y siempre a la vista. Los cuatro primeros no se editan acá
   porque salen del ítem; se cambian en Sistemas e ítems. */
.portada {
  display: grid; gap: 1px; grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
  background: var(--ag-line); border: 1px solid var(--ag-line); border-radius: 12px; overflow: hidden;
}
.portada .dato { background: var(--ag-panel); padding: 12px 14px; min-width: 0; }
.portada .dato > span {
  display: block; font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ag-muted); font-weight: 650;
}
.portada .dato > b { display: block; margin-top: 4px; font-size: 15px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portada .dato > em { display: block; font-style: normal; font-size: 13px; color: var(--ag-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- hoja de control: una fila por control ------------------- */
/* El ITR en papel es una grilla: el control a la izquierda y las casillas
   a la derecha. Esto es lo mismo, con el valor medido en la misma línea
   cuando el control lo pide. */
.ctrls { display: flex; flex-direction: column; }
.ctrl-f {
  display: grid; gap: 10px 16px; align-items: center; padding: 14px 4px;
  grid-template-columns: 34px minmax(0, 1fr) auto auto auto;
  border-top: 1px solid var(--ag-line);
}
.ctrl-f:first-child { border-top: 0; }
.ctrl-f:hover { background: color-mix(in srgb, var(--ag-ctl) 45%, transparent); }
.ctrl-f.sin { box-shadow: inset 3px 0 0 var(--ag-track); }
.ctrl-f.es-ok { box-shadow: inset 3px 0 0 var(--ag-ok); }
.ctrl-f.es-na { box-shadow: inset 3px 0 0 var(--ag-line-strong, var(--ag-muted)); }
.ctrl-f.es-punch { box-shadow: inset 3px 0 0 var(--ag-red); }

.ctrl-f > .num { font-family: var(--font-mono); font-size: 13px; color: var(--ag-muted); text-align: right; }
.ctrl-f > .tx { min-width: 0; }
.ctrl-f > .tx p { margin: 0; line-height: 1.45; }
.ctrl-f > .tx .guia { font-size: 13px; color: var(--ag-muted); margin-top: 3px; }

/* el campo de lectura: aparece sólo si el control la pide */
.ctrl-f .val { position: relative; width: 170px; }
.ctrl-f .val .inp { height: 40px; padding-right: 52px; font-family: var(--font-mono); font-size: 16px; }
.ctrl-f .val .u {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  font-family: var(--font-mono); font-size: 13px; color: var(--ag-muted); pointer-events: none;
}
.ctrl-f .val .inp.mal { border-color: var(--ag-red); box-shadow: 0 0 0 1px var(--ag-red); }

/* las tres opciones, como las casillas del papel */
.opt { display: inline-flex; padding: 3px; gap: 3px; border-radius: 12px;
  background: var(--ag-ctl); border: 1px solid var(--ag-ctl-line); }
.opt button {
  all: unset; cursor: pointer; min-width: 74px; height: 38px; padding: 0 12px;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border-radius: 9px; font-size: 14px; font-weight: 600; color: var(--ag-nav);
  text-align: center; box-sizing: border-box;
}
.opt button:hover { color: var(--ag-ink); }
.opt button:focus-visible { outline: 2px solid var(--ag-blue); outline-offset: 1px; }
.opt button[aria-pressed="true"] { background: var(--ag-ctl-on); color: var(--ag-ink); }
.opt button.v-ok[aria-pressed="true"]  { background: color-mix(in srgb, var(--ag-ok) 20%, transparent); color: var(--ag-ok); }
.opt button.v-na[aria-pressed="true"]  { background: var(--ag-ctl-on); color: var(--ag-ink); }
.opt button.v-pl[aria-pressed="true"]  { background: color-mix(in srgb, var(--ag-red) 16%, transparent); color: var(--ag-red); }
.opt button[disabled] { opacity: .45; cursor: default; }

/* segunda línea: comentario, se despliega a pedido */
.ctrl-f .mas { grid-column: 2 / -1; display: none; }
.ctrl-f.abierto .mas { display: block; }
.ctrl-f .mas .inp { height: 38px; }
.ctrl-f .pu { display: inline-flex; gap: 6px; }

@media (max-width: 1100px) {
  .ctrl-f { grid-template-columns: 30px minmax(0, 1fr) auto; }
  .ctrl-f .val { grid-column: 2 / -1; width: 100%; max-width: 220px; }
  .ctrl-f .opt { grid-column: 2 / 3; }
}
@media (max-width: 680px) {
  .ctrl-f { grid-template-columns: 28px minmax(0, 1fr); }
  .ctrl-f .val, .ctrl-f .opt, .ctrl-f .pu { grid-column: 2 / -1; }
  .opt { width: 100%; }
  .opt button { flex: 1; min-width: 0; height: 46px; }
}

/* En tablet y teléfono la cabecera deja de ser fija: ocupaba media pantalla
   y los controles, que son lo que se viene a hacer, quedaban abajo de todo. */
@media (max-width: 900px) {
  .fld-cab { position: static; padding: 12px 14px; gap: 10px 12px; }
  .fld-cab .quien { flex: 1 1 100%; order: 3; }
  .fld-cab .quien .tag { font-size: 18px; }
  .fld-cab .avance { order: 2; margin-left: auto; align-items: flex-end; }
  .fld-cab .avance .ag-bar { width: 120px; }
  .fld-cab .salidas { order: 4; flex: 1 1 100%; }
  .fld-cab .salidas .ag-btn { flex: 1; }
  /* Abajo ya está la barra de pestañas: una segunda barra fija encima tapa
     el control que se está cargando. Acá va al final y se llega scrolleando. */
  .fld-pie-fijo { position: static; box-shadow: none; }
  .fld-pie-fijo .ag-btn, .fld-pie-fijo form { flex: 1 1 auto; }
  .fld-pie-fijo form .ag-btn { width: 100%; }
}

/* La fila entera es el objetivo del clic cuando la tabla lo declara. */
.ag-table.filas-click tbody tr { cursor: pointer; }
.ag-table.filas-click tbody tr:hover { background: var(--ag-ctl); }

/* Un campo dentro de una tabla no se deja aplastar: la caja scrollea.
   Sin esto, a 820 px el input de "Código" quedaba en 40 px y no se leía. */
.ag-table .inp, .ag-table .sel { min-width: 96px; }
.ag-table td > .inp, .ag-table td > .sel { min-width: 110px; }

/* La columna de acciones no parte los botones en dos renglones. */
.ag-table td.acc { white-space: nowrap; }
/* Usuarios lleva cuatro acciones por fila y contra el borde quedaban cortadas.
   Acá sí envuelven: una fila más alta se lee, un botón cortado no. El flex va
   en los formularios, no en la celda: display:flex sobre un <td> lo saca del
   layout de tabla y la fila deja de crecer con lo que tiene adentro. */
#tUsuarios td.acc { white-space: normal; text-align: right; padding-top: 10px; padding-bottom: 10px; }
#tUsuarios td.acc form { display: inline-block; margin: 3px 0 3px 8px; }
.ag-table td.acc form { display: inline-block; vertical-align: middle; }
.ag-table td.acc > * + * { margin-left: 6px; }
.ag-table th.n { text-align: right; }
.ag-table td.n { text-align: right; }

/* Bloque plegable con la misma piel que una tarjeta. */
.pliegue > summary { cursor: pointer; list-style: none; display: flex; align-items: center;
  justify-content: space-between; gap: 10px; font-weight: 650; }
.pliegue > summary::-webkit-details-marker { display: none; }
.pliegue > summary .chev { transition: transform .18s ease; color: var(--ag-muted); display: inline-flex; }
/* En pantalla angosta el rótulo de estado no entra al lado del título: que
   baje entero, con su flecha al lado, en vez de dejar la flecha sola en un
   tercer renglón. */
.pliegue > summary { flex-wrap: wrap; }
.pliegue > summary > .fila { flex-wrap: nowrap; min-width: 0; }
.pliegue > summary .chev { flex: none; }
.pliegue[open] > summary .chev { transform: rotate(180deg); }
.pliegue > .cuerpo { margin-top: 16px; }

/* Lista de casillas: una grilla que no rompe el nombre largo del protocolo. */
.marcas { display: grid; gap: 8px 18px; grid-template-columns: repeat(auto-fill, minmax(min(290px, 100%), 1fr)); }
.marca { display: flex; align-items: center; gap: 10px; min-width: 0; cursor: pointer;
  padding: 7px 10px; border-radius: 10px; border: 1px solid transparent; }
.marca:hover { background: var(--ag-ctl); border-color: var(--ag-ctl-line); }
.marca .tx { min-width: 0; display: flex; align-items: center; gap: 8px; flex: 1; }
.marca .tx em { font-style: normal; color: var(--ag-muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* Bloque de código de ejemplo (ayuda del CSV). */
.codigo { font-family: var(--font-mono); font-size: 12px; line-height: 1.6; background: var(--ag-ctl);
  border: 1px solid var(--ag-ctl-line); border-radius: 10px; padding: 12px 14px; overflow-x: auto;
  margin: 0; color: var(--ag-ink); }
code.en { font-family: var(--font-mono); font-size: .92em; background: var(--ag-ctl);
  border: 1px solid var(--ag-ctl-line); border-radius: 6px; padding: 1px 6px; }

/* ---------- hoja de controles: una fila por control ------------------ */
/* El ITR en papel es una grilla: cada control con su casilla de apto, no
   aplica o pendiente. Esto es eso, con el campo de medición desplegándose
   en el mismo renglón cuando el control pide un número. */
.hoja { display: flex; flex-direction: column; }
.ren { display: grid; grid-template-columns: 38px minmax(0,1fr) auto auto; gap: 16px;
  align-items: center; padding: 12px 18px; border-bottom: 1px solid var(--ag-line);
  transition: background .15s ease; }
.ren:last-child { border-bottom: 0; }
.ren:hover { background: var(--ag-hover); }
.ren .nro { font-family: var(--font-mono); font-size: 13px; color: var(--ag-muted); text-align: right; }
.ren .tx { font-size: 15px; line-height: 1.45; min-width: 0; }
.ren .tx .ayu { display: block; color: var(--ag-muted); font-size: 13px; margin-top: 3px; }
.ren[data-estado="ok"]    { box-shadow: inset 3px 0 0 var(--ag-ok); }
.ren[data-estado="na"]    { box-shadow: inset 3px 0 0 var(--ag-muted); }
.ren[data-estado="punch"] { box-shadow: inset 3px 0 0 var(--ag-red); }

/* El campo de valor se despliega en la misma línea, sin empujar nada. */
.val { display: grid; grid-template-columns: 0fr; transition: grid-template-columns .22s ease; }
.val > .caja { overflow: hidden; min-width: 0; display: flex; align-items: center; gap: 8px; }
.val.on { grid-template-columns: 1fr; }
.val .inp { width: 140px; height: 44px; font-family: var(--font-mono); font-size: 16px;
  text-align: right; padding: 0 10px; }
.val .u { font-family: var(--font-mono); color: var(--ag-muted); font-size: 14px; min-width: 32px; }

.opts { display: inline-flex; gap: 6px; }
.opts button { all: unset; box-sizing: border-box; cursor: pointer; height: 44px; min-width: 44px;
  padding: 0 14px; border-radius: 11px; border: 1px solid var(--ag-ctl-line); background: var(--ag-ctl);
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-size: 14px; font-weight: 600; color: var(--ag-nav); white-space: nowrap; }
.opts button:hover { background: var(--ag-hover); color: var(--ag-ink); }
.opts button:focus-visible { outline: 2px solid var(--ag-blue); outline-offset: 2px; }
.opts button[aria-pressed="true"].ok { background: var(--ag-ok); border-color: var(--ag-ok); color: #06240f; }
.opts button[aria-pressed="true"].na { background: var(--ag-ctl-on); border-color: var(--ag-line2); color: var(--ag-ink); }
.opts button[aria-pressed="true"].pu { background: var(--ag-red); border-color: var(--ag-red); color: #2b0606; }

/* Encabezado del protocolo, pegado arriba mientras se recorre la hoja. */
.hoja-top { position: sticky; top: 76px; z-index: 6; display: flex; align-items: center; gap: 16px;
  flex-wrap: wrap; padding: 14px 18px; border-radius: 16px; margin-bottom: 18px;
  background: var(--ag-panel); border: 1px solid var(--ag-panel-line); backdrop-filter: blur(22px); }
.hoja-top .avc { display: flex; align-items: center; gap: 10px; min-width: 220px; flex: 1; }

@media (max-width: 900px) {
  .ren { grid-template-columns: 38px minmax(0,1fr); row-gap: 12px; }
  .ren .val, .ren .opts { grid-column: 2 / -1; }
  .val.on { grid-template-columns: 1fr; }
  .hoja-top { position: static; }
}

@media (max-width: 1240px) {
  .cols.sub-detalle { grid-template-columns: 260px minmax(0,1fr); }
}
@media (max-width: 900px) {
  .cols.sub-detalle { grid-template-columns: minmax(0,1fr); }
}
@media (max-width: 1100px) {
  .ag-app { margin: 12px; border-radius: 22px; min-height: calc(100vh - 24px); }
  .ag-top { padding: 12px 16px; }
  .ag-main { padding: 20px 18px 30px; }
  .ag-title { font-size: 30px; }
  .ag-title .sub { font-size: 18px; }
  .cols.c2, .cols.c3, .cols.c4 { grid-template-columns: minmax(0,1fr); }
  .ag-nav { display: none; }
  .menu-btn { display: inline-flex; }
}
@media (max-width: 760px) {
  .ag-app { margin: 0; border-radius: 0; border: 0; min-height: 100vh; }
  .ag-main { padding: 16px 14px 92px; }
  .ag-title { font-size: 26px; }
  .ag-panel { padding: 16px; border-radius: 16px; }
  .tabla-caja { margin: 0 -16px; padding: 0 16px; }
  .proys { grid-template-columns: minmax(0,1fr); }
  .tabbar { display: block; }
  .ag-top .ag-proj .nom { display: none; }
}

/* ---------- impresión ---------------------------------------------------- */
@media print {
  .ag-bg, .ag-shade, .ag-top, .tabbar, .ag-acts, .filtros, .pag { display: none !important; }
  .ag-app { margin: 0; border: 0; background: #fff; }
  .ag-root { background: #fff; color: #000; }
  .ag-panel, .ag-card { background: #fff; border-color: #ccc; backdrop-filter: none; box-shadow: none; }
}


/* ---------- guía del ITR (Biblioteca de ITRs) ----------------------- */
/* Lo que el papel no dice pero el inspector necesita, plegado arriba de la
   planilla: alcance, qué va en el encabezado, qué registrar y los «Ojo». */
.guia-itr .alcance { margin: 0; font-size: 15px; line-height: 1.55; max-width: 78ch; }
.gi-cols { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.gi-cols.dos { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); margin: 12px 0; }
.gi-bloque { background: var(--ag-ctl); border: 1px solid var(--ag-ctl-line); border-radius: 12px; padding: 12px 14px; }
.gi-bloque h4 { margin: 0 0 6px; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ag-muted); font-weight: 650; }
.gi-bloque ul { margin: 0; padding-left: 18px; }
.gi-bloque li, .gi-bloque p { font-size: 14px; line-height: 1.5; margin: 0; }
.gi-bloque li + li { margin-top: 4px; }
.gi-bloque.ojo { border-left: 3px solid var(--ag-amb); }
.gi-leyenda { display: flex; flex-wrap: wrap; gap: 8px 22px; font-size: 13px; color: var(--ag-muted); }
.gi-leyenda b { color: var(--ag-ink); margin-right: 4px; font-family: var(--font-mono); }
.gi-leyenda b.t-ok { color: var(--ag-ok); } .gi-leyenda b.t-red { color: var(--ag-red); }
.gi-reglas > summary { cursor: pointer; font-size: 14px; font-weight: 600; }
.gi-reglas ol { margin: 0; padding-left: 22px; font-size: 14px; line-height: 1.5; }
.gi-reglas ol li + li { margin-top: 4px; }
/* El botón de la guía lleva un rótulo largo —«Ver 43 ITR reales firmados…»—
   y `.ag-btn` no envuelve a propósito, para que ningún botón de acción parta
   su verbo en dos. Este no es un botón de acción, es un enlace con forma de
   botón: a 390 px medía 383 adentro de una caja de 328 y se salía. Acá sí
   envuelve, y la altura la pone el contenido. */
.gi-link { align-self: flex-start; white-space: normal; height: auto;
  min-height: 36px; padding-top: 7px; padding-bottom: 7px; text-align: left; }

/* ---------- equipo de prueba ----------------------------------------- */
.equipos { border: 1px solid var(--ag-line); border-radius: 12px; overflow: hidden; }
.eq-f { display: grid; gap: 12px; align-items: center; padding: 10px 14px;
  grid-template-columns: minmax(0, 2.4fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) 40px;
  border-top: 1px solid var(--ag-line); font-size: 14px; }
.eq-f:first-child { border-top: 0; }
.eq-f.cab { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ag-muted); font-weight: 650;
  background: var(--ag-ctl); }
.eq-f.mal { box-shadow: inset 3px 0 0 var(--ag-red); }
.eq-f form { margin: 0; }

/* ---------- encabezado de columnas de la hoja de control ------------- */
/* Como la planilla en papel: N.º | Descripción | (Lectura) | OK N/A Punch.
   Usa la misma grilla que las filas para que las columnas caigan justas. */
.ctrl-cab { display: grid; gap: 10px 16px; align-items: end; padding: 0 4px 8px;
  grid-template-columns: 34px minmax(0, 1fr) auto auto auto;
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ag-muted); font-weight: 650;
  border-bottom: 1px solid var(--ag-line2, var(--ag-line)); }
.ctrl-cab > span:first-child { text-align: right; }
.ctrl-cab .opts { display: inline-flex; gap: 3px; padding: 0 4px; }
.ctrl-cab .opts span { min-width: 74px; text-align: center; }
.ctrl-cab + .ctrl-f { border-top: 0; }
/* el ancho de «Lectura» y de la columna de íconos tiene que igualar al de las filas */
.ctrl-cab > span:nth-child(3) { width: 170px; }
.ctrl-cab > span:last-child { width: 82px; }

.ctrl-f.falta { box-shadow: inset 3px 0 0 var(--ag-red); }
.ctrl-f .falta-txt:empty { display: none; }
.ctrl-f:target { background: color-mix(in srgb, var(--ag-amb) 10%, transparent); }

/* ---------- qué falta para terminarlo ------------------------------- */
.falta-panel { border-left: 3px solid var(--ag-amb); }
.falta-panel.ok { border-left-color: var(--ag-ok); }
.falta-lista { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.falta-lista li { font-size: 14px; padding-left: 20px; position: relative; line-height: 1.45; }
.falta-lista li::before { content: ''; position: absolute; left: 4px; top: .55em; width: 7px; height: 7px; border-radius: 50%; background: var(--ag-muted); }
.falta-lista li.bloquea::before { background: var(--ag-red); }
.falta-lista li.aviso-li::before { background: var(--ag-amb); }
.falta-lista li.listo { color: var(--ag-ok); }
.falta-lista li.listo::before { background: var(--ag-ok); }
.falta-lista a { color: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--ag-line2, var(--ag-muted)); }
@keyframes llamar { 0%, 100% { box-shadow: none; } 40% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ag-amb) 55%, transparent); } }
.falta-panel.llama { animation: llamar .9s ease 2; }

@media (max-width: 1100px) {
  .ctrl-cab { display: none; }
  .eq-f { grid-template-columns: minmax(0, 1fr) auto; }
  .eq-f.cab { display: none; }
  .eq-f > span:nth-child(2), .eq-f > span:nth-child(3) { display: none; }
}

/* ---------- trazabilidad de firmas --------------------------------------- */
/* Cuatro contadores que además son el filtro: el número es el dato, así que
   manda él y el rótulo va abajo, chico. */
.tar-firmas .tar { display: flex; flex-direction: column; gap: 2px; padding: 16px 18px;
  border: 1px solid var(--ag-line2); border-radius: 14px; background: var(--ag-ctl);
  color: var(--ag-ink); text-decoration: none; }
/* `background-color` y no `background`: el atajo también pisa
   `background-image`, que es por donde va el tinte de la familia, así que
   con el atajo bastaba pasar el mouse por encima para que la tarjeta se
   destiñera. */
.tar-firmas .tar:hover { background-color: var(--ag-hover); text-decoration: none; }
.tar-firmas .tar .n { font-size: 30px; font-weight: 700; line-height: 1.1; font-family: var(--font-mono); }
.tar-firmas .tar .t { font-weight: 650; }
.tar-firmas .tar .d { color: var(--ag-muted); font-size: 13px; }

/* Quién firmó: el nombre arriba y la empresa con la fecha abajo, chiquito. */
.qfirmo { display: flex; flex-direction: column; line-height: 1.35; }
.qfirmo b { font-weight: 650; }
.qfirmo .ag-muted { font-size: 12px; }
/* Nombres que quedaron de antes de la doble firma: están, pero no son firma. */
.qfirmo.sinfirma > span:first-child { color: var(--ag-muted); font-style: italic; }

/* ---------- doble firma del protocolo ------------------------------------ */
/* Dos tarjetas al lado, numeradas, porque el orden importa: calidad primero.
   La que ya está firmada se distingue por el filo, no por un color de fondo:
   lo que tiene que leerse de un vistazo es el nombre, no la tarjeta. */
.firma { border: 1px solid var(--ag-line2); border-radius: 14px; padding: 16px; background: var(--ag-ctl);
  display: flex; flex-direction: column; gap: 12px; }
.firma.puesta { box-shadow: inset 3px 0 0 var(--ag-ok); }
.firma .tit { display: flex; align-items: center; gap: 10px; font-weight: 650; }
.firma .tit .n { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  font-size: 13px; border: 1.5px solid var(--ag-line2); color: var(--ag-muted); flex: none; }
.firma.puesta .tit .n { border-color: var(--ag-ok); color: var(--ag-ok); }
.firma .tit > span:nth-child(2) { margin-right: auto; }

/* La estampa: lo que queda cuando ya firmó. Imita el renglón del papel. */
.firma .estampa { display: flex; flex-direction: column; gap: 3px;
  border-top: 1px solid var(--ag-line2); padding-top: 12px; }
.firma .estampa b { font-size: 17px; }
.firma .estampa span { font-size: 14px; }

/* Los cuatro dígitos: cortito, centrado y separado, para no errarle con el
   dedo en la tablet. 48px de alto, que es tamaño de campo. */
.inp.cod { max-width: 170px; height: 48px; font-family: var(--font-mono); font-size: 22px;
  letter-spacing: 10px; text-align: center; padding: 0 0 0 10px; }


/* ---------- revisión de la ingesta: riel, pasos y panel de origen -------- */
.cols.tri { grid-template-columns: 290px minmax(0,1fr) 340px; }

.pasos { display: flex; align-items: center; flex-wrap: wrap; gap: 0; }
.pasos .p { display: inline-flex; align-items: center; gap: 10px; padding: 0 14px; height: 44px; color: var(--ag-muted); }
.pasos .p .n { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-size: 13px; font-weight: 700; border: 1.5px solid var(--ag-line2); flex: none; }
.pasos .p.hecho { color: var(--ag-ink); }
.pasos .p.hecho .n { border-color: var(--ag-ok); color: var(--ag-ok); }
.pasos .p.actual { color: var(--ag-ink); font-weight: 650; }
.pasos .p.actual .n { background: var(--ag-neon); border-color: var(--ag-neon); color: #04201d; }
.pasos .raya { width: 46px; height: 1px; background: var(--ag-line2); }

/* riel: un subsistema por fila, con lo que falta mirar a la derecha */
.ss button.fila-ss { all: unset; box-sizing: border-box; cursor: pointer; display: block; width: 100%;
  padding: 11px 14px; border-radius: 12px; color: var(--ag-ink); }
.ss button.fila-ss:hover { background: var(--ag-hover); }
.ss button.fila-ss.on { background: var(--ag-ctl-on); box-shadow: inset 3px 0 0 var(--ag-ink); }
.ss .aviso-n { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px;
  padding: 0 6px; border-radius: 6px; font-size: 12px; font-weight: 700; font-family: var(--font-mono);
  background: var(--warn-soft); color: var(--ag-amb); }

/* barra de confianza del corpus entero */
.conf-total { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: var(--ag-track); }
.conf-total i { display: block; height: 100%; }
.conf-total .alta { background: var(--ag-ok); }
.conf-total .media { background: var(--ag-amb); }
.conf-total .baja { background: var(--ag-red); }

/* panel de "de dónde salió" */
.det { position: sticky; top: 92px; display: flex; flex-direction: column; gap: 16px;
  max-height: calc(100vh - 120px); overflow-y: auto; }
.det h3 { font-size: 20px; font-weight: 650; }
.det .motivo { font-size: 14px; line-height: 1.5; color: var(--ag-muted); white-space: pre-wrap; }

@media (max-width: 1340px) {
  .cols.tri { grid-template-columns: 260px minmax(0,1fr); }
  .cols.tri > .det { grid-column: 1 / -1; position: static; max-height: none; }
}
@media (max-width: 900px) {
  .cols.tri { grid-template-columns: minmax(0,1fr); }
}

/* Angosto: la tabla de ítems suelta las dos columnas que ya se ven en otro
   lado — el tipo está en el panel de origen y el subsistema en el riel — para
   no obligar a scrollear la tabla de costado. */
@media (max-width: 1240px) {
  #tItems th:nth-child(5), #tItems td:nth-child(5),
  #tItems th:nth-child(6), #tItems td:nth-child(6) { display: none; }
}

/* Una cola rota no es una cola más: el contador de actas que perdieron su
   respaldo se marca aunque no esté seleccionado. */

/* Campo que el PDF no pudo dar: se marca para que se complete, sin gritar. */
.campo.pedido > span { color: var(--ag-amb); }
.campo.pedido > span em { font-style: normal; font-size: 12px; opacity: .85; }
.campo.pedido .inp { border-color: var(--ag-amb); }

/* La columna de acciones de instrumentos lleva cuatro botones chicos: que
   envuelvan antes que recortarse, igual que en Usuarios. */
/* La columna de acciones no envuelve: con cuatro botones, dejarla envolver
   hace filas de cuatro renglones. Si la tabla no entra, scrollea la caja,
   que para eso está. */
.ag-table td.acc { text-align: right; padding-left: 4px; padding-right: 8px; }
.ag-table td.acc form { display: inline-block; margin: 2px 0 2px 4px; vertical-align: middle; }
.ag-table td.acc > a.ag-icon { margin: 2px 0 2px 4px; vertical-align: middle; }
/* El botón que abre el selector de archivo es un label: tiene que verse igual
   que los demás y responder al teclado. */
label.ag-icon { cursor: pointer; }

/* La fila que se abre para corregir: va pegada a la suya, sin el hover de
   tabla, para que se lea como parte de ella y no como otro registro. */
.ag-table tr.fila-edit > td { background: var(--ag-ctl); padding: 14px 16px 16px; }
.ag-table tr.fila-edit:hover > td { background: var(--ag-ctl); }
.ag-icon.on { border-color: var(--ag-chip-on-line); background: var(--ag-chip-on); }
/* Tabla de instrumentos: la densidad de una planilla, no la de un informe.
   Los botones de acción son cuatro y tienen que entrar en una línea sin
   empujar la tabla fuera de la pantalla. */
.tabla-equipos td { padding-top: 9px; padding-bottom: 9px; }
.tabla-equipos td.acc .ag-icon.sm { width: 30px; height: 30px; border-radius: 9px; }
.tabla-equipos td.acc form, .tabla-equipos td.acc > a.ag-icon { margin-left: 3px; }
/* Anchos fijos: lo que no entra se recorta con puntos suspensivos y queda en
   el title. Es preferible a que una razón social larga empuje toda la tabla
   fuera de la pantalla. */
.tabla-equipos { table-layout: fixed; width: 100%; }
.tabla-equipos .lin { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabla-equipos tr.fila-edit > td { white-space: normal; }

/* Arrastrar o elegir: la misma caja hace las dos cosas. La caja es el propio
   <label>, así que el clic en cualquier parte abre el buscador de archivos
   aunque el JavaScript no haya cargado; el borde punteado es la única pista
   de que además se puede soltar encima. */
.campo > span.suelta-caja { display: flex; flex-direction: column; gap: 10px; padding: 12px;
  border: 1.5px dashed var(--ag-ctl-line); border-radius: 14px; background: var(--ag-field);
  text-transform: none; letter-spacing: 0; font-size: 14px; font-weight: 400;
  color: var(--ag-ink); }
.suelta-txt { display: flex; align-items: center; gap: 8px; font-size: 13px;
  color: var(--ag-muted); }
.suelta-txt svg { flex: none; }
.suelta-caja .inp { background: var(--ag-ctl); }
/* Mientras el archivo está en el aire sobre la caja. */
.suelta.soltando > span.suelta-caja { border-style: solid; border-color: var(--ag-blue);
  background: var(--ag-ctl-on); }
.suelta.soltando .suelta-txt { color: var(--ag-ink); }
/* Los nombres de lo que se soltó: el input sólo dice «2 archivos». */
.suelta-lista { font-size: 13px; line-height: 1.45; color: var(--ag-ink); font-weight: 500;
  overflow-wrap: anywhere; }
.suelta-lista[hidden] { display: none; }
.suelta-mal { color: var(--ag-red); }

/* ---------- Donde se toca con el dedo, y con guante ----------------------
   El sistema está dimensionado para mouse: `.ag-icon.sm` mide 36, las tres
   opciones del control 38, los campos y los botones 42. Con guante de obra
   el mínimo que se acierta es 44x44, y el dedo no tiene la precisión del
   puntero: errar un «OK» y darle a «Punch» abre un pendiente que después
   alguien tiene que ir a cerrar.

   `pointer: coarse` es exactamente esa diferencia. La tablet de campo la
   declara y la máquina de la oficina no, así que la oficina conserva su
   densidad y nadie tiene que acordarse de prender un «modo campo».

   La casilla de la tabla queda afuera: es un control nativo y a 44 ocupa la
   fila. Va a 30, y lo que lleva al protocolo es la fila entera, que ya es
   clickeable. Tildar es la acción secundaria de esa fila, no la de campo. */
@media (pointer: coarse) {
  .ag-btn, .ag-btn.sm { min-height: 44px; }
  .ag-icon, .ag-icon.sm, .tabla-equipos td.acc .ag-icon.sm {
    width: 44px; height: 44px; border-radius: 12px;
  }
  .opt button { height: 44px; }
  .inp, .sel, .ta, .selcap .sel, .ctrl-f .val .inp, .ctrl-f .mas .inp { min-height: 44px; }
  .ta { min-height: 92px; }
  .ag-proj { height: 44px; }
  .pag a, .pag span.n { min-width: 44px; height: 44px; }
  .ag-check { width: 30px; height: 30px; }
}

/* ---------- La tabla ancha en una pantalla angosta ----------------------
   La lista de protocolos pide 981 px para sus ocho columnas. En tablet la
   caja que la contiene mide 703: 588 px que se iban para la derecha, y con
   ellos el estado, el avance y la última carga. Paginar —que es lo que se
   hizo antes— arregló cuántas filas hay, no cuánto mide cada una. La cuenta
   también fallaba en un portátil de 1024, por 36 px.

   Ensanchar no es la salida: ocho columnas de datos no entran en 703 px sin
   dejar la letra ilegible. Lo que se decide acá es qué columna no va cuando
   la pantalla es angosta.

   `col-prosa` es la única columna de texto corrido, así que es la única que
   puede envolver en dos líneas en lugar de empujar la tabla. Eso vale en
   cualquier ancho, y por eso está fuera del media query: en escritorio sobra
   lugar y el título sigue entrando en una línea, porque la tabla reparte por
   contenido.

   `col-sec` es lo que se cae abajo de 1100 px, y se cae por una razón cada
   uno:
     · El subsistema casi siempre ya es el filtro con el que se llegó, y está
       arriba de todo en la pantalla del protocolo.
     · «Últ. carga» dice cuándo tocó alguien, no qué falta. Es dato de
       seguimiento de oficina.
     · La barra de avance y la fracción dicen lo mismo, y la fracción lo dice
       mejor: 17/18 no se cierra, y la barra a ese porcentaje se ve llena.
   Queda lo que se mira para trabajar: especialidad, TAG, qué protocolo es,
   en qué estado está y cuánto le falta.

   La casilla de selección se cae aparte, y sólo con el dedo. Sirve para
   imprimir varios dossiers de una vez, que es algo que se hace sentado; pero
   sentado puede ser también una ventana angosta en un portátil, y ahí la
   función tiene que seguir estando. Sin la casilla, lo que queda entra en
   los 703 px de la tablet sin desbordar nada.

   La caja conserva su `overflow-x` de siempre, que ahora es red y no camino
   obligado. */
.ag-table th.col-avance { width: 180px; }
.ag-table td.col-prosa { white-space: normal; }

@media (max-width: 1100px) {
  .ag-table .col-sec { display: none; }
  .ag-table th.col-avance { width: 92px; }
  .ag-table td.col-prosa { min-width: 170px; line-height: 1.35; padding-top: 9px; padding-bottom: 9px; }
}

@media (max-width: 1100px) and (pointer: coarse) {
  .ag-table .col-sel { display: none; }
}
/* ======================================================================
   VIDRIO LEGIBLE: el mismo material en los dos temas
   ======================================================================
   Assure es vidrio translúcido sobre una fotografía, y eso se queda: en
   claro y en oscuro. Lo que fallaba no era el material, era el canto. Un
   panel al 52 % con un borde de 0,09 de alfa no se termina de despegar de
   lo que tiene atrás, así que seis tarjetas al lado se leían como una
   sola mancha y había que buscarlas con la vista en lugar de verlas.

   La corrección es la de los tableros que miramos de referencia: cada
   cuadro es un objeto, y un objeto se reconoce por tres cosas a la vez.
     · El relleno sube un escalón por cada nivel de anidado (cáscara,
       panel, tarjeta), así que el de adentro siempre es más claro que el
       de afuera y el escalón se ve sin pensarlo.
     · El canto es doble, como el vidrio de verdad: un filo claro arriba,
       donde la luz pega, y una línea de contorno abajo.
     · La sombra lo separa del fondo. Dos capas: una de 1 px que lo
       despega y una ancha que lo hace flotar.

   Las tres salen de cuatro variables, `--ag-filo`, `--ag-sombra`,
   `--ag-sombra-app` y los rellenos, así que el ajuste es por tema y no
   por componente. */

/* --- oscuro: subir el canto y el relleno de las tarjetas ------------- */
/* El relleno de tarjeta estaba en 0,045 de blanco: a ojo, nada. */
.ag-root {
  --ag-panel-line: rgba(255, 255, 255, 0.14);
  --ag-card: rgba(255, 255, 255, 0.075);
  --ag-card-line: rgba(255, 255, 255, 0.14);
  --ag-ctl-line: rgba(255, 255, 255, 0.16);
  --ag-line: rgba(255, 255, 255, 0.10);
  --ag-filo: rgba(255, 255, 255, 0.10);
  --ag-sombra: 0 1px 2px rgba(0, 0, 0, 0.34), 0 16px 34px -20px rgba(0, 0, 0, 0.72);
  --ag-sombra-app: 0 2px 8px rgba(0, 0, 0, 0.42), 0 44px 88px -44px rgba(0, 0, 0, 0.9);
}

/* --- claro: vidrio, pero vidrio limpio ------------------------------- */
/* El problema del claro era que el vidrio estaba sucio: al 58 % de blanco
   sobre una foto gris, ninguna superficie llegaba a ser clara y la tinta
   competía con la transparencia. Se arregla por los dos lados a la vez:
   el relleno sube y lo que hay detrás se aclara, así que la foto se sigue
   viendo —alrededor, y a través— pero el compuesto queda casi blanco y la
   tinta vuelve a tener contra qué leerse. */
.ag-root.ag-light {
  --ag-page: #c9ced3;
  --ag-app: rgba(255, 255, 255, 0.46);
  --ag-app-line: rgba(255, 255, 255, 0.72);
  --ag-panel: rgba(255, 255, 255, 0.74);
  --ag-panel-line: rgba(255, 255, 255, 0.9);
  --ag-card: rgba(255, 255, 255, 0.86);
  --ag-card-line: rgba(17, 18, 19, 0.09);
  --ag-ctl: rgba(255, 255, 255, 0.80);
  --ag-ctl-line: rgba(17, 18, 19, 0.14);
  --ag-hover: rgba(255, 255, 255, 0.95);
  --ag-sel: rgba(255, 255, 255, 0.7);
  --ag-field: rgba(255, 255, 255, 0.85);
  --ag-muted: #474b50;
  --ag-line: rgba(17, 18, 19, 0.10);
  --ag-filo: rgba(255, 255, 255, 0.85);
  --ag-sombra: 0 1px 2px rgba(17, 18, 19, 0.07), 0 14px 30px -18px rgba(17, 18, 19, 0.3);
  --ag-sombra-app: 0 2px 8px rgba(17, 18, 19, 0.07), 0 36px 72px -36px rgba(17, 18, 19, 0.4);
}

/* La foto se desenfoca más y se aclara: adentro de la aplicación tiene que
   ser textura, no contenido, o el vidrio deja de ser legible. Sólo adentro:
   el ingreso no tiene cáscara, la foto es toda su puesta en escena y ahí se
   queda como estaba.

   Con 0,72 de opacidad seguía siendo contenido: una planta gris de la que se
   distinguían los caños, abajo de paneles al 58 % de blanco. El resultado no
   era vidrio sino barro —tres grises apilados, ninguno llegando a blanco— y
   es la razón por la que el tema claro se veía apagado al lado del oscuro.
   En oscuro la traslucidez sobre una foto oscura se lee como profundidad; en
   claro, sobre una foto clara, se lee como suciedad.

   Baja a 0,30, en gris puro y con más desenfoque: la foto queda de textura,
   que es lo único que tenía que ser, y los paneles suben al 90 % para que el
   blanco sea blanco. La foto no se va —es la identidad de la aplicación y es
   la misma que en oscuro—, se corre atrás. */
.ag-root.ag-light:has(.ag-app) {
  /* La foto queda casi apagada —0,12— y la luz la pone el degradé de abajo.
     Medido sobre el píxel de la pantalla real: con la cáscara anterior, lo
     que se veía a través del vidrio era blanco. La superficie de la app daba
     #f1f3f2 contra el #fefefe del panel, o sea nada. El vidrio no era vidrio:
     era una caja blanca con un borde, y por eso el tema claro parecía apagado
     al lado del oscuro.

     Y no era falta de escalón de tono. En oscuro el salto entre la app y el
     panel es 1,025 y en claro era 1,105: el oscuro tiene MENOS escalón y se
     ve mejor. Lo que tiene es algo atrás del vidrio. Esto se lo devuelve al
     claro. */
  --ag-page: #e8eaec;
  /* La foto vuelve a verse. Estaba en 0,12 con 34 px de desenfoque, o sea
     apagada: el campo verde agua quedaba liso y eso es un color plano, no
     vidrio. Sube a 0,42 con la mitad del desenfoque, así que se distingue
     que hay una planta atrás. Sigue en gris puro —la que la pinta de verde
     agua es la luz de arriba, no ella—, que es lo que evita que cada
     proyecto tiña los paneles de un color distinto. */
  --ag-photo: 0.42;
  /* Quedan declarados para el ingreso, que usa `.ag-light` sin `.ag-app` y
     conserva el degradé vertical de siempre. Adentro de la aplicación los
     reemplaza la malla de abajo. */
  --ag-shade-a: rgba(250, 251, 252, 0.50);
  --ag-shade-b: rgba(238, 241, 243, 0.80);
  /* Los paneles se abren —del 90 al 80 %— para que la luz de atrás llegue.
     La tinta sobre el panel sigue en 17:1 contra un mínimo de 4,5, así que
     lo que se gasta acá es margen que sobraba. */
  --ag-app: rgba(255, 255, 255, 0.34);
  --ag-app-line: rgba(255, 255, 255, 0.95);
  /* El panel también se abre. Con la tabla adentro no puede irse tan lejos
     como una tarjeta —veinticinco filas de 13 px piden fondo parejo—, pero
     del 88 al 76 % ya deja ver que hay algo atrás. */
  --ag-panel: rgba(255, 255, 255, 0.76);
  --ag-panel-line: rgba(255, 255, 255, 0.98);
  --ag-ctl: rgba(255, 255, 255, 0.72);
  --ag-filo: rgba(255, 255, 255, 1);
  /* Los cantos suben de 0,09 y 0,14 a 0,10 y 0,16: sobre un panel más opaco
     una línea al 9 % desaparece, y era parte de lo que hacía que las
     tarjetas no tuvieran dónde terminar. */
  --ag-card-line: rgba(17, 18, 19, 0.10);
  --ag-ctl-line: rgba(17, 18, 19, 0.16);
  --ag-line2: rgba(17, 18, 19, 0.20);
  --ag-hover: rgba(255, 255, 255, 0.98);
  --ag-field: rgba(255, 255, 255, 0.95);
}
/* Gris puro y no 0,75: con algo de color, la foto le metía un tinte a los
   paneles translúcidos de arriba, y ese tinte cambiaba de pantalla en
   pantalla según la foto que tocara. Por eso la luz no la pone la foto. */
.ag-light:has(.ag-app) .ag-bg { filter: blur(16px) grayscale(1) brightness(1.05); }

/* La luz que se ve a través del vidrio. Blanco arriba, gris abajo, y nada
   más: ningún color.

   Probamos el campo en verde agua y el problema no era la fuerza sino la
   idea. Un fondo de color le mete ese color a TODO lo que es translúcido
   —los paneles, las tarjetas, los controles—, y acá cada color significa
   algo. Con el verde agua de fondo, una tarjeta de «rechazado» dejaba de
   ser roja y pasaba a ser roja-sobre-verde. El fondo tiene que ser el único
   lugar de la pantalla sin opinión.

   Neutro no quiere decir plano: el degradé y la foto que asoma detrás son
   lo que el vidrio tiene para refractar. El color lo ponen los ítems. */
.ag-light:has(.ag-app) .ag-shade {
  background:
    radial-gradient(100% 62% at 50% 0%, rgba(255, 255, 255, 0.82), transparent 72%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.62) 0%, rgba(222, 226, 230, 0.80) 100%);
}

/* --- el canto y el relieve, iguales en los dos temas ----------------- */
.ag-app { box-shadow: var(--ag-sombra-app); }
.ag-panel, .kpi, .ag-card, .aviso, .tar-firmas .tar, .firma, .vacio .ico {
  box-shadow: inset 0 1px 0 var(--ag-filo), var(--ag-sombra);
}
/* Acá vivía el filo de 3 px del borde izquierdo, repetido como sombra
   interior para que la sombra de relieve no se lo comiera. Ya no hace falta:
   el color da la vuelta entera, y cuatro reglas que pisaban la sombra para
   dibujar una raya eran justamente lo que hacía que la familia se viera
   poco. El filo y la sombra de relieve los sigue poniendo la regla de
   arriba, igual que a cualquier otra tarjeta. */

/* --- una tarjeta que además se clickea ------------------------------- */
/* Si el cuadro es un enlace, tiene que levantarse al pasar por encima:
   es lo único que lo distingue de un cuadro de sólo lectura. 1 px y la
   sombra un poco más larga; con el dedo no hay hover, así que ahí no
   cambia nada y la pista la da el estado `.on`. */
a.kpi, .tar-firmas .tar { transition: transform .12s ease, box-shadow .12s ease; }
a.kpi:hover, .tar-firmas .tar:hover {
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 var(--ag-filo), 0 2px 4px rgba(0, 0, 0, 0.1), 0 20px 40px -20px rgba(0, 0, 0, 0.3);
}
.tar-firmas .tar.on:hover { transform: none; }
@media (prefers-reduced-motion: reduce) {
  a.kpi, .tar-firmas .tar { transition: none; }
  a.kpi:hover, .tar-firmas .tar:hover { transform: none; }
}

/* El filtro elegido se hunde en vez de flotar. La línea gruesa de abajo la
   reemplazó el borde doble en el color de la familia, más arriba; acá sólo
   queda sacarle la sombra de flotar. */
.tar-firmas .tar.on { box-shadow: none; }

/* --- los controles que pinta el navegador --------------------------- */
/* Las casillas, los selectores de fecha y las barras de scroll no los
   dibuja la hoja de estilos: los dibuja el navegador, según `color-scheme`.
   El meta de la cabecera dice «dark light», así que el navegador elegía
   oscuro siempre y en modo claro las casillas salían negras sobre papel
   blanco. Se declara por tema y el meta queda como valor inicial para el
   primer cuadro, antes de que exista la clase. */
html.as-light, .ag-root.ag-light { color-scheme: light; }
html:not(.as-light), .ag-root:not(.ag-light) { color-scheme: dark; }

/* ======================================================================
   FILAS: bandas en vez de una línea de 1 px
   ======================================================================
   Una tabla de ocho columnas y sesenta filas separadas por una línea de
   0,08 de alfa se lee como un bloque de texto: para seguir un renglón de
   punta a punta hay que ayudarse con el dedo. El problema no es el
   contraste de la línea —subirla sólo agrega ruido— es que una línea
   separa pero no identifica: no dice a cuál de los dos renglones que
   toca pertenece cada celda.

   La banda sí. Cada fila par lleva un velo casi imperceptible y el ojo
   sigue el bloque de color, no el borde. Es lo mismo que hace el papel
   cuadriculado de los partes de obra, y es lo que usan los tableros de
   la referencia en sus listas largas.

   Con banda la línea sobra, así que se va. Queda la de la cabecera, que
   no separa filas: separa los rótulos del contenido. */
.ag-root       { --ag-banda: rgba(255, 255, 255, 0.038); --ag-fila-hover: rgba(255, 255, 255, 0.10); }
.ag-root.ag-light { --ag-banda: rgba(17, 18, 19, 0.030); --ag-fila-hover: rgba(255, 255, 255, 1); }

.ag-table tbody > tr > td { border-bottom: 0; }
.ag-table tbody > tr:nth-child(even) > td { background: var(--ag-banda); }

/* El hover y la selección pintan la celda, no la fila: una banda puesta
   en la celda le gana a cualquier fondo puesto en el <tr>. */
.ag-table tbody > tr:hover > td,
.ag-table.filas-click tbody > tr:hover > td { background: var(--ag-fila-hover); }
.ag-table tbody > tr.sel > td { background: var(--ag-sel); }
.ag-table tbody > tr.fila-edit > td,
.ag-table tbody > tr.fila-edit:hover > td { background: var(--ag-ctl); }

/* Las listas cortas son lo mismo que una tabla sin cabecera, así que
   llevan la misma banda. En «Por dónde seguir» eso reemplaza la línea de
   arriba; en la columna de subsistemas se suma al redondeo que ya tenían
   y las deja como fichas apiladas. */
.fila-pend { border-top: 0; }
.pend > .fila-pend:nth-child(even) { background: var(--ag-banda); }
.fila-pend:hover { background: var(--ag-fila-hover); }
.ss > a:nth-child(even), .ss > button.fila-ss:nth-child(even) { background: var(--ag-banda); }
.ss > a:hover, .ss > button.fila-ss:hover { background: var(--ag-fila-hover); }
.ss > a.on, .ss > button.fila-ss.on { background: var(--ag-ctl-on); }

/* ======================================================================
   LA CAJA DE ARCHIVOS, SIN EL CONTROL DEL NAVEGADOR A LA VISTA
   ======================================================================
   «Elegir archivos · Ningún archivo seleccionado» lo dibuja el navegador y
   no hay forma de darle estilo: queda pegado a la izquierda, con su tipo
   de letra y su gris, adentro de una caja que por lo demás es nuestra.

   El input sigue estando y sigue mandando —el required, el formulario y el
   servidor no cambian— pero pasa a ocupar la caja entera, invisible. Así
   el clic abre el buscador desde cualquier punto, el arrastre sigue
   funcionando, la validación puede enfocarlo (por eso no se oculta con
   display:none) y lo que se ve es el ícono, la frase y, cuando hay algo
   elegido, los nombres. */
.campo > span.suelta-caja {
  position: relative; align-items: center; text-align: center;
  padding: 18px 14px; gap: 8px; cursor: pointer;
}
.suelta-caja input[type="file"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
/* El foco del teclado lo pinta la caja, porque el input no se ve. */
.suelta-caja:has(input:focus-visible) { outline: 2px solid var(--ag-blue); outline-offset: 2px; }
.suelta-txt { flex-direction: column; gap: 7px; }
.suelta-ico { width: 36px; height: 36px; border-radius: 12px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ag-ctl-on); color: var(--ag-ink); }
.suelta-txt b { color: var(--ag-ink); font-weight: 650; font-size: 14px; }
.suelta-txt em { font-style: normal; color: var(--ag-muted); font-size: 12.5px; }
.suelta-lista { text-align: center; }

/* ======================================================================
   EL RÓTULO DE UN BOTÓN QUE ES SÓLO UN ÍCONO
   ======================================================================
   Un lápiz, una flecha y una cruz en fila no dicen qué hacen. El `title`
   del navegador sí, pero tarda cerca de un segundo, aparece donde quiere y
   no se puede leer con el dedo. El rótulo propio sale a los 300 ms, al
   lado del botón y hacia la izquierda: ahí nunca lo recorta la caja de
   scroll de la tabla, que sólo recorta a lo ancho por la derecha.

   Sólo con mouse: en una tablet el hover se queda pegado después de tocar
   y el rótulo taparía la fila. Ahí la pista la da el aria-label, que es lo
   que lee el lector de pantalla en los dos casos. */
@media (hover: hover) {
  [data-tip] { position: relative; }
  [data-tip]::after {
    content: attr(data-tip);
    position: absolute; right: calc(100% + 8px); top: 50%; transform: translateY(-50%);
    z-index: 40; white-space: nowrap; pointer-events: none;
    padding: 5px 9px; border-radius: 9px; font-size: 12.5px; font-weight: 600; line-height: 1.2;
    background: var(--ag-pri); color: var(--ag-pri-ink); box-shadow: var(--ag-sombra);
    opacity: 0; transition: opacity .12s ease;
  }
  [data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; transition-delay: .3s; }
}
@media (prefers-reduced-motion: reduce) {
  [data-tip]::after { transition: none; }
}

/* La especialidad sale de la columna del código y pasa a tener la suya,
   igual que en Protocolos. Antes convivían en la misma celda y el código
   arrancaba en una vertical distinta según el instrumento tuviera o no
   disciplina cargada; encima el casillero le comía el ancho y los códigos
   largos se desbordaban. Ahora cada cosa en su columna y las tres leen
   derecho. */
.tabla-equipos td.cod .ag-mono { font-size: 13px; }

/* ---------- el color rodea la tarjeta entera -------------------------- */
/* Una tarjeta decía de qué familia era con un chip de 32 px teñido al 13 %
   y nada más: seis tarjetas distintas con el mismo borde gris no se
   distinguen aunque cada una hable de otra cosa. Y un aviso lo decía con
   una rayita de 3 px en el borde izquierdo, que en una tarjeta de 320 px
   es el uno por ciento de lo que se ve.

   Ahora el color da la vuelta completa: borde, tinte de fondo, chip sólido
   y el número. Lo que NO cambia es cuál es el color: sale del mismo `--c`
   de siempre, el que las clases `tono-*` ya traían, así que la paleta por
   función sigue mandando y esto es sólo cuánto de la tarjeta la muestra.

   Medido sobre el panel de cada tema: con el tinte puesto, la etiqueta
   queda entre 5,07 y 7,03 y el número entre 4,19 y 8,12. El número es de
   42 px y para WCAG eso es texto grande, que pide 3:1; el piso de 4,19 lo
   pasa con margen. */

/* Clases que sólo dicen de qué familia es algo. Sirven para cualquier
   tarjeta, no sólo las de abajo. */
.f-avance  { --c: var(--f-avance); --cf: var(--f-avance-fill); }
.f-sin     { --c: var(--f-sin); --cf: var(--f-sin-fill); }
.f-curso   { --c: var(--f-curso); --cf: var(--f-curso-fill); }
.f-hecho   { --c: var(--f-hecho); --cf: var(--f-hecho-fill); }
.f-firmado { --c: var(--f-firmado); --cf: var(--f-firmado-fill); }
.f-espera  { --c: var(--f-espera); --cf: var(--f-espera-fill); }
.f-rechazo { --c: var(--f-rechazo); --cf: var(--f-rechazo-fill); }

/* Sin familia asignada, el borde queda en el color de una línea: la tarjeta
   se ve igual que antes. Vale para las filas de Actas, Instrumentos y las
   que no son un estado, que no tienen por qué pintarse de nada.

   Va en `:where()`, que no suma especificidad, porque si no este valor por
   omisión le gana a `.f-espera` —dos clases contra una— y la familia no
   llegaría nunca. */
:where(.aviso) { --c: var(--ag-ctl-line); --cf: var(--ag-ctl-line); }
:where(.tar-firmas .tar) { --c: var(--ag-line2); --cf: var(--ag-line2); }
.aviso.ok                  { --c: var(--f-firmado); --cf: var(--f-firmado-fill); }
.aviso.mal, .aviso.danger  { --c: var(--f-rechazo); --cf: var(--f-rechazo-fill); }
.aviso.warn, .aviso.warning{ --c: var(--f-espera);  --cf: var(--f-espera-fill); }
.aviso.info                { --c: var(--f-hecho);   --cf: var(--f-hecho-fill); }

/* Las seis `tono-*` van escritas una por una a propósito: la pantalla de
   ingesta tiene cinco `.kpi` sin tono, y esas tienen que quedar exactamente
   como están. Un `.kpi` suelto no se pinta. */
:is(.kpi.tono-signal, .kpi.tono-ok, .kpi.tono-info, .kpi.tono-red,
    .kpi.tono-amb, .kpi.tono-neutro, .aviso, .tar-firmas .tar) {
  /* En oscuro la tarjeta no lleva fondo: el color queda en el borde, el chip
     y el número, y se termina ahí. No es sólo gusto —sacar el tinte es la
     versión que se lee mejor—: el tinte aclaraba el fondo y le comía
     contraste justo a lo que hay que leer. Sin él el número de «Rechazados»,
     que era el peor de los seis, sube de 3,56 a 4,74 y las etiquetas pasan
     de poco más de 4 a 6,8 o más. Con eso el parche que aclaraba el gris
     secundario adentro de la tarjeta deja de hacer falta: el de siempre da
     7,20.

     En claro el tinte hace lo contrario —aclara sobre blanco en vez de
     aclarar sobre negro— y ahí sí separa la tarjeta del panel, así que se
     queda. Está abajo. */
  /* Dos paradas y no una: un `color-mix` parejo de punta a punta es
     pintura, no vidrio. Lo que hace que algo se lea como vidrio no es el
     color, son tres cosas: que la luz le llegue más arriba que abajo, que
     el canto de arriba devuelva un reflejo, y que lo de atrás se vea. Las
     tres están acá. En oscuro las dos paradas son transparentes: ahí la
     tarjeta no lleva fondo y eso no cambia. */
  --t1: transparent;
  --tm: transparent;
  --t2: transparent;
  /* En oscuro el reflejo es casi nada: sobre un panel casi negro, un blanco
     al 42 % no es un reflejo, es una mancha. */
  --brillo: 0.06;
  border: 2.5px solid var(--c);
  /* El tinte va como imagen y no como color de fondo: así se apoya encima
     del vidrio del panel en vez de reemplazarlo, y la misma regla sirve en
     los dos temas sin tener que escribir dos fondos distintos. */
  /* Tres paradas y no dos, y la del medio corrida al 58 %: medido, con un
     degradé parejo el número de 42 px caía a 3,6:1 porque le tocaba la
     mitad densa. Con la parada intermedia la densidad se corre abajo del
     número, donde está la barra y no hay texto. El vidrio se ve igual y el
     número sube a 4,4. La luz se dibuja alrededor de lo que hay que leer,
     no al revés. */
  /* Dos capas. Arriba el reflejo: una mancha de blanco que se apaga a poco
     menos de la mitad de la tarjeta, que es lo que hace una lámina de
     vidrio cuando le da la luz de frente. Abajo el tinte de la familia. El
     reflejo va primero porque es lo que está más cerca del ojo. */
  background-image:
    linear-gradient(158deg, rgba(255, 255, 255, var(--brillo, 0)) 0%,
                            rgba(255, 255, 255, 0) 46%),
    linear-gradient(158deg, var(--t1) 0%, var(--tm) 62%, var(--t2) 100%);
  /* Los cantos. Arriba y a la izquierda el filo claro, de donde viene la
     luz; abajo el color de la familia muy diluido, que es el grosor del
     vidrio visto de canto. Repite el filo y la sombra porque `box-shadow`
     no se acumula entre reglas. */
  box-shadow: inset 0 1px 0 var(--ag-filo),
              inset 1px 0 0 color-mix(in srgb, var(--ag-filo) 55%, transparent),
              inset 0 -1px 0 color-mix(in srgb, var(--c) 28%, transparent),
              var(--ag-sombra);
}
/* En claro el tinte aclara el fondo en vez de oscurecerlo, así que acá sí
   va, y con él el gris secundario se oscurece.

   El 22 % parejo se abre en 10 arriba y 32 abajo: el promedio es casi el
   mismo, pero la tarjeta deja de ser un rectángulo pintado y pasa a tener
   una luz. Y el blanco de abajo baja al 58 % con desenfoque propio, así que
   el campo verde agua llega hasta adentro de la tarjeta en vez de frenar en
   su borde. Eso es lo que la vuelve transparente de verdad y no translúcida
   de mentira. */
.ag-light :is(.kpi.tono-signal, .kpi.tono-ok, .kpi.tono-info, .kpi.tono-red,
              .kpi.tono-amb, .kpi.tono-neutro, .aviso, .tar-firmas .tar) {
  /* Casi sin relleno, y ésta es la corrección de fondo.

     Veníamos subiendo el tinte buscando «más vidrio» y era al revés: un
     panel con mucho color adentro no es vidrio, es plástico teñido. En las
     referencias —el widget sobre la ola, la tarjeta sobre la flor, el panel
     sobre el campo— el vidrio es NEUTRO y se ve lo que hay atrás; el color
     lo ponen el borde, el número y el ícono, no el relleno. Es exactamente
     lo que ya hace el tema oscuro, y es la razón por la que el oscuro se
     veía mejor desde el principio.

     Así que el tinte baja a un velo —6, 9 y 18 %— y el blanco del fondo al
     32 %: lo que se ve a través de la tarjeta es el fondo de la pantalla,
     no una capa de color. El croma no se pierde, se muda al borde y al
     número, que son los que ya estaban al tope. */
  /* Sin tinte. El paso anterior lo había bajado a un velo del 6 al 18 % y
     esto lo termina: la tarjeta no lleva color de fondo, lleva color en el
     borde, en el número y en el chip. Lo único que queda del relleno es un
     blanco al 12 %, que no es color sino lo que hace que el desenfoque se
     vea: un vidrio perfectamente limpio no se distingue del aire. */
  --t1: transparent;
  --tm: transparent;
  --t2: transparent;
  --brillo: 0.58;
  /* Con el tinte saliendo del token de área, el fondo de la tarjeta se
     satura y el gris de siempre se queda corto justo en una: sobre el azul
     de «terminados» el secundario de 13 px caía a 4,39:1 y el mínimo es
     4,5. Baja dos puntos y queda en 4,82 contra el peor de los cinco. */
  --ag-muted: #41454a;
  background-color: rgba(255, 255, 255, 0.12);
  backdrop-filter: blur(26px) saturate(1.6);
}
/* El hover pisaba el fondo con blanco al 98 % y la tarjeta se volvía opaca
   justo cuando la estás mirando. Se queda translúcida. */
.ag-light .tar-firmas .tar:hover { background-color: rgba(255, 255, 255, 0.30); }
/* El aviso y la tarjeta de fila no traían desenfoque propio —sólo `.kpi` lo
   tenía—, así que eran los únicos que no refractaban nada. */
.aviso, .tar-firmas .tar { backdrop-filter: blur(16px) saturate(1.15); }

/* El chip pasa de teñido a sólido. `--ag-pri-ink` es la tinta que va arriba
   de una superficie del color contrario al fondo: blanca en el tema claro,
   casi negra en el oscuro. Es exactamente lo que necesita un chip pintado
   con el color de la familia, que es oscuro en claro y brillante en oscuro. */
.kpi .chip, .aviso .ico { background: var(--c); color: var(--ag-pri-ink); }
/* El chip es la única mancha de color pleno de la tarjeta y era la más
   plana de todas. Lleva un degradé, pero cada tema en la dirección que NO
   le quita contraste a la tinta de arriba: en oscuro la tinta es casi
   negra, así que el chip se aclara arriba; en claro es blanca, así que se
   oscurece abajo. En los dos casos el texto queda mejor que antes, nunca
   peor. */
.kpi .chip, .aviso .ico {
  background-image: linear-gradient(158deg, color-mix(in srgb, var(--c) 78%, #ffffff), var(--c));
}
.ag-light :is(.kpi .chip, .aviso .ico) {
  background-image: linear-gradient(158deg, var(--c), color-mix(in srgb, var(--c) 82%, #000000));
}
.aviso .ico { width: 32px; height: 32px; border-radius: 10px; margin-top: 0;
              display: inline-flex; align-items: center; justify-content: center; }
/* El chip de la tarjeta grande pasa de 32 a 40 px. Es la única mancha de
   color pleno que tiene, y a 32 px al lado de un número de 42 px no pesaba
   lo suficiente para leerse como parte de la misma cosa. */
.kpi .chip { width: 40px; height: 40px; border-radius: 13px; }
.kpi .chip svg { width: 22px; height: 22px; }

.kpi .v { color: var(--c); }
/* En claro el número se aclara hacia el token de área. No gana croma —las
   tintas ya estaban entre 83 y 100 %— gana LUZ, que es lo que el ojo lee
   como «más vivo»: el rojo pasa de #cf0b2a a #df2542 y el ámbar de #8f5a00
   a #b77300.

   El 34 % sale de medir sobre la pantalla, no de estimar. El número es de
   42 px en la tarjeta y de 30 px en bold en las filas: las dos cosas son
   texto grande y el mínimo es 3:1. Muestreado al lado de cada número, el
   piso con 34 % queda en 3,54:1 —el ámbar de punch, que es la familia más
   justa— y con 40 % baja a 3,33. Entre las dos no hay diferencia que se
   vea (#b16f00 contra #b77300) y sí hay medio punto de margen, así que va
   la de margen.

   La etiqueta y el secundario no se tocan: ésos son texto chico, piden 4,5
   y siguen saliendo de la tinta oscura, que sobre la tarjeta transparente
   da 8,3:1. */
.ag-light .kpi .v { color: color-mix(in srgb, var(--cf) 34%, var(--c)); }
.ag-light .tar-firmas .tar:is(.f-avance, .f-curso, .f-hecho, .f-firmado,
                              .f-espera, .f-rechazo, .rota) {
  --c-n: color-mix(in srgb, var(--cf) 34%, var(--c));
}
/* El número de una tarjeta de fila lleva su propia variable y no `--c` a
   secas: sin familia, `--c` es el color de una línea de 1 px, y un número de
   30 px pintado con eso no se lee. Así el que no tiene familia —y el que la
   tiene neutra— se queda con la tinta de siempre. */
.tar-firmas .tar { --c-n: var(--ag-ink); }
.tar-firmas .tar:is(.f-avance, .f-curso, .f-hecho, .f-firmado, .f-espera, .f-rechazo, .rota) { --c-n: var(--c); }
.tar-firmas .tar .n { color: var(--c-n); }
/* Un número en gris se lee peor que en tinta y no gana nada: «sin novedad»
   ya lo dice el borde. */
.kpi.tono-neutro .v { color: var(--ag-ink); }
/* En oscuro la etiqueta vuelve al gris. Sin fondo de color ya hay tres
   cosas diciendo de qué familia es la tarjeta —borde, chip y número—, y una
   cuarta teñida le sacaba al número el primer lugar en la lectura.

   En claro, donde el tinte sigue puesto, la etiqueta sigue teñida: ahí lo
   que arma la tarjeta es el bloque de color y la etiqueta es parte de él.
   Va mezclada contra la tinta y no contra el gris —sobre el tinte del 22 %
   la mezcla con gris quedaba en 4,0 y la mezcla con tinta da 5,17—. */
.kpi .et { color: var(--ag-muted); font-weight: 500; }
.ag-light .kpi .et { color: color-mix(in srgb, var(--c) 60%, var(--ag-ink)); font-weight: 600; }

/* La tarjeta elegida: el mismo color, más tinte y un borde del doble. Antes
   era una barra gris abajo, que con seis tarjetas de colores ya no dice
   «ésta» sino «ésta tiene algo gris».

   Acá había un `background: none` que venía de cuando el tinte no se
   pintaba con `background-image`. El atajo pisa las dos cosas, así que la
   elegida terminaba siendo la única tarjeta SIN fondo: en el tema claro,
   transparente sobre la foto, y sin el tinte que tenía que distinguirla.
   Era justo al revés de lo que la regla quería decir, y lo que hacía que
   volver a «Todos» pareciera no hacer nada.

   La sombra lleva las tres: el anillo de la elección, el filo de arriba y
   el relieve. Escrita de nuevo tiene que repetirlas, porque `box-shadow`
   no se acumula entre reglas. */
.tar-firmas .tar.on {
  --t1: color-mix(in srgb, var(--c) 18%, transparent);
  --tm: color-mix(in srgb, var(--c) 26%, transparent);
  --t2: color-mix(in srgb, var(--c) 44%, transparent);
  border-color: var(--c);
  box-shadow: inset 0 0 0 2px var(--c), inset 0 1px 0 var(--ag-filo), var(--ag-sombra);
}
.ag-light .tar-firmas .tar.on {
  /* La elegida es la única con relleno en toda la pantalla, así que un
     tinte suave ya la separa de las otras tres. */
  --t1: color-mix(in srgb, var(--cf) 14%, transparent);
  --tm: color-mix(in srgb, var(--cf) 20%, transparent);
  --t2: color-mix(in srgb, var(--cf) 36%, transparent);
}

/* Un contador en cero no tiene por qué pintarse. La familia le sirve igual
   —el borde dice de qué habla la tarjeta y de qué color se enciende si la
   elegís— pero un «0» en rojo llama a mirar algo que no está. */
.tar-firmas .tar.cero .n { color: var(--ag-muted); }

/* ---------- el aviso no es un filtro ---------------------------------
   En Punch y en Instrumentos el aviso cuelga justo debajo de la fila de
   tarjetas, con el mismo rojo que una de ellas, el mismo borde de 2,5 px
   y las mismas esquinas. Puesto ahí se leía como una tarjeta más, y peor:
   como la tarjeta roja elegida. La elegida era otra —«Abiertos»,
   «Todos»—, así que la pantalla decía dos cosas distintas a la vez.

   Se separan por relleno y no por color: las tarjetas que se eligen son
   contorno, y la que está elegida se llena; el aviso, que no se elige
   nunca, está siempre lleno. Es la misma distinción que ya usa Firmas
   entre la tarjeta elegida y las otras tres, aplicada a otra cosa. */
.ag-root .aviso {
  --t1: color-mix(in srgb, var(--c) 10%, transparent);
  --tm: color-mix(in srgb, var(--c) 17%, transparent);
  --t2: color-mix(in srgb, var(--c) 28%, transparent);
  border-width: 1.5px;
}
.ag-root.ag-light .aviso {
  --t1: transparent;
  --tm: transparent;
  --t2: transparent;
}
/* `rota` deja de pintar sólo el borde: cambia la familia de la tarjeta
   entera, que es lo que quería decir. */
.tar-firmas .tar.rota { --c: var(--f-rechazo); --cf: var(--f-rechazo-fill); }

/* ---------- más cosas de la referencia, sólo en claro ----------------

   1 · La barra de avance deja de ser rayitas. En la referencia las barras
   son una pastilla llena con su propio resplandor, y acá la barra es lo
   único de la tarjeta que dice una proporción: estaba dibujada como una
   regla cuando tenía que leerse como un nivel. Pierde la máscara punteada,
   gana altura, esquinas redondas y el brillo de su familia.

   Va sólo en claro porque en oscuro el punteado funciona: sobre un panel
   casi negro una barra llena es una mancha. */
.ag-light .kpi .bar > span {
  height: 8px; border-radius: 999px;
  -webkit-mask-image: none; mask-image: none;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--cf) 76%, #ffffff) 0%,
    var(--cf) var(--p, 0%),
    rgba(17, 18, 19, 0.09) var(--p, 0%),
    rgba(17, 18, 19, 0.09) 100%);
  box-shadow: 0 1px 8px -2px color-mix(in srgb, var(--cf) 70%, transparent);
}

/* 2 · La sombra se tiñe de la familia. En la referencia cada tarjeta de
   color deja abajo un halo de su propio color, y eso es lo que las despega
   del fondo sin necesidad de un borde más grueso. Acá, con el vidrio
   vacío, la sombra es lo único que todavía estaba en gris neutro. */
.ag-light :is(.kpi.tono-signal, .kpi.tono-ok, .kpi.tono-info, .kpi.tono-red,
              .kpi.tono-amb, .kpi.tono-neutro, .aviso, .tar-firmas .tar) {
  --ag-sombra: 0 1px 2px rgba(17, 18, 19, 0.06),
               0 16px 34px -18px color-mix(in srgb, var(--cf) 60%, transparent);
}

/* 3 · El reflejo, también en los paneles. Las tarjetas ya lo tenían y los
   paneles que las contienen no, así que la pantalla tenía dos materiales
   distintos. Una sola mancha de luz arriba a la izquierda alcanza. */
.ag-light .ag-panel {
  background-image: radial-gradient(130% 85% at 0% 0%,
                                    rgba(255, 255, 255, 0.60), transparent 62%);
}

/* Acá vivía la «tarjeta protagonista»: `tono-signal` iba llena de color
   mientras las demás eran vidrio, copiando la tarjeta naranja de la
   referencia. Se saca.

   El motivo es el mismo por el que se vació el resto: una tarjeta llena de
   color no es vidrio, y tener una sola así rompía la regla en vez de
   confirmarla. La jerarquía que esa tarjeta buscaba —que se note cuál es
   el titular de la pantalla— hay que resolverla con algo que no sea
   relleno: tamaño, posición o el orden de lectura.

   Lo que sí queda de aquella tanda es el resplandor del anillo y los
   radios más generosos, que están abajo. */

/* El anillo de avance toma un resplandor de su propio color: es la cifra
   que resume la pantalla y la única cosa con luz propia. */
.ag-light .ring .arco {
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--ag-neon) 60%, transparent));
}

/* Esquinas más generosas, también sólo en claro: con el vidrio vacío el
   borde es casi todo lo que define la tarjeta, y un radio corto lo vuelve
   duro. */
.ag-light .kpi { border-radius: 20px; }
.ag-light :is(.aviso, .tar-firmas .tar) { border-radius: 16px; }

/* ---------- qué filtro está puesto --------------------------------
   Las tarjetas de arriba dicen cuál está elegido, pero la tabla tiene
   veinticinco filas: apenas se baja un poco las tarjetas salen de pantalla
   y abajo no queda nada que lo diga. El color de la familia elegida rodea
   el panel entero —buscador, tabla y paginación— y tiñe la cabecera de la
   tabla; los bordes de los costados acompañan toda la bajada.

   Va en Firmas, Punch, Instrumentos y Actas, que son las cuatro pantallas
   donde una fila de contadores filtra una tabla. La clase y la familia van
   juntas en el panel que hay que rodear, y no en un contenedor: en
   Instrumentos hay otro `.ag-panel` arriba —el de subir certificados— que
   no tiene nada que ver con el filtro, y en Punch hay paneles anidados
   adentro de las filas abiertas.

   La regla no sabe nada de calidad, de categoría A ni de vencidos: sabe de
   `--c`. Qué familia es cada contador lo decide cada vista. */
.panel-filtrado { border-color: var(--c); border-width: 2px; }
.panel-filtrado .ag-table th {
  --tinte-th: color-mix(in srgb, var(--c) 9%, transparent);
  /* La cabecera es sticky y se dibuja encima de las filas que pasan por
     abajo, así que el tinte va APILADO sobre el fondo opaco de siempre y
     no en vez de él: traslúcida se le vería el texto de las filas. */
  background:
    linear-gradient(var(--tinte-th), var(--tinte-th)),
    linear-gradient(var(--ag-panel), var(--ag-panel)),
    var(--ag-page);
  color: var(--ag-ink);
  border-bottom: 2px solid var(--c);
}

/* Cambiar de filtro es una carga de página entera: sin esto el color nuevo
   ya está puesto cuando la pantalla aparece y no se ve que cambió. Medio
   segundo alcanza para que el ojo lo siga hasta abajo. */
@keyframes filtro-entra {
  from { border-color: var(--ag-panel-line); }
}
.panel-filtrado { animation: filtro-entra .5s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .panel-filtrado { animation: none; }
}

/* ======================================================================
   EL CAMBIO DE TEMA, CON TRANSICIÓN
   ======================================================================
   Cambiar de claro a oscuro movía cincuenta superficies de golpe y el salto
   se sentía como un parpadeo. La transición va en una clase que el botón
   pone y saca: si estuviera siempre puesta, cada hover y cada selección de
   fila arrastrarían una animación que nadie pidió. */
.ag-cambiando, .ag-cambiando *, .ag-cambiando *::before, .ag-cambiando *::after {
  transition: background-color .3s ease, background-image .3s ease, color .3s ease,
              border-color .3s ease, box-shadow .3s ease, fill .3s ease, stroke .3s ease,
              opacity .3s ease, filter .3s ease !important;
}
@media (prefers-reduced-motion: reduce) {
  .ag-cambiando, .ag-cambiando *, .ag-cambiando *::before, .ag-cambiando *::after {
    transition: none !important;
  }
}

/* ── Casillas con forma de chip ───────────────────────────────────────────
   Corregir lo que se le asigna a un tipo de item, en la pantalla de revision
   de la ingesta. El control es un checkbox de verdad —se tabula, se marca con
   la barra espaciadora y lo lee un lector de pantalla—; lo unico que cambia es
   que lo que se ve es el chip de siempre.

   El input se esconde con `opacity:0` y 1px, NO con `display:none` ni
   `visibility:hidden`: cualquiera de esas dos lo saca del orden de tabulacion y
   deja la tabla entera inservible con el teclado.

   El estado no se distingue solo por el color: sin marcar es linea de puntos y
   texto apagado, marcado es linea continua y fondo. El hover solo acompaña. */
.togs { display: flex; flex-wrap: wrap; gap: 5px; }
.tog { position: relative; display: inline-flex; cursor: pointer; }
.tog input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.tog .ag-chip {
  height: 28px; padding: 0 9px; font-size: 12px; background: transparent;
  border-style: dashed; color: var(--ag-muted); user-select: none;
  transition: background-color .12s, border-color .12s, color .12s;
}
.tog input:checked + .ag-chip {
  border-style: solid; border-color: var(--ag-chip-on-line);
  background: var(--ag-chip-on); color: var(--ag-ink);
}
.tog:hover .ag-chip { color: var(--ag-ink); }
.tog input:focus-visible + .ag-chip { outline: 2px solid var(--f-foco); outline-offset: 2px; }
.tog input:disabled + .ag-chip { cursor: default; }
.tog:has(input:disabled) { cursor: default; }
@media (prefers-reduced-motion: reduce) { .tog .ag-chip { transition: none; } }

/* La cadena de firmas del acta (Entrega 3): una tarjeta de firma por
   eslabón, apiladas en el orden en que se firman. La que le toca lleva el
   filo de color; la que no aplica queda punteada, porque está resuelta pero
   no es una firma. */
.cadena { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.firma.turno { box-shadow: inset 3px 0 0 var(--ag-red); }
.firma.na { border-style: dashed; }
.firma.na .tit .n { border-style: dashed; }

/* C6: el aviso de un control que otra persona cambió mientras estaba abierto. */
.ctrl-f .choque { grid-column: 2 / -1; margin: 0; font-size: 13px; line-height: 1.4; }
