/* Sistema visual compartido (AD-11, rediseñado en AD-25 sobre la
   dirección visual de las maquetas aprobadas: barra lateral + panel de
   IA, tipografía serif para títulos + sans para el resto, azul marino
   como color primario y ámbar reservado exclusivamente para todo lo
   que proviene de la IA — nunca para otra cosa, para que "esto lo
   propuso la IA, no está confirmado todavía" se reconozca de un
   vistazo en toda la aplicación).

   Las dos fuentes (Source Serif 4, IBM Plex Sans) se cargan desde
   Google Fonts en templates/base.html — es la única dependencia
   externa que se admite a propósito (ver ese archivo); todo lo demás
   (iconos, HTMX, Alpine.js) queda servido desde /static/ propio.

   Los nombres de clase de las tarjetas, tablas y formularios
   (.instrument-card, .data-table, .form-field, .badge, .chain-step...)
   se mantienen sin cambios respecto de la versión anterior de este
   archivo a propósito: home, tablero, asistente_ia y trazabilidad ya
   los usan, y rediseñar solo los valores de los tokens (sin tocar esas
   plantillas) es lo que permite que las cuatro hereden la nueva
   identidad visual sin reescribir una sola de ellas. */

:root {
  --bg: #FAF9F6;
  --bg-card: #FFFFFF;
  --border: #E4DFD2;
  --border-soft: #EDE9DD;
  --ink: #20242E;
  --ink-soft: #5B6172;
  /* Revisión v1.0 (V1-A11Y-01, 8 oct 2026): los tonos de texto tenue
     pasan a ≥ 4,5:1 sobre todos los fondos claros de la app (WCAG 1.4.3,
     medido con axe-core); antes #8B90A0 (2,6 a 3,2:1), #7A81A3 en la
     barra lateral (3,9:1), #2F7D5A en la insignia «Completo» (4,46:1),
     #B8722E y #9A6B2E como texto sobre su tinte (3,3 y 4,2:1). Un texto
     nuevo nunca usa un color propio: usa uno de estos tokens. */
  --ink-faint: #626777;

  --sidebar-bg: #1E2540;
  --sidebar-ink: #B9C0DE;
  --sidebar-ink-dim: #9097B8;
  --sidebar-active-bg: #323B63;

  --accent: #3D4B94;
  --accent-dark: #2A3468;
  --accent-tint: #EEF0FA;
  /* Fase 21c: hover tenue para el botón primario — pedido explícito de
     Rogelio (8 sep 2026): el hover anterior saltaba a --accent-dark, un
     azul marino muy oscuro que además el propio .accordion-toolbar
     button usaba como color de reposo, chocando con el texto negro de
     los párrafos. Este tono es un punto medio entre --accent e
     --ink-soft: sigue leyéndose como "el mismo botón, solo resaltado",
     sin la dureza de --accent-dark. */
  --accent-hover-suave: #4C5683;

  --ai: #8A5E26;
  --ai-tint: #FBF1E4;
  --ai-border: #EAD9BC;

  --success: #2A7352;
  --success-tint: #E9F5EE;
  --warn: #955A20;
  --warn-tint: #FBF1E4;
  --danger: #B23B3B;
  --danger-tint: #FBEAEA;

  --radius: 10px;
  --radius-sm: 6px;
  --space-1: 0.375rem;
  --space-2: 0.75rem;
  --space-3: 1.25rem;
  --space-4: 2rem;

  --font-ui: 'IBM Plex Sans', -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-heading: 'Source Serif 4', Georgia, serif;

  /* Alias retenidos por compatibilidad con estilos existentes que
     todavía referencian los nombres anteriores. */
  --color-bg: var(--bg);
  --color-surface: var(--bg-card);
  --color-text: var(--ink);
  --color-muted: var(--ink-soft);
  --color-primary: var(--accent);
  --color-primary-soft: var(--accent-tint);
  --color-border: var(--border-soft);
  --color-border-strong: var(--border);
  --color-success: var(--success);
  --color-success-soft: var(--success-tint);
  --color-danger: var(--danger);
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05);
  --shadow-md: 0 6px 20px rgba(15, 23, 42, 0.07);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-ui);
  background: var(--bg);
  color: var(--ink);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  font-size: 14px;
}

a { color: var(--accent); }
a:hover { color: var(--accent-dark); }

h1, h2, h3 {
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: -0.01em;
}

svg.icon {
  width: 17px; height: 17px; flex: 0 0 auto;
  stroke: currentColor; fill: none; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Layout general de app (AD-25) ------------------------------------ */

.app {
  display: flex;
  min-height: 100vh;
  align-items: stretch;
}

.sidebar {
  width: 248px;
  flex: 0 0 auto;
  background: var(--sidebar-bg);
  color: var(--sidebar-ink);
  display: flex;
  flex-direction: column;
  padding: 22px 14px;
  gap: 22px;
  overflow: hidden;
  transition: width .15s ease, padding .15s ease, opacity .1s ease;
}

/* Fase 21a: mecánica de ocultar/mostrar y redimensionar los paneles
   izquierdo y derecho, con persistencia en localStorage (ver
   static/core/paneles.js). El estado vive en clases del elemento
   <html> (no <body>: el script que las aplica corre en <head>, antes
   de que <body> exista, para fijar el estado antes del primer render
   y evitar el parpadeo de "panel visible un instante y luego
   oculto") y en la variable --ai-panel-width de :root — nunca como
   estilo o clase puestos directamente sobre .sidebar / #ai-panel —
   porque #ai-panel se reemplaza por completo en cada intercambio HTMX
   fuera de banda al elegir otra fila (ver *_oob.html de cada
   instrumento): un estado puesto ahí se perdería en la siguiente
   selección. Con el estado en un ancestro que el intercambio nunca
   toca, el panel nuevo que HTMX inserta hereda el estado correcto
   automáticamente, sin ningún listener de eventos HTMX. */
:root {
  --ai-panel-width: 320px;
}

html.aim-sidebar-hidden .sidebar {
  width: 0;
  padding-left: 0;
  padding-right: 0;
  opacity: 0;
}

/* Decisión de Rogelio (1 oct 2026): más angostas (antes 22px) para que
   no tapen el borde del contenido. */
.panel-toggle {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--ink-faint);
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  z-index: 15;
  padding: 0;
  transition: left .15s ease, right .15s ease;
  box-shadow: var(--shadow-sm);
}
.panel-toggle:hover { color: var(--ink); background: var(--border-soft); }
.panel-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.panel-toggle--sidebar {
  left: 248px;
  border-radius: 0 8px 8px 0;
  border-left: none;
}
html.aim-sidebar-hidden .panel-toggle--sidebar { left: 0; }

.panel-toggle--ai-panel {
  right: var(--ai-panel-width);
  border-radius: 8px 0 0 8px;
  border-right: none;
}
html.aim-ai-panel-hidden .panel-toggle--ai-panel { right: 0; }

.panel-resize-handle {
  position: fixed;
  top: 64px; /* alto de .topbar — el redimensionador no invade la barra superior */
  bottom: 0;
  right: var(--ai-panel-width);
  width: 6px;
  margin-right: -3px;
  cursor: col-resize;
  z-index: 14;
  transition: right .15s ease;
  touch-action: none; /* el arrastre maneja el gesto — sin esto, el
    navegador puede interpretarlo como un scroll/pan nativo en pantallas
    táctiles y competir con el redimensionado (hallazgo de la revisión
    independiente, Fase 21a: los eventos táctiles sintéticos de las
    pruebas automatizadas no reproducen este conflicto). */
}
.panel-resize-handle:hover { background: var(--accent-tint); }
.panel-resize-handle:focus-visible {
  background: var(--accent-tint);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
html.aim-ai-panel-hidden .panel-resize-handle { display: none; }
/* Sin panel derecho en la pantalla (Inicio, Tablero, Manual…), tampoco su
   franja ni su pestaña: quedaban dibujadas encima del contenido
   (corrección de Rogelio, 12 oct 2026). */
.app:not(:has(> .ai-panel)) .panel-toggle--ai-panel,
.app:not(:has(> .ai-panel)) .panel-resize-handle { display: none; }
html.aim-resizing-ai-panel .panel-resize-handle { background: var(--accent-tint); }
/* Mientras se arrastra, desactivar transiciones (si no, el ancho va
   "a la zaga" del cursor) y evitar selección de texto accidental. */
html.aim-resizing-ai-panel,
html.aim-resizing-ai-panel * { user-select: none !important; }
html.aim-resizing-ai-panel .ai-panel,
html.aim-resizing-ai-panel .panel-toggle--ai-panel,
html.aim-resizing-ai-panel .panel-resize-handle { transition: none !important; }

.brand { display: flex; flex-direction: column; gap: 2px; padding: 0 10px; text-decoration: none; }
.brand-name {
  font-family: var(--font-heading); font-weight: 700; font-size: 19px;
  color: #fff; text-decoration: none;
}
.brand-tag { font-size: 11px; color: var(--sidebar-ink-dim); line-height: 1.35; margin: 0; }
/* Fase 44: subtítulo de la barra lateral, disimulado como selector de
   ambiente cuando la institución tiene más de uno habilitado (pedido
   explícito: que no se vea como un menú convencional). Se apoya en
   `.brand-tag` para el tipo/color — estas reglas solo quitan el chrome
   de <button> y añaden el desplegable, sin ningún ícono de flecha ni
   borde que lo delate como control interactivo en reposo. */
.brand-tag-switcher { position: relative; }
.brand-tag--button {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 0; cursor: pointer;
}
.brand-tag--button:hover, .brand-tag--button:focus-visible { color: var(--sidebar-ink); text-decoration: underline dotted; }
.brand-tag--button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.brand-tag-dropdown {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  min-width: 14rem; background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: var(--radius); box-shadow: var(--shadow-md); padding: 6px; display: flex;
  flex-direction: column; gap: 2px;
}
.brand-tag-dropdown-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 8px 10px; border-radius: var(--radius-sm); font: inherit; font-size: 13px;
  font-weight: 500; color: var(--ink); cursor: pointer;
}
.brand-tag-dropdown-item:hover { background: var(--border-soft); }
.brand-tag-dropdown-item.active { color: var(--accent); font-weight: 600; }
.brand-logo {
  /* Espacio pensado para un logo de hasta 500×300 (relación ~5:3):
     estos son topes de caja (`max-*`, sin `width`/`height` fijos), así
     que un logo más angosto o más bajo se escala hacia abajo
     manteniendo su propia proporción — no se recorta ni se deforma. */
  max-height: 56px; max-width: 200px; width: auto; height: auto; display: block;
}
.brand-name-anon .brand-logo { max-height: 56px; max-width: 220px; }

