/* frawifi - kit Croco (copiado de IRM/JailMeNow; ver server/README.md).
   Tema oscuro (por defecto). TODO color de la app sale de estas variables;
   nunca colores sueltos en las reglas. El tema claro solo redefine variables. */

/* El atributo `hidden` siempre gana: sin esto, cualquier regla con `display` (p. ej.
   `.form-croco label.checkbox { display: inline-flex }`) lo anula y el elemento se ve. */
[hidden] { display: none !important; }

:root {
  /* Tamano del texto de paneles y modales (decision del propietario: el mismo en los dos). En rem,
     asi que sigue a los botones - / + de la topbar (--ui-scale). Las tablas van a 0.88rem. */
  --text-size: 0.9rem;
  /* Controles nativos (desplegables, calendario, scroll) con el tema oscuro; el claro lo cambia abajo. */
  color-scheme: dark;
  --bg: #0b121b;
  --card: #121b26;
  /* Fondo de los menus desplegables: un paso mas claro que --card para despegarlos del panel de detras. */
  --menu-bg: #1d2a3a;
  --fg: #e6edf3;
  --muted: #8b98a5;
  --accent: #2f81f7;
  --error: #f85149;
  --border: #24303c;
  --input-bg: #0a1019;
  --tip-bg: #080d14;
  --overlay-bg: rgba(4, 7, 11, 0.72);
  --row-border: rgba(36, 48, 60, 0.5);
  --row-hover: rgba(47, 129, 247, 0.06);
  /* Fila de tabla bajo el raton: mas marcada que --row-hover (menus, botones), propietario 2026-09-17. */
  --table-row-hover: rgba(47, 129, 247, 0.14);
  --ok: #3fb950;
  --warn: #d29922;
  /* Paleta de series de gráficas (CrocoPulso), además de accent/ok/warn/error. */
  --chart-1: #2dd4bf;
  --chart-2: #a371f7;
  --chart-3: #db61a2;
  --chart-4: #f0883e;
  --chart-5: #7d8eff;
  /* Roles de usuario del portal (badges de CrocoUsuarios): admin y lectura. */
  --rol-admin: #a371f7;
  --rol-lectura: #2f81f7;
  /* Boton de filas por pagina elegido (15/30/50/100): gris, sin competir con el «+» azul (propietario 2026-09-17). */
  --page-active-bg: #323436fa;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f2f5f8;
  --card: #ffffff;
  /* Menus desplegables: gris muy claro para diferenciarlos tanto del panel blanco como del fondo. */
  --menu-bg: #eef2f7;
  --fg: #1a2733;
  --muted: #5b6b7b;
  --accent: #0969da;
  --error: #d1242f;
  --border: #d5dce3;
  --input-bg: #f6f8fa;
  --tip-bg: #ffffff;
  --overlay-bg: rgba(30, 40, 50, 0.45);
  --row-border: rgba(190, 200, 210, 0.5);
  --row-hover: rgba(9, 105, 218, 0.06);
  --table-row-hover: rgba(9, 105, 218, 0.11);
  --ok: #1a7f37;
  --warn: #9a6700;
  --chart-1: #0f766e;
  --chart-2: #6639ba;
  --chart-3: #bf3989;
  --chart-4: #bc4c00;
  --chart-5: #4547c9;
  --rol-admin: #6639ba;
  --rol-lectura: #0969da;
  --page-active-bg: #e5e5e5fa;
}

/* Badge de rol (gestión de usuarios): pastilla tintada + texto de color. */
.rol-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; }
.rol-badge.rol-admin { color: var(--rol-admin); background: color-mix(in srgb, var(--rol-admin) 15%, transparent); }
.rol-badge.rol-lectura { color: var(--rol-lectura); background: color-mix(in srgb, var(--rol-lectura) 15%, transparent); }

/* Campo de texto con botones a la derecha (p. ej. contraseña: ver / generar). */
.campo-con-acciones { display: flex; align-items: center; gap: 6px; }
.campo-con-acciones input { flex: 1 1 auto; min-width: 0; }

/* Modal de alta/edición de usuario: 30% más estrecho que el ancho estándar. */
.ct-modal.ct-modal-wide.ct-modal-angosto { max-width: 602px; }
/* Codigo de 4 cifras del borrado en lote de equipos retirados (2026-10-02): grande y espaciado para leerlo de un vistazo. */
.purge-code { font-size: 1.15em; letter-spacing: 0.15em; color: var(--error); }

/* ---- Ajustes del portal (Configuración → tabla `settings`) ----------------
   Una fila por ajuste: nombre y descripción a la izquierda, valor editable en el
   centro y su propio botón a la derecha. Cada fila se guarda sola — no hay un
   «Guardar todo»: un formulario que manda veinte ajustes de golpe convierte un
   despiste en veinte cambios auditados. */
.ajustes-lista { display: flex; flex-direction: column; }
.ajuste {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(220px, 2fr) auto;
  gap: 0.8rem;
  align-items: start;
  padding: 0.7rem 0.2rem;
  border-top: 1px solid var(--border);
}
.ajuste:first-child { border-top: none; }
.ajuste-clave { font-size: 0.82rem; color: var(--fg); }
.ajuste-desc { margin: 0.2rem 0 0; }
.ajuste-valor {
  width: 100%;
  min-height: 2rem;
  padding: 0.35rem 0.5rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.78rem;
  resize: vertical;
}
.ajuste-valor:focus { outline: 1px solid var(--accent); border-color: var(--accent); }
.ajuste-acciones { display: flex; align-items: center; gap: 0.6rem; }
.ajuste-estado { color: var(--ok); white-space: nowrap; }
.ajuste-estado.error { color: var(--error); }
/* Ajuste que el portal solo lee al arrancar: el aviso va pegado a su nombre, no
   en un párrafo al principio que nadie relaciona con la fila que está tocando. */
.ajuste-aviso {
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--warn) 18%, transparent);
  color: var(--warn);
  font-size: 0.68rem;
}

/* ---- Estado de la base de datos ------------------------------------------ */
.bd-stat { align-items: flex-start; text-align: left; }
@media (max-width: 860px) {
  .ajuste { grid-template-columns: 1fr; }
}

* { box-sizing: border-box; }

/* Tamaño de letra de todo el portal (botones - / + de la topbar, `--ui-scale` de 0.8 a 1.4).
   Todo el CSS va en rem, así que escalar la raíz escala textos, rellenos y controles. */
html { font-size: calc(100% * var(--ui-scale, 1)); }

body {
  margin: 0;
  min-height: 100vh;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--fg);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.topbar {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.9rem 1.5rem;
  border-bottom: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.95rem;
}

.card {
  width: 100%;
  max-width: 520px;
  margin: 3rem 1rem;
  padding: 2rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
}

h1 { margin: 0 0 0.5rem; font-size: 1.4rem; }

.muted { color: var(--muted); }
.small { font-size: 0.85rem; }

/* Enlaces: color en reposo (sin subrayado del navegador) y subrayado solo al pasar el
   raton o con el foco visible. Los clicables que no son <a> usan `.ct-clickable`. */
a { color: var(--accent); text-decoration: none; }
a:hover, a:focus-visible { text-decoration: underline; cursor: pointer; }

form { display: flex; flex-direction: column; gap: 1rem; margin-top: 1.5rem; }

label { display: flex; flex-direction: column; gap: 0.4rem; font-size: 0.95rem; }

label.checkbox { flex-direction: row; align-items: center; gap: 0.5rem; }

input[type="text"],
input[type="password"],
input[type="file"],
input[type="number"],
input[type="search"],
input[type="date"],
input[type="datetime-local"],
input[type="email"],
select {
  padding: 0.6rem 0.7rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 0.95rem;
}

/* Los controles nativos (select, date) heredan el tema: los desplegables y el selector de
   fecha del navegador siguen el modo claro/oscuro en vez de salir en blanco. */
select, input[type="date"], input[type="datetime-local"] { color-scheme: dark; }
:root[data-theme="light"] select,
:root[data-theme="light"] input[type="date"],
:root[data-theme="light"] input[type="datetime-local"] { color-scheme: light; }

input:focus, select:focus { outline: 2px solid var(--accent); border-color: var(--accent); }

/* BOTONES DE TEXTO: una sola medida en todo el portal (decision del propietario, la de
   + Nuevo usuario). Las variantes (.ghost, .secondary, .danger, .tab-alta) solo cambian
   el color: NO fijan padding ni font-size. El borde transparente iguala la altura de los
   botones con borde y sin el. Los botones de icono (.ct-info-btn) llevan su propia medida. */
button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.2rem 0.8rem;
  background: var(--accent);
  color: #fff;
  border: 1px solid transparent;
  border-radius: 8px;
  font-size: 0.9rem;
  line-height: 1.5;
  cursor: pointer;
}

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

button.link {
  background: none;
  color: var(--muted);
  padding: 0;
  font-size: 0.95rem;
}

button.link:hover { color: var(--fg); filter: none; }

.error {
  margin: 1rem 0 0;
  padding: 0.7rem 0.9rem;
  background: rgba(248, 81, 73, 0.12);
  border: 1px solid var(--error);
  border-radius: 8px;
  color: var(--error);
}

code {
  background: var(--input-bg);
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  font-size: 0.9em;
}

.overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.2rem;
  padding: 1.5rem;
  background: var(--overlay-bg);
  backdrop-filter: blur(2px);
}

.overlay[hidden] { display: none; }

.overlay p { max-width: 340px; text-align: center; color: var(--fg); }

.spinner {
  width: 48px;
  height: 48px;
  border: 4px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ---- Dashboard (home del portal) ---- */

.brand { display: flex; align-items: center; gap: 0.55rem; color: var(--fg); }
.brand img { width: 30px; height: 30px; }
/* Nombre de la app (IRM Nova) mas grande que el texto del menu; la version sigue pequena. */
.brand strong { font-size: 1.25rem; letter-spacing: 0.01em; }

.topbar .menu { display: flex; gap: 1.2rem; }
.topbar .menu a { color: var(--fg); padding: 0.3rem 0; }
.topbar .menu a.active { color: var(--accent); border-bottom: 2px solid var(--accent); }
.topbar .menu .disabled { color: var(--muted); cursor: not-allowed; }
.topbar form { margin: 0; }

button.ghost {
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
}
button.ghost:hover { color: var(--fg); border-color: var(--muted); filter: none; }

.topbar-actions { display: flex; align-items: center; gap: 0.6rem; }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  padding: 0.45rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
}
.theme-toggle:hover { color: var(--fg); border-color: var(--muted); filter: none; }

/* Reducir / ampliar la letra del portal: dos botoncitos apilados junto al tema. Medida fija en
   px para que no crezcan ni encojan con la propia escala que controlan. */
.font-scale { display: inline-flex; flex-direction: column; gap: 3px; }
.font-scale button {
  width: 20px;
  height: 16px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1;
}
.font-scale button:hover:not(:disabled) { color: var(--fg); border-color: var(--muted); filter: none; }
.font-scale button:disabled { opacity: 0.35; cursor: default; }

/* Acceso a Gestión de usuarios (solo admin): botón-icono en la topbar, junto al tema. */
.topbar-icon-btn {
  display: inline-flex;
  align-items: center;
  padding: 0.45rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
  text-decoration: none;
}
.topbar-icon-btn:hover { color: var(--fg); border-color: var(--muted); filter: none; }
.topbar-icon-btn.active { color: var(--accent); border-color: var(--accent); }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: inline; }

/* Ancho de pagina (decision del propietario 2026-09-22, que revisa la del tope de 1800px): el contenido
   SIEMPRE se adapta al ancho del navegador, sin maximo. Con las tablas de hoy (Equipos con memoria, disco,
   BitLocker y actividad) el tope de antes obligaba a desplazar en horizontal en monitores grandes; quien
   quiera menos ancho, estrecha la ventana. Se conserva el margen lateral. */
.page {
  width: 100%;
  /* Margen lateral: con la ventana mas estrecha que el contenido, los paneles no quedan pegados al borde. */
  box-sizing: border-box;
  padding: 1.2rem clamp(0.75rem, 2.5vw, 1.5rem) 3rem;
  display: flex;
  flex-direction: column;
  /* El hueco entre bloques de la pagina, en una variable: `.toolbar-rango` lo anula con un margen negativo. */
  --page-gap: 1.2rem;
  gap: var(--page-gap);
}

.toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.8rem; }
/* Cabecera de pagina sin titulo (decision del propietario 2026-09-22: el menu ya dice donde estas): queda solo la
   barra de rango, pegada a la derecha. Se come el hueco de `.page` con un margen negativo y deja un dedo de aire
   debajo (`padding-bottom`, a gusto del propietario), para no gastar alto pero no quedar pegada a la tabla. */
.toolbar-rango { justify-content: flex-end; margin: -0.4rem 0 calc(-1 * var(--page-gap, 1.2rem)); padding-bottom: 0.5rem; }
.toolbar h1 { margin: 0; }
.toolbar h2.panel-titulo { margin: 0; font-size: 1.05rem; }
/* Barra de botones de una pestana sin titulo propio (Distribucion): a la derecha. */
.toolbar.toolbar-right { justify-content: flex-end; }
.ranges label.checkbox { align-self: center; }

/* Autorefresco con periodo elegible (Portal.autoRefresh): uno por tabla, a la derecha de la barra de su
   recuadro, con los colores del tema (fondo de campo, texto y borde por variables). */
