/* MERITUS — componentes. Doce piezas que cubren las 56 plantillas. */

/* ---------- marca ---------- */
/* Mención puntual de "MERITVS" fuera de la barra lateral (portales públicos,
   home.html, inicio.html). Mismo trazo que .app-nav-marca: version viva de
   meritus.css, que estas plantillas ya no cargan. */
.wordmark { color: var(--primary); }
.wordmark svg { height: 1em; width: auto; vertical-align: -.15em; }

/* ---------- shell ---------- */
.app { display: flex; min-height: 100vh; }

/* El nav se queda sólido -- cristal sobre un fondo ya oscuro se ve turbio,
   no traslúcido -- pero gana una sombra lateral para leerse "elevado" sobre
   el nuevo lienzo ambiental de .app-main. */
.app-nav { width: var(--w-nav); flex-shrink: 0; background: var(--nav);
           padding: var(--sp-4) 0; display: flex; flex-direction: column; gap: var(--sp-1);
           box-shadow: 8px 0 24px -6px rgba(46, 26, 20, .22); position: relative; z-index: 2; }
.app-nav-marca { font-family: var(--font-display); font-weight: 700;
                 letter-spacing: .13em; font-size: var(--fs-100);
                 color: var(--accent-pale); padding: 0 var(--sp-4) var(--sp-4);
                 text-decoration: none; display: block; }
.app-nav-marca svg { display: block; width: 100%; height: auto; }
.app-nav-item { color: color-mix(in srgb, var(--on-primary) 72%, transparent);
                padding: var(--sp-2) var(--sp-4); font-size: var(--fs-200);
                text-decoration: none; border-left: 2px solid transparent;
                min-height: var(--tap); display: flex; align-items: center; }
.app-nav-item:hover { color: var(--on-primary);
                      background: color-mix(in srgb, var(--accent-pale) 10%, transparent); }
.app-nav-item.activo { color: var(--on-primary); font-weight: 500;
                       border-left-color: var(--accent-pale);
                       background: color-mix(in srgb, var(--accent-pale) 16%, transparent); }
.app-nav-pie { margin-top: auto; padding: var(--sp-4); font-size: var(--fs-100);
               color: color-mix(in srgb, var(--on-primary) 60%, transparent); }

/* R5 sobre --nav: el foco de base.css usa --primary, que sobre el burdeos
   de la barra lateral (--nav: #4E0E19) da 1,28:1 — muy por debajo del 3:1
   de WCAG 2.2 SC 1.4.11. --accent-pale sí cumple (9,08:1) sobre esa
   superficie oscura, así que aquí se sustituye solo dentro de .app-nav. */
.app-nav :focus-visible { outline-color: var(--accent-pale); }

.app-main { flex: 1; min-width: 0; max-width: var(--w-content);
            padding: 0 var(--sp-5) var(--sp-7); }

.app-head { display: flex; align-items: baseline; gap: var(--sp-3);
            flex-wrap: wrap; padding: var(--sp-4) 0 var(--sp-3);
            border-bottom: 1px solid var(--bd); position: relative; }
.app-head-titulo { font-family: var(--font-editorial); font-size: var(--fs-500);
                   font-weight: 600; color: var(--primary); }
.app-head-acciones { margin-left: auto; display: flex; gap: var(--sp-2);
                     align-items: center; }
/* El gesto único: el filete de oro se traza al cargar. */
.app-head::after { content: ""; position: absolute; left: 0; bottom: -1px;
                   height: 2px; width: 100%; background: var(--accent);
                   transform-origin: left; animation: filete var(--dur-filete) var(--ease) both; }
@keyframes filete { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.migas { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-4);
         font-size: var(--fs-100); color: var(--text-muted-cristal); padding: var(--sp-3) 0 0; }
.migas a { color: var(--text-muted-cristal); text-decoration: none; }

/* ---------- barra global (_barra.html) ---------- */
/* Cinta superior de contexto: institución, «ver como», volver a superadmin.
   Sus controles van EN LÍNEA; sin esto los <form> se apilan (son de bloque). */
.barra-global { display: flex; flex-wrap: wrap; align-items: center;
                gap: var(--sp-2) var(--sp-4); padding: var(--sp-2) var(--sp-5);
                border-bottom: 1px solid var(--bd); font-size: var(--fs-100);
                color: var(--text-muted-cristal); }
.barra-global-grupo { display: flex; align-items: center; gap: var(--sp-2); margin: 0; }
.barra-global a { color: var(--text-muted-cristal); text-decoration: none; }
/* `_barra.html` mete un <small>{{ institucion_nombre }}</small> suelto
   dentro de .barra-global -- la regla de elemento `small` (base.css) le
   gana por coincidencia directa a la herencia de color de `.barra-global`,
   así que sin esto se queda en --text-muted (2.97:1, el mismo fallo que
   .migas) en vez de heredar el tono corregido. */