.nav-group { display: flex; flex-direction: column; gap: 2px; }
.nav-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .09em;
  color: var(--sidebar-ink-dim); padding: 10px 12px 4px;
}
.nav-item {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border-radius: var(--radius-sm); color: var(--sidebar-ink);
  min-height: 36px; text-decoration: none;
}
.nav-item:hover { background: rgba(255, 255, 255, 0.06); color: #fff; }
.nav-item.active { background: var(--sidebar-active-bg); color: #fff; }
.nav-item.active svg { color: #fff; }
.sidebar-footer { margin-top: auto; }
/* Cierre de la v1: número de versión (settings.AIMHE_VERSION) al pie del
   menú lateral y del pie de página público. */
.version-app { margin: var(--space-2) 0 0; font-size: 11px; color: var(--ink-faint); }
.version-app a { color: inherit; }
.sidebar .version-app { padding: 0 var(--space-2); opacity: 0.7; color: inherit; }

.main-col { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }

.topbar {
  /* `min-height` en vez de `height` fija: en la barra pública (sin
     sesión), `.brand-anon` apila logo + subtítulo y con un logo grande
     (hasta 500×300, ver `.brand-logo`) puede superar los 64px de la
     barra autenticada — con `min-height` la fila crece con su
     contenido en vez de recortarlo. `.panel-resize-handle` fija su
     `top` en 64px asumiendo la barra autenticada (corta, sin
     `.brand-anon`), que sigue midiendo exactamente eso. */
  min-height: 64px; flex: 0 0 auto; display: flex; align-items: center;
  justify-content: space-between; padding: 8px 28px;
  border-bottom: 1px solid var(--border); background: var(--bg-card);
}
.brand-anon { display: flex; flex-direction: column; gap: 1px; }
.brand-name-anon {
  font-family: var(--font-heading); font-weight: 700; font-size: 17px;
  color: var(--accent-dark); text-decoration: none;
}
.crumb { font-size: 13px; color: var(--ink-faint); }
.crumb b { color: var(--ink); font-weight: 600; }
.topbar-right { display: flex; align-items: center; gap: 16px; }
.inst-badge {
  display: flex; align-items: center; gap: 8px; background: var(--accent-tint);
  color: var(--accent-dark); padding: 6px 12px; border-radius: 999px;
  font-size: 13px; font-weight: 500;
}
.inst-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.avatar {
  width: 32px; height: 32px; border-radius: 50%; background: var(--accent-dark);
  color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
/* Tamaños fijos del avatar fuera del encabezado. */
.avatar--mini { width: 22px; height: 22px; font-size: 9px; }
.avatar--mini + .avatar-dot, .avatar-wrap .avatar--mini ~ .avatar-dot { width: 7px; height: 7px; border-width: 1.5px; }
.avatar--grande { width: 56px; height: 56px; font-size: 18px; }
.foto-perfil { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; }
.foto-perfil-fila { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-1); }
.data-table .avatar-wrap { vertical-align: middle; margin-right: var(--space-1); }
/* Detalle secundario de una alerta en el Home. */
.alerta-detalle { color: var(--ink-faint); font-size: 0.85rem; }

/* Inicio (rediseño del 12 oct 2026): «Pendiente de ti», una fila por
   instrumento y enlaces a cada registro. */
.inicio-pendientes { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: var(--space-3); }
.inicio-pendientes__grupo--accion { border-color: var(--warn); }
.inicio-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.inicio-lista li { font-size: 13px; line-height: 1.4; }
.inicio-lista a { color: var(--ink); text-decoration: none; }
.inicio-lista a:hover { color: var(--accent); text-decoration: underline; }
/* Tablero de indicadores (rediseño del 15 oct 2026, ver core/tablero.py). */
.tablero-atencion { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: var(--space-3); }
.tablero-atencion__grupo { border-color: var(--warn); }
.tablero-recorrido__caja {
  display: flex; flex-direction: column; align-items: center; padding: var(--space-2) 4px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--ink); text-decoration: none;
}
a.tablero-recorrido__caja:hover { border-color: var(--accent); }
.tablero-recorrido__valor { font-size: 1.4rem; font-weight: 600; font-family: var(--font-heading); }
.tablero-recorrido__etiqueta { font-size: 12.5px; color: var(--ink-soft); }
.tablero-seccion__titulo { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.tablero-bloques { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: var(--space-3); }
.tablero-bloque--matriz, .tablero-bloque--perfil { grid-column: span 2; }
.tablero-barras, .tablero-perfil, .tablero-leyenda { list-style: none; margin: 0; padding: 0; }
.tablero-barras li { display: grid; grid-template-columns: minmax(7rem, 11rem) 1fr 3rem; align-items: center; gap: var(--space-2); padding: 4px 0; font-size: 13px; }
.tablero-barras__barra { height: 0.6rem; border-radius: 999px; background: var(--border-soft); overflow: hidden; }
.tablero-barras__relleno { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.tablero-barras__valor { text-align: right; font-weight: 600; }
/* Cifras sueltas (16 oct 2026, corrección de Rogelio): cada una como cifra
   grande con su etiqueta debajo; la que es parte de otra dice «3 de 4» y
   lleva una barra con esa proporción. El número va antes que su <dt> solo
   a la vista (`order`); en el HTML, la etiqueta sigue primero. */
.tablero-cifras { margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--space-2); }
.tablero-cifras__cifra { display: flex; flex-direction: column; gap: 4px; padding: var(--space-2); border: 1px solid var(--border-soft); border-radius: var(--radius-sm); background: var(--bg); }
.tablero-cifras__cifra dd { margin: 0; }
.tablero-cifras__valor { order: 1; font-family: var(--font-heading); font-size: 1.6rem; font-weight: 600; line-height: 1.1; color: var(--ink); }
.tablero-cifras__valor a { text-decoration: none; }
.tablero-cifras__valor a:hover { text-decoration: underline; }
.tablero-cifras__de { font-family: var(--font-ui); font-size: 13px; font-weight: 400; color: var(--ink-soft); }
.tablero-cifras__etiqueta { order: 2; font-size: 12.5px; color: var(--ink-soft); line-height: 1.35; }
.tablero-cifras__barra { order: 3; height: 0.4rem; border-radius: 999px; background: var(--border-soft); overflow: hidden; margin-top: auto; }
.tablero-cifras__relleno { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.tablero-bloque a { color: var(--accent); }
.tablero-matriz-envoltura { overflow-x: auto; }
.tablero-matriz { border-collapse: separate; border-spacing: 4px; width: 100%; font-size: 13px; }
.tablero-matriz th { font-weight: 600; color: var(--ink-soft); text-align: center; padding: 4px; }
.tablero-matriz tbody th { text-align: right; white-space: nowrap; width: 1%; }
.tablero-matriz__ejes { margin: 0 0 var(--space-1); font-size: 12px; color: var(--ink-soft); }
.tablero-matriz__celda { text-align: center; padding: 10px 4px; border-radius: var(--radius-sm); font-weight: 600; min-width: 3.5rem; }
.tablero-matriz__celda a { display: block; text-decoration: none; color: inherit; }
.tablero-matriz__celda a:hover { text-decoration: underline; }
.tablero-matriz__celda--0 { background: transparent; border: 1px dashed var(--border); color: var(--ink-faint); font-weight: 400; }
.tablero-matriz__celda--1 { background: var(--accent-tint); color: var(--accent-dark); }
.tablero-matriz__celda--2 { background: var(--accent-hover-suave); color: #fff; }
.tablero-matriz__celda--3 { background: var(--accent); color: #fff; }
.tablero-matriz__celda--4 { background: var(--accent-dark); color: #fff; }
.tablero-perfil li { display: grid; grid-template-columns: minmax(9rem, 13rem) 1fr; align-items: center; gap: var(--space-2); padding: 5px 0; font-size: 13px; }
.tablero-perfil__total { color: var(--ink-faint); }
.tablero-perfil__barra { display: flex; height: 1.5rem; border-radius: var(--radius-sm); overflow: hidden; background: var(--border-soft); }
.tablero-perfil__segmento { display: flex; align-items: center; justify-content: center; font-size: 11.5px; font-weight: 600; min-width: 1.4rem; }
.tablero-leyenda { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); margin-top: var(--space-2); font-size: 12.5px; color: var(--ink-soft); }
.tablero-leyenda li { display: flex; align-items: center; gap: 6px; }
.tablero-leyenda__muestra { width: 0.9rem; height: 0.9rem; border-radius: 3px; border: 1px solid var(--border); }
.nivel-insuficiente { background: var(--danger-tint); color: var(--danger); }
.nivel-limitada { background: var(--warn-tint); color: var(--warn); }
.nivel-adecuada { background: var(--accent-tint); color: var(--accent-dark); }
.nivel-solida { background: var(--success-tint); color: var(--success); }
.nivel-indeterminado { background: var(--bg); color: var(--ink-soft); }

/* Inicio + tablero (fusión del 15 oct 2026): estado de la foto, recorrido
   con sus casos, tarjetas plegables por instrumento con minigráfico. */
.inicio-indicadores__titulo { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }
.inicio-indicadores__estado { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.inicio-indicadores__estado form { margin: 0; }
.inicio-subtitulo { margin: var(--space-3) 0 var(--space-2); font-size: 14px; font-weight: 600; color: var(--ink); }
/* El recorrido llena todo el ancho (16 oct 2026, corrección de Rogelio):
   columnas iguales que se reparten la fila, también al abrir o cerrar el
   panel; si no caben, bajan en filas parejas. La flecha va en el hueco
   entre columnas. */
.tablero-recorrido { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(5.25rem, 1fr)); column-gap: 1.25rem; row-gap: var(--space-3); }
.tablero-recorrido li { position: relative; min-width: 0; }
.tablero-recorrido li + li::before { content: "→" / ""; position: absolute; left: -1.05rem; top: 1.45rem; color: var(--ink-faint); }
.tablero-recorrido__columna { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.tablero-recorrido__columna .tablero-recorrido__caja { box-sizing: border-box; min-width: 0; min-height: 5.6rem; justify-content: center; text-align: center; }
.tablero-recorrido__casos { display: flex; flex-wrap: wrap; justify-content: center; gap: 3px; }
.tablero-recorrido__casos a { text-decoration: none; font-size: 11px; }
.tablero-instrumento { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--border-soft); }
.tablero-instrumento__cabeza { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-2); }
.tablero-instrumento__cabeza h3 { margin: 0; font-size: 15px; font-weight: 600; }
.tablero-instrumento__cabeza h3 a { color: var(--ink); text-decoration: none; }
.tablero-instrumento__cabeza h3 a:hover { color: var(--accent); text-decoration: underline; }
.tablero-instrumento__resumen { flex-basis: 100%; font-size: 13px; color: var(--ink-soft); }
.tablero-bloque { min-width: 0; }
/* Un bloque no es una tarjeta clickeable: sin la elevación de `.instrument-card:has(a):hover`. */
.tablero-bloque.instrument-card:hover { box-shadow: none; transform: none; }
.tablero-bloque--ancho { grid-column: 1 / -1 !important; }
.tablero-matriz--compacta .tablero-matriz__celda { position: relative; min-width: 2.4rem; padding: 6px 2px; font-size: 12px; }
.tablero-matriz--compacta thead th { font-size: 11.5px; font-weight: 500; }
.tablero-bloque h4 { margin: 0 0 var(--space-2); font-size: 13.5px; font-weight: 600; }
.inicio-archivados { margin-top: var(--space-4); font-size: 13px; color: var(--ink-soft); }
.inicio-archivados__titulo { font-weight: 600; margin-right: 4px; }

/* En el teléfono, los bloques del tablero operativo se apilan en una
   columna; lo ancho (la matriz de actividad) se desplaza dentro de su
   tarjeta. */
@media (max-width: 768px) {
  .tablero-bloque--matriz, .tablero-bloque--perfil { grid-column: auto; }
  .tablero-bloques { grid-template-columns: 1fr; }
  .tablero-perfil li { grid-template-columns: 1fr; gap: 4px; }
  .tablero-barras li { grid-template-columns: minmax(6rem, 9rem) 1fr 2.5rem; }
}

/* Punto de disponibilidad sobre el avatar (Fase 50) — envolver el
   `.avatar` en `.avatar-wrap` y agregar un `<span class="avatar-dot
   avatar-dot--<color>">` como hermano. Colores fijos de
   `EstadoDisponibilidad.Color` (verde/naranja/rojo/gris_oscuro) más
   "gris", que NO es un valor del catálogo — lo calcula aparte
   `core.presencia` para "fuera de línea" automático por falta de
   sesión activa, alta transparencia a propósito (pedido explícito de
   Rogelio) para no competir visualmente con los 4 colores manuales. */
.avatar-wrap { position: relative; display: inline-flex; flex: 0 0 auto; }
.avatar-dot {
  position: absolute; right: -1px; bottom: -1px; width: 10px; height: 10px;
  border-radius: 50%; border: 2px solid var(--bg-card);
}
.avatar-dot--verde { background: var(--success); }
.avatar-dot--naranja { background: var(--warn); }
.avatar-dot--rojo { background: var(--danger); }
.avatar-dot--gris_oscuro { background: var(--ink-soft); }
.avatar-dot--gris { background: var(--ink-faint); opacity: 0.6; }

/* Campanita de notificaciones (Fase 51) — mismo `.user-menu*` que el
   resto de menús del encabezado, con un badge numérico superpuesto en
   la esquina del ícono (mismo criterio de posicionamiento que
   `.avatar-dot`, arriba). `.notif-badge:empty` se colapsa solo: sin
   notificaciones sin leer no hay nada que dibujar. */
.notif-trigger { position: relative; border: none; background: none; cursor: pointer; padding: 6px; color: var(--ink-soft); min-width: 24px; min-height: 24px; } /* 24 px: WCAG 2.5.8 (revisión v1.0, V1-A11Y-04) */
.notif-trigger .icon { width: 20px; height: 20px; }
.notif-badge {
  position: absolute; top: 2px; right: 2px; min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 999px; background: var(--danger); color: #fff; font-size: 10px; font-weight: 700;
  line-height: 15px; text-align: center;
}
.notif-badge:empty { display: none; }
.notif-dropdown { width: 22rem; max-height: 24rem; overflow-y: auto; }
/* Cierre de la v1 (30 sep 2026, pedido de Rogelio): cada notificación
   es una tarjeta propia (borde, fondo y separación) alineada a la
   izquierda — antes heredaba el `align-items: center` de
   `.user-menu-item` y, apiladas sin separación, se leían como un solo
   texto con la fecha y la resolución centradas. */
.notificacion-item,
.user-menu-item.notificacion-item {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  white-space: normal; text-align: left;
  padding: var(--space-2); margin-bottom: var(--space-1);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-card);
}
.user-menu-item.notificacion-item:hover { border-color: var(--accent); background: var(--bg-card); }
.notificacion-item--nueva,
.user-menu-item.notificacion-item--nueva { background: var(--accent-tint); border-color: var(--accent-tint); }
.notificacion-texto { font-size: 13px; color: var(--ink); }
.notificacion-fecha { font-size: 11px; color: var(--ink-faint); }
/* 26 sep 2026: sección de decisiones pendientes de la campanita. */
.notif-dropdown { width: 26rem; max-height: 30rem; }
.notif-seccion { margin: var(--space-1) var(--space-1) 2px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-faint); }
.notificacion-item--accion { background: var(--warn-tint); border-color: var(--warn-tint); }
.notificacion-item--accion > a.notificacion-texto { text-decoration: none; }
.notificacion-cambios > summary, .notificacion-rechazo > summary { cursor: pointer; }
.notificacion-cambios > summary { font-size: 12px; color: var(--accent); }
.notificacion-cambios { font-size: 12px; }
.notificacion-rechazo > summary { list-style: none; display: inline-block; }
.notificacion-rechazo > summary::-webkit-details-marker { display: none; }
.notificacion-rechazo[open] { flex-basis: 100%; }
.notificacion-rechazo form { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-1); }
.notificacion-rechazo textarea { width: 100%; font: inherit; font-size: 12.5px; padding: 4px 6px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.notificacion-rechazo form button { align-self: flex-start; }
.notificacion-acciones { display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: center; margin-top: var(--space-1); }
.notificacion-resolucion { font-size: 11.5px; color: var(--ink-soft); }
.notificacion-leer-todas { margin: var(--space-1); text-align: center; }
.notificacion-item:has(> .notificacion-enlace):hover { border-color: var(--accent); }
.notificacion-enlace { display: flex; flex-direction: column; gap: 2px; text-decoration: none; color: inherit; }
.notificacion-otros { margin: var(--space-1) 0 0; padding-left: var(--space-3); color: var(--ink-soft); }
.notificacion-otros li { margin-bottom: 2px; }

/* Menú de usuario (Fase 18) — reemplaza los botones sueltos de idioma
   y cerrar sesión que antes vivían directamente en la barra superior;
   ver templates/base.html. [x-cloak] evita el parpadeo del menú
   abierto un instante antes de que Alpine.js termine de inicializar
   (primer uso real de Alpine en el proyecto — cargado desde AD-25 pero
   sin ningún x-data hasta ahora). */
[x-cloak] { display: none !important; }
/* Fase 51 (parche posterior, hallazgo real de Rogelio en producción):
   el atributo nativo `hidden` (usado por el panel "Columnas" de
   `static/core/tablas.js`, vanilla JS sin Alpine) perdía contra
   cualquier clase de autor que fijara `display` (ej. `.button-menu-dropdown
   { display: flex }`) — la hoja de estilos del navegador que hace
   `[hidden]{display:none}` tiene MENOS prioridad que cualquier regla de
   esta hoja, sin importar el orden. El panel de columnas quedaba
   entonces SIEMPRE visible, sin importar `panel.hidden`. Mismo
   remedio que ya usa `[x-cloak]` arriba: forzarlo con `!important`. */
[hidden] { display: none !important; }
.user-menu { position: relative; }
.user-menu-trigger { border: none; cursor: pointer; padding: 0; }
.user-menu-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 20;
  min-width: 12rem; background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: var(--radius); box-shadow: var(--shadow-md); padding: 6px; display: flex;
  flex-direction: column; gap: 2px;
}
.user-menu-item {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: none; padding: 8px 10px; border-radius: var(--radius-sm);
  font: inherit; font-size: 13px; font-weight: 500; color: var(--ink); cursor: pointer;
  text-decoration: none; box-shadow: none;
}
.user-menu-item:hover { background: var(--border-soft); }
.user-menu-item svg { width: 15px; height: 15px; }
.user-menu-item--option { padding-left: 22px; font-weight: 400; }
.user-menu-item--option.active { color: var(--accent); font-weight: 600; }
.user-menu-section { display: flex; flex-direction: column; gap: 1px; }
.user-menu-label {
  padding: 8px 10px 2px; font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-faint);
}
.user-menu-divider { height: 1px; background: var(--border-soft); margin: 4px 2px; }

/* Fase 47: botón con menú desplegable genérico (mismo patrón Alpine.js
   de .brand-tag-switcher/.user-menu) — hoy solo lo usa "Exportar"
   (CSV/PDF), pero queda con nombre genérico para cualquier botón
   futuro que necesite un menú en vez de acciones sueltas. */
.button-menu { position: relative; display: inline-block; }
.button-menu-dropdown {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 20;
  min-width: 8rem; background: var(--bg-card); border: 1px solid var(--border-soft);
  border-radius: var(--radius); box-shadow: var(--shadow-md); padding: 6px; display: flex;
  flex-direction: column; gap: 2px;
}
.button-menu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 8px 10px; border-radius: var(--radius-sm); font: inherit; font-size: 13px;
  font-weight: 500; color: var(--ink); cursor: pointer; text-decoration: none; box-shadow: none;
}
.button-menu-item:hover { background: var(--border-soft); }

/* Fase 55, backlog del 17 sep 2026, punto 3 (18 sep 2026, historia):
   `.content` tenía `max-width: 68rem; margin: 0 auto` — antes de ese
   cambio quedaba pegado al borde izquierdo de `.main-col`, con toda la
   holgura siempre del lado derecho, así que contraer el panel
   IZQUIERDO sí "liberaba" espacio visible pero contraer el DERECHO no
   hacía nada (la holgura se sumaba a la ya existente a la derecha,
   nunca usada); `margin: 0 auto` repartía esa holgura a los dos
   lados, en vez de acumularla solo a la derecha.

   Pedido explícito de Rogelio (20 sep 2026): ya no quiere ningún tope
   fijo de ancho de lectura — que `.content` use TODO el ancho
   disponible de `.main-col`, dejando solo el margen mínimo de su
   propio padding, sin importar si eso viene de una pantalla ancha o
   de contraer sidebar/panel derecho. Se retira `max-width`/`margin:
   auto` por completo: sin un tope, `.content` ya crece a lo ancho de
   `.main-col` por el comportamiento por defecto de un hijo de un
   contenedor flex en columna (`align-items: stretch`, el valor por
   defecto que `.main-col` nunca sobreescribe) — el mismo mecanismo que
   antes solo se notaba al chocar contra el tope de 68rem. */
.content { flex: 1 1 auto; padding: 28px 28px 40px; }
/* Revisión v1.0 (V1-A11Y-03, 8 oct 2026): «Saltar al contenido» — fuera
   de la vista hasta que recibe el foco del teclado (primer Tab de la
   página); el destino, <main>, recibe el foco sin dibujar contorno. */
.saltar-contenido {
  position: absolute; left: 12px; top: -48px; z-index: 1000;
  padding: 8px 14px; border-radius: var(--radius-sm);
  background: var(--accent); color: #fff; font-weight: 600; text-decoration: none;
}
.saltar-contenido:focus { top: 12px; outline: 2px solid var(--accent-dark); outline-offset: 2px; }
.content:focus { outline: none; }

/* AI panel (AD-25) --------------------------------------------------
   Todo lo que la IA produce vive exclusivamente dentro de este panel
   (tono ámbar, siempre etiquetado "IA · ..."), separado a propósito
   del resto de la pantalla — nunca se mezcla en línea con un campo
   confirmado sin que la persona lo haya aceptado explícitamente. */