.auto-refresh { display: inline-flex; align-items: center; gap: 0.4rem; margin-left: auto; }
.auto-refresh-select {
  width: auto;
  padding: 0.25rem 0.5rem;
  font-size: 0.85rem;
  background: var(--input-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.auto-refresh-select:hover, .auto-refresh-select:focus { border-color: var(--muted); }

/* Autorefresco como boton split (POC 0.14.0): recargar (icono, con pastilla del periodo activo) + caret pegado que
   abre las opciones. Reutiliza el desplegable de `.export-menu`. */
.auto-refresh-split { position: relative; }
.auto-refresh-split .split-btn { display: inline-flex; align-items: center; }
.auto-refresh-split .split-refresh { margin-left: 0; border-top-right-radius: 0; border-bottom-right-radius: 0; }
.auto-refresh-split .split-refresh.has-badge { gap: 0.35rem; padding-right: 0.5rem; }
.auto-refresh-split .ar-badge {
  font-size: 0.7rem; line-height: 1; padding: 0.12rem 0.34rem; border-radius: 999px;
  background: var(--accent); color: #fff; font-weight: 600;
}
.auto-refresh-caret {
  display: inline-flex; align-items: center; align-self: center;
  padding: 0.35rem 0.3rem; background: none; color: var(--muted);
  border: 1px solid var(--border); border-left: none; border-radius: 0 8px 8px 0; font-size: 0.7rem;
}
.auto-refresh-caret:hover, .auto-refresh-caret[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); filter: none; }
.auto-refresh-split .auto-refresh-list { right: 0; left: auto; }
/* Opciones en rejilla de 3 columnas: fila 1 «Desactivar» a lo ancho, filas 2-3 los seis periodos (3 por fila).
   Doble clase para ganar a `.export-menu-list` (flex column), definido despues en este fichero. */
.auto-refresh-list.auto-refresh-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.25rem; padding: 0.35rem; min-width: 12rem; }
.auto-refresh-list.auto-refresh-grid .auto-refresh-item { justify-content: center; align-items: center; text-align: center; padding: 0.35rem 0.4rem; border-radius: 6px; white-space: nowrap; }
.auto-refresh-list.auto-refresh-grid .auto-refresh-item strong { font-weight: normal; font-size: 0.78rem; }
.auto-refresh-list.auto-refresh-grid .ar-off { grid-column: 1 / -1; }
.auto-refresh-list.auto-refresh-grid .auto-refresh-item.active { background: var(--accent); }
.auto-refresh-list.auto-refresh-grid .auto-refresh-item.active strong { color: #fff; }

/* Botones de alternar de una franja (rangos 1 h / 24 h / 7 dias, zoom de CrocoPulso...): MISMA medida que
   los de paginacion de CrocoTable (`.ct-count-btn`, «Mostrar: 15 30 50 100»), por decision del propietario.
   Los botones de TEXTO que viven en una franja (.secondary, .tab-alta, .ghost, .danger: exportar, nueva
   asignacion...) conservan la medida comun de `button`. */
.ranges { display: flex; gap: 0.4rem; }
/* Barra de tramos (CrocoRango) suelta: siempre a la derecha; envuelve en pantallas estrechas. */
.ranges-barra { justify-content: flex-end; flex-wrap: wrap; row-gap: 0.4rem; }
.ranges a {
  padding: 0.25rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.8rem;
}
.ranges a:hover { color: var(--fg); text-decoration: none; }
.ranges a.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.ranges button:not(.secondary):not(.tab-alta):not(.ghost):not(.danger):not(.export-menu-item):not(.split-refresh):not(.auto-refresh-caret) {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.55rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  font-size: 0.8rem;
}
.ranges button:not(.secondary):not(.tab-alta):not(.ghost):not(.danger):not(.export-menu-item):not(.split-refresh):not(.auto-refresh-caret):hover:not(:disabled) { color: var(--fg); border-color: var(--muted); filter: none; }
.ranges button:not(.secondary):not(.tab-alta):not(.ghost):not(.danger):not(.export-menu-item):not(.split-refresh):not(.auto-refresh-caret).active { background: var(--accent); border-color: var(--accent); color: #fff; }
.ranges button:disabled { opacity: 0.35; cursor: default; }
/* Botón de alta: solo «+» (lo que hace va en su tooltip). Ancho mínimo para que no quede como una rendija. */
.tab-alta.tab-alta-plus { min-width: 2.1rem; justify-content: center; font-weight: 600; }

.ranges-sep { width: 1px; height: 18px; background: var(--border); margin: 0 0.35rem; align-self: center; }

/* Selector de rango inerte: la vista activa no depende de la ventana temporal
   (p. ej. una foto del estado actual). Se sigue viendo —el rango sigue vigente
   para las demás pestañas— pero no se puede accionar. El contenedor conserva sus
   eventos de ratón para poder explicar el motivo en su tooltip. */
.ranges.inerte { opacity: 0.4; }
.ranges.inerte > * { pointer-events: none; }

.dia-label {
  display: inline-flex;
  align-items: center;
  align-self: center;
  margin-right: 0.2rem;
  font-size: 0.85rem;
}

.rango-form { display: flex; align-items: flex-end; gap: 0.8rem; flex-wrap: wrap; margin: 0.5rem 0; }
.rango-form label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.9rem; color: var(--muted); }
.rango-form input[type="date"] {
  padding: 0.45rem 0.6rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 0.9rem;
  color-scheme: dark;
}
:root[data-theme="light"] .rango-form input[type="date"] { color-scheme: light; }
/* .rango-form button: medida comun de los botones de texto (regla base button). */

.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(105px, 1fr));
  gap: 0.6rem;
}
.stat {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.7rem 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.stat .num { font-size: 1.25rem; font-weight: 600; }
.stat .lbl { font-size: 0.78rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Coletilla pequeña junto al número grande de una card (misma línea, sin saltar) */
.stat .num { white-space: nowrap; }
.stat .num-coletilla { font-size: 0.78rem; font-weight: 400; color: var(--muted); white-space: nowrap; }
.stat.accepted .num, .stat.opened .num, .stat.clicked .num { color: var(--ok); }
.stat.failed .num, .stat.expired .num { color: var(--error); }
.stat.pospuesto .num, .stat.reintento .num { color: var(--warn); }
/* Tarjetas de Estado de la BD: llevan texto, no cifras. La versión de MariaDB
   (`11.8.4-MariaDB-ubu2404`) no cabe en una línea y se salía de la tarjeta. Va
   DESPUÉS de `.stat .num { white-space: nowrap }` de arriba a propósito: misma
   especificidad, y en CSS gana la última regla del fichero. */
.bd-stat .num { white-space: normal; overflow-wrap: anywhere; line-height: 1.25; }

.panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem 1.2rem;
  font-size: var(--text-size);
}
.panel h2 { margin: 0 0 0.8rem; font-size: 1.05rem; }

.tabs { display: flex; gap: 0.3rem; margin-bottom: 1rem; border-bottom: 1px solid var(--border); }
.tab {
  padding: 0.5rem 1rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  color: var(--muted);
  font-size: 0.95rem;
}
.tab:hover { color: var(--fg); filter: none; }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
/* Pestañas que son enlaces entre páginas (Distribución: Paquetes / Asignaciones / Resultados). */
a.tab { text-decoration: none; display: inline-block; }
a.tab:hover, a.tab:focus-visible { text-decoration: none; }
.tab-badge {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.5;
  vertical-align: 0.1em;
}
/* Badge «?»: la pestaña no ha consultado desde que cambiaron los filtros, así que
   no hay número que dar. Se pinta apagado para que no se confunda con una cuenta. */
/* Contador informativo (Equipos: Activos y Todos): gris; el azul queda para lo que pide atención. */
/* Badge de la entrada «Equipos» del menu: los pendientes, a la vista desde cualquier pagina (0.45.0). */
.menu-badge { margin-left: 0.35rem; vertical-align: 0.05em; }

.tab-badge-neutral {
  background: var(--border);
  color: var(--fg);
}
.tab-badge-incognita {
  background: var(--muted);
  color: var(--bg);
}

/* Fila de acciones sobre una tabla (altas, acciones multi-fila...): botones a la derecha */
.tabla-acciones {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
/* Sin esto el `display: flex` de arriba anula el atributo `hidden` (p. ej. Cancelar/Guardar
   de la pestaña Config de la ficha, que solo se muestran con cambios sin guardar). */
.tabla-acciones[hidden] { display: none; }
/* Pie de un panel o formulario: acciones abajo a la derecha, separadas del contenido. */
.form-footer { margin-top: 0.9rem; margin-bottom: 0; }

.tab-alta {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.8rem;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 8px;
  color: #fff;
  font-size: 0.9rem;
}
/* El botón de guardar de un ajuste arranca deshabilitado (nada que guardar
   todavía): sin este estado se veía igual de azul y activo que uno con cambios
   pendientes. */
.tab-alta:disabled { background: var(--muted); border-color: var(--muted); opacity: 0.5; cursor: default; filter: none; }

/* Formulario en modal (alta de regla, futuros CRUD): campo + ayuda por línea */
.form-croco { display: flex; flex-direction: column; gap: 0.8rem; margin: 0.6rem 0; }
/* Parrafos dentro de un formulario de modal: sin margen propio, la separacion la da el `gap` (con los dos sumados
   quedaba un doble salto entre parrafos; decision del propietario 2026-09-19). */
.form-croco > p { margin: 0; }
.form-croco label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.92rem; }
.form-croco label .muted { font-size: 0.8rem; }
.form-croco input[type="number"], .form-croco input[type="text"], .form-croco input[type="email"], .form-croco input[type="password"], .form-croco input[type="date"], .form-croco select, .form-croco textarea {
  padding: 0.45rem 0.6rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 0.9rem;
}
.form-croco textarea { font-family: inherit; line-height: 1.45; resize: vertical; min-height: 6rem; }
.form-croco label.checkbox { flex-direction: row; align-items: center; gap: 0.35rem; display: inline-flex; margin-right: 0.8rem; }

/* Interruptor (switch) tipo dynocheck, en azul: ON = accent, OFF = borde */
.ct-switch {
  position: relative;
  display: inline-block;
  width: 34px;
  height: 20px;
  flex: 0 0 auto;
  vertical-align: middle;
}
.ct-switch input { opacity: 0; width: 0; height: 0; }
.ct-switch .slider {
  position: absolute;
  inset: 0;
  background: var(--border);
  border-radius: 20px;
  transition: background 0.15s;
  cursor: pointer;
}
.ct-switch .slider::before {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  left: 2px;
  top: 2px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.15s;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.ct-switch input:checked + .slider { background: var(--accent); }
.ct-switch input:checked + .slider::before { transform: translateX(14px); }

/* Variante solo lectura: informa del estado pero no es clicable */
.ct-switch-ro .slider { cursor: default; opacity: 0.7; }

/* Campo de formulario con switch: etiqueta a la izquierda, switch a la derecha */
.campo-switch-fila {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.6rem;
}

/* Lista de tipos prioritarios en el modal: filtro arriba y cuerpo con scroll propio */
#prior-filtro { margin: 0.4rem 0 0.6rem; width: 260px; }
.prior-lista { max-height: 45vh; overflow-y: auto; }

/* Subrejilla 2 columnas dentro del formulario (campos cortos emparejados) */
.form-croco-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.8rem 1.2rem;
  align-items: start;
}
.form-croco-grid label { min-width: 0; }
/* Programación de una asignación: tres campos en una fila (se apilan en pantallas estrechas). */
.form-croco-grid.assign-schedule { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 640px) { .form-croco-grid.assign-schedule { grid-template-columns: 1fr; } }

.tab-refresh {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  align-self: center;
  padding: 0.35rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
}
.tab-refresh:hover { color: var(--accent); border-color: var(--accent); filter: none; }

/* Cabecera de tabs con selector de autorefresco junto al botón de refrescar */
/* Estado actual y Ubicaciones quedan fijas a la izquierda; el resto de pestañas
   (Eventos y las que le siguen) se empujan al extremo derecho, justo antes del
   separador y los controles de refresco. */
.tabs-grupo-derecha { margin-left: auto; }
.tabs-divisor { width: 1px; align-self: stretch; background: var(--border); margin: 0.4rem 0.6rem; }

/* Acciones de la fila de pestañas (refrescar, etc.): SIEMPRE a la derecha de la sección
   (norma del propietario), empujadas por margin-left:auto del propio contenedor. */
.tabs-acciones { display: flex; align-items: center; gap: 0.5rem; align-self: center; margin-left: auto; }
.tabs-acciones .tab-refresh { margin-left: 0; }

/* Quitar TODOS los filtros de la página: solo visible si hay alguno activo (en rojo) */
.tab-quitar-filtros {
  display: inline-flex;
  align-items: center;
  padding: 0.35rem;
  background: none;
  border: 1px solid var(--error);
  border-radius: 8px;
  color: var(--error);
}
.tab-quitar-filtros:hover { background: color-mix(in srgb, var(--error) 15%, transparent); filter: none; }
.tab-quitar-filtros[hidden] { display: none; }

/* Fila aparecida en el último refresco (cfg.marcarNuevas): destello de fondo que se
   apaga en 3 s. Se anima el td, no el tr: el fondo del tr queda tapado por las celdas. */
@keyframes ct-fila-nueva {
  from { background-color: color-mix(in srgb, var(--accent) 30%, transparent); }
  to { background-color: transparent; }
}
/* Se mantiene también con `prefers-reduced-motion: reduce`: es un fundido de color,
   sin movimiento ni parpadeo, y es la única señal de que la fila acaba de llegar. */
tr.ct-fila-nueva > td { animation: ct-fila-nueva 3s ease-out both; }

/* Fila con algo que mirar (`cfg.filaClase`). En JailMeNow: un equipo que lleva sin
   reportar más de N veces su cadencia.

   El fondo va en el `td` y no en el `tr` por lo mismo que el destello de arriba:
   el del `tr` queda tapado por las celdas. Color de la paleta (`--warn`) y no un
   naranja suelto, para que valga en los dos temas; al 14% tiñe sin impedir leer,
   y el hover de la fila sigue notándose porque sube al 22%. */
tr.ct-fila-avisa > td { background-color: color-mix(in srgb, var(--warn) 14%, transparent); }
tr.ct-fila-avisa:hover > td { background-color: color-mix(in srgb, var(--warn) 22%, transparent); }

/* Embudo de filtrado por celda: aparece al pasar el ratón sobre la celda.
   `inline-flex` a propósito: poner el td en flex rompería la fila de la tabla. */
.ct-cell-filtro {
  display: inline-flex;
  align-items: center;
  padding: 0;
  margin-left: 0.35rem;
  background: none;
  border: none;
  line-height: 0;
  vertical-align: middle;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.12s;
  cursor: pointer;
  flex: 0 0 auto;
}
td:hover .ct-cell-filtro { opacity: 0.75; }
.ct-cell-filtro:hover { opacity: 1 !important; color: var(--accent); filter: none; }

/* Acción de celda (editar, etc.): se comporta igual que el embudo —oculta hasta
   que el ratón entra en la celda— para que una columna pueda llevar su propia
   acción sin ensanchar la fila con un icono siempre visible. */
.ct-cell-accion {
  display: inline-flex;
  align-items: center;
  padding: 0;
  margin-left: 0.35rem;
  background: none;
  border: none;
  line-height: 0;
  vertical-align: middle;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.12s;
  cursor: pointer;
  flex: 0 0 auto;
}
td:hover .ct-cell-accion { opacity: 0.75; }
.ct-cell-accion:hover { opacity: 1 !important; color: var(--accent); filter: none; }

/* Texto que marca su fila al pulsarlo (nombre del equipo en Agentes): el cursor
   avisa de que se puede pulsar, y gana al `cursor: help` que pone el tooltip. */
.ct-celda-marca, .ct-celda-marca.ct-has-tip { cursor: pointer; }
.ct-celda-marca:hover { color: var(--accent); }
.select-autorefresco {
  padding: 0.35rem 0.55rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 0.85rem;
}

.table-wrap { overflow-x: auto; }
/* Columnas fijas (`col.fixed` de CrocoTable) y casillas de seleccion: pegadas a la izquierda al
   desplazar en horizontal. Fondo opaco para que no se transparente lo que pasa por debajo, y una
   linea a la derecha que marca el borde fijo. Si hay casillas, la fija va detras (34px). */
th.ct-sel, td.ct-sel, th.ct-fixed, td.ct-fixed { position: sticky; left: 0; z-index: 1; background: var(--card); }
/* El `left` de cada columna fija lo pone CrocoTable (`_colocarColumnasFijas`) sumando los anchos
   REALES de las fijas anteriores: admite varias seguidas (casillas + Paquete + acciones rapidas). */
/* Borde + sombra hacia la derecha: deja claro que las columnas que se desplazan pasan POR DEBAJO
   de la fija (con solo la linea de 1px, en tema oscuro parecia una columna mal pintada). */
th.ct-fixed, td.ct-fixed {
  box-shadow: inset -1px 0 0 var(--border), 8px 0 8px -8px color-mix(in srgb, var(--fg) 45%, transparent);
} /* scroll horizontal permitido: hay ocultar/truncar columnas para gestionarlo */

/* Columna de ACCIONES pegada a la DERECHA (decision del propietario 2026-09-19): al desplazar en horizontal no
   desaparece; las columnas pasan POR DEBAJO, con el mismo borde y sombra que la fija de la izquierda, en espejo. */
th.ct-actions, td.ct-actions {
  position: sticky; right: 0; z-index: 1; background: var(--card);
  box-shadow: inset 1px 0 0 var(--border), -8px 0 8px -8px color-mix(in srgb, var(--fg) 45%, transparent);
}
/* Las celdas pegadas llevan fondo opaco: el resaltado de la fila (hover, abierta, aviso) se pinta ENCIMA de ese fondo
   para que se vea igual que en el resto de la fila y no se transparente lo que pasa por debajo. */
tbody tr:hover > td:is(.ct-sel, .ct-fixed, .ct-actions) {
  background: linear-gradient(var(--table-row-hover), var(--table-row-hover)), var(--card);
}
tr.fila-abierta > td:is(.ct-sel, .ct-fixed, .ct-actions) {
  background: linear-gradient(color-mix(in srgb, var(--accent) 14%, transparent), color-mix(in srgb, var(--accent) 14%, transparent)), var(--card);
}
tr.ct-fila-avisa > td:is(.ct-sel, .ct-fixed, .ct-actions) {
  background: linear-gradient(color-mix(in srgb, var(--warn) 14%, transparent), color-mix(in srgb, var(--warn) 14%, transparent)), var(--card);
}
tr.ct-fila-avisa:hover > td:is(.ct-sel, .ct-fixed, .ct-actions) {
  background: linear-gradient(color-mix(in srgb, var(--warn) 22%, transparent), color-mix(in srgb, var(--warn) 22%, transparent)), var(--card);
}

/* Chips de columnas ocultas (clic derecho en cabecera → Ocultar). Solo con `hiddenColumnsMenu: false`; por defecto
   las ocultas van en el desplegable de la cabecera (`.ct-hidden-cols`). */
/* Columnas ocultas: a la DERECHA. Es un recordatorio de lo que se apartó, no
   contenido de la tabla; a la izquierda competía con el título y con los
   controles de la página. `justify-content` y no `margin-left: auto` en el chip:
   así se mantiene el orden al saltar de línea con muchas ocultas. */
.ct-ocultas { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 0.4rem; margin-bottom: 0.5rem; }
.ct-chip {
  padding: 0.15rem 0.55rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg);
  font-size: 0.8rem;
}
.ct-chip:hover { border-color: var(--accent); color: var(--accent); filter: none; }