.barra-global small { color: var(--text-muted-cristal); }
.barra-global select { font: inherit; padding: var(--sp-1) var(--sp-2);
                       border: 1px solid var(--bd); border-radius: var(--r-sm);
                       background: var(--surface); color: var(--text); }

/* ---------- tabla ---------- */
.tabla th { font-size: var(--fs-100); font-weight: 600;
            text-transform: uppercase; letter-spacing: var(--tracking-label);
            color: var(--text-muted); padding: var(--sp-2) var(--sp-3);
            border-bottom: 1px solid var(--bd); }
.tabla td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--bd-soft);
            height: var(--row-h); }
.tabla tr:last-child td { border-bottom: 0; }
.tabla tbody tr:hover { background: color-mix(in srgb, var(--primary) 4%, transparent); }
.tabla--densa td { padding: var(--sp-1) var(--sp-3); }
/* Celda de acciones: varios <form> pueden compartir fila (validar/rechazar,
   sellar/editar). `inline-flex` en vez de `inline-block` para que los que
   llevan más de un control (validar con puntos, rechazar con motivo) se
   ordenen en fila -- si no, la etiqueta de bloque de más abajo los apila
   verticalmente dentro del propio <form>. `flex-wrap` deja que una fila con
   3-4 acciones pase a la línea siguiente en vez de desbordar. */
.tabla td form { display: inline-flex; flex-wrap: wrap; align-items: center;
                 gap: var(--sp-1) var(--sp-2); margin: var(--sp-1) var(--sp-2) var(--sp-1) 0;
                 vertical-align: top; }
/* `:not(.campo)`: usuarios.html y poderes.html también meten <label
   class="campo"> (casillas de rol/capacidad) dentro de un <form> de esta
   misma celda, y esas YA tienen su propia fila resuelta por
   `.rejilla-campos label.campo` / el `label{display:block}` de base.css
   -- no son el caso que esta regla arregla (validar-con-puntos,
   rechazar-con-motivo) y no deben cambiar de forma incidental. */
.tabla td form label:not(.campo) { display: inline-flex; align-items: center;
                       gap: var(--sp-1); font-size: var(--fs-100); white-space: nowrap; }
/* Versión "densa" de `.campo input` para los controles sueltos de esta
   celda (no llevan la clase `.campo`: son de un solo uso, dentro de un
   <form> ya minúsculo, y pedirían el mismo ancho de columna que un filtro).
   Excluye checkbox/radio -- igual que `.campo input[type="checkbox"]` más
   abajo -- para no envolver una casilla nativa en un recuadro con borde. */
.tabla td form input:not([type="checkbox"]):not([type="radio"]),
.tabla td form select, .tabla td form textarea {
  width: auto; min-height: var(--tap-denso); padding: 2px var(--sp-2);
  font-size: var(--fs-100); background: var(--surface); color: var(--text);
  border: 1px solid var(--bd); border-radius: var(--r-md); }
.tabla td form input[type="number"] { width: 4.5em; }
.tabla td form textarea { resize: vertical; }
.tabla-vacia { padding: var(--sp-6); text-align: center; color: var(--text-muted);
               font-size: var(--fs-200); }
/* Restricción G-responsive: la tabla hace scroll, nunca la página. */
.tabla-scroll { overflow-x: auto; }

/* ---------- tarjeta ---------- */
.card { background: var(--glass-bg); backdrop-filter: blur(var(--glass-blur));
        -webkit-backdrop-filter: blur(var(--glass-blur));
        border: 1px solid var(--glass-border);
        border-radius: var(--r-lg); box-shadow: var(--sh-elevada-1), var(--sh-elevada-2);
        margin: var(--sp-4) 0; overflow: hidden; }
/* Una tarjeta que envuelve una tabla se queda sólida: el cristal perjudica
   la legibilidad de filas densas (decisión validada con mockup, 2026-09-13).
   `:has()` en vez de una clase nueva -- así no hay que tocar las ~15
   plantillas que envuelven una .tabla en .card. */
.card:has(.tabla, .tabla-vacia) { background: var(--surface); backdrop-filter: none;
                     -webkit-backdrop-filter: none; border-color: var(--bd);
                     box-shadow: var(--sh-1); }
.card-cabecera { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--bd-soft);
                 font-weight: 600; font-size: var(--fs-200); }
.card-cuerpo { padding: var(--sp-4); }