.ai-panel {
  width: var(--ai-panel-width); flex: 0 0 auto; background: var(--bg-card);
  border-left: 1px solid var(--border); display: flex; flex-direction: column;
  overflow: hidden;
}
html.aim-ai-panel-hidden .ai-panel {
  width: 0 !important; border-left: none;
}
.ai-head { padding: 18px 20px 14px; border-bottom: 1px solid var(--border-soft); }
.ai-head-top { display: flex; align-items: center; gap: 8px; }
.ai-head-top svg { color: var(--ai); }
.ai-head-top h2 { font-size: 15px; margin: 0; }
.ai-head .caption { font-size: 11.5px; color: var(--ink-faint); margin: 5px 0 0; line-height: 1.5; }
.ai-body { padding: 16px 20px; display: flex; flex-direction: column; gap: 14px; overflow: auto; }
.ai-msg { background: var(--ai-tint); border: 1px solid var(--ai-border); border-radius: var(--radius); padding: 13px 14px; }
.ai-msg .tag {
  display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 600;
  letter-spacing: .03em; text-transform: uppercase; color: var(--ai); margin-bottom: 7px;
}
.ai-msg p { margin: 0; font-size: 13px; color: var(--ink); line-height: 1.55; }
.ai-actions { display: flex; gap: 8px; margin-top: 11px; flex-wrap: wrap; }
.ai-btn {
  font-size: 12px; font-weight: 500; padding: 7px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--ai-border); color: var(--ai); background: #fff; cursor: pointer;
  font-family: inherit; text-decoration: none; display: inline-block;
}
.ai-btn.primary { background: var(--ai); color: #fff; border-color: var(--ai); }
.ai-empty { font-size: 12.5px; color: var(--ink-faint); }
/* Fase 22: texto de orientación de GuiaInstrumento, pasado por
   `linebreaks` — mismo tamaño/interlineado que `.ai-msg p` para que se
   vea coherente con el resto del panel; solo el primer párrafo pierde
   el margen superior porque `.ai-body` ya aporta separación con `gap`. */
.ai-guia-texto p { margin: 10px 0 0; font-size: 13px; color: var(--ink); line-height: 1.55; }
.ai-guia-texto p:first-child { margin-top: 0; }
.ai-foot { padding: 14px 20px 20px; border-top: 1px solid var(--border-soft); }

/* Pestañas del panel derecho (Fase 21b) ------------------------------
   Ver templates/core/_ai_panel_tabs.html y static/core/ai-panel-tabs.js.
   Misma arquitectura que la Fase 21a de arriba: la pestaña activa vive
   en el atributo data-ai-tab del elemento <html> — nunca en #ai-panel,
   que el intercambio HTMX fuera de banda reemplaza por completo al
   elegir otra fila (ver *_oob.html de cada instrumento) — así que el
   contenido nuevo que HTMX inserta ya nace mostrando la pestaña
   correcta por CSS puro, sin esperar a ningún listener de eventos. El
   botón resaltado como activo sigue la misma regla en vez de basarse
   en aria-selected: ese atributo viene "de fábrica" marcando siempre
   Comentarios en el marcado que sirve el servidor (que no sabe qué
   pestaña había elegido la persona) y solo se corrige por accesibilidad
   después del intercambio — ver ai-panel-tabs.js —, no para decidir el
   aspecto visual.

   Fase 49: se retiró la pestaña "Asistente IA" (contenido deprecado —
   la burbuja flotante de IA, Fase 39, ya cubre chat y "registros
   semejantes" vía el comando /semejantes; decisión explícita de
   Rogelio, 16 sep 2026). Con eso, ya no queda ninguna pestaña
   "especial" que justifique un color de acento propio (--ai) distinto
   del resto de la aplicación — las 4 que quedan usan --accent, el
   mismo que cualquier otro control activo ("heredan" el color del
   panel, no uno reservado para IA). Además, cada pestaña pasa de un
   botón corto y ancho a uno angosto y alto (analogía de Rogelio: una
   pestaña de carpeta/binder, no una lengüeta plana) — ocupa menos
   ancho para no competir con el contenido, pero es lo bastante alta
   para encontrarla a simple vista; al pasar el mouse, la etiqueta
   completa aparece como una superposición debajo del botón (nunca
   cambia el ancho del botón en sí, así ninguna pestaña vecina se
   desplaza). */
.ai-panel-tabs { display: flex; border-bottom: 1px solid var(--border-soft); padding: 0 6px; gap: 2px; }
/* Cambios de la v1 (1 oct 2026), decisión de Rogelio: nombre completo de
   cada pestaña en una sola línea (el corte con guion de `hyphens: auto`
   no está disponible en todos los navegadores y partía las palabras sin
   guion), las cuatro del registro se reparten el ancho del panel, la
   Guía queda angosta y separada al final, y contador en las que tienen
   filas (9 oct 2026: la Guía también ocupa una parte igual, ver abajo). Solo con el panel en su ancho mínimo se recorta con "…" (el
   `title` del botón conserva el nombre completo). Reemplaza la etiqueta
   recortada + superposición al pasar el mouse de la Fase 49. */
.ai-tab {
  flex: 1 1 0; min-width: 0; position: relative; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 4px; height: 52px; padding: 0 2px;
  border: none; border-bottom: 3px solid transparent; background: none;
  cursor: pointer; font-family: inherit; color: var(--ink-faint); box-shadow: none;
  transition: background .12s ease, color .12s ease;
}
/* 9 oct 2026, corrección de Rogelio: todas las pestañas se reparten el
   ancho por igual (con dos, 50 % cada una); antes la Guía tenía 40 px
   fijos y, en pantallas con pocas pestañas, quedaba desproporcionada.
   Conserva la separación del resto. */
.ai-tab--aparte { margin-left: 5px; }
.ai-tab--aparte::before {
  content: ""; position: absolute; left: -4px; top: 10px; bottom: 10px; border-left: 1px solid var(--border-soft);
}
.ai-tab svg { width: 17px; height: 17px; }
.ai-tab-icono { position: relative; display: inline-flex; }
.ai-tab-contador {
  position: absolute; top: -6px; left: 12px; min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 8px; background: var(--accent); color: #fff;
  font-size: 9.5px; font-weight: 700; line-height: 15px; text-align: center;
}
.ai-tab-contador[hidden] { display: none; }
.ai-tab-label {
  font-size: 9.5px; font-weight: 500; letter-spacing: -.005em; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ai-tab:hover, .ai-tab:focus-visible { color: var(--ink); background: var(--border-soft); }
.ai-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.ai-tab-panel { display: none; }
.ai-tab-panel[data-ai-tab-panel="comentarios"] { display: block; }
.ai-tab[data-ai-tab-button="comentarios"] { color: var(--accent); border-bottom-color: var(--accent); }

html[data-ai-tab="guia"] .ai-tab-panel { display: none; }
html[data-ai-tab="guia"] .ai-tab-panel[data-ai-tab-panel="guia"] { display: block; }
html[data-ai-tab="guia"] .ai-tab[data-ai-tab-button="comentarios"] { color: var(--ink-faint); border-bottom-color: transparent; }
html[data-ai-tab="guia"] .ai-tab[data-ai-tab-button="guia"] { color: var(--accent); border-bottom-color: var(--accent); }

html[data-ai-tab="archivos"] .ai-tab-panel { display: none; }
html[data-ai-tab="archivos"] .ai-tab-panel[data-ai-tab-panel="archivos"] { display: block; }
html[data-ai-tab="archivos"] .ai-tab[data-ai-tab-button="comentarios"] { color: var(--ink-faint); border-bottom-color: transparent; }
html[data-ai-tab="archivos"] .ai-tab[data-ai-tab-button="archivos"] { color: var(--accent); border-bottom-color: var(--accent); }

html[data-ai-tab="relacionados"] .ai-tab-panel { display: none; }
html[data-ai-tab="relacionados"] .ai-tab-panel[data-ai-tab-panel="relacionados"] { display: block; }
html[data-ai-tab="relacionados"] .ai-tab[data-ai-tab-button="comentarios"] { color: var(--ink-faint); border-bottom-color: transparent; }
html[data-ai-tab="relacionados"] .ai-tab[data-ai-tab-button="relacionados"] { color: var(--accent); border-bottom-color: var(--accent); }

/* Fase 59: pestaña "Trazabilidad" (core/_trazabilidad_lateral.html). */
html[data-ai-tab="trazabilidad"] .ai-tab-panel { display: none; }
html[data-ai-tab="trazabilidad"] .ai-tab-panel[data-ai-tab-panel="trazabilidad"] { display: block; }
html[data-ai-tab="trazabilidad"] .ai-tab[data-ai-tab-button="comentarios"] { color: var(--ink-faint); border-bottom-color: transparent; }
html[data-ai-tab="trazabilidad"] .ai-tab[data-ai-tab-button="trazabilidad"] { color: var(--accent); border-bottom-color: var(--accent); }

.traza-lateral { display: flex; flex-direction: column; gap: var(--space-2); }
.traza-lateral .caption { margin: 0; }
.traza-alcance { display: flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.traza-alcance-opcion {
  flex: 1 1 0; padding: 6px 8px; border: none; border-radius: 0; box-shadow: none; background: transparent;
  font: inherit; font-size: 12px; font-weight: 600; color: var(--ink-soft); cursor: pointer;
}
.traza-alcance-opcion + .traza-alcance-opcion { border-left: 1px solid var(--border); }
.traza-alcance-opcion[aria-pressed="true"] { background: var(--accent); color: #fff; }
.traza-filtro { width: 100%; }
.traza-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.traza-evento { border: 1px solid var(--border-soft); border-radius: var(--radius-sm); padding: 8px 10px; font-size: 12.5px; }
.traza-evento[hidden] { display: none; }
.traza-evento-cabeza { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.traza-evento-nombre { font-weight: 600; color: var(--ink); }
.traza-evento-fecha { font-size: 11px; color: var(--ink-faint); white-space: nowrap; }
.traza-evento-registro, .traza-evento-responsable { margin: 2px 0 0; color: var(--ink-soft); }
.traza-evento-responsable { font-size: 11.5px; }
.traza-evento .field-meta { margin: 4px 0 0; }
.traza-evento .diff-list { margin: 4px 0 0; padding-left: 16px; }
.traza-lateral [data-traza-ver-mas] { align-self: flex-start; }
.traza-enlace-it { font-size: 12px; }

/* Fase 59, pieza 3: filtros y paginación del registro completo de I-T
   (templates/trazabilidad/index.html). */
.traza-filtros {
  display: grid; gap: var(--space-2); align-items: end; margin-bottom: var(--space-2);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.traza-filtros .form-field { margin-bottom: 0; }
.traza-filtros input[type="date"], .traza-filtros input[type="search"] {
  width: 100%; padding: 0.6rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; color: inherit; background: var(--bg-card);
}
.traza-filtros-texto { grid-column: span 2; }
.traza-filtros-acciones { display: flex; gap: var(--space-1); }
.traza-paginacion { display: flex; align-items: center; justify-content: center; gap: var(--space-2); margin-top: var(--space-2); }
@media (max-width: 768px) { .traza-filtros-texto { grid-column: auto; } }

.section-heading {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-2); margin: 0 0 var(--space-3) 0;
}
.section-heading h2 {
  margin: 0; font-size: 1.05rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-soft); font-weight: 700; font-family: var(--font-ui);
}

/* Fase 60 (auditoría de diseño, 23 sep 2026), punto sobre títulos y
   subtítulos: recuadro del título/subtítulo al tope de cada módulo o
   instrumento (I-1 a I-9, Organización, Perfil de la institución) —
   pedido explícito de Rogelio para que "sobresalga como título", con
   un color de fondo propio y configurable (`PlatformSettings.
   module_header_bg_color`, ver `templates/base.html`). SIEMPRE
   visible: a propósito no es un `.accordion-section` — es la
   identidad del módulo, no contenido que tenga sentido recoger. */
.modulo-encabezado {
  background: var(--module-header-bg, var(--accent-tint));
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: var(--space-3) 1.5rem; margin-bottom: var(--space-3);
}
.modulo-encabezado h1 { margin: 0 0 4px; font-size: 24px; }
.modulo-encabezado p { margin: 0; color: var(--ink-faint); font-size: 13px; }
/* Página de catálogo (7 oct 2026): título a la izquierda, «Regresar» a la derecha. */
.modulo-encabezado--catalogo { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }

.status-card {
  /* Fase 60 (auditoría de diseño, 23 sep 2026): fondo configurable
     (`PlatformSettings.record_header_bg_color`) — pedido explícito de
     Rogelio de distinguir la tarjeta de título de UN registro
     particular de `.modulo-encabezado` de arriba, con un tono más
     claro que denote subordinación (mismo mecanismo de variable CSS,
     con `--bg-card` — blanco — como respaldo si no hay
     `platform_settings` en el contexto). */
  background: var(--record-header-bg, var(--bg-card)); border: 1px solid var(--border-soft); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); padding: var(--space-3) 1.5rem;
  /* Fase 60 (auditoría de diseño, 22 sep 2026), punto 4b: antes cada
     plantilla decidía a mano, con un `style="margin-bottom:..."`
     suelto, si dos tarjetas consecutivas quedaban separadas o casi
     pegadas — de ahí que algunas (p. ej. el formulario de verificación
     de Fase 56) se quedaran sin ningún margen. Con un margen por
     defecto en la clase, cualquier `.status-card` nueva ya sale bien
     espaciada sin acordarse de repetir el `style`; un `style` propio
     (p. ej. `margin:0` en una tarjeta dentro de una lista con su
     propio `gap`) lo sigue pudiendo pisar, la especificidad de un
     atributo `style` siempre gana. */
  margin-bottom: var(--space-3);
}
.status-card h2 { margin: 0 0 var(--space-1) 0; font-size: 1.1rem; }
.status-card h2:last-child { margin-bottom: 0; }
/* Tabla de solo lectura dentro de una tarjeta de contexto (funciones heredadas de I-5). */
.status-card .data-table:last-child { margin-bottom: 0; }
/* Fase 60 (auditoría de diseño, 22 sep 2026), punto 4a: título de
   sección de `core/_verificacion_actor.html` — existía el parámetro
   `titulo_seccion` desde la Fase 56, pero ningún instrumento lo pasaba
   todavía, así que nunca tuvo su propio estilo. */
.verificacion-actor__titulo { margin: 0 0 var(--space-2) 0; font-size: 1.05rem; font-weight: 600; }
.verificacion-actor { margin-bottom: var(--space-2); }
.verificacion-actor:last-child { margin-bottom: 0; }

main > section + section,
.content > section + section { margin-top: var(--space-4); }

/* Tarjetas de instrumento -------------------------------------------- */

.instrument-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--space-3);
}
.instrument-card {
  background: var(--bg-card); border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: var(--space-3); display: flex; flex-direction: column; gap: 0.6rem;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.instrument-card:has(a):hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.instrument-card--progress { border-color: var(--accent); }
.instrument-card--complete { border-color: var(--success); }
.instrument-card h3 { margin: 0; font-size: 1rem; font-family: var(--font-ui); font-weight: 600; }
.instrument-card h3 a { color: var(--ink); text-decoration: none; }
.instrument-card h3 a:hover { color: var(--accent); }
.instrument-card .instrument-status { font-size: 0.85rem; color: var(--ink-soft); margin: 0; }
.instrument-card--pending { background: transparent; box-shadow: none; border-style: dashed; }
.instrument-card--pending h3 { color: var(--ink-soft); }

.badge {
  display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.75rem; font-weight: 700;
  padding: 0.15rem 0.6rem; border-radius: 999px; width: fit-content;
}
.badge--progress { color: var(--accent); background: var(--accent-tint); }
.badge--complete { color: var(--success); background: var(--success-tint); }
.badge--empty { color: var(--ink-soft); background: var(--border-soft); }
.badge--warning { color: var(--warn); background: var(--warn-tint); }
.badge--danger { color: var(--danger); background: var(--danger-tint); }

.progress-bar { height: 0.4rem; border-radius: 999px; background: var(--border-soft); overflow: hidden; }
.progress-bar__fill { height: 100%; background: var(--accent); border-radius: 999px; }
.instrument-card--complete .progress-bar__fill { background: var(--success); }

/* Kanban de hitos (Fase 41, segunda pieza) --------------------------- */

.kanban-board {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--space-3);
  align-items: start;
}
.kanban-columna {
  background: var(--bg-card); border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: var(--space-2); display: flex; flex-direction: column; gap: 0.6rem;
}
.kanban-columna__titulo {
  margin: 0; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--ink-soft); font-weight: 700; font-family: var(--font-ui);
}
.kanban-columna__contador { font-weight: 400; text-transform: none; letter-spacing: normal; }
.kanban-columna__vacio { font-size: 0.8rem; color: var(--ink-faint); margin: 0; }
.kanban-tarjeta {
  display: block; background: var(--bg); border: 1px solid var(--border-soft); border-radius: 6px;
  padding: 0.6rem 0.7rem; text-decoration: none; color: var(--ink);
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
a.kanban-tarjeta:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.kanban-tarjeta__titulo { margin: 0 0 0.25rem; font-size: 0.9rem; font-weight: 600; }
.kanban-tarjeta__meta { margin: 0; font-size: 0.78rem; color: var(--ink-soft); }
.kanban-columna--cumplido { border-color: var(--success); }
.kanban-columna--retrasado { border-color: var(--warn); }
.kanban-columna--cancelado { border-color: var(--danger); }

/* Gantt de hitos (Fase 41, tercera pieza) ---------------------------- */

.gantt-encabezado {
  display: flex; justify-content: space-between; font-size: 0.78rem; color: var(--ink-soft);
  margin-bottom: 0.4rem;
}
.gantt-plan { margin-bottom: var(--space-3); }
.gantt-plan__titulo { margin: 0 0 0.5rem; font-size: 0.95rem; font-weight: 600; }
.gantt-plan__titulo a { color: var(--ink); }
.gantt-fila { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.35rem; }
.gantt-fila__etiqueta {
  width: 12rem; flex: 0 0 auto; font-size: 0.82rem; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.gantt-fila__pista { position: relative; flex: 1 1 auto; height: 1.3rem; background: var(--border-soft); border-radius: 4px; }
.gantt-barra {
  position: absolute; top: 0; height: 100%; min-width: 3px; border-radius: 4px;
  background: var(--accent);
}
.gantt-barra--cumplido { background: var(--success); }
.gantt-barra--retrasado { background: var(--warn); }
.gantt-barra--cancelado { background: var(--danger); opacity: 0.55; }
.gantt-punto-hito {
  position: absolute; top: 50%; width: 0.7rem; height: 0.7rem; border-radius: 50%;
  background: var(--accent); transform: translate(-50%, -50%);
}
.gantt-punto-hito--cumplido { background: var(--success); }
.gantt-punto-hito--retrasado { background: var(--warn); }
.gantt-punto-hito--cancelado { background: var(--danger); opacity: 0.55; }
.gantt-marcador {
  position: absolute; top: -0.35rem; width: 0; height: 0; transform: translateX(-50%);
  border-left: 0.35rem solid transparent; border-right: 0.35rem solid transparent;
  border-bottom: 0.55rem solid var(--ink-soft);
}
.gantt-marcador--realizado { border-bottom-color: var(--success); }
.gantt-vacio { font-size: 0.85rem; color: var(--ink-faint); }

/* Pie (páginas sin institución activa) ------------------------------ */

.site-header { background: var(--bg-card); padding: var(--space-3) 1.5rem; border-bottom: 1px solid var(--border-soft); }
.site-header__row {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
  gap: var(--space-2); max-width: 68rem; margin: 0 auto;
}
.site-header h1 { margin: 0 0 0.15rem 0; font-size: 1.4rem; }
.site-header h1 a { color: var(--accent-dark); text-decoration: none; }
.tagline { margin: 0; color: var(--ink-soft); max-width: 38rem; font-size: 0.875rem; }
.site-nav { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); font-size: 0.875rem; padding-top: 0.2rem; }
.site-nav a { color: var(--ink); text-decoration: none; font-weight: 600; }
.site-nav a:hover { color: var(--accent); }
/* Revisión v1.0 (V1-A11Y-01): «Iniciar sesión» es un botón primario dentro de
   .site-nav — conserva su texto blanco (antes oscuro sobre el fondo, 2,3:1). */
.site-nav a.button, .site-nav a.button:hover { color: #fff; }
.site-nav__logout { display: inline; }
.language-switcher select {
  font: inherit; font-size: 0.85rem; padding: 0.3rem 0.5rem; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-card); color: inherit;
}
.site-footer {
  margin-top: var(--space-4); padding: var(--space-3) 1.5rem; color: var(--ink-faint);
  font-size: 0.8rem; border-top: 1px solid var(--border-soft); text-align: center;
}

/* Home — portada y tarjetas de acceso (Fase 10) ------------------ */

.home-portada { margin-bottom: var(--space-4); display: grid; grid-template-columns: minmax(0, 1fr); }
.home-portada > * { grid-column: 1 / -1; }
.home-portada > .portada-hero { grid-row: 1; }
.home-portada > .portada-acceso { grid-row: 2; }
/* Tarjeta de solicitar acceso: con el azul de la marca, para que destaque
   dentro de la presentación (22 oct 2026, Rogelio). */
.portada-acceso {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); margin-top: var(--space-2);
  padding: var(--space-4) var(--space-3); border-radius: var(--radius); background: var(--sidebar-bg); color: #fff; box-shadow: var(--shadow-md);
}
.portada-acceso h2 { margin: 0; font-family: var(--font-heading); font-size: 1.45rem; line-height: 1.2; color: #fff; }
.portada-acceso p { margin: 0; font-size: 15px; line-height: 1.55; color: var(--sidebar-ink); }
.portada-acceso .button { background: #fff; color: var(--sidebar-bg); border-color: #fff; font-size: 15px; padding: 0.7rem 1.2rem; }
.portada-acceso .button:hover { background: var(--accent-tint); color: var(--sidebar-bg); }
/* En escritorio, la tarjeta va dentro de la presentación, a la derecha. */
@media (min-width: 900px) {
  .home-portada:has(> .portada-hero) { grid-template-columns: minmax(0, 1fr) 23rem; }
  .home-portada > .portada-hero { grid-row: 1; padding-right: calc(23rem + 2 * var(--space-4)); }
  .home-portada:has(> .portada-hero) > .portada-acceso { grid-column: 2; grid-row: 1; align-self: center; margin: 0 var(--space-4) 0 0; }
}

/* Portada pública (22 oct 2026, rediseño pedido por Rogelio). El contenido
   es HTML de la plataforma (PlatformSettings.home_portada_html_*, core 0094):
   solo usa estas clases, nunca `style=""`. */
.portada-hero {
  padding: var(--space-4) var(--space-4) var(--space-3); border-radius: var(--radius);
  background: linear-gradient(135deg, var(--accent-tint) 0%, var(--bg-card) 70%); border: 1px solid var(--border-soft);
}
.portada-hero__antetitulo { margin: 0 0 var(--space-1); font-size: 12.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
.portada-hero h1 { margin: 0 0 var(--space-2); max-width: 40rem; font-family: var(--font-heading); font-size: clamp(1.6rem, 1.1rem + 2vw, 2.4rem); line-height: 1.15; color: var(--ink); }
.portada-hero__entrada { margin: 0; max-width: 44rem; font-size: 1.05rem; line-height: 1.6; color: var(--ink-soft); }
.portada-seccion { margin-top: var(--space-4); }
/* Títulos de sección en burbuja, para darles más peso (22 oct 2026, Rogelio). */
.portada-seccion > h2 {
  display: inline-block; margin: 0 0 var(--space-2); padding: 0.35rem 1.1rem; border-radius: 999px;
  background: var(--sidebar-bg); color: #fff; font-family: var(--font-heading); font-size: 1.45rem; line-height: 1.3;
}
.portada-seccion > p { margin: 0 0 var(--space-3); color: var(--ink-soft); font-size: 14.5px; line-height: 1.55; }
/* Herramientas de la aplicación: cuatro por fila en escritorio. */
.portada-herramientas { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
@media (min-width: 560px) { .portada-herramientas { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .portada-herramientas { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.portada-herramienta {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; align-content: start; column-gap: var(--space-2);
  padding: var(--space-3); border-radius: var(--radius); background: var(--bg-card); border: 1px solid var(--border-soft);
}
.portada-herramienta svg {
  width: 2.5rem; height: 2.5rem; padding: 0.55rem; border-radius: 50%; box-sizing: border-box;
  background: var(--accent-tint); color: var(--sidebar-bg); fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.portada-herramienta h3 { margin: 0; font-size: 15px; color: var(--ink); }
.portada-herramienta p { grid-column: 1 / -1; margin: var(--space-2) 0 0; font-size: 13px; line-height: 1.5; color: var(--ink-soft); }
.portada-etapas { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: var(--space-3); counter-reset: etapa; }
/* El número y el ícono van a la izquierda del título (22 oct 2026, Rogelio). */
.portada-etapa {
  position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; column-gap: var(--space-2);
  align-content: start; padding: var(--space-3); border-radius: var(--radius); background: var(--bg-card); border: 1px solid var(--border-soft);
  counter-increment: etapa;
}
.portada-etapa::before {
  content: counter(etapa); display: inline-flex; align-items: center; justify-content: center; width: 1.8rem; height: 1.8rem;
  border-radius: 50%; background: var(--accent); color: #fff; font-weight: 600; font-size: 14px;
}
.portada-etapa h3 { margin: 0; font-size: 15px; color: var(--ink); }
.portada-etapa--transversal { background: var(--accent-tint); border-color: var(--accent-tint); }
.portada-etapa--transversal::before { background: var(--accent-dark); }
.portada-etapa p { grid-column: 1 / -1; margin: var(--space-2) 0 0; font-size: 13px; line-height: 1.5; color: var(--ink-soft); }
.portada-etapa ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.portada-etapa li { font-size: 13px; color: var(--ink); }
.portada-etapa li b { display: inline-block; min-width: 2.4rem; color: var(--accent-dark); font-weight: 600; }
.portada-seccion > .portada-traza {
  max-width: none; margin: var(--space-2) 0 0; color: #fff; padding: var(--space-2) var(--space-3); border-radius: var(--radius);
  background: var(--accent-dark); color: #fff; font-size: 13.5px; line-height: 1.5;
}
.portada-traza b { color: #fff; }
.portada-principios { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--space-2) var(--space-3); }
@media (min-width: 900px) { .portada-principios { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.portada-principios li { padding: var(--space-2) 0 var(--space-2) var(--space-3); border-left: 3px solid var(--accent); }
.portada-principios h3 { margin: 0 0 4px; font-size: 14.5px; color: var(--ink); }
.portada-principios p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-soft); }
@media (max-width: 640px) {
  .portada-hero { padding: var(--space-3); }
}

/* Pie de página de la plataforma (PlatformSettings.footer_html_*). */
.pie-plataforma { max-width: 52rem; margin: 0 auto; }
.pie-plataforma p { margin: 0 0 4px; line-height: 1.5; }
.pie-plataforma__marca { font-family: var(--font-heading); font-size: 1rem; color: var(--ink); }
.pie-plataforma__marca em { font-family: var(--font-ui); font-size: 0.8rem; color: var(--ink-soft); }
.pie-plataforma p.pie-plataforma__derechos { margin-top: var(--space-2); }
.site-footer .pie-plataforma a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }

.access-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-3); margin-top: var(--space-3); }

/* Columnas de tabla ajustables (Fase 10) — el asa de redimensionar
   aparece en la esquina inferior derecha del contenido de la celda;
   arrastrarla ensancha visualmente la columna entera porque el <td>
   contenedor crece con su contenido (table-layout por defecto). */
.col-resizable {
  resize: horizontal; overflow: auto; display: block;
  min-width: 6em; max-width: 70vw; white-space: normal; word-break: break-word;
}

/* Formularios --------------------------------------------------- */

/* Convenciones de diseño visual, documento maestro (26 sep 2026), D2:
   una sola escala de espaciado entre campos — 12px (`--space-2`), y 0
   en el último campo de su bloque (el contenedor ya pone su propio
   relleno/margen). Antes, ~190 campos pisaban este margen a mano con
   `style="margin-bottom:10px|8px|0"`, cada plantilla con su criterio. */
.form-field { margin-bottom: var(--space-2); }
.form-field:last-child { margin-bottom: 0; }
.form-field label,
.form-field__titulo,
.accordion-section-body .form-field__titulo { display: block; font-weight: 600; margin: 0 0 0.35rem; font-size: 0.9rem; color: var(--ink); }
/* D5: título de un bloque que no es un campo con `<label for>` propio
   ("Actores afectados", "Tareas", "Puestos"…) — se ve exactamente igual
   que la etiqueta de un campo. Antes: `<p class="field-meta"
   style="font-weight:600">`, más chico y gris que cualquier `label`. */
/* Casilla + etiqueta en una misma línea. */
.form-field--casilla { display: flex; align-items: center; gap: var(--space-1); }
.form-field--casilla input[type="checkbox"] { width: auto; margin: 0; }
.form-field--casilla label { margin: 0; }
/* Fila de campos en columnas dentro de un formulario — distinta de
   `.instrument-grid` (la grilla de tarjetas del Home/Tablero/I-T, con
   columnas automáticas y hueco de 20px), que antes se reutilizaba
   aquí con `style="margin-bottom:10px"` a mano. Las columnas de cada
   fila siguen en `style="grid-template-columns:…"`: es disposición
   propia de cada formulario, no espaciado. */
.campos-grid { display: grid; gap: var(--space-2) var(--space-3); margin-bottom: var(--space-2); }
.campos-grid:last-child { margin-bottom: 0; }
.campos-grid--ancho-angosto { grid-template-columns: 2fr 1fr; }
.campos-grid--mitades { grid-template-columns: 1fr 1fr; }
.campos-grid > .form-field, .campos-grid > * > .form-field:last-child { margin-bottom: 0; }
/* Fila horizontal de campos (Código + Nombre en I-3 a I-5, agregar
   una relación en I-2…). Antes, cada `<form>` repetía a mano
   `style="display:flex;align-items:flex-end;gap:8px;flex-wrap:wrap"` y
   cada campo `margin-bottom:0`. `--separada`: línea divisoria debajo,
   cuando la fila comparte sección con más contenido (Nombre en I-3/I-4). */
.fila-campos { display: flex; align-items: flex-end; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-2); }
.fila-campos:last-child { margin-bottom: 0; }
.fila-campos > .form-field { margin-bottom: 0; flex: 1 1 12rem; }
.fila-campos > .form-field--codigo { flex: 0 0 8rem; }
/* Casilla dentro de una fila de campos: ancho natural y alineada con la base de los inputs. */
.fila-campos > .form-field--casilla { flex: 0 0 auto; padding-bottom: 0.65rem; }
/* Fila compacta de controles (solicitud de acceso, estructura de un ambiente). */
.fila-compacta { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.fila-compacta--arriba { align-items: flex-start; }
.fila-compacta__texto { flex: 1 1 12rem; }
.fila-campos--separada { margin-bottom: var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border-soft); }
/* Lista de relaciones editables (procesos prerrequisitos/relacionados
   de I-2): un elemento por línea, con su botón "Quitar" compacto. */
.lista-relaciones { margin: 0 0 var(--space-2); padding-left: 1.2em; font-size: 13.5px; }
.lista-relaciones li + li { margin-top: var(--space-1); }
.form-en-linea { display: inline; }
/* Botón compacto (notificaciones, filas de I-2). `.button.button--compacto`
   además de la clase sola: un `<summary class="button …">` (el
   "Rechazar" de la campanita) recibía el relleno grande de `.button`,
   declarada más abajo, y quedaba el doble de alto que sus vecinos
   (hallazgo de Rogelio en el móvil, 27 sep 2026). Borde transparente
   en el primario para que mida lo mismo que el secundario y el de
   peligro, que sí tienen borde. */
.button--compacto, .button.button--compacto {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 28px; padding: 3px 10px; font-size: 11.5px; font-weight: 600; line-height: 1.2;
  border: 1px solid transparent; box-shadow: none;
}
.button--compacto.button--secundario { border-color: var(--border); }
.button--compacto.button--peligro { border-color: var(--danger); }
/* Estado vacío de una sección seguido de su formulario de alta. */
p.field-meta + .fila-campos { margin-top: var(--space-2); }
/* Valor de solo lectura bajo un `.form-field__titulo` (vista de un
   registro sin permiso de edición, p. ej. detalle de Actor de I-2). */
.accordion-section-body p.dato-lectura, p.dato-lectura { margin: 0 0 var(--space-2); font-size: 13.5px; color: var(--ink); }
.accordion-section-body p.dato-lectura:last-child, p.dato-lectura:last-child { margin-bottom: 0; }
/* Segunda línea de la tarjeta de título con insignias de estado. */
.status-card__insignias { margin: var(--space-1) 0 0; }
/* Fila dinámica repetible (criterio de I-6, condición crítica de I-7,
   hito/tarea/indicador/punto de revisión de I-9): recuadro de borde
   suave — antes cada plantilla lo escribía a mano con radios/rellenos
   distintos (6px/8px, 8px/10px). La tarea, anidada dentro de un hito,
   usa el mismo recuadro sin sombra adicional. */
.fila-dinamica { border: 1px solid var(--border-soft); border-radius: var(--radius-sm); padding: var(--space-2); margin-bottom: var(--space-2); background: var(--bg-card); }
/* Sub-sección dentro de una sección del acordeón ("Tareas" de un hito,
   "Nuevo registro de seguimiento"): línea divisoria + título con el
   mismo estilo que el título de un campo. */
.subseccion { margin: var(--space-3) 0 var(--space-2); padding-top: var(--space-3); border-top: 1px solid var(--border-soft); }
.subseccion:last-child { margin-bottom: 0; }
.subseccion:first-child { margin-top: 0; padding-top: 0; border-top: none; }

/* Fase 60 (auditoría de diseño, 23 sep 2026), pedido explícito de
   Rogelio: ayuda contextual junto al título de un campo — sustituye
   la convención anterior de una nota entre paréntesis impresa debajo
   del campo. Ver core/_ayuda_campo.html y static/core/ayuda-campo.js
   (solo el clic/tap necesita JS; hover y foco de teclado los resuelve
   el CSS de abajo). `display: inline-flex` en el `<span>` envolvente
   para que quede en la misma línea que el texto del `<label>` que lo
   precede, sin heredar el `display:block` de `.form-field label`. */
.ayuda-campo { position: relative; display: inline-flex; vertical-align: middle; margin-left: 4px; }
.ayuda-campo__boton {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; width: 24px; height: 24px; max-width: none; margin: -4.5px; padding: 0; border: none; border-radius: 50%;
  background: none; color: var(--ink-faint); cursor: pointer; box-shadow: none;
}
/* Revisión v1.0 (V1-A11Y-03/04, 8 oct 2026): el círculo se sigue viendo
   de 15 px y en el mismo lugar (margen negativo), pero el botón que
   recibe el clic o el toque mide 24 × 24 (WCAG 2.5.8); y el foco de
   teclado se ve con un contorno, no solo con un cambio de color
   (WCAG 2.4.7). */
.ayuda-campo__boton > span {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border: 1px solid currentColor; border-radius: 50%;
  font-size: 10px; line-height: 1; font-weight: 700;
}
.ayuda-campo__boton:hover, .ayuda-campo__boton:focus-visible { color: var(--accent); background: none; }
.ayuda-campo__boton:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
summary:focus-visible .ayuda-campo__boton { color: var(--accent); }
/* Fase 60, hallazgo real de la revisión de código independiente: con
   `display: none` (el estado "cerrado" original), un lector de
   pantalla no anuncia el texto de `aria-describedby` del botón — un
   elemento con `display:none` sale por completo del árbol de
   accesibilidad, sin importar el atributo que lo describa. `opacity:
   0` + `pointer-events: none` deja el popover fuera de la vista y sin
   poder recibir clics mientras está "cerrado", pero SIGUE en el árbol
   de accesibilidad — así `aria-describedby` (ver core/_ayuda_campo.html)
   sí lo anuncia al enfocar el botón, con foco de teclado o sin él. */
/* QA de la v1 (30 sep 2026): cerrado, además, `scale(0)` — un popover
   solo transparente seguía ocupando su caja y, cerca del borde derecho,
   ensanchaba la página en el celular (desplazamiento horizontal). El
   ancho máximo nunca pasa del de la pantalla, y ayuda-campo.js lo
   corre hacia la izquierda si al abrirse no cabe. */
.ayuda-campo__popover {
  position: absolute; z-index: 30; left: 0; top: calc(100% + 6px);
  width: max-content; max-width: min(20rem, calc(100vw - 32px)); background: var(--ink); color: #fff;
  font-size: 12px; font-weight: 400; line-height: 1.4; text-align: left;
  padding: 8px 10px; border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
  opacity: 0; pointer-events: none; transition: opacity .1s ease;
  transform: scale(0); transform-origin: top left;
}
.ayuda-campo__boton:hover + .ayuda-campo__popover,
.ayuda-campo__boton:focus-visible + .ayuda-campo__popover,
summary:focus-visible .ayuda-campo__popover,
.ayuda-campo--activo > .ayuda-campo__popover { opacity: 1; pointer-events: auto; transform: none; }
/* WCAG 1.4.13 (revisión v1.0): Escape oculta también el popover abierto por
   foco o por el ratón, hasta que el foco o el puntero se mueva. */
.ayuda-campo.ayuda-campo--descartado > .ayuda-campo__popover { opacity: 0; pointer-events: none; transform: scale(0); }
.form-field .help-text { display: block; color: var(--ink-faint); font-size: 0.82rem; margin-top: 0.3rem; }
.form-field input[type="text"], .form-field input[type="email"], .form-field input[type="tel"],
.form-field input[type="url"], .form-field input[type="password"], .form-field textarea, .form-field select {
  width: 100%; padding: 0.6rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; color: inherit; background: var(--bg-card);
}
.form-field input:focus, .form-field textarea:focus, .form-field select:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
/* Campo fijo (p. ej. el Código de I-3 a I-5): se lee, no se edita. */
.form-field input:disabled { background: var(--bg); color: var(--ink-soft); cursor: default; }
.form-field ul { list-style: none; margin: 0; padding: 0; }
.form-field .errorlist { color: var(--danger); font-size: 0.85rem; margin: 0.25rem 0 0 0; padding: 0; list-style: none; }

/* Fase 52: campo de texto estructurado reutilizable `structured_list`
   (core/_structured_list.html, static/core/structured_list.js) — cada
   idea confirmada con Enter es una tarjeta propia, arrastrable para
   reordenar, editable y eliminable por separado. */
/* Recorrido de la v1 (30 sep 2026): lista obligatoria en un alta —
   asterisco en la etiqueta del campo y aviso si se intenta enviar vacía
   (ver templates/core/_structured_list.html, `requerido`). */
.form-field:has(> [data-structured-list][data-requerido]) > label::after { content: " *"; color: var(--danger); }
/* Selectores reforzados a propósito: `.accordion-section-body p` y las
   reglas de borde de los inputs de formulario les ganaban. */
.structured-list p.structured-list__error { color: var(--danger); font-size: 12.5px; margin: var(--space-1) 0 0; }
.structured-list.structured-list--error input.structured-list__nuevo { border-color: var(--danger); }
.structured-list__items {
  list-style: none; margin: 0 0 var(--space-1) 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.4rem;
}
.structured-list__item {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer;
  padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  /* Fase 55 (18 sep 2026): "burbuja" con fondo azulado muy transparente,
     pedido explícito de Rogelio — un rgba() a partir de --accent en vez
     de --accent-tint (opaco), para que se lea como una superposición
     tenue y no como un color plano nuevo. */
  background: rgba(61, 75, 148, 0.06);
}
.structured-list__item:hover { background: rgba(61, 75, 148, 0.1); }
.structured-list__item--arrastrando { opacity: 0.4; }
.structured-list__handle {
  display: flex; cursor: grab; color: var(--ink-faint); flex: 0 0 auto;
}
.structured-list__handle .icon { width: 16px; height: 16px; }
.structured-list__texto { flex: 1 1 auto; min-width: 0; word-break: break-word; }
.structured-list__edicion {
  flex: 1 1 auto; min-width: 0; padding: 0.3rem 0.5rem;
  border: 1px solid var(--accent); border-radius: var(--radius-sm); font: inherit; color: inherit;
  background: var(--bg-card); cursor: text;
}
.structured-list__accion {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; border: none; background: transparent;
  color: var(--ink-faint); cursor: pointer; border-radius: var(--radius-sm);
}
.structured-list__accion:hover { background: var(--accent-tint); color: var(--accent-dark); }
.structured-list__accion .icon { width: 15px; height: 15px; }
.structured-list__nuevo {
  /* Fase 55 (18 sep 2026): más bajo a propósito — cada Enter confirma
     una idea suelta, así que debe sentirse como un campo de captura
     rápida, no como un bloque de texto largo (antes casi parecía un
     <textarea> por el padding vertical). */
  width: 100%; padding: 0.35rem 0.6rem; border: 1px dashed var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: 0.85rem; color: inherit; background: var(--bg-card);
}
.structured-list__nuevo:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Fase 55, backlog del 17 sep 2026, punto 4 (18 sep 2026): doble lista
   arrastrable de "Conceder acceso" (core/_panel_acceso_recurso.html,
   static/core/acceso-doble-lista.js) — dos columnas conectadas por
   SortableJS con el mismo `group`, más un botón "Añadir"/"Quitar" por
   ítem como alternativa accesible sin arrastrar. Mismo lenguaje visual
   que `.structured-list__*` (Fase 52) a propósito — dos widgets con la
   misma idea de "tarjeta arrastrable", conviene que se vean parecidos. */
.acceso-doble-lista {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);
}
@media (max-width: 640px) {
  .acceso-doble-lista { grid-template-columns: 1fr; }
}
.acceso-doble-lista__lista {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.4rem;
  min-height: 2.5rem; /* zona de destino visible aun con la lista vacía */
}
.acceso-doble-lista__item {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: rgba(61, 75, 148, 0.06);
}
.acceso-doble-lista__item:hover { background: rgba(61, 75, 148, 0.1); }
.acceso-doble-lista__handle { display: flex; cursor: grab; color: var(--ink-faint); flex: 0 0 auto; }
.acceso-doble-lista__nombre { flex: 1 1 auto; min-width: 0; word-break: break-word; font-size: 13px; }
/* Elemento heredado o bloqueado (autoridad por unidad/comité en "Con
   acceso concedido", actores heredados de I-2…): convención de Rogelio
   (3 oct 2026) — el MISMO aspecto que cualquier otro elegido; lo único que
   cambia es que no lleva el ícono de arrastrar ni la ✕ de quitar (y su
   línea de motivo dice de dónde viene). Antes, fondo gris y borde
   punteado. */
.acceso-doble-lista__item--heredado { cursor: default; flex-wrap: wrap; }
.acceso-doble-lista__item--heredado .acceso-doble-lista__nombre { flex: 1 1 auto; font-weight: 500; }
.acceso-doble-lista__motivo { flex: 1 1 100%; font-size: 11.5px; color: var(--ink-faint); }
.acceso-doble-lista__boton {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; border: none; background: transparent;
  color: var(--ink-faint); cursor: pointer; border-radius: var(--radius-sm); font-size: 13px;
}
.acceso-doble-lista__boton:hover { background: var(--accent-tint); color: var(--accent-dark); }
.acceso-doble-lista__vacio { font-size: 12.5px; color: var(--ink-faint); padding: 0.5rem 0.1rem; }
.acceso-doble-lista__filtro {
  width: 100%; padding: 0.35rem 0.6rem; margin-bottom: 6px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); font: inherit; font-size: 0.85rem;
  color: inherit; background: var(--bg-card);
}
.acceso-doble-lista__filtro:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.acceso-doble-lista__item.sortable-ghost { opacity: 0.4; }
.acceso-doble-lista__item.sortable-chosen { border-color: var(--accent); }
.acceso-doble-lista[data-acceso-pendiente] { opacity: 0.6; pointer-events: none; }
/* Cierre de la v1 (30 sep 2026): la misma doble lista como campo de un
   formulario (core/_doble_lista.html, static/core/doble-lista.js). Las
   listas largas (procesos, actores de una institución grande) se
   desplazan dentro de su columna en vez de alargar la ficha. */
.doble-lista .acceso-doble-lista__lista { max-height: 16rem; overflow-y: auto; padding-right: 2px; }
.doble-lista .doble-lista__titulo {
  display: block; margin: 0 0 var(--space-1); font-size: 13px; font-weight: 600; color: var(--ink-soft);
}
/* Ajustes de Rogelio (1 oct 2026): doble lista con búsqueda en el servidor
   (core/_doble_lista_buscable.html) — cada opción es una tarjeta de tres
   líneas (persona: nombre / puesto / unidad; proceso: unidad / tipo /
   nombre), la línea del nombre resaltada; con dos cajas (Responsables y
   Miembros), un botón con texto por caja. */
.tarjeta-opcion { align-items: flex-start; }
.tarjeta-opcion .acceso-doble-lista__handle { padding-top: 2px; }
.tarjeta-opcion__lineas { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.tarjeta-opcion__principal { font-size: 13px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.tarjeta-opcion__linea { font-size: 11.5px; color: var(--ink-faint); overflow-wrap: anywhere; }
.tarjeta-opcion__acciones { display: flex; flex-direction: column; gap: 4px; flex: 0 0 auto; }
.acceso-doble-lista__boton--texto { width: auto; padding: 0 var(--space-1); font-size: 11.5px; white-space: nowrap; }
.doble-lista--buscable .acceso-doble-lista__lista { max-height: 22rem; }
.doble-lista .doble-lista__titulo--segunda { margin-top: var(--space-3); }
.doble-lista__mas { list-style: none; display: flex; justify-content: center; padding: var(--space-1) 0; }

/* Fase 21c: sistema de botones estandarizado (pedido explícito de
   Rogelio, 8 sep 2026) — cuatro estilos, mismo aspecto y misma
   ubicación relativa en cualquier pantalla:
     - primario  (button, .button): la acción afirmativa principal de
       un formulario o tarjeta — Guardar, Aprobar, "+ Nuevo".
     - .button--secundario: acciones neutras que acompañan a la
       principal — Restablecer, Cancelar, Rechazar.
     - .button--terciario: acciones de bajo peso visual, en línea con
       texto — Expandir/Recoger. Ya no reutiliza el azul de los
       enlaces (--accent) en reposo, para que no compita con el texto
       de párrafo ni con la acción primaria; gana un fondo suave al
       pasar el cursor en vez de solo oscurecer el texto.
     - .button--peligro: acciones destructivas o de baja reversión —
       Archivar. Antes era un `style` suelto en I-2; ahora es clase
       reutilizable. */
/* Ajustes de Rogelio (1 oct 2026): ningún botón de la aplicación parte su
   texto en dos líneas (documento maestro, 2.2). Si una fila no alcanza, se
   reparten los botones (`.fila-acciones` usa flex-wrap), nunca el texto;
   solo un botón más ancho que su contenedor entero se recorta con "…". */
button, .button {
  display: inline-block; background: var(--accent); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 0.65rem 1.35rem; font: inherit; font-weight: 700; font-size: 0.9rem; cursor: pointer; box-shadow: var(--shadow-sm);
  transition: background .12s ease;
  white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
/* El avatar es un <button>: su punto de disponibilidad sobresale a propósito. */
.user-menu-trigger { overflow: visible; }
button:hover, .button:hover { background: var(--accent-hover-suave); }
/* Enlace con aspecto de botón (navega a otra página, p. ej. Kanban/Gantt agregados de I-9). */
a.button { text-decoration: none; line-height: normal; }
a.button:hover { color: #fff; }
a.button--secundario:hover { color: var(--ink); }
.button--secundario { background: var(--bg-card); color: var(--ink); border: 1px solid var(--border); box-shadow: none; }
.button--secundario:hover { background: var(--border-soft); }
/* .button--terciario se define junto con .accordion-toolbar button,
   más abajo — comparten exactamente el mismo estilo. */
.button--peligro {
  background: var(--bg-card); color: var(--danger); border: 1px solid var(--danger); box-shadow: none;
  transition: background .12s ease;
}
.button--peligro:hover { background: var(--danger-tint); }
/* Fase 60 (auditoría de diseño, 22 sep 2026), punto 1: convención de
   fila de botones — dos o más acciones/controles nunca van apilados
   sin espacio entre ellos; siempre en una fila horizontal (con wrap en
   pantallas angostas). `.boton-guardar-proponer` ya no trae su propio
   margin-top: ahora vive siempre dentro de un `.fila-acciones`
   (`_accion_guardar_proponer.html` con `id_formulario`, junto a
   "Exportar"/"Ver grafo"), que es quien decide el espacio respecto al
   contenido de arriba. */
.fila-acciones { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-3); margin-bottom: var(--space-3); }
/* Formulario angosto de pocos campos (Perfil de la institución). */
.form-angosto { max-width: 32rem; }
.boton-guardar-proponer { margin-top: 0; }

/* Fase 55 (18 sep 2026): modal de "acción final" guardar/proponer —
   primer modal real de la aplicación (ver
   core/_accion_guardar_proponer.html, static/core/
   accion-guardar-proponer.js). */
.accion-guardar-modal {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: var(--space-3);
}
.accion-guardar-modal__fondo { position: absolute; inset: 0; background: rgba(15, 23, 42, 0.45); }
.accion-guardar-modal__caja {
  position: relative; background: var(--bg-card); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: var(--space-3) 1.5rem; max-width: 34rem; width: 100%;
}
.accion-guardar-modal__caja:focus { outline: none; }
.accion-guardar-opcion {
  display: flex; gap: 10px; align-items: flex-start; padding: 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); margin-bottom: var(--space-1); cursor: pointer; font-weight: 400;
}
.accion-guardar-opcion:has(input:checked) { border-color: var(--accent); background: var(--accent-tint); }
.accion-guardar-opcion input[type="radio"] { margin-top: 3px; width: auto; }
.accion-guardar-opcion small { display: block; color: var(--ink-faint); font-weight: 400; }
.accion-guardar-modal__botones { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
.accion-guardar-modal__botones [disabled] { opacity: 0.5; cursor: not-allowed; }

/* Fase 57 (I-9, rework mayor, parte 6 de 6, 19 sep 2026): superposición
   genérica de "botón → superposición → cerrar" (ver
   static/core/superposicion.js) — a diferencia de .accion-guardar-modal
   (una caja chica y fija para un formulario), esta es una caja grande
   con scroll interno, pensada para contenido visual (Kanban/Gantt hoy,
   grafos en la Fase 58). */
.superposicion {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: var(--space-3);
}
.superposicion__fondo { position: absolute; inset: 0; background: rgba(15, 23, 42, 0.45); }
.superposicion__caja {
  position: relative; background: var(--bg-card); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: var(--space-3) 1.5rem; max-width: 70rem; width: 100%;
  max-height: 90vh; overflow-y: auto;
}
.superposicion__caja:focus { outline: none; }
.superposicion__encabezado {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.superposicion__acciones { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-left: auto; }
.superposicion__encabezado h2 { margin: 0; }

.notice {
  background: var(--bg-card); border: 1px solid var(--border-soft); border-left: 4px solid var(--accent);
  border-radius: var(--radius-sm); padding: 1rem 1.25rem; margin-bottom: var(--space-3);
}
.notice--informativa { font-size: 13px; }
.notice > p:first-child { margin-top: 0; }
.notice > p:last-child { margin-bottom: 0; }
/* AD-27d: primer uso real de django.contrib.messages en la app (antes
   solo el admin lo usaba) — variantes por `message.tags` para que un
   error se distinga de una confirmación de un vistazo. */
.notice--success { border-left-color: var(--success); background: var(--success-tint); }
.notice--error { border-left-color: var(--danger); background: var(--danger-tint); }
.notice--warning { border-left-color: var(--warn); background: var(--warn-tint); }
/* Revisión v1.0 (V1-UX-04): páginas 403, 404 y 500 (templates/40x.html, 500.html). */
.pagina-error { max-width: 40rem; margin: var(--space-4) auto; }
.pagina-error--sola { padding: var(--space-4) 16px; }

/* Fase 48: notificación "toast" — aparece, confirma, desaparece sola
   (static/core/toast.js). Mismos tokens de color que .notice, pero
   flotante y con transición de entrada/salida en vez de bloque
   persistente en el flujo de la página. */
#toast-container {
  position: fixed; bottom: var(--space-3); right: var(--space-3); z-index: 100;
  display: flex; flex-direction: column; gap: 8px; max-width: 22rem;
}
.toast {
  background: var(--bg-card); border: 1px solid var(--border-soft); border-left: 4px solid var(--accent);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md); padding: 0.75rem 1rem; font-size: 0.875rem;
  opacity: 0; transform: translateY(8px); transition: opacity .18s ease, transform .18s ease;
}
.toast--visible { opacity: 1; transform: translateY(0); }
.toast--success { border-left-color: var(--success); background: var(--success-tint); }
.toast--error { border-left-color: var(--danger); background: var(--danger-tint); }
.toast--warning { border-left-color: var(--warn); background: var(--warn-tint); }

/* Fase 49: envoltorio de scroll horizontal, armado por
   static/core/tablas.js alrededor de cualquier <table
   data-tabla-filtrable> — nunca en la plantilla, para no repetirlo a
   mano en cada una de las ~14 tablas de la aplicación. */
.data-table-wrapper { overflow-x: auto; margin-bottom: var(--space-3); }
.data-table-wrapper .data-table { margin-bottom: 0; }

.data-table {
  width: 100%; background: var(--bg-card); border: 1px solid var(--border-soft);
  /* Fase 60 (auditoría de diseño, 22 sep 2026), punto 5: antes usaba
     `--radius-sm` (6px) y sin sombra — dos tokens de "caja" distintos
     a los de `.status-card` (`--radius`, 10px, con `--shadow-sm`) para
     lo que visualmente debería leerse como el mismo tipo de tarjeta.
     Unificado a los mismos tokens.

     Fase 60 (auditoría de diseño, 23 sep 2026), hallazgo real de
     Rogelio ("parece que no tienen bordes, o más fino"): con
     `border-collapse: collapse`, el borde exterior de un `<table>` con
     `border-radius` no se pinta de forma confiable en todos los
     navegadores — el borde de las celdas "gana" el conflicto en las
     esquinas, dejando el contorno redondeado sin trazo visible,
     mientras que `.status-card`/`.accordion-section` (`<div>`/
     `<details>` normales, sin ese conflicto) sí se ven bien
     enmarcados. `border-collapse: separate` con `border-spacing: 0`
     evita el problema por completo: el borde exterior de la tabla
     vuelve a pintarse igual de nítido que el de cualquier sección,
     cubriendo también el encabezado (primera fila dentro del mismo
     borde) — sin cambiar nada del espaciado entre celdas, que ya
     dependía solo de `border-bottom` por fila, no de la fusión de
     bordes contiguos. */
  border-collapse: separate; border-spacing: 0;
  border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden; margin-bottom: var(--space-3);
}
.data-table th, .data-table td { text-align: left; padding: 0.6rem 0.9rem; border-bottom: 1px solid var(--border-soft); font-size: 0.92rem; }
.data-table th { background: #FCFBF7; color: var(--ink-faint); font-weight: 600; text-transform: uppercase; font-size: 0.72rem; letter-spacing: .04em; }
.data-table tr:last-child td { border-bottom: none; }
.data-table tr.selected { background: var(--accent-tint); }
/* Celda de dato secundario (campus, padre, tipo… en las tablas de
   Organización) — antes `style="color:var(--ink-soft)"` en cada `<td>`. */
.data-table td.celda-secundaria { color: var(--ink-soft); }
/* Texto tenue en línea ("—" de un dato vacío, motivo junto a un nombre). */
.texto-tenue { color: var(--ink-faint); }
/* Apodo bajo el identificador en la primera celda de I-3/I-4/I-5. */
.celda-apodo { font-weight: 400; color: var(--ink-faint); font-size: 11px; }
/* Aviso compacto dentro de un campo (actores heredados de I-2 en I-3 a I-5). */
.notice--compacta { font-size: 12.5px; padding: var(--space-1) var(--space-2); margin-bottom: var(--space-1); }
.notice--compacta p, .accordion-section-body .notice--compacta p { margin: 0; font-size: 12.5px; }
.notice--compacta p + p { margin-top: 2px; }
.field-card--nuevo { border-style: dashed; }
.ai-empty--relleno { padding: var(--space-2); }
.accion-guardar-modal__caja h3 { margin-top: 0; }
/* Tabla "ficha" de solo lectura (encabezado a la izquierda). */
.data-table--ficha th { width: 14rem; }
.data-table--angosta { max-width: 32rem; }
/* Toda fila de una tabla que abre un detalle al hacer clic. */
.data-table tbody tr[hx-get] { cursor: pointer; }
/* "Ver detalle" desplegable dentro de una celda (Seguimiento de I-9). */
.data-table details > summary { cursor: pointer; color: var(--accent); font-size: 12.5px; margin-top: var(--space-1); }
.data-table details > .lista-contexto { margin-top: var(--space-1); }

/* Fase 49: botón "Columnas" + panel de mostrar/ocultar y reordenar,
   armado por completo por static/core/tablas.js.

   Corregido (16 sep 2026, hallazgo de Rogelio en producción): el botón
   era un `button--secundario` de ancho completo con la palabra
   "Columnas" — debía ser un ícono pequeño, mismo espíritu que el botón
   "Gestionar columnas" de Zoho Desk (referencia que dio Rogelio):
   discreto, cuadrado, sin texto visible (el nombre queda en
   `title`/`aria-label` para accesibilidad).

   Corregido de nuevo (Fase 60, auditoría de diseño, 22 sep 2026, punto
   1, hallazgo real): entre el 16 sep y esta pieza el ícono vivía junto
   a `.data-table-wrapper` en una misma fila flex para no ocupar una
   línea propia — pero eso le quitaba a la tabla el ancho del ícono
   (~36px) que ninguna sección vecina perdía, dejándola visiblemente
   más angosta. `.tabla-toolbar` reemplaza esa fila: ahora es una fila
   propia, alineada a la derecha, ARRIBA de `.data-table-wrapper` (ya
   no lo envuelve) — la tabla vuelve a tener el ancho completo de la
   columna. */
.tabla-toolbar { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.tabla-toolbar > .button, .tabla-toolbar > button { flex-shrink: 0; }
.tabla-columnas { position: relative; }
.tabla-columnas-boton {
  /* Fase 60 (auditoría de diseño, 23 sep 2026), hallazgo real de
     Rogelio: un tamaño fijo en píxeles (30x30) quedaba más bajo que
     "Nuevo"/cualquier `.button--secundario` de la misma fila (altura
     dictada por su padding en `rem`, no un valor fijo) — mismo padding
     vertical que ese botón (`0.65rem`) para que ambos midan lo mismo
     de alto sin duplicar un número mágico; solo el padding horizontal
     se achica, ya que este botón no lleva texto, solo el ícono. */
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0.65rem; border-radius: var(--radius-sm);
  background: var(--bg-card); border: 1px solid var(--border); color: var(--ink-soft); cursor: pointer;
}
.tabla-columnas-boton:hover { background: var(--border-soft); color: var(--ink); }
.tabla-columnas-boton .icon { width: 16px; height: 16px; }
.tabla-columnas-panel {
  min-width: 16rem; max-height: 60vh; overflow-y: auto;
}
.tabla-columnas-item {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-sm);
}
.tabla-columnas-item:hover { background: var(--border-soft); }
.tabla-columnas-item label { flex: 1 1 auto; font-size: 13px; font-weight: 500; cursor: pointer; }
.tabla-columnas-item input[type="checkbox"] { width: auto; margin: 0; }
.tabla-columnas-item button {
  background: none; border: 1px solid var(--border); box-shadow: none; color: var(--ink-soft);
  width: 22px; height: 22px; padding: 0; font-size: 11px; border-radius: var(--radius-sm); cursor: pointer;
}
.tabla-columnas-item button:hover { background: var(--border-soft); }
.tabla-columnas-item button:disabled { opacity: .35; cursor: default; }
.tabla-columnas-item button:disabled:hover { background: none; }

/* Fase 21e: filtro por columna + límite de filas con "Ver más" — la
   fila de filtros y el pie de "Ver más"/"sin resultados" los arma por
   completo static/core/tablas.js sobre cualquier <table
   data-tabla-filtrable>; aquí solo su apariencia. */
.data-table tr.data-table-filtros:last-child td { padding: 6px 0.9rem; background: #FCFBF7; border-bottom: 1px solid var(--border-soft); }
/* Fase 60 (auditoría de diseño, 23 sep 2026), hallazgo real de
   Rogelio: la caja de filtro de la primera/última columna quedaba con
   un margen visible hasta el borde de la tabla — el `padding`
   horizontal de la celda de arriba (pensado como separación ENTRE
   cajas de filtro contiguas) se aplicaba también en los dos extremos,
   donde en cambio debe llegar a cero para que el filtro toque el
   borde de la tabla, igual que cualquier otro contenido de una
   sección. El padding propio del `<input>` (línea de abajo) sigue
   dando espacio interno al texto sin que se pegue al trazo del borde. */
.data-table tr.data-table-filtros:last-child td:first-child { padding-left: 0; }
.data-table tr.data-table-filtros:last-child td:last-child { padding-right: 0; }
.data-table-filtros input[type="search"] {
  width: 100%; padding: 4px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: 0.82rem; color: inherit; background: var(--bg-card);
}
.data-table-filtros input[type="search"]:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.data-table-vermas td { text-align: center; padding: 10px 0.9rem; border-bottom: none; }
.data-table-sin-resultados td { text-align: center; color: var(--ink-faint); font-style: italic; padding: 14px 0.9rem; border-bottom: none; }

/* AD-29: diff antes/después de un evento de trazabilidad (I-T).
   Fase 28 (Plan Vigente, 11 sep 2026): estilo "control de cambios" de
   un procesador de texto — lo quitado tachado en rojo (mismo tono que
   una acción destructiva, `--danger`), lo agregado subrayado en verde
   (mismo tono que una confirmación, `--success`) — convención estándar
   de control de cambios, y ya reservados en la paleta para "quitado"/
   "agregado" en vez de introducir un color nuevo. */
.diff-list { list-style: none; margin: 0; padding: 0; }
.diff-list li { margin-bottom: 0.25rem; font-size: 0.85rem; }
.diff-list li:last-child { margin-bottom: 0; }
.diff-campo { color: var(--ink-faint); font-weight: 600; }
.diff-del { color: var(--danger); text-decoration: line-through; }
.diff-ins { color: var(--success); text-decoration: underline; font-weight: 600; }

.chain-step { border-left: 3px solid var(--accent); padding: 0.4rem 0 0.4rem 1rem; margin-bottom: var(--space-2); }
.chain-step h4 { margin: 0 0 0.25rem; }
.mensaje-asistente { border-left-color: var(--ai); background: var(--ai-tint); }
.mensaje-usuario { border-left-color: var(--border); }
.mensaje-meta { margin: 0 0 0.2rem; font-size: 0.82rem; color: var(--ink-faint); }
.mensaje-contenido { margin: 0 0 0.35rem; white-space: pre-line; }
.mensaje-fuentes { margin: 0 0 0.35rem; font-size: 0.82rem; color: var(--ink-faint); font-style: italic; }
.mensaje-accion { display: inline-block; margin-right: var(--space-1); }

/* Pantallas de instrumento propio (I-1, I-3 — AD-25) ---------------- */

.tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: var(--space-3); }
.tab {
  padding: 8px 14px; border-radius: 999px; font-size: 12.5px; font-weight: 500;
  border: 1px solid var(--border); color: var(--ink-soft); background: var(--bg-card); text-decoration: none;
}
.tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.field-card { background: var(--bg-card); border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 20px 22px; margin-bottom: 16px; }
/* Fase 21c: I-1 pasa a envolver cada campo en un .accordion-section
   (ver i1.html) — sin esto, la tarjeta propia de .field-card quedaría
   "una tarjeta dentro de otra tarjeta". Dentro del acordeón pierde su
   propio borde/fondo/relleno; el borde ya lo pone .accordion-section. */
.accordion-section-body > .field-card { background: none; border: none; border-radius: 0; padding: 0; margin-bottom: 0; }
/* El <summary> del acordeón ya repite el nombre del campo (ver i1.html)
   — dentro de la sección no hace falta repetirlo una segunda vez en
   .field-label; la escala de nivel pasa a ocupar sola esa fila. */
.accordion-section-body > .field-card .field-top > div:first-child .field-label { display: none; }
.field-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; margin-bottom: 14px; flex-wrap: wrap; }
.field-label { font-size: 14.5px; font-weight: 600; }
/* Documento maestro de convenciones (26 sep 2026): texto secundario
   ("Confirmada por…", subtítulo de una tarjeta, estado vacío de una
   sección) a 12.5px — mismo tamaño que ya usaban las listas de las
   tarjetas de contexto y `.threshold-note p`. Hallazgo real: dentro de
   un acordeón, `.accordion-section-body p` (más específica) le ganaba a
   `.field-meta` y el mismo texto salía a 13.5px en negro, mientras
   que fuera del acordeón salía a 11.5px en gris — ver la regla
   `.accordion-section-body p.field-meta` más abajo. */
.field-meta { font-size: 12.5px; color: var(--ink-faint); margin-top: 3px; }
p.field-meta { margin: 0 0 var(--space-1); line-height: 1.5; }
p.field-meta:last-child { margin-bottom: 0; }
/* D6: estado vacío de una página/tabla, o "sin autorización". */
.instrument-status { font-size: 13px; color: var(--ink-soft); margin: 0 0 var(--space-3); }
.instrument-status:last-child { margin-bottom: 0; }
/* Texto secundario DEBAJO de un control dentro del mismo campo
   ("Confirmada por…", "Verificado por…"). */
.form-field p.field-meta:not(:first-child) { margin-top: var(--space-1); }
/* "Confirmada por…" pertenece a la casilla de arriba, no al campo de abajo. */
.form-field--casilla + p.field-meta { margin-top: calc(-1 * var(--space-1)); margin-bottom: var(--space-2); }
/* D4: lista "etiqueta: valor" de solo lectura dentro de una tarjeta de
   contexto (Nota contextual de I-4 a I-6, funciones heredadas de I-5,
   alertas de I-7, perfil de I-8, condiciones de I-9). Antes, cada
   tarjeta repetía a mano `style="margin:0;padding-left:1.1em;
   font-size:12.5px;color:var(--ink-soft)"`. */
.lista-contexto { margin: 0; padding-left: 1.1em; font-size: 12.5px; color: var(--ink-soft); line-height: 1.5; }
.lista-contexto li + li { margin-top: var(--space-1); }
.lista-contexto strong { color: var(--ink); }
.lista-contexto .vacio { color: var(--ink-faint); }
.lista-contexto details { display: inline; }
.lista-contexto summary { display: inline; cursor: pointer; color: var(--accent); }
/* Fase 56 (18 sep 2026): la escala de nivel de suficiencia de I-1 pasó
   de pastillas de radio (`.level-pill`/`.level-radio`, retiradas en
   esta misma fase — I-1 era su último consumidor) a un `<select>`
   simple envuelto por Tom Select (Fase 54c) — `.level-scale` sigue de
   contenedor, ahora de un solo `<select>` en vez de un grupo de
   pastillas. */
.level-scale { display: flex; gap: 6px; flex: 0 0 auto; flex-wrap: wrap; }
/* Fase 60 (auditoría de diseño, 22 sep 2026), punto 2: el <select> de
   nivel de suficiencia (envuelto por Tom Select) quedaba comprimido en
   estado cerrado — `.ts-wrapper` trae `width: 100%` (regla global, la
   correcta para cualquier Tom Select dentro de un `.form-field` de
   bloque normal) pero `.level-scale` es `flex: 0 0 auto`: un hijo al
   100% dentro de un padre cuyo ancho depende del contenido es un caso
   circular que el navegador resuelve colapsando al mínimo (solo el
   relleno/flecha del control, sin espacio real para el texto). Se
   fuerza un ancho propio en este caso puntual — el resto de los Tom
   Select de la app, en contenedores de bloque o de grid, no lo
   necesitan y no se tocan. */
.level-scale .ts-wrapper { width: auto; min-width: 11rem; }
/* Cierre de la v1 (30 sep 2026, pedido de Rogelio): en la tarjeta de un
   campo de I-1 el selector de nivel ocupa todo el ancho, alineado a la
   izquierda y con su etiqueta visible — antes quedaba a la derecha y el
   texto del nivel se cortaba. */
.field-card .level-scale { flex: 1 1 100%; display: block; }
.field-card .level-scale .ts-wrapper { width: 100%; }
/* Fase 60 (auditoría de diseño, 23 sep 2026), hallazgo real de
   Rogelio: la descripción completa de cada nivel vive ahora solo
   dentro de cada `<option>` (ver contextualizacion/_campo_card.html —
   ya no se imprime aparte, debajo del selector), así que el menú
   ABIERTO necesita poder ensancharse más allá del control cerrado
   (`.ts-dropdown` hereda por defecto el mismo ancho angosto del
   control) para no cortar ese texto. */
.level-scale .ts-dropdown { width: max-content; min-width: 100%; max-width: min(90vw, 28rem); }
.level-scale .ts-dropdown .option { white-space: normal; }
.field-card.htmx-settling { opacity: 0.6; transition: opacity 0.15s ease; }

/* Fase 55 (18 sep 2026): chips de selección — mismo lenguaje visual que
   tenía la escala de nivel de I-1 antes de la Fase 56, pero con nombre
   propio para no acoplarse a ese componente (retirado) ni a su
   `hx-trigger` (I-1 auto-enviaba el formulario en cada cambio; estos
   chips no deben disparar eso). Sirven tanto para selección única
   (input radio, nivel de participación) como múltiple (input
   checkbox, funciones D/P/V/I/U/S/A) — el estado marcado se ve igual
   en los dos casos. */
.chip-group { display: flex; gap: 6px; flex-wrap: wrap; }
.chip-option,
.form-field label.chip-option {
  padding: 6px 10px; border-radius: var(--radius-sm); font-size: 11.5px; font-weight: 500;
  border: 1px solid var(--border); color: var(--ink-faint); white-space: nowrap; background: var(--bg-card);
  cursor: pointer; font-family: inherit; display: inline-flex; align-items: center;
}
.chip-input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.chip-input:checked + .chip-option { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-dark); }
.chip-input:focus-visible + .chip-option { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Fase 55: tarjeta de un actor de "Responsables y funciones" — mismo
   criterio visual que .instrument-card, resumen en el <summary> del
   <details>. */
.actor-card__resumen { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.actor-card__nombre { font-weight: 600; }
.actor-card__puesto { color: var(--ink-faint); font-size: 12px; }

/* Fase 55: comentario compacto que crece con el contenido — reemplaza
   el <textarea rows="2"> grande de antes (ver
   static/core/auto-grow-textarea.js). */
textarea.auto-grow { resize: none; overflow: hidden; min-height: 2.2rem; }

/* Fase 55: bloque de un comité participante (integrantes agrupados por
   responsables/miembros, información derivada — nunca configurable). */
.comite-participante { border: 1px solid var(--border-soft); border-radius: var(--radius); padding: var(--space-2); margin-bottom: var(--space-2); }
.comite-participante__header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: 6px; }
.comite-participante__grupo { margin: 6px 0 0; }
.comite-participante__grupo-titulo { font-size: 11.5px; font-weight: 600; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .02em; margin: 0 0 3px; }
.comite-participante__persona { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; padding: 2px 0; }
.comite-participante__rol { color: var(--ink-faint); font-size: 12px; }
/* Cierre de la v1 (30 sep 2026): las etiquetas de la tarjeta de I-1
   ("Descripción cualitativa", "Evidencia") eran de 11.5px y grises —
   ahora iguales a la etiqueta de cualquier otro campo. */
.ta-label { margin: 12px 0 5px; }
.ta-label label { display: block; font-weight: 600; font-size: 0.9rem; color: var(--ink); margin: 0; }

.progress-line { display: flex; align-items: center; gap: 14px; margin: 4px 0 var(--space-3); }
.progress-line .bar { flex: 1 1 auto; height: 7px; border-radius: 4px; background: var(--border-soft); overflow: hidden; }
.progress-line .bar > div { height: 100%; background: var(--accent); border-radius: 4px; }
.progress-line .n { font-size: 12.5px; color: var(--ink-soft); white-space: nowrap; }

/* Fase 60 (auditoría de diseño, 22 sep 2026), punto 6: `.threshold-note`
   tenía su propia paleta suelta (`#F4F7EE`/`#DCE7CC` de éxito,
   `#E8CBA6` de advertencia) en vez de los mismos tokens que ya usa
   `.notice--success`/`.notice--warning` — dos verdes y dos naranjas
   ligeramente distintos conviviendo en la misma aplicación, sin que
   nadie lo hubiera decidido a propósito. Unificado a `--success`/
   `--success-tint`/`--warn`/`--warn-tint`, mismo criterio de acento
   que `.notice` (franja de color a la izquierda, resto del borde
   neutro) — se conserva la estructura propia de `.threshold-note`
   (fila con ícono, Fase 30) porque `.notice` no la tiene y no hacía
   falta reescribir sus 9 usos para lograr el mismo objetivo real:
   un solo color de fondo por significado en todo el sitio. */
.threshold-note {
  display: flex; gap: 10px; align-items: flex-start;
  background: var(--success-tint); border: 1px solid var(--border-soft); border-left: 4px solid var(--success);
  border-radius: var(--radius-sm); padding: 12px 14px; margin-bottom: var(--space-3);
}
.threshold-note svg { color: var(--success); margin-top: 1px; }
.threshold-note p { margin: 0; font-size: 12.5px; color: var(--ink-soft); }
.threshold-note b { color: var(--ink); }
.threshold-note.no { background: var(--border-soft); border-left-color: var(--border); }
.threshold-note.no svg { color: var(--ink-faint); }
.threshold-note.warn { background: var(--warn-tint); border-left-color: var(--warn); }
.threshold-note.warn svg { color: var(--warn); }
/* Ajustes de Rogelio (1 oct 2026): sección fija "Notificaciones" de la
   ficha de un registro (core/_notificaciones.html), siempre visible entre
   la tarjeta del registro y Expandir/Recoger, ordenada por urgencia con un
   color por tipo: requiere tu acción = ámbar (`.warn`, la propuesta
   pendiente); alerta = rojo suave (`.alerta`); estado del sistema = verde
   (`.threshold-note`) o gris (`.no`); notas contextuales heredadas = azul
   suave (las `.status-card` de contexto, aquí dentro). */
.threshold-note.alerta { background: var(--danger-tint); border-left-color: var(--danger); }
.threshold-note.alerta svg { color: var(--danger); }
.notificaciones {
  background: var(--bg-card); border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: var(--space-2) var(--space-3) var(--space-3); margin-bottom: var(--space-3);
}
.notificaciones__encabezado { margin-bottom: var(--space-2); }
.notificaciones__titulo { margin: 0; font-size: 1rem; font-weight: 600; }
.notificaciones__subtitulo { margin: 2px 0 0; font-size: 12px; color: var(--ink-faint); }
.notificaciones__lista > * { margin-bottom: var(--space-2); }
.notificaciones__lista > *:last-child { margin-bottom: 0; }
.notificaciones .status-card {
  background: var(--accent-tint); border: 1px solid var(--border-soft); border-left: 4px solid var(--accent);
  border-radius: var(--radius-sm); padding: 12px 14px; box-shadow: none;
}
.notificaciones .status-card h2 { font-size: 0.9rem; font-weight: 600; }
/* Autoguardado (1 oct 2026): estado junto a "Exportar" en las fichas que
   se guardan solas (static/core/autoguardado.js). */
/* Barra de borrador local (static/core/borrador-local.js). */
.borrador-local__cuerpo { flex: 1 1 auto; }
.borrador-local__acciones { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-1); }

/* AD-30: edición con aprobación (I-3 y, en fases futuras, otros
   instrumentos con derivados) ----------------------------------------- */
.approval-actions { display: flex; gap: 10px; margin-top: 10px; }

/* AD-31: Muro — panel de comentarios embebido y feed institucional --- */
.panel-comentarios-titulo { font-size: 14.5px; margin: var(--space-3) 0 8px; }
.lista-comentarios { list-style: none; margin: 0 0 var(--space-2); padding: 0; }
.comentario { border-bottom: 1px solid var(--border-soft); padding: 10px 0; }
.comentario:last-child { border-bottom: none; }
.comentario-meta { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; font-size: 12px; color: var(--ink-faint); }
.comentario-fecha { color: var(--ink-faint); }
.comentario-registro { color: var(--ink-faint); }
.comentario-registro a { color: var(--accent); }
.comentario-texto { margin: 4px 0 0; white-space: pre-line; overflow-wrap: anywhere; }
.comentario-menciones { font-size: 12px; color: var(--ink-faint); margin: 4px 0 0; }
.campo-texto-comentario { margin-bottom: 8px; }
.form-comentario textarea {
  width: 100%; min-height: 88px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 10px; font: inherit; resize: vertical;
}
.lista-comentarios--muro .comentario-meta { font-size: 12.5px; }
/* AD-31 (retroalimentación 2 sep 2026): desplegable de menciones @token
   que reemplaza la lista estática anterior — ver el <script> al final
   de _panel_comentarios.html. */
.mencion-sugerencias {
  list-style: none; margin: 2px 0 0; padding: 4px 0; position: relative;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-card); max-width: 320px; max-height: 168px;
  overflow-y: auto; box-shadow: 0 4px 14px rgba(32, 36, 46, 0.12); font-size: 13px;
}
.mencion-sugerencias li { padding: 5px 10px; cursor: pointer; }
.mencion-sugerencias li.activo, .mencion-sugerencias li:hover { background: var(--accent-tint); }
.comentario-error { color: var(--danger); font-size: 12.5px; margin: 6px 0 0; }

/* Columna lateral compartida (Fase 14, AD-32) ------------------------
   Retroalimentación de Rogelio sobre la Fase 13 (2 sep 2026): los
   comentarios del muro deben vivir en la columna derecha junto al
   panel de IA, no sueltos al final del contenido central. El panel de
   la derecha (`.ai-panel`, antes exclusivo de la IA desde AD-25) ahora
   puede alojar más de una sección apilada dentro del MISMO <aside> de
   320px — Comentarios primero, IA después — reutilizando .ai-head/
   .ai-body para que ambas se vean como una sola pieza coherente, no
   dos widgets distintos pegados. */
.lateral-seccion + .lateral-seccion,
.lateral-seccion + .ai-head { border-top: 1px solid var(--border-soft); }
.lateral-seccion-body { padding: 14px 20px 18px; }
.lateral-seccion .panel-comentarios-titulo { display: none; } /* el título ya lo da .ai-head */
.lateral-seccion .lista-comentarios { max-height: 260px; overflow-y: auto; }
.lateral-seccion .form-comentario textarea { min-height: 64px; }

/* Fase 23a: pestaña "Archivos adjuntos" del panel derecho — mismo
   criterio visual que la sección de comentarios de arriba (AD-31),
   reutilizando .lateral-seccion/.ai-head/.ai-body como envoltorio. */
.lista-documentos { list-style: none; margin: 0 0 var(--space-2); padding: 0; }
.documento { border-bottom: 1px solid var(--border-soft); padding: 10px 0; }
.documento:last-child { border-bottom: none; }
.documento-meta { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 12px; color: var(--ink-faint); }
.documento-meta a { color: var(--accent); font-weight: 500; font-size: 13px; }
.documento-tamano { white-space: nowrap; }
.documento-descripcion { margin: 4px 0 0; font-size: 12.5px; color: var(--ink-soft); }
.documento-eliminar-form { margin: 0; }
.form-documento { margin-top: 10px; }
.form-documento input[type="file"] { display: block; font-size: 12.5px; margin-bottom: 6px; }
.form-documento input[type="text"] {
  width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 10px; font: inherit; font-size: 12.5px;
}
.documento-error { color: var(--danger); font-size: 12.5px; margin: 6px 0 0; }
.lateral-seccion .lista-documentos { max-height: 260px; overflow-y: auto; }

/* Elementos relacionados (Fase 37, segunda pieza) — mismo criterio
   visual que Archivos adjuntos, arriba. */
.lista-elementos { list-style: none; margin: 0 0 var(--space-2); padding: 0; }
.elemento-vinculado { border-bottom: 1px solid var(--border-soft); padding: 10px 0; }
.elemento-vinculado:last-child { border-bottom: none; }
.elemento-vinculado-meta { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.elemento-vinculado-tipo {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-faint);
}
.elemento-vinculado-nombre { font-size: 13px; font-weight: 500; }
.elemento-vinculado-nota { margin: 4px 0 0; font-size: 12.5px; color: var(--ink-soft); }
.form-elemento-vinculo { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.form-elemento-vinculo select, .form-elemento-vinculo input[type="text"] {
  width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 10px; font: inherit; font-size: 12.5px;
}
.elemento-vinculo-error { color: var(--danger); font-size: 12.5px; margin: 0; }
.lateral-seccion .lista-elementos { max-height: 260px; overflow-y: auto; }

/* Acordeón de secciones (Fase 14, AD-32; regla de visibilidad y estilo
   revisados en Fase 21c) ------------------------------------------
   Pedido explícito de Rogelio (2 sep 2026): que las secciones de un
   registro se puedan recoger/expandir, con botones para expandir/
   recoger todas a la vez — y que el patrón sea el mismo en todos los
   instrumentos ("que no haya muchos cambios entre instrumentos... que
   sea bastante familiar"). Construido sobre <details>/<summary>
   nativos, mismo criterio que .edit-disclosure (AD-30) — sin librería
   nueva; el comportamiento de expandir/recoger todo lo agrega
   static/core/accordion.js alternando el atributo `open`. Accesible
   por teclado y sin JS de por sí (cada sección se abre individualmente
   aunque el script no cargue).

   Fase 21c (8 sep 2026), pedido explícito de Rogelio: el toolbar
   "Expandir/Recoger" solo tiene sentido — y por tanto solo debe
   incluirse en la plantilla — cuando un `.accordion` agrupa MÁS DE UNA
   `.accordion-section`; con una sola sección expandirla o recogerla ya
   es lo mismo que abrir/cerrar esa única sección con su propio
   <summary>. Ver el doc de la fase para el detalle de qué pantalla
   quedó con toolbar y cuál no. El estilo del botón del toolbar ahora
   es el mismo que `.button--terciario` (ver el bloque de botones más
   arriba): texto gris neutro en reposo — antes era --accent, el mismo
   azul de los enlaces, y chocaba con el texto negro de los párrafos —
   con un fondo lavanda suave (--accent-tint) al pasar el cursor. */
.accordion-toolbar { display: flex; justify-content: flex-end; gap: var(--space-3); margin-bottom: var(--space-2); }
.accordion-toolbar button, .button--terciario {
  display: inline-block; background: none; border: none; box-shadow: none; color: var(--ink-soft);
  font: inherit; font-weight: 600; font-size: 0.82rem; padding: 6px 10px; border-radius: var(--radius-sm);
  cursor: pointer; transition: background .12s ease, color .12s ease;
}
.accordion-toolbar button:hover, .button--terciario:hover { background: var(--accent-tint); color: var(--accent); }
.accordion-section {
  background: var(--bg-card); border: 1px solid var(--border-soft); border-radius: var(--radius);
  margin-bottom: 10px; padding: 0 18px;
}
.accordion-section > summary {
  cursor: pointer; list-style: none; padding: 14px 0; font-weight: 600; font-size: 13.5px;
  color: var(--ink); display: flex; align-items: center; gap: 8px;
}
.accordion-section > summary::-webkit-details-marker { display: none; }
.accordion-section > summary::before { content: "▸"; font-size: 10px; color: var(--ink-faint); flex: 0 0 auto; }
.accordion-section[open] > summary::before { content: "▾"; }
.accordion-section[open] > summary { border-bottom: 1px solid var(--border-soft); }
.accordion-section > .accordion-section-body { padding: 14px 0 18px; }
.accordion-section-body p.field-meta { margin: 0 0 var(--space-1); font-size: 12.5px; color: var(--ink-faint); line-height: 1.5; }
.accordion-section-body p.field-meta:last-child { margin-bottom: 0; }
.accordion-section-body p.instrument-status { margin: 0; font-size: 13px; color: var(--ink-soft); }
/* Fase 22: texto de ManualPagina.cuerpo, pasado por `linebreaks` —
   mismo criterio tipográfico que `.ai-guia-texto p` (Fase 22). */
.accordion-section-body p { margin: 10px 0 0; font-size: 13.5px; color: var(--ink); line-height: 1.6; }
.accordion-section-body p:first-child { margin-top: 0; }

/* Administración de plataforma — catálogo de ambientes (AD-27d) ----- */

.admin-inline-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.admin-inline-form input[type="text"] {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; color: inherit; background: var(--bg-card);
}
.admin-inline-form input[type="text"]:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.admin-row-actions { display: flex; gap: 6px; flex: 0 0 auto; }
.admin-componente-card .field-top { margin-bottom: 10px; }
.admin-field-row + .admin-field-row { border-top: 1px solid var(--border-soft); }
.admin-field-row { padding: 10px 0; }
.admin-field-row .admin-inline-form { flex: 1 1 auto; }
.admin-field-row .admin-inline-form input[name="slug"] { width: 10rem; flex: 0 0 auto; }
.admin-field-row .admin-inline-form input[name="etiqueta"],
.admin-field-row .admin-inline-form input[name="placeholder_ia"] { flex: 1 1 10rem; }
.admin-new-row { margin-top: 4px; padding-top: 10px; border-top: 1px dashed var(--border); }
.admin-new-row input[name="nombre"],
.admin-new-row input[name="etiqueta"],
.admin-new-row input[name="placeholder_ia"] { flex: 1 1 10rem; }
.admin-new-row input[name="slug"] { width: 10rem; flex: 0 0 auto; }

/* Fase 39 (segunda pieza, 13 sep 2026): widget flotante de IA,
   disponible en toda la aplicación — ver templates/core/_widget_asistente.html
   y static/core/paneles.js (estado abierto/cerrado). */
.widget-asistente {
  position: fixed; right: var(--space-3); bottom: var(--space-3); z-index: 60;
}
.widget-asistente-boton {
  width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--ai-border);
  background: var(--ai); color: #fff; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.widget-asistente-boton:hover { background: var(--accent-dark); }
.widget-asistente-boton .icon { width: 24px; height: 24px; }
.widget-asistente-panel {
  display: none; position: fixed; right: var(--space-3); bottom: calc(52px + var(--space-3) * 2);
  width: 340px; max-width: calc(100vw - var(--space-3) * 2); height: 440px; max-height: 70vh; max-height: 70dvh;
  flex-direction: column; background: var(--bg-card); border: 1px solid var(--ai-border);
  border-radius: var(--radius); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.22); overflow: hidden;
  transition: width .15s ease, height .15s ease, max-height .15s ease;
}
html.aim-widget-asistente-abierto .widget-asistente-panel { display: flex; }
/* Maximizar (20 sep 2026, pedido explícito de Rogelio): mismo criterio
   de persistencia que el resto de este archivo — estado como clase de
   <html> (ver static/core/paneles.js), nunca puesto directamente sobre
   .widget-asistente-panel. Ancla siempre abajo a la derecha (igual que
   el tamaño normal) — deliberadamente NO es una superposición de
   pantalla completa (static/core/superposicion.js): esa mecánica
   muestra/oculta un elemento SEPARADO con contenido ya renderizado una
   sola vez, mientras que el panel del widget es un único árbol de DOM
   vivo (mensajes que llegan por streaming, formulario con su propio
   estado) — agrandarlo en su lugar evita duplicar esa lógica o mover
   nodos de un contenedor a otro. */
html.aim-widget-asistente-maximizado .widget-asistente-panel {
  width: min(560px, calc(100vw - var(--space-3) * 2));
  height: min(720px, calc(100vh - 96px));
  height: min(720px, calc(100dvh - 96px));
  max-height: calc(100vh - 96px);
  max-height: calc(100dvh - 96px);
}
/* Hallazgo real de producción (20 sep 2026, reporte de Rogelio,
   AMPLIADO el 21 sep 2026): el panel del widget (siempre anclado
   abajo a la derecha del VIEWPORT, no de `.main-col`) tapaba
   `.ai-panel` — el panel derecho de Comentarios/Guía/Archivos/
   Relacionados vive en esa misma esquina. La corrección original
   (20 sep) solo cubría el tamaño MAXIMIZADO (560px), asumiendo que en
   tamaño normal (340px) el solapamiento "apenas se notaba" — un
   supuesto incorrecto: `.ai-panel` mide 320px por defecto
   (`--ai-panel-width`), MENOS que los 340px del widget en tamaño
   normal, así que ya lo tapaba por completo, no solo una esquina.
   Cuando `.ai-panel` está visible, el widget corre su borde derecho
   hasta `--ai-panel-width` (la misma variable que
   static/core/paneles.js ya mantiene al día) para quedar A LA
   IZQUIERDA de `.ai-panel` en vez de encima — en CUALQUIER tamaño, no
   solo maximizado — sí puede llegar a tapar parte del contenido
   central, que es un compromiso mucho menor que ocultar un panel de
   navegación entero. El `max-width` que se ajusta acá también acota
   por sí solo el ancho maximizado (560px) cuando hace falta —
   `max-width` siempre gana sobre un `width` mayor, sin necesitar un
   selector combinado aparte para ese caso. Sin este selector cuando
   `.ai-panel` está oculto (`aim-ai-panel-hidden`): no hay nada de qué
   correrse, y `--ai-panel-width` de todas formas sigue guardando el
   último ancho ANTES de ocultarlo (paneles.js nunca lo pone en 0), así
   que aplicar este descuento sin la condición `:not(...)` habría
   corrido el widget de más sin necesidad.

   `.app:has(> .ai-panel)` (no solo `html:not(.aim-ai-panel-hidden)`):
   `{% block ai_panel_wrapper %}` (templates/base.html) queda VACÍO en
   pantallas sin panel derecho propio (Inicio, Organización...) — ahí
   no existe ningún `<aside class="ai-panel">` en el DOM, así que
   `aim-ai-panel-hidden` nunca se agrega a `<html>` (nada que ocultar)
   y, sin este `:has()`, `:not(.aim-ai-panel-hidden)` sola daría falso
   positivo ahí, corriendo el widget hacia la izquierda sin ningún
   panel del que alejarse. El botón de ocultar/mostrar panel derecho
   de todas formas se renderiza SIEMPRE (fuera de ese bloque, en
   templates/base.html) sin importar si hay panel o no — un hallazgo
   preexistente de esa plantilla, sin relación con este archivo.

   Hallazgo de la revisión de código independiente (21 sep 2026): esta
   regla, a diferencia de la anterior (que solo aplicaba maximizado),
   ahora corre TAMBIÉN en celulares, sin que nadie haya maximizado
   nada — antes esto solo importaba si alguien maximizaba el widget a
   propósito en un teléfono (raro). En un viewport angosto (390px de
   ancho, valores por defecto), `right` queda en 340px y `max-width`
   en apenas 30px: el panel del widget se reduce a una tira
   inutilizable en vez del layout de ancho completo que ya define el
   `@media (max-width: 480px)` de más abajo. Esta regla queda acotada
   a viewports más anchos que ese mismo punto de quiebre — en
   celulares gana el layout de ancho completo, sin importar si
   `.ai-panel` está presente (que en celulares de todas formas no
   tiene su propio quiebre responsive, un problema preexistente y
   aparte de `.ai-panel` en sí, no de este widget).

   Responsividad móvil M1 (27 sep 2026): desde que `.ai-panel` SÍ tiene
   su quiebre (oculto hasta 768px, ver el final de este archivo), este
   corrimiento aplica solo por encima de ese ancho. */
@media (min-width: 769px) {
  html:not(.aim-ai-panel-hidden) .app:has(> .ai-panel) .widget-asistente-panel {
    right: calc(var(--ai-panel-width) + var(--space-3));
    max-width: calc(100vw - var(--ai-panel-width) - var(--space-3) * 2);
  }
}
.widget-asistente-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-2) var(--space-3); background: var(--ai-tint); border-bottom: 1px solid var(--ai-border);
}
.widget-asistente-header h2 { margin: 0; font-size: 14px; }
.widget-asistente-header-acciones { display: flex; align-items: center; gap: var(--space-1); }
.widget-asistente-cerrar {
  background: none; border: none; color: var(--ink-soft); font-size: 20px; line-height: 1;
  cursor: pointer; padding: 2px 6px; box-shadow: none;
}
.widget-asistente-nueva-conversacion {
  background: none; border: none; color: var(--ink-soft); line-height: 1;
  cursor: pointer; padding: 2px 4px; box-shadow: none; display: flex;
}
.widget-asistente-nueva-conversacion .icon { width: 16px; height: 16px; }
.widget-asistente-maximizar {
  background: none; border: none; color: var(--ink-soft); line-height: 1;
  cursor: pointer; padding: 2px 4px; box-shadow: none; display: flex;
}
.widget-asistente-maximizar .icon { width: 16px; height: 16px; }
/* Mini-plan de UX del widget, segunda pieza (21 sep 2026): envoltura
   propia para el botón "bajar al final" — ver el comentario de diseño
   en templates/core/_widget_asistente.html sobre por qué el botón no
   puede ser hijo de `.widget-asistente-mensajes` (el que hace scroll).
   Esta envoltura hereda el `flex: 1 1 auto; min-height: 0` que antes
   tenía `.widget-asistente-mensajes` directamente (ver el hallazgo de
   producción del 20 sep 2026, más abajo, que sigue aplicando igual —
   solo cambió QUIÉN de los dos declara esas dos propiedades). */