/* Menú contextual de columna */
.ct-menu {
  position: fixed;
  z-index: 2100;
  min-width: 180px;
  padding: 0.25rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
}
.ct-menu button {
  padding: 0.45rem 0.7rem;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--fg);
  font-size: 0.88rem;
  text-align: left;
}
.ct-menu button:hover { background: var(--row-hover); color: var(--accent); filter: none; }
/* Opciones alineadas a la izquierda: icono del propietario y texto (columna de iconos alineada aunque falte alguno). */
.ct-menu button { display: flex; align-items: center; justify-content: flex-start; gap: 0.55rem; text-align: left; width: 100%; }
.ct-menu-icon { display: inline-flex; width: 16px; justify-content: center; flex: none; }
.ct-menu-icon svg { width: 16px; height: auto; max-height: 16px; }

/* Dato truncado (menú de columna → Truncar dato) */
td.ct-trunc { max-width: 12ch; overflow: hidden; text-overflow: ellipsis; }

/* Columna de selección de filas (CrocoTable selectable): checkbox estrecho, centrado. */
th.ct-sel, td.ct-sel { width: 34px; text-align: center; padding-left: 0; padding-right: 0; }
th.ct-sel input, td.ct-sel input { cursor: pointer; margin: 0; }

table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
th, td { padding: 0.25rem 0.6rem; text-align: left; white-space: nowrap; }
/* La columna de nombre se ajusta a su contenido; solo recorta (ellipsis + tooltip)
   nombres excepcionalmente largos, para no romper el ancho de la tabla. */
td.ct-ellipsis { max-width: 340px; overflow: hidden; text-overflow: ellipsis; }
th { color: var(--muted); font-weight: 500; border-bottom: 1px solid var(--border); }
tbody tr { border-bottom: 1px solid var(--row-border); }
tbody tr:hover { background: var(--table-row-hover); }
th.r, td.r { text-align: right; }

/* ---- CrocoTable (control reutilizable) ---- */

/* Carga: la tabla se atenúa y aparece un spinner centrado sobre ella, para que
   un refresco (cambio de rango, orden, página) no parezca un parpadeo. */
.crocotable { position: relative; }
.crocotable.ct-loading { opacity: 0.6; transition: opacity 0.15s; }
.crocotable.ct-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: croco-spin 0.7s linear infinite;
  pointer-events: none;
}

@keyframes croco-spin { to { transform: rotate(360deg); } }

.ct-th { display: flex; flex-direction: column; gap: 0.3rem; }
th.r .ct-th { align-items: flex-end; }

.ct-sortable { cursor: pointer; user-select: none; }
.ct-sortable:hover { color: var(--fg); }

/* La columna por la que se ordena, ENTERA en azul: la flecha sola es un icono de
   14px perdido en una fila de cabeceras y no se encuentra de un vistazo. */
.ct-sortable.ct-orden-activo, .ct-sortable.ct-orden-activo:hover { color: var(--accent); }

/* La flecha solo se ve en la columna activa; en las demás aparece al pasar el
   ratón por la cabecera —una flecha apagada fija se lee como "ordenada por aquí
   pero descolorida"—. Misma idea que el embudo de filtrar por celda. */
.ct-arrow { display: inline-flex; align-items: center; margin-left: 0.25rem; line-height: 0; opacity: 0; transition: opacity 0.12s; }
th:hover .ct-arrow { opacity: 0.45; }
.ct-arrow.active { opacity: 1; color: var(--accent); }

.ct-filter {
  width: 100%;
  /* Sin ancho minimo (2026-09-23): el input mengua con la columna al redimensionarla; el tope lo pone el titulo. */
  min-width: 0;
  box-sizing: border-box;
  padding: 0.25rem 0.45rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  /* Norma del propietario: el texto del filtro va LEVEMENTE por debajo del de las celdas (`table` = 0.88rem). */
  font-size: 0.78rem;
}
/* El input de filtro es `type="search"`, y la regla generica `input[type="search"]` (especificidad 0,1,1) pisa a
   `.ct-filter` (0,1,0): el VALOR escrito salia con 0.95rem (mayor que la tabla) mientras el placeholder si obedecia.
   Se iguala la especificidad para que manden estos 0.78rem. */
input.ct-filter,
input.ct-filter::placeholder { font-size: 0.78rem; }
/* Y lo mismo con el relleno (0,6rem en la regla generica): la caja de texto medía 35 px frente a los 26 px del selector
   de valores preestablecidos; con el relleno de `.ct-filter` las dos miden lo mismo (propietario 2026-10-09). */
input.ct-filter { padding: 0.25rem 0.45rem; border-radius: 6px; }
.ct-filter:focus { outline: 1px solid var(--accent); border-color: var(--accent); }

/* Caja del filtro de columna: el input (la sintaxis del filtro, comas y `!`, va en SU tooltip desde el 2026-09-23;
   antes en un «?» flotante) y la «×» de borrar. */
/* `width: 100%`: `.ct-th` es flex-column y las columnas alineadas a la derecha
   llevan `align-items: flex-end`, que encogería la caja al ancho de su contenido
   y con ella el input (que mide el 100% de la caja). */
.ct-filter-box { position: relative; display: block; width: 100%; }
.ct-filter-box .ct-filter { padding-right: 0.45rem; }
/* Norma del propietario (todas las tablas): la «×» de borrar el filtro JUSTO detrás del texto escrito, solo si hay
   texto: [texto ×    ]. La «x» nativa de `input type="search"` se oculta; la «×» es un botón propio que crocotable.js
   coloca midiendo el texto. Con texto se reserva a la derecha el sitio de la «×», para que un texto largo nunca quede
   debajo. */
.ct-filter-box .ct-filter:not(:placeholder-shown) { padding-right: calc(0.3rem + 14px); }
.ct-filter::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; display: none; }
.ct-filter-borrar {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
}
.ct-filter-borrar:hover { color: var(--fg); background: var(--row-hover); filter: none; }

/* Filtro de columna por SELECCION (`col.filterPicker`, propietario 2026-10-08, nacido en «Etiquetas»): resumen de
   solo lectura con la pinta del input («3 etiquetas»; vacio = «Cualquiera» apagado) + la «×» en linea detras del
   texto + engranaje a la derecha que abre el modal de casillas. */
.ct-filter-pick { display: flex; align-items: center; }
/* Resumen + «×» + engranaje, todo dentro de la misma caja que un filtro de texto; el engranaje pegado a la derecha
   (propietario 2026-10-09). */
.ct-pick-value { flex: 1 1 auto; display: flex; align-items: center; gap: 0.3rem; min-width: 0; line-height: 1.1; cursor: pointer; user-select: none; }
.ct-filter-box .ct-pick-value { padding-right: 0.3rem; }
.ct-pick-value:focus-visible { outline: 1px solid var(--accent); border-color: var(--accent); }
.ct-pick-value.ct-pick-empty .ct-pick-text { color: var(--muted); }
.ct-pick-text { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-pick-value .ct-filter-borrar { position: static; transform: none; flex: 0 0 auto; }
.ct-pick-settings { flex: 0 0 auto; margin-left: auto; line-height: 0; }
/* Mas especifico que `.ct-info-btn svg` (16 px): el engranaje cabe en la caja sin estirarla. */
.ct-pick-value .ct-pick-settings svg { display: block; width: 14px; height: 14px; }
.ct-pick-modal .ct-pick-help { margin: 0 0 0.6rem; }
.ct-pick-tools { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; margin-bottom: 0.6rem; }
.ct-pick-clear { font-size: 0.82rem; padding: 0.25rem 0.6rem; }
.ct-pick-list { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; max-height: 16rem; overflow: auto; padding: 0.2rem 0; }
.ct-pick-list label.checkbox, .ct-pick-modal .tags-picker label.checkbox { margin: 0; }
/* Selector de valores preestablecidos (estado, nivel, SO…): una casilla por linea, que son pocas y se leen de arriba abajo. */
.ct-pick-list.enum-picker { flex-direction: column; gap: 0.3rem; }
.ct-pick-list.enum-picker label.checkbox { align-items: center; }

.ct-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.8rem;
}
.ct-info { min-width: 160px; }
.ct-middle { display: flex; align-items: center; gap: 0.25rem; flex-wrap: wrap; justify-content: center; }
.ct-sep { width: 1px; height: 16px; background: var(--border); margin: 0 0.6rem; }
.ct-nav { display: flex; align-items: center; gap: 0.25rem; min-width: 160px; justify-content: flex-end; }
.ct-pagelbl { padding: 0 0.5rem; }

.ct-count-btn {
  padding: 0.25rem 0.55rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  font-size: 0.8rem;
}
.ct-count-btn:hover { color: var(--fg); border-color: var(--muted); filter: none; }
/* Tamano de pagina elegido: gris del tema (--page-active-bg), sin competir con el «+» azul (propietario 2026-09-17). */
.ct-count-btn.active { background: var(--page-active-bg); border-color: var(--muted); color: var(--fg); font-weight: 600; }

.ct-page-btn {
  display: flex;
  align-items: center;
  padding: 0.3rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
}
.ct-page-btn:hover:not(:disabled) { color: var(--fg); border-color: var(--muted); filter: none; }
.ct-page-btn:disabled { opacity: 0.35; cursor: default; }

/* Botón de ayuda (?) junto a títulos: alterna un panel explicativo */
.help-btn {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: 0.4rem;
  padding: 0.15rem;
  background: none;
  border: none;
  border-radius: 50%;
  color: var(--muted);
}
.help-btn:hover { color: var(--accent); filter: none; }
.help-btn.active { color: var(--accent); }

.ct-has-tip { cursor: help; }
.ct-sortable.ct-has-tip, .ct-count-btn.ct-has-tip, .ct-page-btn.ct-has-tip { cursor: pointer; }
.ct-ellipsis { cursor: default; }

