/* IRM - kit Croco (copiado de JailMeNow/CrocoMail y adaptado; 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);
  --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;
}

: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);
  --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;
}

/* 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; }

/* ---- 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): crece hasta 1800px en pantallas grandes; por debajo
   ocupa el ancho disponible (width 100%), asi que en un portatil de 1440px o menos se ve igual
   que antes. No se va a ancho completo a proposito: en un monitor de 2560, cruzar equipo y
   estado obligaria a recorrer la fila con la vista de un extremo al otro. */
.page {
  width: 100%;
  max-width: 1800px;
  /* 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;
  gap: 1.2rem;
}

.toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.8rem; }
.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. */
.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; }
.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 {
  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 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; }

.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 */

/* Chips de columnas ocultas (clic derecho en cabecera → Ocultar) */
/* 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(--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%;
  min-width: 90px;
  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; }
.ct-filter:focus { outline: 1px solid var(--accent); border-color: var(--accent); }

/* Caja del filtro de columna: el input y, flotando dentro a la derecha, el «?»
   que explica la sintaxis (comas, `!`). Oculto hasta que el ratón entra en la
   caja, igual que el embudo de filtrar por celda: el tooltip es largo y no debe
   saltar solo por rozar el input. */
/* `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%; }
/* Caja VACÍA: no se reserva sitio para el «?». Está a `opacity: 0` hasta que el
   ratón entra, y lo único que puede tapar entonces es el placeholder. Reservarle
   ancho permanente le quitaba ~1.5rem al texto en TODAS las columnas. */
.ct-filter-box .ct-filter { padding-right: 0.45rem; }
/* Norma del propietario (todas las tablas): el «?» SIEMPRE pegado a la derecha y 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 (se pintaba al final y empujaba al «?»); la «×» es un botón propio que
   crocotable.js coloca midiendo el texto. Con texto se reserva a la derecha el sitio de la «×» y del «?»,
   para que un texto largo nunca quede debajo de ellos. */
.ct-filter-box .ct-filter:not(:placeholder-shown) { padding-right: calc(0.3rem + 13px + 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; }
.ct-filter-ayuda {
  position: absolute;
  /* Pegado al borde: es un icono de ayuda, no contenido del filtro. */
  right: 0.3rem;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border: 1px solid var(--muted);
  border-radius: 999px;
  color: var(--muted);
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
  cursor: help;
  opacity: 0;
  transition: opacity 0.12s;
}
.ct-filter-box:hover .ct-filter-ayuda { opacity: 1; }
.ct-filter-ayuda:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }

.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; }
.ct-count-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.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; }
.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); }
.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...). */
.kv-grid { width: auto; border-collapse: collapse; font-size: 0.9rem; }
/* Separacion entre columnas (bloques en lista de la ficha: parches, usuarios...): 1.5rem 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.3rem 1.5rem 0.3rem 0; color: var(--muted); font-weight: 500; vertical-align: top; white-space: nowrap; border: none; }
.kv-grid td { padding: 0.3rem 1.5rem 0.3rem 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.1rem 0 0.3rem; }
.kv-grid .kv-index { color: var(--muted); font-variant-numeric: tabular-nums; }
.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). */
.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-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.4rem 1rem; font-size: 0.9rem; }
.device-facts .k { color: var(--muted); font-size: 0.78rem; display: block; }
.device-facts .v { overflow-wrap: anywhere; }
.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). */
th.ct-actions, td.ct-actions { width: auto; 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; }
/* 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; }

/* 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,
   pegados entre sí: el autorefresco pierde aquí su `margin-left: auto` para no separarse del rango. */
.activity-bar .ranges-barra { margin-left: auto; }
.activity-bar .auto-refresh { margin-left: 0; }
.activity-help { cursor: help; border-bottom: 1px dotted var(--muted); }

/* 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; }

/* 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; }
.gm-columns { display: grid; grid-template-columns: 1fr 2.4rem 1fr; gap: 0.5rem; }
/* 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; }
.timing-inherited { color: var(--muted); opacity: 0.7; margin-right: 0.15em; }

/* 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(--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; }
.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. */
.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; }