.widget-asistente-mensajes-envoltura { position: relative; flex: 1 1 auto; min-height: 0; }
.widget-asistente-mensajes {
  /* Hallazgo real de producción (20 sep 2026, revisión general de
     Rogelio): sin `min-height: 0`, un flex item con `overflow-y: auto`
     NO se ve forzado a respetar el espacio que le da el contenedor —
     por defecto, `min-height` de un flex item es `auto` (= el alto de
     su propio contenido), así que con historial suficiente (se cargan
     hasta 30 mensajes al abrir el widget, ver `contexto_widget`) este
     div crece más allá del alto fijo de `.widget-asistente-panel`
     (440px/70vh) en vez de encoger y mostrar su propio scroll. Como el
     panel tiene `overflow: hidden`, lo que sobra —el `<form>` de envío,
     que va DESPUÉS en el DOM— queda recortado y se ve como si la caja
     de texto hubiera desaparecido.

     Segundo hallazgo real de producción, con el botón de maximizar (20
     sep 2026): faltaba `overflow-x: hidden`. Por la regla de la propia
     especificación de CSS, declarar `overflow-y: auto` SIN tocar
     `overflow-x` no deja a este último en `visible` — el navegador lo
     convierte también en `auto` (si un eje es `visible` y el otro no,
     el `visible` pasa a `auto`), así que cualquier contenido apenas
     más ancho que el panel (un margen/padding redondeado al píxel, una
     palabra larga sin espacios) dispara una barra de scroll horizontal
     real dentro del panel — más notoria cuanto más ancho es el panel
     maximizado, pero ya latente también en el tamaño normal. `overflow-wrap`
     en `.widget-asistente-mensaje-contenido` (abajo) evita que una
     palabra/URL larga sea la causa en primer lugar. */
  height: 100%; overflow-y: auto; overflow-x: hidden;
  padding: var(--space-2) var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.widget-asistente-bajar {
  position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
  width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--ai-border);
  background: var(--bg-card); color: var(--ink-soft); box-shadow: var(--shadow-sm);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.widget-asistente-bajar:hover { color: var(--ink); }
.widget-asistente-bajar .icon { width: 15px; height: 15px; }
.widget-asistente-mensaje { font-size: 13px; line-height: 1.4; }
.widget-asistente-mensaje-registro { margin: 0 0 2px; font-size: 11px; color: var(--ink-faint); }
/* Mini-plan de UX del widget (21 sep 2026, primera pieza, a partir de
   comparar con Chatscope/Chatwoot): hora de cada turno, siempre del
   mismo lado que su burbuja (mismo criterio de indentación que
   `.widget-asistente-mensaje--usuario`/`--asistente`, ver más abajo) —
   y separador de fecha, centrado, cuando el historial cargado (hasta
   30 mensajes) cruza más de un día (ver
   `asistente_ia.views.contexto_widget`). */
.widget-asistente-mensaje-hora { margin: 2px 0 0; font-size: 10.5px; color: var(--ink-faint); }
.widget-asistente-mensaje--usuario .widget-asistente-mensaje-hora { text-align: right; }
.widget-asistente-mensaje--asistente .widget-asistente-mensaje-hora { text-align: left; }
.widget-asistente-separador-fecha {
  margin: var(--space-1) 0; text-align: center; font-size: 11px; color: var(--ink-faint);
}
/* Hallazgo de la revisión de código independiente del streaming (19
   sep 2026): aviso de que la respuesta se cortó sin terminar bien,
   agregado DEBAJO del texto parcial ya mostrado (nunca lo reemplaza —
   lo parcial también sirve). */
.widget-asistente-mensaje-aviso-corte { margin: 4px 0 0; font-size: 11px; color: var(--danger); }
/* Hallazgo ALTO de la revisión de código independiente del streaming
   (19 sep 2026): la versión servida por Django usa `linebreaksbr`
   (saltos de línea reales, <br>) — la burbuja armada por JS durante el
   streaming solo hace `.textContent =`, así que sin `white-space`
   cualquier "\n" real queda invisible y la respuesta se ve toda en una
   sola línea mientras se transmite (y, en el chat de texto libre, para
   siempre — esa burbuja nunca se vuelve a renderizar). `pre-line` no
   rompe el `<br>` que ya insertan las burbujas renderizadas por el
   servidor, solo agrega el caso que faltaba. */
.widget-asistente-mensaje-contenido {
  margin: 0; padding: 8px 10px; border-radius: var(--radius-sm); white-space: pre-line;
  overflow-wrap: anywhere;
}
.widget-asistente-mensaje--usuario .widget-asistente-mensaje-contenido {
  background: var(--accent-tint); margin-left: 20%;
}
.widget-asistente-mensaje--asistente .widget-asistente-mensaje-contenido {
  background: var(--ai-tint); margin-right: 20%;
}
/* Chatbot, sub-pieza 2 (markdown, 20 sep 2026): lo mínimo para lo que
   `renderizarMarkdownLite()` puede producir (ver widget-asistente.js) —
   negrita/cursiva no necesitan regla propia, son etiquetas nativas. */
.widget-asistente-mensaje-contenido p { margin: 0 0 6px; }
.widget-asistente-mensaje-contenido p:last-child { margin-bottom: 0; }
.widget-asistente-mensaje-contenido ul,
.widget-asistente-mensaje-contenido ol { margin: 0 0 6px; padding-left: 18px; }
.widget-asistente-mensaje-contenido ul:last-child,
.widget-asistente-mensaje-contenido ol:last-child { margin-bottom: 0; }
.widget-asistente-mensaje-contenido code {
  background: rgba(0, 0, 0, 0.08); border-radius: 3px; padding: 1px 4px; font-size: 12px;
}
.widget-asistente-codigo {
  background: rgba(0, 0, 0, 0.08); border-radius: var(--radius-sm); padding: 8px; margin: 0 0 6px;
  overflow-x: auto; font-size: 12px;
}
.widget-asistente-codigo code { background: none; padding: 0; }
/* 26 sep 2026: títulos, separadores y tablas del markdown del asistente
   (antes se veían los "##", "---" y "|" crudos). Títulos discretos: el
   chat es una ventana chica, no un documento. */
.widget-asistente-titulo { margin: var(--space-2) 0 var(--space-1); font-weight: 700; font-size: 0.95em; color: var(--ink); }
.widget-asistente-titulo:first-child { margin-top: 0; }
.widget-asistente-separador { border: none; border-top: 1px solid var(--border-soft); margin: var(--space-2) 0; }
.widget-asistente-tabla-envoltorio { overflow-x: auto; margin: var(--space-1) 0 var(--space-2); }
.widget-asistente-tabla { border-collapse: collapse; font-size: 0.9em; width: 100%; }
.widget-asistente-tabla th, .widget-asistente-tabla td { border: 1px solid var(--border-soft); padding: 4px 6px; text-align: left; vertical-align: top; }
.widget-asistente-tabla th { background: var(--bg); font-weight: 600; }
/* Chatbot, sub-pieza 1 (streaming + indicador de carga, 19 sep 2026):
   burbuja del asistente mientras todavía no llegó ningún fragmento de
   la respuesta — tres puntos animados, mismo criterio visual que
   "está escribiendo…" de cualquier chat moderno. */
.widget-asistente-mensaje--escribiendo .widget-asistente-mensaje-contenido {
  min-height: 1.1em;
}
.widget-asistente-mensaje--escribiendo .widget-asistente-mensaje-contenido::after {
  content: "\2022\2022\2022";
  display: inline-block;
  letter-spacing: 2px;
  animation: widget-asistente-escribiendo 1.2s infinite steps(1);
}
@keyframes widget-asistente-escribiendo {
  0%, 33% { opacity: 0.25; }
  34%, 66% { opacity: 0.6; }
  67%, 100% { opacity: 1; }
}
.widget-asistente-vacio { font-size: 12.5px; color: var(--ink-faint); margin: 0; }
.widget-asistente-comandos {
  padding: 6px var(--space-2) 8px; font-size: 11px; color: var(--ink-faint);
  border-top: 1px solid var(--border-soft);
}
.widget-asistente-comandos-texto { margin: 0; }
/* Mini-plan de UX del widget, tercera pieza (21 sep 2026): chips de
   acción rápida para los comandos "/" del registro activo — mismo
   criterio visual de "esto es del asistente de IA" que `.mencion-chip`
   (fondo/borde ámbar), pero interactivo (`cursor: pointer`, hover),
   nunca confundirlo con una pastilla de mención ya resuelta. */
.widget-asistente-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.widget-asistente-chip {
  background: var(--ai-tint); color: var(--ai); border: 1px solid var(--ai-border);
  border-radius: 999px; padding: 3px 10px; font-size: 11px; font-weight: 600;
  cursor: pointer; box-shadow: none;
}
.widget-asistente-chip:hover { background: var(--ai-border); }
.widget-asistente-form {
  /* Hallazgo real de producción (21 sep 2026, primera vez que Rogelio
     lo probó en un navegador de escritorio real — hasta ahora solo se
     había probado desde el celular): sin `align-items: flex-end`, el
     valor por defecto (`stretch`) hace que el botón "Enviar" se
     estire para igualar el alto del editor de texto — que puede
     crecer hasta 120px (`.widget-asistente-editor`, `max-height:
     120px`) a medida que la persona escribe varias líneas — en vez de
     quedarse en un tamaño compacto normal. Esto es un defecto del
     diseño original del formulario (sub-pieza 1, 19 sep 2026), nunca
     antes notado por falta de verificación visual real en escritorio,
     no algo introducido por una pieza posterior. `flex-end` ancla
     ambos elementos (editor y botón) al borde INFERIOR de la fila en
     vez de estirarlos — el botón ya no depende del alto del editor
     para su propio tamaño (ver `.widget-asistente-enviar` más abajo,
     que le da un tamaño explícito en vez de heredarlo por completo
     del `button` genérico de toda la aplicación). */
  display: flex; align-items: flex-end; gap: 6px; padding: var(--space-2); border-top: 1px solid var(--border-soft);
}
.widget-asistente-enviar {
  flex: 0 0 auto; padding: 8px 14px; font-size: 13px;
}
/* Eliminación de IA fuera del chat (19 sep 2026): "@"/"/" embebidos en
   la propia caja de texto — reemplaza el control externo de dos
   <select> + botón "Insertar" (ver static/core/widget-asistente.js). */
.widget-asistente-editor-envoltura { position: relative; flex: 1 1 auto; }
.widget-asistente-editor {
  box-sizing: border-box; width: 100%; min-height: 38px; max-height: 120px;
  overflow-y: auto; padding: 6px 8px; font: inherit; white-space: pre-wrap;
  word-break: break-word; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-card);
}
.widget-asistente-editor:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.widget-asistente-editor--vacio::before {
  content: attr(data-placeholder); color: var(--ink-faint); pointer-events: none;
}
.mencion-chip {
  display: inline-block; background: var(--ai-tint); color: var(--ai);
  border: 1px solid var(--ai-border); border-radius: 999px; padding: 0 8px;
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.widget-asistente-autocompletado {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 4px); z-index: 5;
  margin: 0; padding: 4px; list-style: none; max-height: 180px; overflow-y: auto;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}