/* Rejilla de tarjetas (hubs de acceso: home.html, inicio.html). Cada tarjeta
   clicable envuelve un .card en un .kpi-link (ver mas abajo) para heredar
   display:block + sin subrayado; .card en si no lo necesita en su uso normal
   (apilado), asi que la regla vive aqui, no en .card. */
.rejilla-tarjetas { display: grid; gap: var(--sp-4); margin: var(--sp-4) 0;
                    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.rejilla-tarjetas .card { margin: 0; height: 100%; }
.rejilla-tarjetas .card:hover { border-color: var(--primary); }

/* ---------- cifras ---------- */
/* R4 — superficie cálida SIEMPRE con filete de oro. */
.cifras { display: flex; flex-wrap: wrap; background: var(--surface-warm);
          border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
          border-radius: var(--r-md); margin: var(--sp-4) 0; }
/* El reparto, el filete separador y el escalonado se aplican al HIJO DIRECTO de
   .cifras, sea `.cifra` o el `<a class="kpi-link">` que la envuelve cuando es
   clicable. Con selectores atados a `.cifra` el envoltorio los rompía en silencio. */
.cifras > * { flex: 1; min-width: var(--w-cifra-min);
              animation: entra var(--dur-filete) var(--ease) both; }
.cifras > * + * { border-left: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); }
.cifras > *:nth-child(2) { animation-delay: var(--dur-fila); }
.cifras > *:nth-child(3) { animation-delay: calc(var(--dur-fila) * 2); }
.cifras > *:nth-child(4) { animation-delay: calc(var(--dur-fila) * 3); }
/* `.cifra-valor`/`.cifra-etiqueta` son <span> (inline) en la plantilla --
   sin `display:flex` aquí, el `margin-top` de `.cifra-etiqueta` no hace
   nada (no-op sobre un elemento inline) y las dos quedan pegadas en la
   misma línea en vez de número arriba / etiqueta debajo. */
.cifra { padding: var(--sp-3) var(--sp-4); display: flex; flex-direction: column; }
.cifra-valor { font-size: var(--fs-700); font-weight: 600; line-height: 1;
               color: var(--text-data); font-variant-numeric: tabular-nums; }
.cifra-valor--marca { color: var(--primary); }
.cifra-etiqueta { font-size: var(--fs-100); font-weight: 600; margin-top: var(--sp-1);
                  text-transform: uppercase; letter-spacing: var(--tracking-label);
                  color: var(--accent-text); }
@keyframes entra { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; } }

/* Cifra clicable. El nombre .kpi-link lo impone G1 (tests/test_panel_dashboard.py
   está acoplado a él); se reutiliza aquí en vez de inventar un segundo nombre. */
.kpi-link { text-decoration: none; color: inherit; display: block; }
.kpi-link:hover .cifra-valor,
.kpi-link:hover .cifra-etiqueta { color: var(--primary); }

/* ---------- botones ---------- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
       min-height: var(--tap); padding: var(--sp-2) var(--sp-4);
       font-size: var(--fs-200); font-weight: 600; text-decoration: none;
       border: 1px solid transparent; border-radius: var(--r-pill);
       background: var(--primary); color: var(--on-primary);
       box-shadow: 0 4px 12px -2px color-mix(in srgb, var(--primary) 45%, transparent);
       transition: transform var(--dur-presion) var(--ease), box-shadow var(--dur-presion) var(--ease),
                   background var(--dur-filete) var(--ease); }
.btn:hover { background: var(--primary-hover); color: var(--on-primary); transform: translateY(-1px);
             box-shadow: 0 6px 16px -2px color-mix(in srgb, var(--primary) 50%, transparent); }
.btn:active { transform: scale(.97); }
.btn--sec { background: transparent; color: var(--primary); border-color: var(--primary);
            box-shadow: none; }
.btn--sec:hover { background: color-mix(in srgb, var(--primary) 8%, transparent); box-shadow: none; }
.btn--fantasma { background: transparent; color: var(--text-muted); border-color: var(--bd); box-shadow: none; }
.btn--fantasma:hover { box-shadow: none; }
.btn--sm { min-height: var(--tap-denso); padding: var(--sp-1) var(--sp-3); font-size: var(--fs-100); }
/* `.btn` fija `display: inline-flex`, que gana a la regla `[hidden]` del
   navegador: sin esta línea, un botón con el atributo `hidden` se ve igualmente. */
.btn[hidden] { display: none; }

/* ---------- estados (R1: icono + palabra, nunca color solo) ---------- */
.pill { display: inline-flex; align-items: center; gap: var(--sp-1);
        padding: 2px var(--sp-3); border-radius: var(--r-pill);
        font-size: var(--fs-100); font-weight: 600; white-space: nowrap;
        background: var(--neutral-bg); color: var(--neutral);
        box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent); }