/* Tooltip flotante compartido (estilo ARPShield; sustituye al title nativo) */
.ct-tooltip {
  position: fixed;
  z-index: 2000;
  max-width: 340px;
  padding: 0.45rem 0.65rem;
  background: var(--tip-bg);
  border: 1px solid var(--accent);
  border-radius: 8px;
  color: var(--fg);
  font-size: 0.82rem;
  line-height: 1.35;
  white-space: pre-line; /* respeta \n (tooltips multilínea, p. ej. leyenda de estados) */
  word-break: break-word;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.ct-tooltip.visible { visibility: visible; opacity: 1; }
/* Tooltip con formato (CrocoTable.tipLines): etiquetas siempre subrayadas, valores no; `ancho` para huellas y rutas. */
.ct-tooltip .tip-label { text-decoration: underline; text-underline-offset: 2px; }
.ct-tooltip.ancho { max-width: min(44rem, calc(100vw - 16px)); }

/* Celda con nodo extra tras el texto (p. ej. enlace al panel de core): el texto
   recorta con ellipsis y el extra queda separado 30px sin salirse de la celda. */
td.ct-flex { display: flex; align-items: center; gap: 6px; }
td.ct-flex .ct-ellipsis { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-ext-link { display: inline-flex; align-items: center; flex: 0 0 auto; line-height: 1; color: var(--muted); }
.ct-ext-link:hover { color: var(--accent); }
.ct-ext-links { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.icono-ok { color: var(--ok); }
/* Copiar IP: solo se ve al pasar el ratón por la fila, igual que el embudo de filtro. */
.ct-copy-ip { opacity: 0; transition: opacity 0.12s; }
tr:hover .ct-copy-ip { opacity: 1; }

/* Columna de acciones + botón de info por fila */
th.ct-actions, td.ct-actions { width: 44px; text-align: center; }
.ct-info-btn {
  display: inline-flex;
  align-items: center;
  padding: 0; /* sin padding: que no infle la altura de la fila */
  line-height: 1;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--muted);
}
.ct-info-btn:hover { color: var(--accent); background: rgba(47, 129, 247, 0.12); filter: none; }
/* Correo ya marcado prioritario (Recientes): icono fijo en ámbar, sin acción */
.ct-prio-on, .ct-prio-on:hover { color: var(--warn); background: none; cursor: default; }

/* Modal (CrocoModal) */
.ct-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: var(--overlay-bg);
  backdrop-filter: blur(2px);
}
.ct-modal {
  width: 100%;
  max-width: 560px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
.ct-modal.ct-modal-wide { max-width: 860px; }

/* Modal de la configuración efectiva de fail2ban: 1200px, con tope de pantalla
   para que en un portátil no se salga. Más ancho que el estándar porque el
   documento son regex y comandos de iptables, y cuanto menos se parta, menos hay
   que reconstruir mentalmente; el corte marcado (`↵`) se encarga del resto.

   OJO: NO se encadena con `.ct-modal-wide`. Esa clase la pone `CrocoModal.accion`
   (los modales con botonera), no `CrocoModal.open`, así que un selector que la
   exigiera no aplicaría nunca aquí. Va DESPUÉS de la regla de `wide` para ganar
   también si algún día este modal pasara por `accion`. */
.ct-modal.ct-modal-config { max-width: min(1200px, 96vw); }
.ct-modal-head { padding: 1rem 1.2rem; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 0.6rem; }
.ct-modal-head h3 { margin: 0; font-size: 1.05rem; }
.ct-modal-x {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--muted);
}
.ct-modal-x:hover { color: var(--fg); background: var(--row-hover); filter: none; }

/* Modal de ayuda (CrocoModal.ayuda): término subrayado + explicación */
.ct-ayuda { margin: 0; }
.ct-ayuda dt { margin-top: 0.7rem; }
.ct-ayuda dt:first-child { margin-top: 0; }
/* Dos estilos, nunca mezclados en un mismo modal: puntos numerados en negrita;
   glosarios de términos subrayados sin negrita. */
.ct-ayuda dt.ct-ayuda-num { font-weight: 600; }
.ct-ayuda dt.ct-ayuda-termino { text-decoration: underline; }

/* Ítem destacado del modal de ayuda: nota importante con fondo suave */
.ct-ayuda-destacado {
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.6rem 0.8rem;
  margin-top: 0.8rem;
}
.ct-ayuda-destacado dt { margin-top: 0; }
.ct-ayuda dd { margin: 0.15rem 0 0; color: var(--muted); font-size: 0.9rem; }

/* Icono de ayuda dentro de una cabecera de columna (col.help) */
.ct-th-help {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: 0.3rem;
  padding: 0.1rem;
  background: none;
  border: none;
  color: var(--muted);
}
.ct-th-help:hover { color: var(--accent); filter: none; }
/* Botón de cambio de vista dentro de una cabecera de columna (col.headerExtra): a la derecha del título, como el
   «ver en plano / ver en árbol» de la columna «Grupo» de Equipos (0.51.0). */
.ct-th-toggle {
  float: right;
  display: inline-flex;
  align-items: center;
  margin-left: 0.4rem;
  padding: 0.1rem;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
}
.ct-th-toggle svg { width: 16px; height: 16px; }
.ct-th-toggle:hover { color: var(--accent); filter: none; }
.ct-modal-body { padding: 1rem 1.2rem; overflow-y: auto; font-size: var(--text-size); }
.ct-modal-body p:first-child { margin-top: 0; }
/* Pie de CUALQUIER modal: botones juntos abajo a la derecha (norma del propietario). Los botones
   del pie (Cerrar, Cancelar, Aceptar) usan la MEDIDA COMUN de la regla base `button`: aqui solo
   se fija su color, nunca padding ni font-size. */
.ct-modal-foot { padding: 0.8rem 1.2rem; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; align-items: center; gap: 0.5rem; }
.ct-modal-close { background: var(--accent); color: #fff; }
.ct-modal-close:hover { filter: brightness(1.1); }

/* Pie de modal de ACCIÓN: Cancelar (gris) a la izquierda de Aceptar, los dos a la derecha. */
.ct-modal-foot-accion { justify-content: flex-end; }

/* Resumen en vivo junto al botón principal (p. ej. "(2 nuevos, 1 quitado) total 7 activos") */
.ct-foot-resumen {
  margin-left: auto;
  margin-right: 0.8rem;
  align-self: center;
  color: var(--muted);
  font-size: 0.85rem;
}
/* Cancelar del modal = el gris de `button.ghost`; Aceptar = el azul de la regla base. Medida comun. */
.ct-modal-cancelar { background: none; border: 1px solid var(--border); color: var(--muted); }
.ct-modal-cancelar:hover { color: var(--fg); border-color: var(--muted); filter: none; }
.ct-modal-aceptar { background: var(--accent); color: #fff; }
.ct-modal-aceptar:hover:not(:disabled) { filter: brightness(1.1); }
.ct-modal-aceptar:disabled { opacity: 0.4; cursor: default; }

/* ---- Pulso de envíos (gráfica horizontal de líneas) ---- */
.pulso-wrap { position: relative; }
.pulso-canvas {
  display: block;
  width: 100%;
  height: 128px;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: crosshair;
}
/* Eje inferior en 3 zonas: leyenda (izda) · lectura del ratón (centro) · agrupación (dcha). */
.pulso-eje {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 0.35rem;
  color: var(--muted);
  font-size: 0.78rem;
}
.pulso-grupos { display: flex; gap: 0.4rem; }
.pulso-grupo {
  padding: 0.25rem 0.6rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  font-size: 0.78rem;
}
.pulso-grupo:hover { color: var(--fg); border-color: var(--muted); filter: none; }
.pulso-grupo.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.pulso-grupo[hidden] { display: none; }
.pulso-leyenda { display: flex; flex-wrap: wrap; gap: 0.25rem 1.1rem; align-items: center; color: var(--muted); font-size: 0.8rem; }
.pulso-leyenda i { display: inline-block; width: 3px; height: 12px; margin-right: 0.35rem; vertical-align: -2px; }
.pulso-serie {
  padding: 0;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 0.8rem;
  cursor: pointer;
}
.pulso-serie:hover { color: var(--fg); filter: none; }
.pulso-serie.apagada { opacity: 0.35; }
/* Icono de gráfica en una cabecera de acordeón que no monta CrocoPulso (p. ej. Actividad de los agentes). */
.pulso-icono { display: inline-flex; vertical-align: middle; margin-right: 0.35rem; }
/* Serie oculta por superar el tope (PULSE_SERIES_MAX): tachada; su tooltip da el motivo. */
.pulso-serie.excedida { opacity: 0.5; text-decoration: line-through; }
/* Total de cada serie en su chip de la leyenda (lo pone `cfg.formatoTotal`): mas apagado que el nombre. */
.pulso-serie-total { margin-left: 0.3rem; opacity: 0.75; font-variant-numeric: tabular-nums; }
/* Selector de modo de la grafica (Carriles / En uso / Apps): mismos botonicos que la agrupacion. */
.pulso-modos { display: flex; gap: 0.3rem; }

/* Acordeón (details/summary) para paneles plegables como las gráficas del home. */
.acordeon > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  list-style: none;
  color: var(--fg);
  font-size: 1.05rem;
  font-weight: 600;
}
.acordeon > summary::-webkit-details-marker { display: none; }
.acordeon > summary::before {
  content: "▸";
  color: var(--muted);
  font-size: 0.9rem;
}
.acordeon[open] > summary::before { content: "▾"; }
.acordeon > summary:hover { color: var(--accent); }
/* Linea de descripcion de una seccion (Configuracion y demas acordeones): pegada a su titulo y a lo que explica
   (decision del propietario 2026-09-22); el margen de parrafo del navegador gastaba un renglon entero. */
.acordeon > p.muted.small,
.panel > p.muted.small { margin: 0.15rem 0 0.4rem; }
.acordeon .pulso { margin-top: 0.8rem; }
.icono-grafica { flex: none; }

/* Tarjeta con desglose: una línea por elemento (clave a la izquierda, valor a la
   derecha) en lugar de un único número grande. */
.stat-lista .lineas { display: flex; flex-direction: column; gap: 0.15rem; }
.stat-lista .linea {
  display: flex;
  justify-content: space-between;
  gap: 0.8rem;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}
.stat-lista .linea .k { color: var(--fg); }
.stat-lista .linea .v { color: var(--fg); font-weight: 600; }
.stat-lista .linea.muted .k, .stat-lista .linea.muted { color: var(--muted); }

/* Filtro de las gráficas por fila (tabla "Por partner"): gris = fuera del filtro,
   en color = incluido en las gráficas. */
.pulso-filtro { padding: 0; background: none; border: none; line-height: 0; cursor: pointer; }
.pulso-filtro .icono-grafica { filter: grayscale(1); opacity: 0.45; transition: filter 0.15s, opacity 0.15s; }
.pulso-filtro:hover { filter: none; }
.pulso-filtro:hover .icono-grafica { opacity: 0.75; }
.pulso-filtro.activo .icono-grafica { filter: none; opacity: 1; }
/* Indicador en la cabecera: solo aparece si hay partners filtrados. */
.pulso-filtro-quitar { padding: 0; margin-left: 0.35rem; background: none; border: none; line-height: 0; }
.pulso-filtro-quitar[hidden] { display: none; }

/* ==== IRM: clases propias del portal (hito 5) =============================== */

/* Badge de estado, generalizado para devices.status, tasks.status y
   agent_update_results.status. Colores solo por variables del tema. */
.status-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap; }
.status-badge.status-active,
.status-badge.status-ok,
.status-badge.status-applied { color: var(--ok); background: color-mix(in srgb, var(--ok) 15%, transparent); }
.status-badge.status-pending,
.status-badge.status-queued,
.status-badge.status-offered,
.status-badge.status-downloaded { color: var(--warn); background: color-mix(in srgb, var(--warn) 15%, transparent); }
.status-badge.status-running { color: var(--accent); background: color-mix(in srgb, var(--accent) 15%, transparent); }
.status-badge.status-disabled,
.status-badge.status-retired,
.status-badge.status-cancelled,
.status-badge.status-expired { color: var(--muted); background: color-mix(in srgb, var(--muted) 15%, transparent); }
.status-badge.status-error,
.status-badge.status-timeout,
.status-badge.status-hash_mismatch,
.status-badge.status-download_error,
.status-badge.status-signature_invalid,
.status-badge.status-rolled_back { color: var(--error); background: color-mix(in srgb, var(--error) 15%, transparent); }
/* Formulario compacto en rejilla (p. ej. editar/crear paquete): 3 columnas; `.span-2` ocupa dos y
   `.span-all` la fila entera. Las etiquetas son `display: flex`, que anularia `hidden`: de ahi la
   regla explicita. En pantallas estrechas pasa a una columna. */
.form-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.7rem 0.8rem; margin: 0.6rem 0; }
.form-grid-3 > .span-2 { grid-column: span 2; }
.form-grid-3 > .span-all { grid-column: 1 / -1; }
.form-grid-3 > [hidden] { display: none; }
.form-grid-3 label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.9rem; }
.form-grid-3 .field-head { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Todos los controles con el tema (antes el desplegable y el numerico salian con el estilo nativo del navegador). */
.form-grid-3 input, .form-grid-3 select {
  width: 100%;
  box-sizing: border-box;
  padding: 0.45rem 0.6rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-size: 0.9rem;
}
@media (max-width: 560px) {
  .form-grid-3 { grid-template-columns: 1fr; }
  .form-grid-3 > .span-2 { grid-column: auto; }
}
/* Circulo de estado (`Portal.statusDotNode`): mismos colores que la pastilla; el estado va en el tooltip. */
.status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; vertical-align: middle; background: var(--muted); }
.status-dot.status-active, .status-dot.status-ok, .status-dot.status-applied { background: var(--ok); }
.status-dot.status-pending, .status-dot.status-queued, .status-dot.status-offered, .status-dot.status-downloaded { background: var(--warn); }
.status-dot.status-running { background: var(--accent); }
.status-dot.status-disabled, .status-dot.status-retired, .status-dot.status-cancelled, .status-dot.status-expired { background: var(--muted); }
.status-dot.status-error, .status-dot.status-timeout, .status-dot.status-hash_mismatch, .status-dot.status-download_error,
.status-dot.status-signature_invalid, .status-dot.status-rolled_back { background: var(--error); }

/* Iconos de software (pestaña Software de la ficha): PNG 16x16 del producto o, si aún no hay,
   un círculo gris de 16x16 con el color del tema (igual en claro y oscuro). */
.sw-name { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; }
.sw-name .sw-name-text { overflow: hidden; text-overflow: ellipsis; }
.sw-icon { flex: 0 0 16px; width: 16px; height: 16px; }
.sw-icon-none { display: inline-block; border-radius: 50%; background: var(--muted); opacity: 0.55; }

/* Barra de acciones sobre una tabla (botones a la izquierda, contador de seleccion). */
.actions-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-bottom: 0.6rem; }
.actions-bar .spacer { flex: 1 1 auto; }
/* .actions-bar button: medida comun de los botones de texto (regla base button). */
button.secondary { background: var(--card); border: 1px solid var(--border); color: var(--fg); }
button.secondary:hover:not(:disabled) { border-color: var(--accent); filter: none; }
button.danger { background: color-mix(in srgb, var(--error) 15%, transparent); border: 1px solid var(--error); color: var(--error); }
button.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--error) 28%, transparent); filter: none; }
button:disabled { opacity: 0.5; cursor: default; }

/* Barra de progreso (subidas, rollout). Colores del tema. */
.progress-bar { width: 100%; height: 0.6rem; border-radius: 999px; background: var(--border); overflow: hidden; }
.progress-bar > span { display: block; height: 100%; background: var(--accent); transition: width 0.15s; }
progress.progress-bar { appearance: none; border: none; }
progress.progress-bar::-webkit-progress-bar { background: var(--border); border-radius: 999px; }
progress.progress-bar::-webkit-progress-value { background: var(--accent); border-radius: 999px; }
progress.progress-bar::-moz-progress-bar { background: var(--accent); border-radius: 999px; }

/* Tabla clave-valor recursiva de los bloques de inventario (Hardware, SO, Red...). Compacta (propietario 2026-09-19):
   0.1rem arriba y abajo por celda. */
/* Secciones de inventario lado a lado (decision del propietario 2026-09-22): a la derecha de una seccion ancha
   (equipo, modulos de memoria, BIOS) va otra corta (chasis, total de memoria, TPM) con su nombre delante. Si no
   cabe, baja debajo en vez de estrujar la de la izquierda. */
/* Columna Disco de Equipos: el tamano y, pegada debajo, una barra con lo ocupado en sus volumenes (decision del
   propietario 2026-09-22). Fina y SIN cambiar el alto de la fila: lo que ocupa (2 px de barra + 1 de separacion) se
   devuelve con el margen negativo de abajo. Colores del tema; el dato exacto y de cuando es, en el tooltip. */
.disk-cell { display: inline-flex; flex-direction: column; align-items: stretch; min-width: 5.5rem; }
.disk-bar { display: block; height: 2px; margin: 1px 0 -3px; border-radius: 1px; background: var(--border); overflow: hidden; }
.disk-bar-fill { display: block; height: 100%; background: var(--accent); }
/* Color por ocupacion (propietario 2026-10-01): cuanto menos libre queda, mas contraste. Hasta el 75 % azul; desde el
   75 % naranja; desde el 90 % rojo; por encima del 95 % rojo fuerte, mas grueso y con el carril oscurecido para que
   salte a la vista. */
.disk-bar.disk-warn .disk-bar-fill { background: var(--warn); }
.disk-bar.disk-high .disk-bar-fill { background: var(--error); }
.disk-bar.disk-crit { height: 3px; margin-bottom: -4px; background: color-mix(in srgb, var(--error) 35%, var(--border)); }
.disk-bar.disk-crit .disk-bar-fill { background: var(--error); box-shadow: 0 0 3px var(--error); }
/* Con la columna de ancho fijado (`td.ct-resized > .ct-clip`, que recorta con overflow hidden) la barra caia en los 3 px
   del margen negativo y se recortaba (aviso del propietario 2026-10-01): el recorte se ensancha por abajo esos px y
   el margen negativo los devuelve, asi que la fila sigue midiendo lo mismo y la barra se ve. */
td.ct-resized > .ct-clip:has(> .disk-cell) { padding-bottom: 4px; margin-bottom: -4px; }

/* Cabecera de columna con icono (`col.icon`, p. ej. la lupa de «Software» en Equipos): el SVG delante del titulo,
   del tamano del texto y del mismo color. */
.ct-th-con-icono { display: inline-flex; align-items: center; gap: 0.3rem; }
.ct-th-con-icono svg { width: 13px; height: 13px; }

.kv-aside-row { display: flex; align-items: flex-start; gap: 0.6rem; flex-wrap: wrap; }
/* La seccion de la derecha se separa bien de la tabla que tiene al lado y su nombre se lee igual que el de las
   secciones de la izquierda: mayusculas pequenas (decision del propietario 2026-09-22). */