.widget-asistente-autocompletado-item { padding: 6px 8px; border-radius: var(--radius-sm); cursor: pointer; font-size: 12.5px; }
.widget-asistente-autocompletado-item strong { display: block; }
.widget-asistente-autocompletado-item small { color: var(--ink-faint); }
.widget-asistente-autocompletado-item--activo,
.widget-asistente-autocompletado-item:hover { background: var(--ai-tint); }
@media (max-width: 480px) {
  .widget-asistente-panel { right: var(--space-2); left: var(--space-2); width: auto; }
  .widget-asistente { right: var(--space-2); bottom: var(--space-2); }
}

/* Fase 54b/54c: convención de selección con búsqueda (Tom Select) —
   reemplaza los grupos de casillas de cualquier M2M (54b) y envuelve
   también todo <select> de un solo valor de la app (54c, ver
   core/_multiselect.html, static/core/tomselect.js). Estas reglas solo
   retocan colores/tipografía sobre el CSS por defecto de la librería
   (static/vendor/tom-select.min.css, cargado antes que este archivo)
   para que combine con el resto de la interfaz — la
   estructura/comportamiento del widget los deja intactos. */
.ts-wrapper { width: 100%; }
.ts-wrapper.single .ts-control, .ts-control {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-card); padding: 0.4rem 0.5rem; box-shadow: none;
  font: inherit; color: var(--ink); min-height: 2.4rem;
}
.ts-wrapper.focus .ts-control { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-tint); }
.ts-control input { font: inherit; color: var(--ink); }
.ts-control input::placeholder { color: var(--ink-faint); }
.ts-wrapper .item {
  background: var(--accent-tint); color: var(--accent-dark); border: none;
  border-radius: 999px; padding: 0.2rem 0.35rem 0.2rem 0.7rem; font-size: 0.85rem; font-weight: 500;
}
.ts-wrapper.plugin-remove_button .item .remove {
  border-left: none; color: var(--accent-dark); opacity: 0.7; padding: 0 0.5rem;
}
.ts-wrapper.plugin-remove_button .item .remove:hover { opacity: 1; background: transparent; color: var(--danger); }
/* Cierre de la v1 (30 sep 2026, pedido de Rogelio): el valor elegido de
   un <select> de un solo valor se ve con el mismo chip azul que la
   selección múltiple (antes, desde la 54c, era texto plano) — una sola
   convención visual para "lo elegido" en toda la aplicación. Un valor
   largo (p. ej. una escala con su descripción) pasa a la línea
   siguiente en vez de cortarse. */