.pill::before { font-weight: 700; }
.pill.validado  { background: var(--ok-bg);     color: var(--ok); }
.pill.validado::before  { content: "\2713"; }   /* ✓ */
.pill.pendiente { background: var(--warn-bg);   color: var(--warn); }
.pill.pendiente::before { content: "\25F7"; }   /* ◷ */
.pill.rechazado { background: var(--danger-bg); color: var(--danger); }
.pill.rechazado::before { content: "\2715"; }   /* ✕ */
.pill.borrador::before  { content: "\25CB"; }   /* ○ */

/* ---------- avatar ---------- */
.avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover;
          vertical-align: middle; margin-right: var(--sp-2); border: 1px solid var(--bd); }

/* ---------- campos ---------- */
.campo { position: relative; margin: var(--sp-3) 0; max-width: var(--w-prose); }
.campo-etiqueta { font-size: var(--fs-100); font-weight: 600; margin-bottom: var(--sp-1); }
.campo input, .campo select, .campo textarea {
  width: 100%; min-height: var(--tap); padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-300); background: var(--surface); color: var(--text);
  border: 1px solid var(--bd); border-radius: var(--r-md);
  box-shadow: inset 0 1px 2px rgba(44, 42, 38, .05);
  transition: border-color .15s, box-shadow .15s; }
.campo input:focus, .campo select:focus, .campo textarea:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent); }
.campo input[type="checkbox"], .campo input[type="radio"] {
  width: auto; min-height: 0; accent-color: var(--primary); margin-right: var(--sp-2); }
.campo-ayuda { font-size: var(--fs-100); color: var(--text-muted); margin-top: var(--sp-1); }
.campo-error { font-size: var(--fs-100); color: var(--danger); margin-top: var(--sp-1); }
/* Opción dentro de un campo de checkboxes/radios (cuestionarios, multiselección). */
.campo-opcion { display: flex; align-items: center; gap: var(--sp-2);
                padding: var(--sp-2) var(--sp-3); margin: var(--sp-1) 0;
                border: 1px solid var(--bd); border-radius: var(--r-md);
                font-size: var(--fs-200); cursor: pointer; }
.campo-opcion:hover { background: color-mix(in srgb, var(--primary) 5%, transparent); }
.campo-opcion input { width: auto; min-height: 0; accent-color: var(--primary); }
/* Resultados del combobox de búsqueda de investigador (ancla en `.campo`,
   que por eso lleva `position: relative`). `top: 100%` los abre justo debajo
   del campo: sin él quedaban en su posición estática y, como
   `.rejilla-campos div.campo` es una columna centrada, la lista se centraba
   SOBRE el propio campo y tapaba el input. */
.combobox-resultados { position: absolute; z-index: 10; top: 100%; left: 0; right: 0;
                        margin: 2px 0 0; padding: var(--sp-1) 0; list-style: none;
                        background: var(--surface); border: 1px solid var(--bd);
                        border-radius: var(--r-md); box-shadow: var(--sh-2);
                        max-height: 240px; overflow-y: auto; }
.combobox-resultados li { padding: var(--sp-2) var(--sp-3); cursor: pointer; font-size: var(--fs-200); }
.combobox-resultados li:hover { background: color-mix(in srgb, var(--primary) 8%, transparent); }
/* Una tarjeta que contiene un combobox no recorta su desplegable: la lista se
   abre por debajo del campo y, con `overflow: hidden`, la tarjeta se comía todo
   lo que pasaba de su borde inferior. Las demás tarjetas siguen recortando.
   `position: relative; z-index: 1` sube la tarjeta por encima de lo que la
   sigue: el cristal (`backdrop-filter`) hace de ella un contexto de apilado,
   así que el `z-index: 10` de la lista no sale de la tarjeta, y los `.campo`
   posteriores (posicionados) se pintaban encima de la lista. */
.card:has(.combobox-resultados) { overflow: visible; position: relative; z-index: 1; }
/* Rejilla de casillas (poderes por perfil, filtros múltiples). */
.rejilla-campos { display: grid; gap: var(--sp-2);
                  grid-template-columns: repeat(auto-fit, minmax(var(--w-campo-min), 1fr)); }
/* Dos formas de `.campo` conviven aquí y necesitan direcciones de flex
   opuestas:
   - `<label class="campo">` (poderes.html: casilla + su propio texto en la
     misma etiqueta) quiere fila, casilla y texto uno al lado del otro.
   - `<div class="campo">` (los otros 7 filtros: `.campo-etiqueta` aparte,
     encima del select/input) quiere columna. Antes la regla compartida
     ponía `align-items: center` sin fijar dirección para ninguna de las
     dos, así que la fila implícita (el valor por defecto de flex) también
     metía la etiqueta del `<div>` AL LADO de su select/input -- apretados
     en los 220px de `--w-campo-min` -- en vez de solo centrar el bloque
     dentro de una fila más alta (p. ej. junto al multiselect de Categoría). */