.kv-aside { display: flex; align-items: baseline; gap: 1rem; min-width: 0; padding-left: 2rem; }
.kv-aside-label { color: var(--muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; }

.kv-grid { width: auto; border-collapse: collapse; font-size: 0.9rem; }
/* Separacion entre columnas (bloques en lista de la ficha: parches, usuarios...): 1rem a la derecha de
   cada celda, sin sobrante tras la ultima, para que los valores no se peguen («KB5027397Update»). */
.kv-grid th { text-align: left; padding: 0.1rem 1rem 0.1rem 0; color: var(--muted); font-weight: 500; vertical-align: top; white-space: nowrap; border: none; }
.kv-grid td { padding: 0.1rem 1rem 0.1rem 0; vertical-align: top; border: none; overflow-wrap: anywhere; }
.kv-grid th:last-child, .kv-grid td:last-child { padding-right: 0; }
.kv-grid tr:hover { background: transparent; }
.kv-grid .kv-grid { margin: 0 0 0.15rem; }
.kv-grid .kv-index { color: var(--muted); font-variant-numeric: tabular-nums; }
/* Fila de cabeceras de las tablas de la ficha (objetos y listas): linea debajo, gris mas tenue que el texto de la
   cabecera (propietario 2026-09-19). */
.kv-grid tr.kv-head > th { border-bottom: 1px solid color-mix(in srgb, var(--muted) 40%, transparent); }
/* Secciones de un bloque de la ficha (device, chassis, memory...): titulo en MAYUSCULAS y mas aire debajo de cada una
   (propietario 2026-09-19). Solo el primer nivel: las tablas anidadas siguen compactas. */
.kv-sections > tbody > tr > th { text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.78rem; padding-top: 0.2rem; }
.kv-sections > tbody > tr > th, .kv-sections > tbody > tr > td { padding-bottom: 0.7rem; }
.kv-list { margin: 0; padding-left: 1.1rem; }
.kv-bool-true { color: var(--ok); }
.kv-bool-false { color: var(--muted); }
.kv-null { color: var(--muted); }
.block-meta { color: var(--muted); font-size: 0.82rem; margin: 0 0 0.6rem; }

/* Salida de una tarea (output_tail) y otros textos monoespaciados. */
.tail-pre {
  margin: 0;
  max-height: 55vh;
  overflow: auto;
  padding: 0.8rem;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.4;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Cabecera de la ficha de equipo: titulo + rejilla de datos clave. */
/* Recuadro del equipo en la ficha: la cabecera ocupa el ancho y los iconos de accion quedan arriba a la
   derecha dentro del mismo recuadro; en pantallas estrechas bajan debajo (flex-wrap). */
/* Ficha bajo la tabla de Equipos: abierta ocupa al menos una ventana de alto. Asi su titulo puede quedarse arriba de
   la pantalla al pasar de equipo con las flechas aunque la ficha nueva sea corta (si el documento fuera mas bajo, el
   navegador recortaria el scroll). Cerrada lleva `hidden` y no ocupa nada. */
.device-sheet-host { min-height: 100vh; }
/* Actividad bajo la ficha (2026-09-24): un hueco claro entre el detalle del equipo y su actividad. */
#device-sheet-activity { margin-top: 1.2rem; }
#device-sheet-activity[open] > summary { margin-bottom: 0.8rem; }
/* Persiana del detalle de la ficha (2026-09-24): pegada al recuadro de la cabecera, sin borde superior ni hueco entre
   los dos (era la «línea de separación»); las pestañas cuelgan del summary. */
.device-sheet .device-sheet-detail { margin-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; border-top: 0; }
.device-sheet .device-panel { margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.device-sheet .device-sheet-detail > .tabs { margin-top: 0.6rem; }
.device-panel { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.6rem 1rem; }
.device-panel-main { flex: 1 1 32rem; min-width: 0; }
.device-panel-actions { flex: 0 0 auto; margin: 0; align-self: flex-start; }
.device-header { display: flex; flex-direction: column; gap: 0.6rem; }
.device-header .title { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.device-header .title h1 { margin: 0; }
.device-header .title .device-owner { margin-left: auto; color: var(--fg); font-size: 1.05rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 40%; }
/* Cada dato ocupa lo que necesita (no columnas iguales) y su valor no se parte en varias lineas: la fila crece
   en horizontal y salta de linea entre datos, nunca dentro de uno (decision del propietario 2026-09-16). */
.device-facts { display: flex; flex-wrap: wrap; gap: 0.4rem 1.6rem; font-size: 0.9rem; }
.device-facts .k { color: var(--muted); font-size: 0.78rem; display: block; }
.device-facts > div { max-width: 100%; min-width: 0; }
.device-facts .v { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
.warn-text { color: var(--warn); }
.ok-text { color: var(--ok); }
.error-text { color: var(--error); }
tr.ct-fila-muted > td { color: var(--muted); }

/* Usuario en la barra superior y lista de detalle en modales. */
.topbar-user { color: var(--muted); font-size: 0.88rem; white-space: nowrap; }
.detail-list { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 1rem; margin: 0.4rem 0 0.8rem; font-size: 0.9rem; }
.detail-list dt { color: var(--muted); }
.detail-list dd { margin: 0; overflow-wrap: anywhere; }

/* Pagina de error del portal (404, 403, 500). */
.error-page { text-align: center; padding: 3rem 1rem; }
.error-page .code { font-size: 3rem; font-weight: 700; color: var(--muted); }

/* ---------------------------------------------------------------------------
   Acciones por fila como botones de icono (`Portal.iconButton`, estilo kit).
   Refina lo heredado del kit: colores por variable (los dos temas), estado
   deshabilitado y columna que crece con el numero de iconos.
   --------------------------------------------------------------------------- */
.ct-info-btn { justify-content: center; cursor: pointer; }
.ct-info-btn svg { display: block; }
.ct-info-btn:hover:not(:disabled) { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); filter: none; }
/* Acciones destructivas o irreversibles: rojas SOLO al pasar el raton. */
.ct-info-btn.ct-danger:hover:not(:disabled) { color: var(--error); background: color-mix(in srgb, var(--error) 16%, transparent); }
.ct-info-btn:disabled { color: var(--muted); opacity: 0.4; cursor: default; background: none; filter: none; }
.ct-info-btn:disabled:hover { color: var(--muted); background: none; }
.ct-info-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* La columna de acciones se ajusta al numero de iconos (44px para uno, mas si hay varios). */
/* Columna de iconos: sin tirador de ancho y SIEMPRE al ancho de su contenido mas ancho (2026-09-23): `width: 1%` hace que la
   tabla (al 100 %) no le reparta el sitio sobrante, que va a las columnas de datos. */
th.ct-actions, td.ct-actions { width: 1%; min-width: 44px; white-space: nowrap; }
td.ct-actions .ct-ext-links { display: inline-flex; justify-content: center; gap: 5px; }

/* Iconos en una barra de acciones (cabecera de la ficha, formulario de config):
   algo de aire para que sean tan pulsables como los botones de texto vecinos. */
.actions-bar .ct-info-btn, .toolbar .ct-info-btn { padding: 0.35rem; }

/* Movil (~400px): los iconos se aprietan antes de romper la tabla. */
@media (max-width: 480px) {
  td.ct-actions .ct-ext-links { gap: 2px; }
  .actions-bar .ct-info-btn, .toolbar .ct-info-btn { padding: 0.25rem; }
}

/* Tamano unico del icono dentro del boton: los aportados por el propietario llegan a
   24x24 o a 18x18, asi que el tamano se fija aqui (nunca reescribiendo su viewBox). */
.ct-info-btn svg { display: block; width: 16px; height: 16px; }
/* Hueco de una accion que esa fila no tiene (Portal.actionSlot): mismo ancho que un icono, para que cada icono caiga
   siempre en la misma vertical de la tabla. */
.ct-info-slot { display: inline-block; width: 16px; height: 16px; flex: 0 0 auto; }
/* Accion dentro de una celda de datos (p. ej. asignar junto a la Version en Agentes): icono algo
   menor, a la derecha del valor, visible solo con el raton sobre la celda o con foco de teclado.
   Se oculta con visibility para que la columna no cambie de ancho al aparecer. En pantallas sin
   raton (hover: none) se muestra siempre. */
.cell-with-action { display: inline-flex; align-items: center; gap: 0.35rem; vertical-align: middle; }  /* middle: a la altura del embudo de filtrar por celda */
.ct-info-btn.cell-hover-btn { padding: 0.1rem; visibility: hidden; }
.ct-info-btn.cell-hover-btn svg { width: 13px; height: 13px; }
td:hover .cell-hover-btn, .cell-hover-btn:focus-visible { visibility: visible; }
@media (hover: none) { .ct-info-btn.cell-hover-btn { visibility: visible; } }
/* Etiqueta de formulario con tooltip de ayuda (modales creados por JS): subrayado punteado y cursor de ayuda. */
.label-tip { border-bottom: 1px dotted var(--muted); cursor: help; width: fit-content; }

/* Icono de fila «activado»: el MISMO icono, en color de acento, en vez de
   cambiar de dibujo. El tooltip dice lo que hara el clic (quitar). */
.ct-info-btn.is-active { color: var(--accent); }

/* Firma verificada de una release (Agentes): un ✓ verde; el detalle va en el tooltip. */
.sig-ok { color: var(--ok); font-weight: 700; font-size: 1.05em; cursor: default; }
/* Icono oficial de «firmado» (multicolor, img/signed.svg; 0.31.0). Con aviso, una marca ambar en la esquina. */
/* `vertical-align: middle`: el embudo de filtrar por celda (`.ct-cell-filtro`) va a su derecha, centrado; con la linea base
   por defecto el icono (line-height 0) quedaba mas alto que el embudo. */
.sig-badge { position: relative; display: inline-flex; vertical-align: middle; align-items: center; justify-content: center; line-height: 0; cursor: default; }
.sig-icon { width: 20px; height: auto; display: block; }
.sig-warn::after { content: ""; position: absolute; top: -2px; right: -4px; width: 8px; height: 8px; border-radius: 50%; background: var(--warn); box-shadow: 0 0 0 1.5px var(--bg); }

/* Texto que se copia con un clic (Portal.copyChip): parece texto en negrita con subrayado punteado;
   al copiar se pone verde un momento. */
.copy-chip {
  display: inline;
  padding: 0 0.15rem;
  border: none;
  border-bottom: 1px dotted var(--muted);
  border-radius: 3px;
  background: none;
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: copy;
}
.copy-chip:hover { color: var(--accent); border-bottom-color: var(--accent); filter: none; }
.copy-chip.copy-chip-ok { color: var(--ok); border-bottom-color: var(--ok); }
/* «Desarchivar» = el icono de archivar volteado en vertical (`flipY` de Portal.iconButton). */
.ct-info-btn.ct-flip-y svg { transform: scaleY(-1); }

/* Ambito de una accion de fila con seleccion multiple (`Portal.scopeChoice`). */
.scope-choice { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.4rem; }
.scope-choice label.checkbox { margin-right: 0; }

/* Boton de texto con icono delante (exportaciones: mismo icono de descarga en todo el portal). */
button.secondary svg, button.tab-alta svg, button.ghost svg { width: 14px; height: 14px; margin-right: 0.35rem; vertical-align: -2px; }

/* Celdas de fecha (`Portal.dateColumn`): nunca se recortan a media cadena. En modo
   truncado el kit ya pinta la forma compacta relativa ("3h"), no una fecha mutilada. */
td.ct-date { max-width: none; white-space: nowrap; }

/* Columna centrada (`col.align: "center"`): contadores. Cabecera y embudo de filtro incluidos. */
th.c, td.c { text-align: center; }
th.c .ct-th { align-items: center; }

/* Clicable que no es un enlace (contadores pintados como span, celdas que filtran):
   mismo color de enlace, sin subrayado en reposo y subrayado al pasar el raton. */
.ct-clickable { color: var(--accent); cursor: pointer; text-decoration: none; }
.ct-clickable:hover, .ct-clickable:focus-visible { text-decoration: underline; }

/* Pista corta a la derecha del titulo de un acordeon (p. ej. el tope de tamano). */
.acordeon > summary .acordeon-pista { margin-left: auto; color: var(--muted); font-size: 0.82rem; font-weight: 400; }

/* Equipos: barra de la gráfica «Actividad de los agentes» (rango · ayuda · recargar). */
.activity-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-top: 0.8rem; }
.activity-bar .spacer { flex: 1; }
/* La barra de tramos (CrocoRango) y lo que la sigue (autorefresco) se van a la derecha de la barra de la gráfica.
   Con rango delante van pegados entre sí (el autorefresco pierde su `margin-left: auto` para no separarse de él);
   sin rango (Equipos, 0.39.0: el rango se fue a la cabecera de la página) el autorefresco se va solo a la derecha,
   como en el resto de las tablas. */
.activity-bar .ranges-barra { margin-left: auto; }
.activity-bar .ranges-barra + .auto-refresh { margin-left: 0; }
.activity-help { cursor: help; border-bottom: 1px dotted var(--muted); }
/* Equipos (0.60.0): barra de rango de la tabla + interruptor «Filtro gráfico» encima de las pestañas, y debajo la
   gráfica de llamadas de los equipos de la tabla (solo con el filtro encendido). Mismos tramos de color que el resto. */
.table-pulse-bar { margin-top: 0; margin-bottom: 0.6rem; }
.table-pulse-bar label.checkbox { margin: 0; gap: 0.5rem; }
/* Con el filtro gráfico apagado la barra de rango queda inerte y atenuada: ya no recorta las tablas. */
.table-pulse-bar .ranges-off { opacity: 0.35; pointer-events: none; }
#table-pulse-chart { margin-bottom: 0.8rem; }

/* Quitar todos los filtros de UNA tabla (boton integrado en su cabecera, `clearFiltersButton` del kit):
   naranja por variable en los dos temas; solo visible cuando hay algun filtro de columna escrito. */
.ct-clear-filters {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.2rem;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--warn);
  cursor: pointer;
}
.ct-clear-filters:hover { background: color-mix(in srgb, var(--warn) 15%, transparent); filter: none; }
.ct-clear-filters svg { display: block; width: 16px; height: 16px; }
th.ct-clear-cell { width: 1%; text-align: center; vertical-align: bottom; }
/* `hidden` debe ganar al display:inline-flex del boton. */
.ct-clear-filters[hidden] { display: none; }

/* Columnas ocultas como desplegable (`hiddenColumnsMenu` del kit, activo por defecto en todas las tablas desde el
   2026-09-15): boton con su icono (img/icons/columns.svg) en la misma celda que el de quitar filtros, solo
   visible con alguna columna oculta. Sustituye a la fila de chips `.ct-ocultas` encima de la tabla. */
.ct-hidden-cols {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.2rem;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--fg);
  cursor: pointer;
  vertical-align: middle;
}
.ct-hidden-cols:hover, .ct-hidden-cols[aria-expanded="true"] { background: var(--row-hover); color: var(--accent); filter: none; }
.ct-hidden-cols svg { display: block; width: 16px; height: 16px; }
.ct-hidden-cols[hidden] { display: none; }
.ct-clear-filters { vertical-align: middle; }
th.ct-clear-cell { white-space: nowrap; }
/* El desplegable colgado de un boton lleva el mismo fondo que los del refresco y Exportar (`--menu-bg`, algo distinto
   al de la tabla), no el de las tarjetas que usa el menu contextual de columna. */
.ct-menu.ct-menu-drop { background: var(--menu-bg); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25); }
/* «×» a la derecha de cada columna oculta en el desplegable. */
.ct-menu-x { margin-left: auto; padding-left: 1rem; opacity: 0.7; }