.ts-wrapper.single .item {
  white-space: normal; max-width: 100%; line-height: 1.35; padding: 0.2rem 0.7rem;
}
.ts-wrapper.single .ts-control { padding-right: 2rem; }
/* La opción vacía ("— Sin definir —", "— Ninguno —") no es una elección:
   texto tenue, sin chip. */
.ts-wrapper.single .item[data-value=""] {
  background: transparent; color: var(--ink-faint); font-weight: 400; padding-left: 0;
}
.ts-wrapper.plugin-clear_button .clear-button {
  color: var(--ink-faint); opacity: 0.7; background: transparent;
}
.ts-wrapper.plugin-clear_button .clear-button:hover { opacity: 1; color: var(--danger); }
.ts-dropdown {
  border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
  font: inherit; margin-top: 2px;
}
.ts-dropdown .option { padding: 0.5rem 0.75rem; color: var(--ink); }
.ts-dropdown .option.active { background: var(--accent-tint); color: var(--accent-dark); }
.ts-dropdown .no-results { padding: 0.5rem 0.75rem; color: var(--ink-faint); }
.ts-dropdown .optgroup-header { color: var(--ink-faint); font-weight: 600; font-size: 0.78rem; padding: 0.4rem 0.75rem 0.2rem; }
@media (max-width: 480px) {
  .ts-wrapper, .ts-dropdown { width: 100% !important; }
}