.rejilla-campos .campo { margin: 0; display: flex; min-height: var(--tap); }
.rejilla-campos label.campo { align-items: center; }
.rejilla-campos div.campo { flex-direction: column; justify-content: center; }
/* Encabezado de bloque dentro de la rejilla de poderes (Tanda E del Bloque 3,
   D-10): con 37 casillas por perfil, una lista plana no se lee. `grid-column:
   1 / -1` lo saca de la retícula de columnas y le da la fila entera; sin eso se
   coloca en una celda, al lado de una casilla, y no separa nada. */
.rejilla-grupo { grid-column: 1 / -1; margin: var(--sp-3) 0 0;
                 font-size: var(--fs-100); font-weight: 600;
                 text-transform: uppercase; letter-spacing: var(--tracking-label);
                 color: var(--text-muted); }

/* Agrupado por sección de la ficha dinámica (T.x, Tarea 14): sin esto, 38
   campos seguidos son un muro inusable. `<fieldset>`/`<details>` nativos
   (según `plegable` en FieldDef) con el mismo espaciado que `.campo`. */
.ficha-seccion { margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4);
                 border: 1px solid var(--bd); border-radius: var(--r-md); }
.ficha-seccion > legend, .ficha-seccion > summary {
  font-size: var(--fs-100); font-weight: 600; padding: 0 var(--sp-2); cursor: default; }
.ficha-seccion > summary { cursor: pointer; }
.ficha-seccion .campo:first-of-type { margin-top: var(--sp-2); }

/* Sub-bloque de participantes de la ficha T.x (Tarea 14): tinte de marca
   (`--primary`, ronda de arreglo I-1 de la revisión — antes un morado
   ajeno a la paleta, `#ddd6fe`/`rgba(124,58,237,.05)`, copiado sin querer
   del origen en React) con el mismo `color-mix` que ya usan `.cifras`/
   `.btn--sec:hover` para variantes tintadas, nunca hex/rgba sueltos. */
.bloque-participantes {
  padding: var(--sp-4);
  background: color-mix(in srgb, var(--primary) 5%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--primary) 20%, var(--bd));
  border-radius: var(--r-md);
}
.participantes-lista { list-style: none; padding: 0; margin: 0 0 var(--sp-3) 0;
  display: flex; flex-direction: column; gap: var(--sp-2); }
.participantes-lista li { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  background: var(--surface); border: 1px solid var(--bd); border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3); }
.participante-nombre { flex: 1 1 180px; font-size: var(--fs-200); }
.participante-responsable { display: flex; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-100); font-weight: 600; cursor: pointer; white-space: nowrap; }
.participante-sin-quitar { font-size: var(--fs-100); color: var(--text-muted); }
.participantes-picker { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.participantes-picker select { flex: 1 1 260px; max-width: 420px; min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3); font-size: var(--fs-300); background: var(--surface);
  color: var(--text); border: 1px solid var(--bd); border-radius: var(--r-md); }

/* ---------- avisos ---------- */
.aviso { padding: var(--sp-3) var(--sp-4); margin: var(--sp-3) 0;
         border-left: 3px solid var(--text-muted); border-radius: 0 var(--r-md) var(--r-md) 0;
         background: var(--neutral-bg); font-size: var(--fs-200); max-width: var(--w-prose); }
.aviso--success { background: var(--ok-bg);     border-left-color: var(--ok);     color: var(--ok); }
.aviso--warning { background: var(--warn-bg);   border-left-color: var(--warn);   color: var(--warn); }
.aviso--error   { background: var(--danger-bg); border-left-color: var(--danger); color: var(--danger); }
.aviso--info    { background: var(--neutral-bg); border-left-color: var(--text-muted); color: var(--text); }

/* ---------- paginación ---------- */
.paginacion { display: flex; gap: var(--sp-1); align-items: center;
              margin: var(--sp-4) 0; font-size: var(--fs-200); }
.paginacion-item { min-width: var(--tap); min-height: var(--tap);
                   display: inline-flex; align-items: center; justify-content: center;
                   border: 1px solid var(--bd); border-radius: var(--r-md);
                   text-decoration: none; }
.paginacion-item[aria-current="page"] { background: var(--primary);
                                        color: var(--on-primary); border-color: var(--primary); }

/* ---------- barra de proporción (informes) ---------- */
/* El ancho del relleno lo calcula la vista: `style="width:{{ pct|unlocalize }}%"`.
   Ese atributo es DATO, no estilo suelto, y se conserva — igual que en el Gantt.
   Ojo al `|unlocalize`: sin él, «83,5%» es CSS inválido y la barra sale vacía. */