/* Matriz de Monitorizacion > Activacion: un check (icono del propietario) por capacidad encendida, en acento si
   sigue viva y apagado si ya caduco; al pasar por la celda aparece la x para apagar SOLO esa capacidad. */
/* Celda-interruptor de una capacidad en la matriz: SIEMPRE el mismo boton, centrado, para que todas queden en la
   misma vertical. Encendido = acento (pulsar apaga, se atenua al pasar el raton); apagado = gris tenue (pulsar
   enciende, se aviva al pasar el raton). */
/* Celda de capacidad (Monitorizacion > Activacion): check + tiempo que queda, centrados; el tiempo, en gris y menor. */
.cap-cell { display: inline-flex; align-items: center; gap: 0.3rem; }
.cap-left { font-variant-numeric: tabular-nums; white-space: nowrap; min-width: 3.4em; text-align: left; }
.cap-left.cap-soon { color: var(--warn); }
/* Iconos del disparo de capturas en la celda de la matriz (no confundir con .cap-trigger, que es el bloque del modal). */
.cap-fire { display: inline-flex; width: 14px; height: 14px; color: var(--muted); }
.cap-fire svg { width: 14px; height: 14px; }
button.cap-check { display: inline-flex; align-items: center; justify-content: center; background: none; border: 0; padding: 0.1rem; cursor: pointer; transition: opacity 0.12s, color 0.12s; }
button.cap-check svg { display: block; width: 16px; height: 16px; }
button.cap-check.cap-on { color: var(--accent); opacity: 1; }
button.cap-check.cap-on:hover, button.cap-check.cap-on:focus-visible { opacity: 0.55; }
button.cap-check.cap-off { color: var(--muted); opacity: 0.4; }
button.cap-check.cap-off:hover, button.cap-check.cap-off:focus-visible { opacity: 1; color: var(--accent); }

/* Capturas de pantalla (Monitorizacion > Uso > equipo): rejilla de miniaturas y visor en modal. */
/* Las capturas van en el mismo flujo que las graficas del dia: el hueco lo pone el `gap` comun, sin margen propio. */
/* 10 miniaturas por fila; en pantallas estrechas caben menos sin romperse (decision del propietario 2026-09-16). */
.screenshots-grid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 0.5rem; }
@media (max-width: 1400px) { .screenshots-grid { grid-template-columns: repeat(5, 1fr); } }
@media (max-width: 700px) { .screenshots-grid { grid-template-columns: repeat(3, 1fr); } }
.screenshot-cell { display: flex; flex-direction: column; gap: 0.2rem; padding: 0.3rem; background: var(--menu-bg); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; text-align: center; }
.screenshot-cell:hover, .screenshot-cell:focus-visible { border-color: var(--accent); outline: none; }
.screenshot-thumb { width: 100%; height: auto; border-radius: 4px; display: block; }
.screenshot-reason { justify-content: center; align-items: center; min-height: 110px; cursor: default; }
.screenshot-reason:hover { border-color: var(--border); }
.screenshot-reason-label { color: var(--warn); font-weight: 600; }
/* Paginacion de las capturas: lo mas compacta posible (los botones traen el padding grande de `.tab`). */
.screenshots-pager { display: inline-flex; align-items: center; gap: 0.25rem; }
.screenshots-pager > button.tab { padding: 0.1rem 0.35rem; min-width: 0; line-height: 1.2; }
.screenshot-viewer { text-align: center; }
/* Pie del visor: texto + candado (mismo color que el texto del pie) si la captura llego sellada desde el equipo.
   `[hidden]` manda sobre el flex. */
.screenshot-pie { display: inline-flex; align-items: center; gap: 0.4rem; }
.shot-sealed { display: inline-flex; color: inherit; line-height: 0; cursor: default; }
.shot-sealed[hidden] { display: none; }
.shot-sealed svg { width: 10.5px; height: 14px; }
/* Visor de una captura: lo mas grande que quepa en la ventana, pero NUNCA mayor que la imagen capturada (sin
   `width`, una <img> no pasa de su tamano natural; los `max-*` solo la encogen si no cabe). */
/* `width: fit-content`: el diálogo se ciñe a la imagen (que ya está acotada por sus `max-*`), en vez de ocupar
   siempre el ancho máximo y dejar bandas vacías a los lados. */
.ct-modal.ct-modal-shot { width: fit-content; max-width: min(96vw, 1600px); }
.screenshot-full { max-width: 100%; max-height: calc(85vh - 7rem); height: auto; width: auto; border-radius: 4px; }
/* Modal de capacidades de un alcance: una fila por capacidad (check + nombre, nota y duracion debajo). */
/* Capacidades en rejilla 2x2 (dos columnas): el modal no crece tanto en alto. En movil, una columna. */
/* `align-items: start`: cada celda ocupa lo que necesita, sin estirarse al alto de la mas larga de su fila. */
.cap-edit-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.6rem; align-items: start; }
@media (max-width: 640px) { .cap-edit-grid { grid-template-columns: 1fr; } }
.cap-edit-row { padding: 0.6rem 0.7rem; border: 1px solid var(--border); border-radius: 6px; min-width: 0; }
/* label.cap-toggle: gana a `.form-croco label` (que apila en columna) para poner el check A LA IZQUIERDA y el nombre al lado. */
.form-croco label.cap-toggle { flex-direction: row; align-items: center; justify-content: flex-start; gap: 0.4rem; font-weight: 600; cursor: pointer; }
.cap-toggle input { margin: 0; flex: none; }
.cap-edit-note { margin: 0.2rem 0 0.35rem 1.5rem; }
.cap-edit-row > label:not(.cap-toggle) { margin-left: 1.5rem; }
/* Bloque de disparo de las capturas: los campos apilados y algo mas juntos, dentro de la celda de «Capturas». */
.cap-trigger { margin-left: 1.5rem; display: flex; flex-direction: column; gap: 0.4rem; }
.cap-trigger label { margin-left: 0; }
.ct-menu button:hover .ct-menu-x { opacity: 1; }

/* Editor de reglas «buscar → reemplazar» (Configuración → OS_NAME_REPLACEMENTS). */
.rules-editor { display: flex; flex-direction: column; gap: 0.4rem; }
.rules-editor > button { align-self: flex-start; }
.rules-table { border-collapse: collapse; width: 100%; }
.rules-table th { text-align: left; font-weight: 500; font-size: 0.8rem; color: var(--muted); padding: 0 0.3rem; }
.rules-table td { padding: 0.15rem 0.3rem; vertical-align: middle; }
.rules-table input { width: 100%; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
.rules-preview { margin: 0; padding-left: 1.1rem; max-height: 14rem; overflow: auto; font-size: 0.85rem; }

/* Etiquetas de equipos (plan irm-etiquetas): pastilla del color de la paleta del tema. Directa = relleno suave;
   heredada del grupo = borde discontinuo sin relleno. El color va por clase tc-<color> (sin estilos en linea). */
.tag-chips { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; vertical-align: middle; }
.tag-chip {
  --tag-color: var(--accent);
  display: inline-flex;
  align-items: center;
  padding: 0 0.45rem;
  border: 1px solid var(--tag-color);
  border-radius: 999px;
  color: var(--tag-color);
  background: color-mix(in srgb, var(--tag-color) 14%, transparent);
  font-size: 0.75rem;
  line-height: 1.5;
  white-space: nowrap;
}
.tag-chip-inherited { border-style: dashed; background: transparent; }
.tc-accent { --tag-color: var(--accent); }
.tc-ok { --tag-color: var(--ok); }
.tc-warn { --tag-color: var(--warn); }
.tc-error { --tag-color: var(--error); }
.tc-chart-1 { --tag-color: var(--chart-1); }
.tc-chart-2 { --tag-color: var(--chart-2); }
.tc-chart-3 { --tag-color: var(--chart-3); }
.tc-chart-4 { --tag-color: var(--chart-4); }
.tc-chart-5 { --tag-color: var(--chart-5); }
.tc-muted { --tag-color: var(--muted); }
/* Selector de etiquetas de los modales: casillas en filas que se reparten, con scroll si hay muchas. */
.tags-picker { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; max-height: 16rem; overflow: auto; padding: 0.2rem 0; }
.form-croco .tags-picker label.checkbox { margin-right: 0; }
.device-facts .tag-chips { white-space: normal; }

/* Configuracion > Iconos de software: resumen a la izquierda y cuadricula de iconos a la derecha (debajo en pantallas estrechas). */
.icons-layout { display: flex; gap: 1.5rem; align-items: flex-start; flex-wrap: wrap; }
.icons-layout .detail-list { flex: 0 0 auto; margin-top: 0; }
.icon-grid {
  flex: 1 1 320px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(28px, 1fr));
  gap: 0.35rem;
  max-height: 22rem;
  overflow: auto;
  padding: 0.4rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--input-bg);
}
.icon-cell { display: flex; align-items: center; justify-content: center; height: 28px; border-radius: 6px; }
.icon-cell:hover, .icon-cell:focus-visible { background: var(--row-hover); outline: 1px solid var(--border); }
.icon-cell img { width: 16px; height: 16px; }

/* SO como icono (Windows multicolor, img/os/windows.svg) y plataforma de un paquete (icono + arquitectura). */
.os-icon { width: 16px; height: 16px; vertical-align: middle; }
.platform { display: inline-flex; align-items: center; gap: 0.4rem; }

/* «?» de ayuda junto al título de un acordeón (icono del propietario img/icons/help.svg): el texto va en su tooltip. */
.acordeon-help { display: inline-flex; align-items: center; margin-left: 0.45rem; color: var(--muted); cursor: help; vertical-align: middle; }
.acordeon-help svg { width: 18px; height: 18px; }
.acordeon-help:hover, .acordeon-help:focus-visible { color: var(--fg); }

/* Norma del propietario (todos los modales con Cancelar): Cancelar a la IZQUIERDA del pie y la acción siempre a la derecha. */
.ct-modal-foot-accion .ct-modal-cancelar { margin-right: auto; }

/* Menú de una tabla de Distribución (exportar, altas…) dentro de su recuadro, en la barra de la tabla. */
.actions-bar .table-menu { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }

/* Columna «Tiempos» de Equipos: marca de heredado «↳», en color claro para que no se confunda con el valor. */
/* Miembros de un grupo junto a su nombre (Portal.groupNode): «Pruebas [4]». */
/* Gestor de grupo (Portal.openGroupManager): dos zonas, cada una con su pie Cancelar / Guardar. */
.group-manager { display: flex; flex-direction: column; gap: 0.8rem; }
.gm-zone { border: 1px solid var(--border); border-radius: 8px; padding: 0.7rem 0.8rem; }
.gm-zone h4 { margin: 0 0 0.5rem; }
.gm-search-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; }
.gm-search-row .gm-search { flex: 1; width: 100%; box-sizing: border-box; }
/* minmax(0, 1fr) y min-width: 0: sin ellos la columna crece hasta el texto mas largo (nowrap) y se sale del modal
   en vez de recortar con «…» (propietario 2026-09-17). */
.gm-columns { display: grid; grid-template-columns: minmax(0, 1fr) 2.4rem minmax(0, 1fr); gap: 0.5rem; }
.gm-columns > * { min-width: 0; }
/* Columna central con « > » y « < »: solo se ven con algo marcado en su lado. */
.gm-move { display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 0.6rem; padding-top: 1.2rem; }
.gm-move-btn { width: 2.2rem; justify-content: center; padding-inline: 0; font-weight: 600; }
.gm-list { height: 230px; overflow: auto; border: 1px solid var(--border); border-radius: 6px; padding: 0.3rem; margin-top: 0.25rem; }
.gm-item { display: flex; flex-direction: row; align-items: baseline; gap: 0.4rem; padding: 0.15rem 0.3rem; border-radius: 4px; cursor: pointer; font-size: 0.9rem; }
.gm-item input { width: auto; margin: 0; flex: none; }
/* Una línea por equipo: el nombre entero y el detalle recortado con «…» si no cabe (tooltip con el texto completo). */
.gm-item { white-space: nowrap; min-width: 0; }
.gm-host { flex: none; }
.gm-detail { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Buscador del gestor: mismo aspecto que los filtros de columna (.ct-filter). */
.gm-search-row input, .gm-search-row select { background: var(--input-bg); color: var(--fg); border: 1px solid var(--border); border-radius: 6px; padding: 0.25rem 0.45rem; font: inherit; font-size: 0.9rem; }
.gm-item:hover { background: var(--row-hover); }
.gm-item.gm-nuevo > span:first-of-type { color: var(--ok); }
.gm-item.gm-quitado > span:first-of-type { color: var(--warn); text-decoration: line-through; }
.gm-actions { display: flex; margin-top: 0.4rem; }
/* Equipo que hereda la etiqueta de su grupo: bloqueado (sin casilla), se quita desde el grupo. */
.gm-item.gm-locked { cursor: default; color: var(--muted); }
.gm-lock { width: 13px; flex: none; text-align: center; color: var(--muted); }
/* Engranaje de una etiqueta dentro de una celda con varias: aparece solo al pasar por ESA etiqueta. */
td:hover .tag-manage .cell-hover-btn { visibility: hidden; }
td .tag-manage:hover .cell-hover-btn, td .tag-manage .cell-hover-btn:focus-visible { visibility: visible; }
.gm-actions-end { justify-content: flex-end; }
.gm-footer { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; margin-top: 0.6rem; min-height: 1.8rem; }
.group-count { color: var(--muted); font-size: 0.85em; }
/* Tiempo HEREDADO (del grupo o de los Ajustes): atenuado. El color normal queda para lo fijado en ese equipo. */
.timing-inherited { color: var(--muted); }

/* Configuracion > Base de datos: tarjetas de estado (4 por fila, 1 en movil) y tabla de las tablas mas grandes. */
.db-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 0.7rem; margin: 0.4rem 0 1rem; }
.db-card { display: flex; flex-direction: column; gap: 0.2rem; padding: 0.7rem 0.9rem; border: 1px solid var(--border); border-radius: 8px; background: var(--card); }
.db-card-label { font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.db-card-value { font-size: 1.05rem; }
.db-card-value.db-ok { color: var(--ok); }
.db-card-value.db-warn { color: var(--warn); }
.db-card-value.db-err { color: var(--error); }
.db-card-sub { font-size: 0.75rem; color: var(--muted); }
.db-subtitle { font-size: 0.95rem; margin: 0.4rem 0; }
.db-table-wrap { overflow-x: auto; }
.db-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.db-table th { text-align: left; font-weight: 600; color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--border); }
.db-table td { padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--row-border); }
.db-table tr:hover td { background: var(--table-row-hover); }
.db-table .num { text-align: right; font-variant-numeric: tabular-nums; }

/* Menú de las tablas (norma del propietario, mismo formato en todas): a la DERECHA de la barra, en este orden:
   interruptores (p. ej. «Mostrar archivados»), CSV, XLSX y el alta («+ Paquete», «+ Asignación»); después, el
   autorefresco y recargar. La explicación de la tabla, si la hay, queda a la izquierda. */
.actions-bar .table-menu { margin-left: auto; }
.actions-bar .table-menu ~ .auto-refresh { margin-left: 0; }