/* Fase 58 (Grafos), quinta sub-pieza (19 sep 2026): pulido visual
   transversal a las 4 páginas de grafo/árbol de Cytoscape.js. Colores
   de la paleta por tipo de nodo elegidos deliberadamente FUERA de la
   familia ámbar/naranja (ver la nota de --ai/--ai-tint al tope de este
   archivo: ese tono queda reservado exclusivamente para contenido que
   proviene de la IA, nunca para otra cosa). */
.grafo-tooltip {
  position: fixed;
  z-index: 200;
  background: var(--ink);
  color: #fff;
  padding: 4px 10px;
  border-radius: var(--radius-sm, 4px);
  font-size: 12px;
  line-height: 1.3;
  max-width: 240px;
  pointer-events: none;
}
.grafo-lienzo { width: 100%; height: 70vh; min-height: 420px; border: 1px solid var(--border-soft); border-radius: 8px; background: var(--surface); }
.grafo-leyenda {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
  color: var(--ink-soft);
}
.grafo-leyenda li { display: flex; align-items: center; gap: 5px; }
.grafo-leyenda__color {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex: 0 0 auto;
}
/* Misma paleta que la configuración de Cytoscape (estilos por tipo de nodo en core/grafo.py) — si cambia allá, cambiar acá. */
.grafo-leyenda__color--tension { background: #0072B2; }
.grafo-leyenda__color--foco { background: #56B4E9; }
.grafo-leyenda__color--caso { background: #009E73; }
.grafo-leyenda__color--valoracion { background: #6b4fa0; }
.grafo-leyenda__color--decision { background: #CC79A7; }
.grafo-leyenda__color--entrada { background: #1b998b; }
.grafo-leyenda__color--plan { background: #4a4e69; }
.grafo-leyenda__color--proceso { background: #7a8ba0; }
/* Jerarquía de elementos (Plan Vigente, punto 27): color por posición del tipo; misma paleta que core.grafo.PALETA_TIPOS_ELEMENTO. */
.grafo-leyenda__color--paleta-0 { background: #0072B2; }
.grafo-leyenda__color--paleta-1 { background: #009E73; }
.grafo-leyenda__color--paleta-2 { background: #CC79A7; }
.grafo-leyenda__color--paleta-3 { background: #56B4E9; }
.grafo-leyenda__color--paleta-4 { background: #6b4fa0; }
.grafo-leyenda__color--paleta-5 { background: #1b998b; }
.grafo-leyenda__color--paleta-6 { background: #4a4e69; }
.grafo-leyenda__color--paleta-7 { background: #882255; }
.grafo-leyenda__color--indefinido { background: #c9cfdd; border: 1px dashed #7a8ba0; }
/* Los dos botones "Elementos"/"Tipos" de la superposición: el activo, relleno. */
.button--secundario[aria-pressed="true"] { background: var(--accent); color: #fff; border-color: var(--accent); }
/* Opciones de elemento como ficha (static/core/tomselect.js, data-opciones-ficha). */
.ts-ficha { display: flex; flex-direction: column; gap: 2px; }
.ts-ficha__nombre { color: var(--ink); }
.ts-ficha__detalle { font-size: 12px; color: var(--ink-soft); }
.ts-ficha__tipo { font-size: 11px; opacity: 0.85; margin-left: var(--space-1); }
/* Tipos de elemento con sangría según su nivel en la jerarquía (Plan Vigente, punto 27). */
.tipo-elemento--nivel-1 { margin-left: var(--space-3); }
.tipo-elemento--nivel-2 { margin-left: calc(var(--space-3) * 2); }
.tipo-elemento--nivel-3 { margin-left: calc(var(--space-3) * 3); }
.tipo-elemento--nivel-4 { margin-left: calc(var(--space-3) * 4); }
@media (max-width: 640px) {
  /* En el celular, la sangría se reduce para no comerse el ancho. */
  .tipo-elemento--nivel-1 { margin-left: var(--space-1); }
  .tipo-elemento--nivel-2 { margin-left: calc(var(--space-1) * 2); }
  .tipo-elemento--nivel-3 { margin-left: calc(var(--space-1) * 3); }
  .tipo-elemento--nivel-4 { margin-left: calc(var(--space-1) * 4); }
}

/* Responsividad móvil, pieza M1 (27 sep 2026) ------------------------
   Decisiones de Rogelio: menú desplegable ☰; panel derecho oculto que
   se abre a pantalla completa; todo usable, priorizando consultar.
   Punto de quiebre único: 768px (el mismo de static/core/paneles.js,
   sección 4). El estado móvil vive en sus propias clases de <html>
   (`aim-movil-menu-abierto`/`aim-movil-panel-abierto`, sin persistir);
   las clases de escritorio de la Fase 21a (`aim-sidebar-hidden`/
   `aim-ai-panel-hidden`) se ignoran aquí a propósito, para que la
   preferencia guardada en una pantalla ancha no deje a nadie sin menú
   en el teléfono. Va al final del archivo para ganarle en la cascada
   a las reglas de escritorio. */
.topbar-movil-boton, .movil-cerrar, .movil-fondo, .ai-panel-barra-movil, .detalle-barra-movil { display: none; }

@media (max-width: 768px) {
  .topbar-movil-boton, .movil-cerrar {
    display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 40px; height: 40px; padding: 0; border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--bg-card); color: var(--ink); cursor: pointer;
  }
  .topbar-movil-boton .icon, .movil-cerrar .icon { width: 20px; height: 20px; }
  .topbar-movil-boton:focus-visible, .movil-cerrar:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
  /* El botón del panel derecho solo si la página tiene panel. */
  .topbar-movil-boton--panel { display: none; }
  .app:has(> .ai-panel) .topbar-movil-boton--panel { display: inline-flex; }

  /* Los controles de escritorio (flechas en los bordes, franja para
     redimensionar) no tienen sentido en un teléfono. */
  .panel-toggle, .panel-resize-handle { display: none !important; }

  /* Menú lateral como cajón. */
  .sidebar,
  html.aim-sidebar-hidden .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 90;
    width: min(288px, 86vw); padding: 16px 14px 22px; opacity: 1;
    overflow-y: auto; transform: translateX(-105%);
    /* Cerrado también `visibility: hidden` (tras la animación): si no,
       Tab y el lector de pantalla recorrían todos los enlaces del menú
       fuera de la pantalla (revisión independiente). */
    visibility: hidden; transition: transform .2s ease, visibility 0s linear .2s;
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.25);
  }
  html.aim-movil-menu-abierto .sidebar { transform: none; visibility: visible; transition: transform .2s ease; }
  .movil-cerrar--sidebar {
    align-self: flex-end; margin-bottom: -12px;
    background: transparent; border-color: rgba(255, 255, 255, 0.18); color: var(--sidebar-ink);
  }
  .sidebar .nav-item { min-height: 44px; } /* objetivo táctil cómodo */
  html.aim-movil-menu-abierto .movil-fondo {
    display: block; position: fixed; inset: 0; z-index: 89; background: rgba(15, 23, 42, 0.45);
  }

  /* Panel derecho a pantalla completa, bajo su barra "Cerrar". */
  .ai-panel, html.aim-ai-panel-hidden .ai-panel { display: none; }
  html.aim-movil-panel-abierto .ai-panel {
    display: flex; position: fixed; top: 56px; left: 0; right: 0; bottom: 0; z-index: 90;
    width: auto !important; border-left: none;
  }
  html.aim-movil-panel-abierto .ai-panel-barra-movil {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
    position: fixed; top: 0; left: 0; right: 0; height: 56px; z-index: 91; padding: 0 12px 0 16px;
    background: var(--bg-card); border-bottom: 1px solid var(--border);
  }
  .ai-panel-barra-movil__titulo { font-weight: 600; font-size: 14px; }
  .ai-panel-barra-movil .button--secundario { display: inline-flex; align-items: center; gap: 6px; }
  .ai-panel-barra-movil .icon { width: 16px; height: 16px; }
  /* Con el cajón o el panel abiertos, la página de atrás no se desplaza. */
  html.aim-movil-menu-abierto, html.aim-movil-panel-abierto { overflow: hidden; }

  /* Barra superior compacta: ☰ · migas (recortadas) · acciones. */
  .topbar { padding: 8px 12px; gap: 8px; min-height: 56px; }
  .crumb { flex: 1 1 auto; min-width: 0; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar-right { gap: 8px; flex: 0 0 auto; }
  .topbar-right .inst-badge { display: none; }
  #ver-institucion-select { max-width: 7.5rem; }
  .notif-dropdown { width: calc(100vw - 24px); position: fixed; left: 12px; right: 12px; top: 60px; }

  /* Contenido: márgenes menores y espacio abajo para la burbuja de IA. */
  .content { padding: 16px 14px 88px; }

  /* Formularios en una sola columna — `!important` porque la mayoría
     de las rejillas fijan sus columnas en línea, en la plantilla. */
  .campos-grid { grid-template-columns: 1fr !important; }

  /* Superposiciones (Kanban, Gantt, grafos) a pantalla completa. */
  .superposicion { padding: 0; }
  .superposicion__caja { max-height: 100vh; height: 100%; border-radius: 0; padding: 16px; }

  #toast-container { left: 12px; right: 12px; max-width: none; }

  /* Pieza M2: detalle de un registro a pantalla completa, bajo su barra
     "← Volver a la lista". Capa 55: por encima del contenido, pero
     debajo de la burbuja de IA (60, sigue disponible), del cajón y del
     panel derecho (89-91) y de modales/superposiciones (100). */
  html.aim-movil-detalle-abierto #detalle-wrapper {
    position: fixed; top: 56px; left: 0; right: 0; bottom: 0; z-index: 55;
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    background: var(--bg); padding: 16px 14px 88px;
  }
  html.aim-movil-detalle-abierto .detalle-barra-movil {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
    position: fixed; top: 0; left: 0; right: 0; height: 56px; z-index: 56; padding: 0 12px;
    background: var(--bg-card); border-bottom: 1px solid var(--border);
  }
  html.aim-movil-detalle-abierto { overflow: hidden; }
  /* `position: fixed` + `z-index` hace del envoltorio su propio
     contexto de apilamiento: las superposiciones (grafo, Kanban,
     Gantt) y el modal guardar/proponer que viven DENTRO del detalle
     quedaban topadas en la capa 55, debajo de la barra "Volver" y de
     la burbuja — con su "Cerrar" inalcanzable (revisión independiente).
     Mientras una está abierta, el envoltorio entero sube a su capa. */
  html.aim-movil-detalle-abierto #detalle-wrapper:has(.superposicion:not([hidden]), [data-accion-guardar-modal]:not([hidden])) {
    z-index: 100;
  }
  /* Tras "Volver a la lista" la lista queda sola: el detalle no se
     dibuja debajo de ella (su contenido sigue en el DOM, sin perderse). */
  html:not(.aim-movil-detalle-abierto) #detalle-wrapper { display: none; }

  /* Tablas de la lista: filas más compactas y objetivo táctil cómodo;
     el desplazamiento horizontal ya lo da `.data-table-wrapper`. */
  .data-table th, .data-table td { padding: 0.55rem 0.6rem; }
  .data-table--ficha th { width: auto; }
  /* Tablas que no pasan por static/core/tablas.js (sin
     `.data-table-wrapper`): Trazabilidad, Inicio, Tablero,
     Administración... desplazamiento horizontal propio, nunca de toda
     la página. */
  .content table.data-table:not(.data-table-wrapper > table) { display: block; overflow-x: auto; }
}

/* Chatbot, pieza 2 (27 sep 2026): tarjeta "antes / después" de una
   propuesta redactada por el asistente (templates/asistente_ia/
   _borrador_propuesta.html). Se apoya en `.ai-msg` (tono ámbar de todo
   lo que produce la IA, AD-25); aquí solo la disposición interna. */
.propuesta-ia { margin-top: var(--space-2); display: flex; flex-direction: column; gap: var(--space-2); }
.propuesta-ia .tag { margin-bottom: 0; }
.propuesta-ia__registro { font-weight: 600; }
.propuesta-ia__campo { border-top: 1px solid var(--ai-border); padding-top: var(--space-1); }
.propuesta-ia__etiqueta { font-weight: 600; font-size: 12.5px; }
.propuesta-ia__columnas { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); margin-top: 4px; }
.propuesta-ia__rotulo { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-faint); }
.propuesta-ia ul { margin: 2px 0 0; padding-left: 1.1rem; font-size: 12.5px; line-height: 1.45; }
.propuesta-ia__antes ul { color: var(--ink-soft); }
.propuesta-ia__despues ul { color: var(--ink); }
.propuesta-ia .ai-actions { margin-top: 0; }
.propuesta-ia .ai-actions form { margin: 0; }
.propuesta-ia__nota, .propuesta-ia__motivo { font-size: 12px; color: var(--ink-soft); }
/* En el widget (340px) o en un teléfono, antes y después uno debajo del otro. */
.widget-asistente-panel .propuesta-ia__columnas { grid-template-columns: 1fr; }
html.aim-widget-asistente-maximizado .widget-asistente-panel .propuesta-ia__columnas { grid-template-columns: 1fr 1fr; }
@media (max-width: 480px) {
  html.aim-widget-asistente-maximizado .widget-asistente-panel .propuesta-ia__columnas { grid-template-columns: 1fr; }
}

/* Hallazgo de Rogelio en su teléfono (27 sep 2026): el widget de IA
   extendido medía `100vh - 96px` anclado abajo — en un navegador móvil
   `100vh` incluye el área que tapa la barra de direcciones, así que el
   borde de arriba (con Nueva conversación, Extender/Restaurar y
   Cerrar) quedaba fuera de la pantalla y no había forma de salir. En
   el móvil, extendido = la pantalla visible completa, anclado arriba y
   abajo (un elemento fijo con top/bottom se ajusta al alto visible
   real); la burbuja se oculta mientras tanto (tapaba "Enviar", y el
   encabezado ya trae Cerrar). Mismo punto de quiebre que el resto de
   la responsividad móvil. */
@media (max-width: 768px) {
  html.aim-widget-asistente-maximizado .widget-asistente-panel {
    top: 0; right: 0; bottom: 0; left: 0;
    width: auto; max-width: none; height: auto; max-height: none;
    border-radius: 0; border: none;
  }
  html.aim-widget-asistente-maximizado.aim-widget-asistente-abierto .widget-asistente-boton { display: none; }

  /* Botones de la campanita: objetivo táctil cómodo y parejo. */
  .notificacion-acciones .button--compacto { min-height: 36px; padding: 6px 14px; font-size: 12.5px; }
}

/* Teléfono con bandera y prefijo (intl-tel-input, static/core/telefono.js).
   Ajuste de Rogelio (2 oct 2026): el botón del país heredaba el azul oscuro
   de `button` — fondo claro con buen contraste, y un poco más de aire a la
   derecha del prefijo para que no quede pegado al número. */
.campo-telefono { display: block; }
.campo-telefono .iti { width: 100%; }
.campo-telefono .iti__selected-country,
.campo-telefono .iti__selected-country:hover {
  background: var(--accent-tint); color: var(--ink); box-shadow: none;
  border-right: 1px solid var(--border); border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}
.campo-telefono .iti__selected-country-primary { padding: 0 6px 0 10px; }
.campo-telefono .iti__selected-dial-code { color: var(--ink); padding-right: 6px; }
.campo-telefono .iti__country-list button,
.campo-telefono .iti__country { background: none; color: var(--ink); }

/* I-5: funciones heredadas de I-2 junto a los actores involucrados (2 oct 2026). */
.funciones-heredadas { margin: var(--space-2) 0 0; padding-left: 1.1em; font-size: 13px; color: var(--ink-soft); }

/* Manual: página de cada pantalla armada desde su Guía (2 oct 2026). */
.manual-guia h3 { font-size: 14px; margin: var(--space-3) 0 var(--space-1); }
.manual-guia__nota { color: var(--ink-soft); font-style: italic; }

/* Revisión v1.0 (V1-A11Y-05, 8 oct 2026): una zona desplazable que
   static/core/accesibilidad.js vuelve enfocable muestra su foco. */
[data-tabindex-desplazable]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Revisión v1.0 (V1-CUM-01): aviso y procedimientos de privacidad
   (templates/core/privacidad.html), texto largo editado como HTML. */
.texto-legal { max-width: 52rem; line-height: 1.6; }
.texto-legal h2 { font-size: 1.15rem; margin: var(--space-4) 0 var(--space-2); }
.texto-legal h2:first-of-type { margin-top: var(--space-3); }
.texto-legal .data-table { margin: var(--space-2) 0 var(--space-3); }
.texto-legal li + li { margin-top: 4px; }
.pie-privacidad { font-size: 13px; color: var(--ink-faint); }

/* Barra y pie públicos (22 oct 2026, pedido de Rogelio): azul oscuro de la
   marca, el mismo del menú lateral, para que el logo blanco se vea. */
.topbar--publica { background: var(--sidebar-bg); border-bottom-color: var(--sidebar-bg); }
.topbar--publica .brand-name-anon { color: #fff; }
.topbar--publica .tagline { color: var(--sidebar-ink); }
.topbar--publica .site-nav a:not(.button) { color: #fff; }
.topbar--publica .language-switcher select { background: transparent; color: #fff; border-color: var(--sidebar-ink-dim); }
.topbar--publica .language-switcher option { color: var(--ink); }
.site-footer { background: var(--sidebar-bg); color: var(--sidebar-ink); border-top-color: var(--sidebar-bg); }
.site-footer .version-app, .site-footer .version-app a { color: var(--sidebar-ink-dim); }
.site-footer .pie-plataforma__marca { color: #fff; }
.site-footer .pie-plataforma__marca em { color: var(--sidebar-ink); }


/* Inicio de sesión y recuperación de contraseña (22 oct 2026, pedido de
   Rogelio): los campos se estiraban a todo el ancho de la pantalla. El
   formulario va en una tarjeta angosta y centrada, con el botón al ancho
   de la tarjeta. */
.cuenta-tarjeta {
  max-width: 26rem; margin: var(--space-4) auto; padding: var(--space-4) var(--space-3);
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-md);
}
.cuenta-tarjeta > h2 { margin-top: 0; }
.cuenta-tarjeta button[type="submit"] { width: 100%; }
.cuenta-tarjeta__enlaces {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-1) var(--space-2);
  margin: var(--space-3) 0 0; font-size: 14px;
}