.fila-dato { display: flex; align-items: center; gap: var(--sp-3); }
.pista { flex: 1; height: 16px; overflow: hidden; border-radius: var(--r-pill);
         background: color-mix(in srgb, var(--primary) 8%, transparent); }
.pista > div { height: 100%; border-radius: var(--r-pill); background: var(--primary); }

/* ---------- impresión ---------- */
/* Los informes de dirección se imprimen: hay un botón «Imprimir / PDF». Sin esto
   saldrían la barra lateral y la cinta de contexto en el papel.
   Se añade `.migas` a la lista de ocultos, fuera del bloque literal que dio el
   controlador: es la cinta "⌂ Inicio …" de `_barra.html`, que vive FUERA de
   `.barra-global` (esa solo envuelve los selectores "ver como"/"ver rol") y por
   tanto no la tapaba `.barra-global{display:none}` sola. Sigue siendo solo CSS,
   dentro del fichero que esta ronda autoriza tocar. */
.no-print { display: revert; }
@media print {
  body { background: var(--surface); }
  .app-nav, .barra-global, .migas, .app-head-acciones, .no-print, .paginacion { display: none; }
  .app { display: block; }
  .app-main { max-width: none; padding: 0; }
  .app-head::after { animation: none; }
  .card { break-inside: avoid; background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; }
  a { text-decoration: none; color: var(--text); }
}

/* ---------- ficha (pares dato–valor) ---------- */
.ficha { display: grid; grid-template-columns: max-content 1fr;
         gap: var(--sp-2) var(--sp-5); align-items: baseline; margin: var(--sp-4) 0; }
.ficha-dato { font-size: var(--fs-100); font-weight: 600; text-transform: uppercase;
              letter-spacing: var(--tracking-label); color: var(--text-muted); }
.ficha-valor { font-size: var(--fs-200); }

/* ---------- superficie editorial (portales públicos e informes) ---------- */
.editorial { max-width: var(--w-prose); margin: 0 auto; padding: var(--sp-7) var(--sp-5); }
.editorial h1 { font-size: var(--fs-600); }
.editorial p { font-size: var(--fs-300); line-height: var(--lh-prose); margin: var(--sp-4) 0; }
.editorial small { color: var(--text-muted-cristal); }

/* ---------- responsive ---------- */
/* El colapso a "iconos" por debajo de 1100px prometido en la spec no es
   viable sin un juego de iconos que no existe: el contenido íntegro de cada
   .app-nav-item es un <span> de texto (T5, seis bases de app), así que
   ocultar el span dejaba enlaces de 44px clicables y vacíos, sin wordmark
   (el enlace a /) ni pie (cierre de sesión inalcanzable). En vez de un corte
   intermedio sin contenido, la barra lateral completa (vertical) se mantiene
   hasta 1100px y por debajo pasa directamente a la cinta horizontal
   desplazable, que sí conserva texto, wordmark y pie. */
@media (max-width: 1100px) {
  .app { flex-direction: column; }
  .app-nav { width: 100%; flex-direction: row; overflow-x: auto; padding: var(--sp-2); box-shadow: 0 8px 24px -6px rgba(46, 26, 20, .22); }
  .app-nav-marca { padding: 0 var(--sp-3); }
  .app-nav-item { border-left: 0; border-bottom: 2px solid transparent; }
  .app-nav-item.activo { border-left: 0; border-bottom-color: var(--accent-pale); }
  .app-nav-pie { margin-top: 0; margin-left: auto; }
  .app-main { padding: 0 var(--sp-4) var(--sp-6); }
  .cifras > * + * { border-left: 0; border-top: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); }
  .cifras { flex-direction: column; }
}

/* ---------- gantt ---------- */
/* Cronograma de membresías de grupo (registro/grupo.html) y trayectoria de
   persona (registro/trayectoria.html). Las posiciones y anchos de las
   barras (`style="left:…%;width:…%"`) los calcula la vista: son DATOS, no
   estilo suelto, y viven en la plantilla — no se tocan aquí. Esta hoja solo
   dibuja el carril fijo. Las dimensiones internas que mantienen la barra
   centrada en su carril (alto de fila, ancho de la columna de nombre,
   alto/línea de la barra) van literales, no a un token: son geometría
   interna acoplada de una sola pieza, igual que un filete de 1px (G4) —
   snap-earlas a la escala de espaciado rompería la alineación vertical. */
.gantt { display: flex; flex-direction: column; gap: var(--sp-1); }
.gantt-eje { position: relative; height: 1.1rem; margin-left: 170px;
             border-bottom: 1px solid var(--bd); }