/* Botón único «Exportar ▾» (Portal.exportMenu): despliega CSV / XLSX debajo, alineado a la derecha del botón. */
.export-menu { position: relative; display: inline-flex; }
.export-menu-btn { display: inline-flex; align-items: center; gap: 0.35rem; }
.export-menu-caret { font-size: 0.75em; opacity: 0.8; }
/* Desplegable de Exportar: linea de arriba con lo que se va a exportar (marcadas o todo lo filtrado). */
.export-menu-scope { margin: 0 0 0.35rem; padding-bottom: 0.35rem; border-bottom: 1px solid var(--border); }

.export-menu-list {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  min-width: 15rem;
  padding: 0.3rem;
  background: var(--menu-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
}
.export-menu-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  width: 100%;
  padding: 0.45rem 0.6rem;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--fg);
  text-align: left;
}
.export-menu-item:hover, .export-menu-item:focus-visible { background: var(--row-hover); filter: none; }

/* Menú del usuario de la topbar (a la derecha del todo): su login como botón con icono de perfil; despliega
   «Mis datos» y «Cerrar sesión» debajo, alineado a la derecha. */
.user-menu { position: relative; display: inline-flex; }
.user-menu-btn { display: inline-flex; align-items: center; gap: 0.4rem; background: none; border: 1px solid var(--border); color: var(--fg); }
.user-menu-btn:hover { border-color: var(--muted); filter: none; }
.user-menu-icon svg, .user-menu-item svg { width: 16px; height: 16px; }
.user-menu-icon { display: inline-flex; color: var(--muted); }
.user-menu-list {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  min-width: 11rem;
  padding: 0.3rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
}
.user-menu-logout { margin: 0; border-top: 1px solid var(--border); margin-top: 0.25rem; padding-top: 0.25rem; }
.user-menu-item { display: flex; align-items: center; gap: 0.5rem; white-space: nowrap; width: 100%; padding: 0.5rem 0.6rem; background: none; border: 0; border-radius: 6px; color: var(--fg); text-align: left; }
.user-menu-item span[data-icon] { display: inline-flex; color: var(--muted); }
.user-menu-item:hover, .user-menu-item:focus-visible { background: var(--row-hover); filter: none; }

/* Modal «Perfil»: dos secciones, cada una con su botón de guardar abajo a la derecha y su mensaje de estado. */
/* Perfil: datos personales y contraseña en dos columnas; por debajo de ~720px, apiladas. */
.profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 0 0.9rem; align-items: stretch; }
.profile-grid .profile-section { display: flex; flex-direction: column; }
.profile-grid .profile-section-foot { margin-top: auto; padding-top: 0.6rem; }
.profile-login { color: var(--accent); }
.profile-section { border: 1px solid var(--border); border-radius: 8px; padding: 0.8rem 1rem; margin-bottom: 0.9rem; }
.profile-section h3 { margin: 0 0 0.3rem; font-size: 0.95rem; }
.profile-section .form-croco { margin: 0.4rem 0 0; }
.profile-section-foot { display: flex; align-items: center; justify-content: flex-end; gap: 0.6rem; margin-top: 0.6rem; }
.profile-section-foot .ok-text { color: var(--ok); }

/* Menús desplegables (usuario de la topbar, Exportar): cada opción alineada a la IZQUIERDA, icono + texto. */
.user-menu-item, .export-menu-item { justify-content: flex-start; text-align: left; }
.export-menu-item { align-items: flex-start; }

/* Campo de solo lectura (p. ej. el login en «Perfil»): se ve como dato, no como campo editable. */
.form-croco input.input-readonly { background: transparent; color: var(--muted); cursor: default; pointer-events: none; }

/* Autorefresco dentro de la fila de pestañas (Equipos > Activos): pegado al resto de acciones, sin empujar. */
.tabs-acciones .auto-refresh { margin-left: 0; }

/* Menú «Exportar»: formato (título) y su explicación alineados a la IZQUIERDA; la explicación, más pequeña. */
.export-menu-item { align-items: flex-start; justify-content: flex-start; text-align: left; }
.export-menu-item .muted { font-size: 0.72rem; }

/* Celda «Tiempos» de Equipos (admin): parece texto; al pasar el ratón, subrayado discontinuo y abre la configuración. */
.timings-edit { padding: 0; margin: 0; background: none; border: 0; border-bottom: 1px dashed transparent; border-radius: 0; color: inherit; font: inherit; cursor: pointer; }
.timings-edit:hover, .timings-edit:focus-visible { border-bottom-color: var(--muted); filter: none; }

/* Vista «Uso» (Portal.usageView): lista de eventos de sesion y energia en cuatro columnas. */
.usage-events-box { margin: 0.4rem 0 0.8rem; }
.usage-events-box summary { cursor: pointer; color: var(--accent); font-size: 0.92rem; }
.usage-events { display: grid; gap: 0.15rem 0.8rem; grid-template-columns: auto auto auto auto; justify-content: start; margin-top: 0.4rem; }
.usage-event { display: contents; font-size: 0.9rem; }
@media (max-width: 700px) {
    .usage-events { grid-template-columns: 1fr; }
    .usage-event { display: block; border-bottom: 1px solid var(--border); padding: 0.25rem 0; }
}

/* Seccion «Actividad de un equipo por dia» (Portal.usageDeviceDay): barra con el dia, los saltos y el cierre. */
.usage-day-bar { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
.usage-day-bar input[type="date"] { font-size: 0.82rem; padding: 0.2rem 0.35rem; }
.usage-day-paso { padding: 0.15rem 0.45rem; line-height: 1.2; }
/* «Zona equipo»: la actividad se rotula en la hora DEL EQUIPO (0.28.0). Va en la barra del dia porque es parte
   de «que estoy mirando», junto al dia; se oculta si el equipo no reporta zona. La barra puede plegarse en varias
   lineas, pero la casilla y su texto NUNCA se separan (`white-space: nowrap` + `flex-shrink: 0`). */
.usage-day-zona { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: 0.3rem; font-size: 0.82rem; color: var(--muted); cursor: pointer; white-space: nowrap; flex-shrink: 0; }
.usage-day-zona input { flex: 0 0 auto; }
.usage-day-zona span { white-space: nowrap; }
.usage-day-zona input { margin: 0; cursor: pointer; }
/* Flechas «equipo anterior / siguiente» de la actividad de un dia: van DENTRO del titulo, separadas de el, para que
   se lean como «que equipo estoy viendo» y no se confundan con los pasos de dia, que viven en la barra de la derecha. */
/* Al final de la fila del titulo (`margin-left: auto`), pegadas a los mandos del dia: el sitio no se mueve al
   cambiar de equipo, asi que se puede ir pulsando sin mover el raton. */
.usage-day-nav { display: inline-flex; align-items: center; gap: 0.45rem; margin-left: auto; margin-right: 0.9rem; flex-shrink: 0; }
.usage-day-nav-btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.1rem 0.2rem; border: 0; background: none; color: inherit; cursor: pointer; line-height: 0; }
.usage-day-nav-btn svg { width: 0.85em; height: 1.05em; }
.usage-day-nav-btn:hover { color: var(--accent); }
/* Sin equipo al que ir: en gris, pero ahi (el hueco queda reservado y el tooltip lo explica). */
.usage-day-nav-btn[aria-disabled="true"] { color: var(--muted); opacity: 0.4; cursor: default; }
.usage-day-nav-btn[aria-disabled="true"]:hover { color: var(--muted); }
/* Sin marca de foco (decision del propietario 2026-09-17): al pulsar una flecha se queda el foco en ella y
   el circulo del navegador ensuciaba la fila. Se puede seguir pasando de equipo con las teclas ← y →. */
.usage-day-nav-btn:focus, .usage-day-nav-btn:focus-visible { outline: none; box-shadow: none; }
.usage-day-nav-pos { font-size: 0.82rem; font-weight: 400; color: var(--muted); white-space: nowrap; }
/* Titular del equipo, bajo el titulo de su actividad: pequeno y atenuado, que acompana sin competir con el titulo. */
.usage-day-titulo { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.usage-day-titular { margin: 0; font-size: 0.8rem; color: var(--muted); }
/* Zona en la misma linea del titulo (actividad bajo la ficha de Equipos, 2026-09-24). */
.usage-day-zona-inline { font-size: 0.8rem; font-weight: 400; color: var(--muted); }

/* ---- Actividad por dia: secciones y acordeones internos (decision del propietario 2026-09-16) ---- */
/* Actividad del dia: las cuatro graficas (aplicaciones, estado, interactividad, capturas) en un solo flujo, todas
   separadas por el mismo hueco; ninguna lleva margen propio. */
.usage-section { margin-top: 0.8rem; display: flex; flex-direction: column; gap: 0.6rem; }
/* Acordeon dentro de un panel (no lleva su propio marco): solo el summary como fila plegable. */
details.acordeon-inner { margin-top: 0.6rem; }
details.acordeon-inner > summary { cursor: pointer; font-weight: 600; font-size: 0.95rem; padding: 0.35rem 0; color: var(--fg); list-style: revert; }
details.acordeon-inner > summary:hover { color: var(--accent); }
/* Controles en la cabecera de un acordeon interno (paginador de capturas...): misma fila que el titulo, pegados a la
   derecha (`margin-left: auto`: la cabecera es flex por `.acordeon > summary`, ahi un `float` no hace nada).
   Con el texto normal del cuerpo, no el negrita del titulo; plegado el acordeon, no se ven. */
details.acordeon-inner > summary > .acordeon-tools { margin-left: auto; display: inline-flex; align-items: center; gap: 0.6rem; font-weight: 400; font-size: 0.85rem; color: var(--fg); cursor: default; }
details.acordeon-inner:not([open]) > summary > .acordeon-tools { display: none; }
.screenshots-timeline { min-height: 2rem; }

/* ---- Etiquetas: color como fila de puntos (uno marcado) + vista previa en el formulario ---- */
.tag-form-color { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.92rem; }
/* Cadencias de la etiqueta (0.61.0): dos campos en fila, como en la ficha del equipo. */
.tag-form .tag-form-timings { margin-top: 0.4rem; }
/* Engranaje «Tiempos de la ruta» en el árbol LDAP: fijo y en color cuando la ruta ya fija algún tiempo. */
.ldap-node .ldap-timings-set { color: var(--accent); margin-left: 0.2rem; }
.tag-form-color .tag-form-label { font-size: 0.92rem; }
.tag-color-dots { display: flex; flex-wrap: wrap; gap: 0.45rem; align-items: center; }
/* Sin --tag-color propio: lo pone su clase tc-<color> (definida antes; con el valor aqui, el azul ganaba a todas). */
.tag-color-dot { position: relative; display: inline-flex; width: 22px; height: 22px; cursor: pointer; margin: 0; }
.tag-color-dot input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.tag-color-dot .tag-color-dot-ball { width: 100%; height: 100%; border-radius: 50%; background: var(--tag-color); border: 2px solid transparent; box-sizing: border-box; transition: transform 0.12s, border-color 0.12s; }
.tag-color-dot:hover .tag-color-dot-ball { transform: scale(1.12); }
.tag-color-dot input:checked + .tag-color-dot-ball { border-color: var(--fg); box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--tag-color); }
.tag-color-dot input:focus-visible + .tag-color-dot-ball { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Cuerpo del panel «Capturas de pantalla» (mismo formato que un panel CrocoPulso): sus dos acordeones internos. */
.screenshots-body { padding: 0.2rem 0.6rem 0.6rem; }
/* Barra de la rejilla de capturas: rango elegido en la grafica + boton de reset. Sin el margen de `.toolbar`. */
.screenshots-zoom { display: inline-flex; align-items: center; gap: 0.4rem; }
.screenshots-panel .toolbar:has(.screenshots-pager) { margin: 0 0 0.3rem; min-height: 0; }
/* Los dos tiempos del disparo de capturas, en la misma fila dentro de la celda «Capturas». */
.cap-trigger-row { display: flex; gap: 0.5rem; }
.cap-trigger-row > label { flex: 1 1 0; min-width: 0; margin-left: 0; }
/* Fila del equipo que se esta viendo abajo (actividad en Monitorizacion > Uso, ficha en Equipos): fondo distinto para localizarla. */
tr.fila-abierta > td { background: color-mix(in srgb, var(--accent) 14%, transparent); }
tr.fila-abierta > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
/* Ficha del equipo bajo la tabla de Equipos (0.30.0, plan irm-ficha-en-equipos): sin barra propia; las flechas de
   equipo (Portal.deviceNav, mismas clases que en Uso) van a la izquierda de los iconos de accion y el ✕ al final. */
.device-panel-actions { display: flex; align-items: center; }
.device-panel-actions .usage-day-nav { margin-left: 0; margin-right: 1.2rem; }
.device-panel-actions .device-sheet-close { margin-left: 0.6rem; }
/* Rotulo de un campo con su pista al lado (entre parentesis, menor y mas clara), en vez de debajo del input. */
.campo-titulo { display: inline-flex; align-items: baseline; gap: 0.35rem; }
.campo-titulo > .muted, .campo-titulo > span { font-size: 0.78rem; color: var(--muted); font-weight: 400; }
/* Configuracion de capturas: bloques de opciones separados por un filete. */
.cap-bloque + .cap-bloque { margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
.cap-bloque-titulo { display: block; font-size: 0.92rem; margin-bottom: 0.25rem; }
/* Opciones excluyentes en fila (radios), en vez de un desplegable. */
.cap-opciones { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; }
.form-croco label.cap-opcion { flex-direction: row; align-items: center; gap: 0.35rem; cursor: pointer; font-size: 0.9rem; }
.cap-opcion input { margin: 0; flex: none; }
/* Los botones de rango no van pegados al borde superior de su fila. */
.ranges { padding-top: 0.35rem; }
/* El radio elegido no se queda con el recuadro de foco tras el clic (solo con teclado, :focus-visible). */
.cap-opcion input:focus { outline: none; }
.cap-opcion input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- Rejilla de capturas: seleccion multiple y acciones sobre lo marcado ---- */
/* La imagen es un boton (abre la captura grande); el pie marca la captura. */
.screenshot-open { padding: 0; border: 0; background: none; display: block; width: 100%; cursor: zoom-in; }
.screenshot-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Pie de la miniatura (fecha · monitor): mas pequeno que `.small` para que no compita con la imagen (0.31.0). */
.screenshot-pick { cursor: pointer; user-select: none; font-size: 0.72rem; line-height: 1.25; }
.screenshot-pick:hover { color: var(--accent); }
/* Solo miniatura (0.32.0): la imagen completa se descarto en el equipo por falta de espacio sin red. */
.screenshot-reduced .screenshot-thumb { opacity: 0.85; outline: 1px dashed var(--warn); outline-offset: -1px; }
.screenshot-reduced-badge { color: var(--warn); }
.cap-spool-hint { margin: 0.35rem 0 0; }
.screenshot-cell.marcada { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, var(--menu-bg)); }
.screenshots-acciones { display: inline-flex; align-items: center; gap: 0.35rem; margin-left: 0.5rem; }
/* La configuracion de capturas empieza tras un filete: la duracion de arriba es comun a todas las capacidades. */
.cap-edit-row > .cap-trigger { margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
/* Fila «Tamano de la imagen» + su valor: el selector se lleva el doble de ancho que el numero, para que su texto
   («Porcentaje del tamano de la pantalla») se lea entero. */
.cap-trigger-row > label:first-child { flex: 2 1 0; }
.cap-trigger-row > label + label { flex: 1 1 0; }
/* Modal abierto en UNA sola capacidad (atajo de Capturas): una columna, no la rejilla 2x2. */
.cap-edit-grid.cap-edit-grid-solo { grid-template-columns: 1fr; }
/* Boton de configurar la capacidad, en la cabecera de su grafica. */
.pulso-config { margin-left: 0.4rem; vertical-align: middle; }
/* Boton flotante para deshacer el zoom, sobre la esquina superior izquierda de la grafica. */
.pulso-con-reset { position: relative; }
.pulso-con-reset > .pulso-zoom-reset { position: absolute; top: 0.3rem; left: 0.3rem; z-index: 2;
  background: var(--card); border: 1px solid var(--border); border-radius: 6px; padding: 0.15rem; }
/* Visor de capturas: carrusel (flechas a los lados de la imagen). */
.screenshot-carrusel { display: flex; align-items: center; justify-content: center; gap: 0.5rem; }
.screenshot-nav { background: none; border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
  padding: 0.3rem 0.5rem; color: var(--fg); flex: none; }
.screenshot-nav:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.screenshot-nav:disabled { opacity: 0.35; cursor: default; }
/* «Hace» que acompana a una fecha (data-utc-ago): atenuado, para que mande la fecha. */
.utc-ago { color: var(--muted); font-size: 0.85em; }
/* Las fechas del equipo, en su propia fila bajo el resto de datos. */
/* Filas de datos de la ficha pegadas entre si (propietario 2026-09-19): la cabecera separa sus piezas 0.6rem; entre dos
   filas de datos se deja solo 0.25rem. */
.device-facts + .device-facts { margin-top: -0.35rem; }

/* Agentes > Logs: nivel de cada activacion como cuatro radios en la celda (DEBUG, INFO, WARN, ERROR). */
.log-level-cell { display: inline-flex; gap: 0.9rem; align-items: center; }
.log-level-radios { display: inline-flex; gap: 0.95rem; align-items: center; }
.log-level-radio { display: inline-flex; cursor: pointer; }
.log-level-radio input { margin: 0; cursor: pointer; accent-color: var(--accent); }
.log-level-radio input:disabled { cursor: not-allowed; }
/* + / - de la caducidad, a la derecha de «Hasta» (botones de icono, con su propia medida). */
.log-days { display: inline-flex; gap: 0.25rem; }
.log-days-btn { font-weight: 600; font-size: 1rem; }
.log-days-btn:disabled { opacity: 0.35; cursor: default; }

/* Datos del equipo sellados con otra llave (0.34.0): candado de aviso en Equipos y boton de descartar en la ficha. */
.seal-foreign-mark { display: inline-flex; color: var(--warn); }
.seal-foreign-mark svg { width: 0.8rem; height: 0.8rem; }
.seal-foreign { display: inline-flex; align-items: center; gap: 0.3rem; }
.seal-discard-btn { color: var(--warn); }

/* ---- Roles del portal (0.31.0, plan irm-roles-portal) ---- */
/* Editor de un rol: una fila por pagina, agrupadas por seccion del menu; tres columnas de radio (sin acceso, lectura,
   escritura). La de escritura se atenua en las paginas que solo se consultan. */
.role-grid { display: grid; grid-template-columns: minmax(10rem, 1fr) repeat(3, 6.5rem); gap: 0.15rem 0.5rem; align-items: center; margin-top: 0.4rem; }
.role-grid-head, .role-grid-row { display: contents; }
.role-grid-head > span { font-size: 0.82rem; color: var(--muted); text-align: center; padding-bottom: 0.2rem; border-bottom: 1px solid var(--border); }
.role-grid-head > span:first-child { text-align: left; }
.role-grid-section { grid-column: 1 / -1; margin-top: 0.5rem; font-weight: 600; font-size: 0.85rem; color: var(--accent); }
.role-grid-page { padding-left: 0.6rem; }
.role-grid-cell { text-align: center; }
.role-grid-cell input { margin: 0; cursor: pointer; }
.role-grid-cell.muted input { cursor: not-allowed; }
/* Ambito de un usuario: listas de grupos con scroll, buscador de equipos y equipos elegidos como pastillas. */
.scope-detail h3 { margin: 0.7rem 0 0.3rem; font-size: 0.9rem; }
.scope-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: 0.2rem 0.8rem; max-height: 12rem; overflow: auto; padding: 0.3rem; border: 1px solid var(--border); border-radius: 6px; }
.scope-results { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.4rem 0; }
/* Buscador de equipo del modal de capacidades: el input y, debajo, las sugerencias (solo cuando son pocas). */
.device-picker { display: flex; flex-direction: column; }
.device-picker .scope-results { margin: 0.3rem 0 0; }
.scope-result { font-size: 0.82rem; }
.scope-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.scope-chip { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.1rem 0.2rem 0.1rem 0.5rem; border: 1px solid var(--border); border-radius: 999px; font-size: 0.82rem; }
.scope-chip-remove { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 0 0.3rem; line-height: 1; }
.scope-chip-remove:hover { color: var(--error); }
/* Modal de asignacion de release (0.49.0): una seccion por tipo de destino (Grupos, Etiquetas, Equipos) con su filtro,
   su lista, «Anadir» y los chips de ese tipo debajo. Solo disposicion y tokens del tema. */
.target-section { border: 1px solid var(--border); border-radius: 8px; padding: 0.7rem 0.9rem; }
.target-section h3 { margin: 0 0 0.2rem; font-size: 0.95rem; }
.target-section > p { margin: 0 0 0.5rem; }
.target-section .form-croco-row { display: flex; align-items: flex-end; gap: 0.5rem; }
.target-section .form-croco-row > label { flex: 1; min-width: 0; }
.target-section select[size] { width: 100%; font-size: 0.88rem; }
.target-section .scope-chips { margin-top: 0.5rem; }
/* Titulos de seccion del modal de ambito (Grupos, Etiquetas, Rutas LDAP, Equipos sueltos), subrayados (2026-09-24). */
.scope-detail h3 { text-decoration: underline; text-underline-offset: 3px; }
/* Cabecera de seccion del modal de ambito: titulo a la izquierda y filtro de la lista a la derecha. */
.scope-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; }
.scope-head h3 { margin-bottom: 0.3rem; }
.scope-head .scope-filter { width: 16rem; max-width: 50%; padding: 0.3rem 0.5rem; font-size: 0.85rem; margin-bottom: 0.3rem; }
.scope-list > label[hidden] { display: none; }
/* Lo marcado, en azul (decision del propietario 2026-09-24): en el selector de destinos se ve de un vistazo que grupos,
   rutas y etiquetas estan elegidos, y los equipos sueltos elegidos (chips) van igual. */