.gantt-eje .yl { position: absolute; top: 0; font-size: var(--fs-100);
                 color: var(--text-muted); transform: translateX(-50%); }
.gantt-fila { display: flex; align-items: center; gap: var(--sp-2); }
.gantt-fila .nombre { width: 170px; flex: 0 0 170px; font-size: var(--fs-200);
                       white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.track { position: relative; flex: 1; height: 1.5rem; border-radius: var(--r-sm);
         background: linear-gradient(var(--bd), var(--bd)) no-repeat center / 100% 1px; }
.track .gl { position: absolute; top: 0; bottom: 0; width: 1px;
             background: var(--bd); opacity: .6; }
.barra { position: absolute; top: .18rem; height: 1.15rem; border-radius: var(--r-sm);
         color: var(--on-primary); font-size: var(--fs-100); line-height: 1.15rem;
         text-align: center; min-width: 3px; overflow: hidden; box-shadow: var(--sh-1); }
/* `.prog` queda del original (sin uso hoy en la plantilla); se conserva por
   fidelidad al bloque que se traslada, no es un añadido nuevo. */
.barra.prog { opacity: .55; }
/* Contorno del grupo PRINCIPAL de la persona (no confundir con el ★ del IP). */
.barra.princ { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--on-primary) 90%, transparent),
                            var(--sh-1); }
/* Banda de contrato/afiliación con la universidad (solo trayectoria.html). */
.barra.afil { background: repeating-linear-gradient(45deg,
              var(--ok), var(--ok) 6px,
              color-mix(in srgb, var(--ok) 75%, black) 6px,
              color-mix(in srgb, var(--ok) 75%, black) 12px); }
.gantt-leyenda { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
                 align-items: center; margin-top: var(--sp-4); padding-top: var(--sp-2);
                 border-top: 1px solid var(--bd); font-size: var(--fs-100);
                 color: var(--text-muted); }
.gantt-leyenda .tit { font-weight: 700; }
.gantt-leyenda .item { display: inline-flex; align-items: center; gap: var(--sp-1); }
.gantt-leyenda .muestra { width: .85rem; height: .85rem; border-radius: var(--r-sm);
                          display: inline-block; background: var(--bd); }
.gantt-leyenda .muestra.princ { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--on-primary) 90%, transparent);
                                 border: 1px solid var(--bd); }
.gantt-leyenda .muestra.afil { background: repeating-linear-gradient(45deg,
                                var(--ok), var(--ok) 4px,
                                color-mix(in srgb, var(--ok) 75%, black) 4px,
                                color-mix(in srgb, var(--ok) 75%, black) 8px); }
.gantt-leyenda .sub { flex-basis: 100%; }

/* Asiento anulado en el libro de registro (grupo.html/trayectoria.html): una
   enmienda lo anula pero el asiento original sigue visible, tachado, para
   no perder el rastro notarial. No es del Gantt, pero solo lo usan estas
   dos plantillas y esta es la única hoja que esta tarea autoriza tocar. */
.anulado { text-decoration: line-through; color: var(--text-muted); }

/* ---------- panorama del ecosistema (panel.html) ---------- */
/* Dashboard /panel/<slug>/. Marcado SIN CAMBIOS respecto al original salvo
   quitar el <style> propio: aqui viven los ganchos de G1 (`.kpi-link` en las
   ocho cifras, `<a href="/faq/.../"><g class="node">` del SVG) y la Tarea 11
   los conserva literales — hay pruebas acopladas
   (tests/test_panel_dashboard.py). */