.scope-list > label.checkbox:has(input:checked) > span { color: var(--accent); }
.scope-list > label.checkbox:has(input:checked) { font-weight: 600; }
/* Las etiquetas conservan su color propio: marcadas, solo en negrita. */
.tags-picker label.checkbox:has(input:checked) .tag-chip { font-weight: 700; }
.scope-chip { color: var(--accent); border-color: var(--accent); font-weight: 600; }
/* Rutas LDAP del modal de ambito: una por linea (son largas) y con scroll si hay muchas. */
.scope-list.scope-list-ldap { grid-template-columns: 1fr; max-height: 14rem; overflow-y: auto; }

/* Equipos > LDAP (0.50.0, plan irm-arbol-ldap): una tabla con filas de contenedor y filas de equipo. La sangría va por
   profundidad (`--depth` en la celda); la flecha pliega y despliega; los contenedores vacíos heredan ct-fila-muted. */
.ldap-cell { display: inline-flex; align-items: center; gap: 0.3rem; min-width: 0; }
.ldap-indent { display: inline-block; width: calc(var(--depth, 0) * 1.1rem); flex: none; }
.ldap-toggle {
  flex: none;
  width: 1.3rem;
  height: 1.3rem;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
}
.ldap-toggle:hover:not(:disabled) { color: var(--fg); background: var(--menu-bg); filter: none; }
.ldap-toggle:disabled { opacity: 0.35; cursor: default; }
.ldap-node-name { font-weight: 600; cursor: pointer; }
tr.ldap-node-row > td { background: color-mix(in srgb, var(--accent) 6%, var(--card)); }
/* Fila de contenedor (solo ruta, sin datos de equipo): lo mas baja posible (propietario 2026-09-23). El tirador de
   plegar y el embudo no suman altura. */
tr.ldap-node-row > td { padding-top: 0; padding-bottom: 0; line-height: 1.35; }
tr.ldap-node-row .ldap-toggle { height: 1.1rem; }
tr.ldap-node-row .ct-cell-filtro { line-height: 0; }
.ldap-counts { flex: none; }
.ldap-leaf { font-size: 0.8rem; opacity: 0.6; }
.ldap-node-row .ldap-cell, td.ct-resized .ldap-cell { max-width: 100%; }

/* Columnas redimensionables (todas las columnas con titulo, decision del propietario 2026-09-23): tirador en el
   borde derecho de la cabecera (arrastrar = ancho a mano, minimo el ancho del titulo; doble clic = ajustar al
   contenido). Con ancho fijado, la celda recorta con puntos suspensivos; el contenido flexible (.ldap-cell) se encoge dentro. */
th.ct-resizable { position: sticky; }
th.ct-resizable:not(.ct-fixed) { position: relative; }
.ct-resize-handle {
  position: absolute;
  top: 0;
  right: -3px;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  z-index: 2;
}
.ct-resize-handle:hover, body.ct-resizing .ct-resize-handle { background: color-mix(in srgb, var(--accent) 35%, transparent); }
body.ct-resizing { cursor: col-resize; user-select: none; }
/* Columnas que se mueven (`reorderableColumns`, prueba 2026-09-24): se arrastra el TITULO; la cabecera de destino marca con
   una barra de color por que lado caera. */
.ct-th-drag { cursor: grab; }
.ct-th-drag:active { cursor: grabbing; }
th.ct-dragging { opacity: 0.45; }
.ct-menu > button.ct-menu-sep { border-top: 1px solid var(--border); margin-top: 0.2rem; padding-top: 0.45rem; }
th.ct-drop-antes { box-shadow: inset 3px 0 0 var(--accent); }
th.ct-drop-despues { box-shadow: inset -3px 0 0 var(--accent); }
td.ct-resized { overflow: hidden; text-overflow: ellipsis; }
/* Envoltorio de la celda con ancho fijado: bloque del ancho de la columna que recorta por dentro (ver `_aplicarAncho`). */
td.ct-resized > .ct-clip { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
td.ct-resized > .ct-clip > .ldap-cell { max-width: calc(100% - 1.4rem); }
/* Sigue en linea (inline-flex): el embudo de filtrar por celda va a su derecha, en la MISMA fila; se le deja sitio. */
td.ct-resized .ldap-cell { vertical-align: middle; }
td.ct-resized .ldap-node-name, td.ct-resized .ldap-cell > a, td.ct-resized .cell-with-action { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* Columnas Grupos / Etiquetas / Equipos de la tabla de asignaciones: recortadas con «…»; el tooltip lista todo. */
.target-names { white-space: nowrap; }
td.ct-targets { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
td.ct-targets .tag-chips { flex-wrap: nowrap; }
@media (max-width: 700px) {
    .role-grid { grid-template-columns: minmax(7rem, 1fr) repeat(3, 3.5rem); }
}

/* SIN RECUADRO DE FOCO (decision del propietario 2026-09-18): nada se queda con el borde del navegador al pulsarlo o
   al recorrerlo con el teclado (cabeceras de acordeon, botones, iconos, enlaces, miniaturas...). Tras usar las flechas
   ← → el navegador pasaba a «modo teclado» y pintaba un recuadro blanco en el siguiente elemento pulsado.
   `!important` porque debe ganar a las reglas `:focus-visible` sueltas de mas arriba. EXCEPCION: los campos donde se
   escribe o se elige (input, select, textarea) conservan su borde de color: ahi hace falta saber donde se teclea.
   Lo que ya cambia de fondo, color o subrayado con `:focus-visible` lo sigue haciendo: eso no es un borde. */
:not(input):not(select):not(textarea):focus,
:not(input):not(select):not(textarea):focus-visible { outline: none !important; }
/* Casillas y radios son <input>, pero su recuadro es el mismo borde que se quiere evitar. */
input[type="checkbox"]:focus, input[type="radio"]:focus,
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible { outline: none !important; }

/* Tira de estado por equipo en la tabla de Equipos (plan irm-equipos-tira-estado): un lienzo de ancho fijo por fila.
   El fondo tenue deja ver el HUECO (apagado o suspendido) como hueco y no como celda rota. Colores: los pinta
   Portal.stateStrip con las variables del tema. */
.state-strip {
    display: block;
    width: 220px;
    height: 12px;
    border-radius: 2px;
    background: color-mix(in srgb, var(--border) 45%, transparent);
}

/* Leyenda de la tira en el tooltip de su cabecera: una muestra de color por estado (el color lo pone Portal.stateStrip
   con la variable CSS del estado). «Encendido» a media altura y el hueco con el fondo de la tira, como se pintan. */
.strip-legend { display: flex; flex-direction: column; gap: 0.25rem; text-align: left; }
.strip-legend-row { display: flex; align-items: center; gap: 0.5rem; }
.strip-swatch { flex: 0 0 26px; height: 10px; border-radius: 2px; opacity: 0.9; }
.strip-swatch-on { height: 5px; }
.strip-swatch-gap { background: color-mix(in srgb, var(--border) 45%, transparent); outline: 1px dashed var(--border); }
.strip-swatch-tenue { opacity: 0.45; }

/* Equipo que lleva mas de 2 latidos + 10 s sin llamar (tabla de Equipos): nombre en gris, como desactivado. */
a.device-silent, a.device-silent:visited { color: var(--muted); }

/* Agentes > Ocupacion (plan irm-agentes-ocupacion, 0.41.0). Totales del parque sobre la tabla: las tarjetas `.stat` del
   kit, algo mas anchas porque llevan recuento + peso; la del total, en el color de acento. */
.storage-totals { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); margin-bottom: 0.8rem; }
.storage-totals .storage-total .num { color: var(--accent); }
/* Modal de borrado (Portal.storageDeleteModal): casillas en linea, barra de la franja sobre la grafica, recuento previo
   destacado (atenuado mientras se calcula) y la nota permanente con el aviso a la izquierda. */
.storage-kinds { display: flex; flex-wrap: wrap; gap: 0.2rem 0.6rem; }
.storage-chart-bar { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; }
.storage-chart-bar > button { flex: none; }
.storage-summary { margin: 0; font-weight: 600; }
.storage-summary.storage-summary-loading { font-weight: 400; color: var(--muted); }
.storage-confirm { display: flex; flex-direction: column; gap: 0.4rem; }
.storage-confirm > p { margin: 0; }
.storage-delete .error { margin: 0; }
.storage-note { border-left: 3px solid var(--warn); padding-left: 0.6rem; }

/* Suplantación de usuario (2026-09-24): el botón del usuario en naranja mientras dura. */
.user-menu-btn.user-menu-impersonating { color: var(--warn); border-color: var(--warn); }
/* Reinicio pendiente (2026-09-24): icono del propietario en naranja, centrado en su celda. */
.pending-reboot-icon { display: inline-flex; color: var(--warn); line-height: 0; }
.pending-reboot-icon svg { width: 16px; height: 16px; }
/* Columna BitLocker por unidad de sistema (2026-10-01): escudo verde si esta cifrada; sin cifrar, el mismo escudo muy
   tenue (color apagado y media opacidad), que fue lo que pidio el propietario. */
.bitlocker-system { display: inline-flex; line-height: 0; }
.bitlocker-system svg { width: 15px; height: 19px; }
.bitlocker-system.bitlocker-off { color: var(--muted); opacity: 0.45; }