.kpis { display: grid; gap: var(--sp-3);
        grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.kpi { border: 1px solid var(--bd); border-radius: var(--r-lg);
       padding: var(--sp-3) var(--sp-4); background: var(--surface);
       border-left: 5px solid var(--c, var(--primary)); }
.kpi .num { font-size: var(--fs-700); font-weight: 700; line-height: 1; }
.kpi .lbl { color: var(--text-muted); font-size: var(--fs-100); margin-top: var(--sp-1); }
/* El .kpi-link de la cifra (mas arriba) ya pone text-decoration:none/color:inherit/
   display:block; esta regla solo anade el resalte de foco/hover propio de .kpi. */
.kpi-link:hover .kpi { border-color: var(--c, var(--primary)); }

/* Los ocho `--c` de arriba y los `fill` que hacen juego en los <rect> del SVG
   de abajo son la MISMA paleta por dato (personas=azul, grupos=verde…): la
   Tarea 14 los sustituyo por la rampa --serie-1..8, en el mismo orden que las
   ocho tarjetas KPI. El noveno nodo (Categorias) no tiene tarjeta KPI propia
   y usa var(--primary); el color de las flechas (antes literal) usa
   var(--text-muted), igual que `.elbl` y `.edge`. Cero color literal. */
.card-cuerpo svg { width: 100%; height: auto; min-width: 760px; }
svg a { cursor: pointer; text-decoration: none; }
.node .t { fill: var(--on-primary); font-weight: 600; font-size: 14px; }
.node .n { fill: var(--on-primary); font-weight: 700; font-size: 22px; }
.edge { stroke: var(--text-muted); stroke-width: 1.6; fill: none; }
.elbl { fill: var(--text-muted); font-size: 11px; }

/* Estados de la tabla de indicadores de cumplimiento (RD 640 y similares).
   No son `.pill`: no llevan icono, pero R1 se cumple igual porque el texto
   siempre lleva la palabra del estado ({{ it.estado }}), nunca solo el color. */
.chip { display: inline-block; padding: 2px var(--sp-3); border-radius: var(--r-pill);
        font-size: var(--fs-100); font-weight: 600; color: var(--on-primary); }
.chip-cumple { background: var(--ok); }
.chip-no_cumple { background: var(--danger); }
.chip-sin_datos { background: var(--neutral); }
.chip-informativo { background: var(--primary); }

/* ---------- panel de ayuda (contextual por página) ---------- */
/* Botón «?» en la zona de acciones de la cabecera y panel lateral con el texto
   de la pantalla. No hay ningún popover/drawer previo en el sistema: esta es la
   primera pieza flotante, de ahí el z-index explícito (el resto de la hoja no
   usa ninguno). Panel fijo a la derecha desde 900px; por debajo, hoja inferior
   a lo ancho. Texto plano (D-8): `pre-line` respeta los saltos guardados. */
.btn-ayuda { display: inline-flex; align-items: center; justify-content: center;
             flex: none; width: var(--tap); height: var(--tap);
             font-size: var(--fs-400); font-weight: 700; line-height: 1;
             color: var(--primary); background: var(--surface);
             border: 1px solid var(--primary); border-radius: var(--r-pill); }
.btn-ayuda:hover { background: color-mix(in srgb, var(--primary) 8%, var(--surface)); }
.btn-ayuda[aria-expanded="true"] { color: var(--on-primary); background: var(--primary); }

.panel-ayuda { position: fixed; z-index: 20; top: 0; right: 0; bottom: 0;
               width: min(380px, 100vw); display: flex; flex-direction: column;
               background: var(--surface); color: var(--text);
               border-left: 1px solid var(--bd-strong); box-shadow: var(--sh-2); }
.panel-ayuda[hidden] { display: none; }
.panel-ayuda__cabecera { display: flex; align-items: flex-start; gap: var(--sp-3);
                         padding: var(--sp-4) var(--sp-5);
                         border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
.panel-ayuda__titulo { flex: 1; font-family: var(--font-editorial);
                       font-size: var(--fs-400); color: var(--primary); }
.panel-ayuda__cerrar { flex: none; min-width: var(--tap); min-height: var(--tap);
                       color: var(--text-muted); background: transparent;
                       border: 1px solid var(--bd); border-radius: var(--r-pill); }
.panel-ayuda__cerrar:hover { color: var(--text); border-color: var(--bd-strong); }
.panel-ayuda__cuerpo { overflow-y: auto; white-space: pre-line;
                       padding: var(--sp-4) var(--sp-5) var(--sp-6);
                       font-size: var(--fs-300); line-height: var(--lh-prose); }
.panel-ayuda__pie { flex: none; padding: var(--sp-3) var(--sp-5) var(--sp-4);
                    border-top: 1px solid var(--bd); }

@media (max-width: 899px) {
  .panel-ayuda { top: auto; left: 0; width: 100%; max-height: 70vh;
                 border-left: 0; border-top: 1px solid var(--bd-strong);
                 border-radius: var(--r-lg) var(--r-lg) 0 0; }
}

/* ---------- tarjetas de convocatoria de incentivo (fila 15, D-15) ----------
   La auditoria pedia un "modal"; MERITUS no tiene componente modal y anadirlo
   por esto seria superficie de UI nueva para un beneficio marginal. Lo que la
   fila reclama de verdad -codigo, curso y descripcion visibles- se resuelve
   con tarjetas apiladas y `<details>` nativo, sin una linea de JS. */
.convocatoria-lista { display: flex; flex-direction: column; gap: var(--sp-3); }
.convocatoria-tarjeta { padding: var(--sp-3); background: var(--surface);
                        border: 1px solid var(--bd); border-radius: var(--r-md); }
.convocatoria-tarjeta > p { margin: 0 0 var(--sp-2) 0; }
.convocatoria-meta { font-size: var(--fs-100); color: var(--text-muted); }
