:root {
  --green: #00B78C; --green-d: #009a76;
  --navy: #013a52; --bg: #f4f6f8; --card: #fff;
  --line: #e3e8ee; --txt: #1f2a33; --muted: #6b7c8a; --danger: #d9534f;
  --warn: #e08a00; --info: #035a7e;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--txt);
  font: 15px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
a { color: var(--green-d); text-decoration: none; }

/* 🧨🧨 08/09/2026 · AQUÍ HABÍA `height: 56px` FIJO, y ése era el fallo: con el `flex-wrap`
   del `nav` (puesto el 06/09 para que no se perdiera el último menú) los menús pasan a DOS
   líneas, no caben en 56 px y «Mi cuenta»/«Salir» acababan encima de ellos.
   ⭐ `min-height` deja que la barra CREZCA en vez de desbordarse. */
.topbar { background: var(--navy); color: #fff; display: flex; align-items: center;
  justify-content: space-between; padding: 6px 20px; min-height: 56px;
  flex-wrap: wrap; row-gap: 6px; }
/* El nav ocupa el hueco que quede: así envuelve DENTRO de la barra, sin empujar nada fuera. */
.topbar nav { flex: 1 1 auto; }
.topbar .brand { color: #fff; font-weight: 700; font-size: 18px; display: flex; align-items: center; gap: 10px; }
.topbar .brand-logo { height: 34px; background: #fff; padding: 4px 9px; border-radius: 7px; display: block; }
/* 🧨🧨 06/09/2026 · `flex-wrap: wrap` NO ES COSMÉTICA, ES LO QUE HACE QUE SE
   VEAN TODOS LOS MENÚS. Esta barra era `flex` sin wrap: al añadir el módulo 9
   («Alertas») dejaron de caber y el último se salía por la derecha —y como
   `html, body` llevan `overflow-x: hidden`, NO HABÍA FORMA DE LLEGAR A ÉL—.
   El menú existía, estaba activo y con permiso: simplemente quedaba fuera de la
   pantalla. El usuario lo dijo así: «no veo la pestaña».
   ⭐ Con wrap, cuando no caben pasan a una segunda línea en vez de desaparecer.
   ⛔ NO se arregla bajando el tamaño de letra (norma del usuario). */
.topbar nav { display: flex; align-items: center; gap: 18px;
              flex-wrap: wrap; row-gap: 8px; }
.topbar nav a { color: #cfe3ec; }
.topbar nav a:hover, .topbar nav a.active { color: #fff; }
.topbar .who { color: #7fa9bd; font-size: 13px; }
.topbar .logout { color: #fff; border: 1px solid #2a5a72; padding: 5px 12px; border-radius: 6px; }

@media (max-width: 720px) {
  .topbar { height: auto; flex-direction: column; align-items: center; padding: 10px 12px; gap: 8px; }
  .topbar .brand { justify-content: center; font-size: 17px; }
  .topbar nav { justify-content: center; flex-wrap: wrap; gap: 8px 14px; }
  .topbar .who { display: inline-block; color: #fff; background: #ffffff1f; border: 1px solid #ffffff45; padding: 5px 11px; border-radius: 7px; font-weight: 600; font-size: 13px; }
}

.wrap { max-width: 1100px; margin: 0 auto; padding: 22px 18px 70px; }
.page-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; flex-wrap: wrap; gap: 10px; }
.page-head h1 { margin: 0; font-size: 23px; }
.head-actions { display: flex; gap: 10px; flex-wrap: wrap; }
h1, h2, h3 { color: var(--navy); }

.card { background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 18px 20px; margin-bottom: 18px; }
.card h2 { margin: 0 0 14px; font-size: 17px; }
.card.narrow { max-width: 480px; }
.muted { color: var(--muted); }
.empty { padding: 28px 0; text-align: center; color: var(--muted); }

label { display: block; margin-bottom: 12px; font-size: 13px; color: var(--muted); }
input, select, textarea { display: block; width: 100%; margin-top: 4px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px; font-size: 15px; color: var(--txt); background: #fff; }
textarea { resize: vertical; font-family: inherit; line-height: 1.4; }

.btn, button { display: inline-block; cursor: pointer; border: 0; border-radius: 8px;
  background: var(--green); color: #fff; padding: 9px 16px; font-size: 14px; font-weight: 600; font-family: inherit; }
.btn:hover, button:hover { background: var(--green-d); }
.btn.ghost { background: #eef2f5; color: var(--navy); }
.btn.ghost:hover { background: #e3e9ee; }
.btn.navy { background: var(--navy); }
.btn.danger { background: #fbe9e9; color: var(--danger); }
.btn.danger:hover { background: #f5d6d6; }
/* NARANJA de "cerrar rápido" (20/08/2026): lo usa el botón de finalizar la revisión
   administrativa que está en la barra de arriba del detalle de orden. Va en naranja a
   propósito, para que NO se confunda con el verde de "Guardar cambios" que tiene al lado:
   uno guarda y se queda, el otro cierra la orden y la manda al histórico. */
.btn.warn, button.warn { background: var(--warn); color: #fff; }
.btn.warn:hover, button.warn:hover { background: #c47700; }
.btn.small { padding: 5px 11px; font-size: 13px; }
.btn.big { padding: 16px 22px; font-size: 18px; width: 100%; }
button:disabled { opacity: .4; cursor: default; }

.row-form { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; }
.row-form label { margin: 0; flex: 1 1 180px; }
.row-form button { height: 39px; }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid3 { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 14px; }
@media (max-width: 760px) { .grid2 { grid-template-columns: 1fr; } }

/* tarjetas KPI */
.kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px,1fr)); gap: 14px; margin-bottom: 18px; }
.kpi { background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: 16px; text-align: center; }
.kpi .n { font-size: 30px; font-weight: 800; color: var(--navy); }
.kpi .l { font-size: 13px; color: var(--muted); }
.kpi.alert .n { color: var(--warn); }
.kpi.go .n { color: var(--green-d); }

/* ── LOS 4 RECUADROS DE CABECERA, EN UNA SOLA LÍNEA TAMBIÉN EN EL MÓVIL ──────
   Petición del usuario (05/09/2026): *«haz los botones más pequeños, que en el
   móvil entren los 4 en una sola línea»*.

   🧨 CÓMO SE HA COMPACTADO, Y CÓMO NO: se recorta `padding`, `gap` y el
      `border-radius`. **NO se toca el `font-size`** — es una norma explícita del
      usuario (23/07/2026: «me has bajado el tamaño de letra, eso no me lo bajes,
      que luego se ve mal»). Lo que se acorta para ganar sitio es el TEXTO de las
      etiquetas, en la plantilla, no la letra.
   ⭐ `repeat(4, 1fr)` en vez del `auto-fill` general: con `auto-fill` el móvil
      partía la fila en dos, que es justo lo que se quería evitar.
   ⚠️ Solo afecta a `.kpis-4`; el resto de pantallas con `.kpis` siguen igual. */
.kpis-4 { grid-template-columns: repeat(4, 1fr); }
/* 🔘 08/10/2026: con el PPPoE DESACTIVADO desde Configuración quedan TRES
   recuadros; sin esta variante la rejilla de 4 dejaría un hueco vacío. */
.kpis-3 { grid-template-columns: repeat(3, 1fr); }

/* Recuadros que ENCIENDEN/APAGAN su gráfica (05/09/2026). El aspecto de
   «encendido» lo pone `.kpi-activo`, que ya existía más arriba. */
.kpi-conmuta { cursor: pointer; user-select: none;
               transition: border-color .12s, box-shadow .12s; }
.kpi-conmuta:hover { border-color: var(--green); }
.kpi-conmuta.kpi-activo { border-color: var(--green); box-shadow: inset 0 -3px 0 var(--green); }

@media (max-width: 700px) {
  .kpis-4, .kpis-3 { gap: 6px; margin-bottom: 12px; }
  .kpis-4 .kpi, .kpis-3 .kpi { padding: 10px 3px; border-radius: 10px; }
  /* `letter-spacing` negativo aprieta las cifras largas (p. ej. 1014 usuarios
     PPPoE) sin reducir el tamaño de letra: así entran en una columna estrecha
     y se siguen leyendo igual de grandes. */
  .kpis-4 .kpi .n, .kpis-3 .kpi .n { letter-spacing: -1px; line-height: 1.15; }
  /* La etiqueta puede ocupar dos líneas sin romper la rejilla. */
  .kpis-4 .kpi .l, .kpis-3 .kpi .l { line-height: 1.2; }
}
a.kpi { display: block; text-decoration: none; color: inherit; cursor: pointer; transition: border-color .12s, box-shadow .12s; }
a.kpi:hover { border-color: var(--green); box-shadow: 0 2px 8px rgba(0,0,0,.06); }
a.kpi.kpi-activo { border-color: var(--green); box-shadow: inset 0 -3px 0 var(--green); }

/* tablas */
.tbl { width: 100%; border-collapse: collapse; }
.tbl th, .tbl td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--line); font-size: 14px; vertical-align: middle; }
.tbl th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }
.tbl tr:hover td { background: #fafbfc; }

/* badges de estado */
.badge { display: inline-block; font-size: 12px; padding: 3px 10px; border-radius: 20px; font-weight: 600; white-space: nowrap; }
.b-pendiente { background: #eef2f5; color: var(--navy); }
.b-asignada { background: #cfeefa; color: var(--info); }
.b-citada { background: #d9f0ff; color: #035a7e; }
.b-en_curso { background: #fff3d6; color: var(--warn); }
.b-pausada { background: #eef2f5; color: #6b7c8a; }
/* 🌙 10/09/2026 · Equipo CON pausa programada configurada (pero fuera de ella ahora).
   Azul apagado: es información, no una alarma. Dentro de la ventana se usa `.b-pausada`,
   el mismo gris de «pausado», porque es exactamente lo que está pasando. */
.b-luna { background: #eaf1f8; color: #3c6a97; }
/* La rejilla de tramos del panel del equipo: compacta, que son 4 campos por fila. */
.bloque-pausas { margin-top: 14px; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 10px; background: #fbfcfd; }
.tbl-pausas { width: auto; }
.tbl-pausas th { font-size: 12px; color: var(--muted); font-weight: 600; padding: 2px 8px 4px 0; }
.tbl-pausas td { padding: 2px 8px 2px 0; }
.tbl-pausas input { padding: 4px 6px; }
.b-revision_administrativa { background: #efe7fb; color: #6a3fb5; }
.b-finalizada { background: #e7f6f1; color: var(--green-d); }
.b-cancelada { background: #fbe9e9; color: var(--danger); }
.b-propuesta { background: #efe7fb; color: #6a3fb5; }
.b-check_inicial { background: #ffe7c2; color: #9a5b00; }
.tag-tipo { background: #e7f6f1; color: var(--green-d); font-size: 12px; padding: 3px 9px; border-radius: 6px; font-weight: 600; }

/* prioridad */
.prio { display: inline-block; font-size: 12px; padding: 3px 10px; border-radius: 20px; font-weight: 700; white-space: nowrap; }
.prio.alta { background: #fbe9e9; color: var(--danger); }
.prio.normal { background: #eef2f5; color: var(--muted); }
.prio.baja { background: #eef2f5; color: var(--muted); }
/* fila/tarjeta de orden con prioridad ALTA: marcada en rojo */
.tbl tr.fila-alta td { background: #fdf2f2; }
.tbl tr.fila-alta td:first-child { box-shadow: inset 3px 0 0 var(--danger); }

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { background: #eef2f5; color: var(--navy); font-size: 12px; padding: 3px 9px; border-radius: 20px; }

.alert { padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; font-size: 14px; }
.alert.error { background: #fbe9e9; color: var(--danger); }
.alert.ok { background: #e7f6f1; color: var(--green-d); }
.alert.info { background: #cfeefa; color: var(--info); }
.alert.warn { background: #fff3d6; color: #9a6b00; border: 1px solid #e0b400; }
.alert.warn a { color: #7a5400; font-weight: 700; text-decoration: underline; }
@keyframes blink { 50% { opacity: .35; } }
.alert.blink { background: #fff3d6; color: #9a6b00; border: 1px solid #e0b400; font-weight: 700;
  animation: blink 1s steps(2, start) infinite; font-size: 15px; }

/* login */
.login-box { max-width: 370px; margin: 8vh auto 0; background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 30px; }
.login-logo { text-align: center; margin-bottom: 14px; }
.login-logo img { max-width: 220px; width: 100%; }
.login-box h1 { margin: 0; font-size: 23px; text-align: center; }
.login-box .muted { margin: 4px 0 20px; text-align: center; }

/* detalle de orden */
.dl { display: grid; grid-template-columns: 150px 1fr; gap: 6px 14px; font-size: 14px; }
.dl dt { color: var(--muted); }
.dl dd { margin: 0; }
.obs { white-space: pre-wrap; background: #fafbfc; border: 1px solid var(--line); border-radius: 8px; padding: 12px; font-size: 14px; }

/* fichaje técnico */
.ficha-card { text-align: center; }
.estado-grande { font-size: 22px; font-weight: 800; margin: 6px 0 16px; }
#sig-pad { border: 2px dashed var(--line); border-radius: 10px; width: 100%; height: 200px; touch-action: none; background: #fff; }
.cron { font-size: 40px; font-weight: 800; color: var(--navy); letter-spacing: 2px; }

.bar { background: #eef2f5; border-radius: 6px; height: 22px; overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--green); }

/* comentarios tipo conversación */
.comentario { border: 1px solid var(--line); border-left: 4px solid var(--green); border-radius: 10px;
  padding: 10px 14px; margin-bottom: 10px; background: #fff; }
.comentario.importado { border-left-color: var(--muted); background: #fafbfc; }
.comentario .com-head { font-size: 13px; margin-bottom: 4px; color: var(--navy); display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.comentario .com-fecha { margin-left: auto; color: var(--muted); font-size: 12px; }
.comentario .com-body { white-space: pre-wrap; font-size: 14px; }

/* Móvil: evita que las tablas (órdenes, etc.) se salgan de la pantalla — scroll horizontal dentro de la tarjeta */
html, body { overflow-x: hidden; }
@media (max-width: 760px) {
  .tbl { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tbl th, .tbl td { white-space: nowrap; }
  .wrap { padding: 16px 12px 70px; }
  .card { padding: 14px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   PICA · añadidos propios sobre la hoja heredada de Gest CVIP
   (el bloque de arriba se mantiene tal cual para que las dos plataformas se
    vean IGUAL; lo específico de PICA va SIEMPRE aquí abajo, separado, para
    poder actualizar la base de Gest sin pisar lo nuestro)
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Selector de EMPRESA en la barra superior ─────────────────────────────
   Es la pieza más importante de la cabecera: indica sobre qué empresa se está
   trabajando. Va destacado a propósito — trabajar en la empresa equivocada es
   el error más caro de una plataforma multiempresa. */
.topbar .empresa-sel { display: flex; align-items: center; gap: 8px; }
.topbar .empresa-sel select {
  width: auto; margin: 0; padding: 5px 10px; font-size: 13px; font-weight: 600;
  background: #ffffff1a; color: #fff; border: 1px solid #ffffff45; border-radius: 7px;
}
.topbar .empresa-sel select option { color: #1f2a33; }   /* legible al desplegar */
.topbar .empresa-sel .et { color: #7fa9bd; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }

/* ── Semáforo de estado de equipo (ping) ──────────────────────────────────
   Verde = respondió · rojo = no respondió · gris = nunca se ha comprobado. */
.sem { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
       vertical-align: middle; margin-right: 6px; }
.sem.ok    { background: var(--green); }
.sem.ko    { background: var(--danger); }
.sem.nunca { background: #c8d2da; }

/* ── Salida de consola (ping/escaneo) ─────────────────────────────────────
   Monoespaciada y con scroll propio: la salida real del comando, tal cual. */
.consola { background: #0d1b22; color: #d7e6ee; border-radius: 10px; padding: 14px 16px;
  font: 13px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: pre-wrap; overflow-x: auto; max-height: 420px; overflow-y: auto; }
.consola .ok { color: #4fd1a5; }
.consola .ko { color: #ff8f8f; }

/* ── Tarjetas de módulo del panel ─────────────────────────────────────────*/
.modulos { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px,1fr)); gap: 14px; }
.modulo-card { display: block; background: #fff; border: 1px solid var(--line); border-radius: 12px;
  padding: 18px; color: inherit; transition: border-color .12s, box-shadow .12s; }
.modulo-card:hover { border-color: var(--green); box-shadow: 0 2px 8px rgba(0,0,0,.06); }
.modulo-card .ico { font-size: 26px; }
.modulo-card h3 { margin: 8px 0 6px; font-size: 17px; }
.modulo-card p { margin: 0; font-size: 13px; color: var(--muted); }
.modulo-card.apagado { opacity: .55; }

/* ── Credencial oculta/revelada en la ficha de equipo ─────────────────────*/
.cred { font-family: ui-monospace, Menlo, Consolas, monospace; background: #f2f5f7;
  border: 1px solid var(--line); border-radius: 7px; padding: 5px 10px; display: inline-block; min-width: 120px; }

/* ── Chip de módulo (en la administración de empresas) ────────────────────*/
.chip.on  { background: #e7f6f1; color: var(--green-d); font-weight: 600; }
.chip.off { background: #eef2f5; color: var(--muted); }

/* ── Selector de EMPRESA con buscador (25/08/2026) ─────────────────────────
   Sustituye al <select> inicial: con muchas empresas un desplegable no sirve.
   Estructura: <details> en la barra → panel blanco con buscador + lista de
   botones. Ver templates/_selector_empresa.html. */
.topbar .empresa-sel { position: relative; }
.topbar .empresa-sel > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 8px;
  background: #ffffff1a; border: 1px solid #ffffff45; border-radius: 7px;
  padding: 5px 11px; color: #fff; font-size: 13px;
}
.topbar .empresa-sel > summary::-webkit-details-marker { display: none; }
.topbar .empresa-sel > summary::after { content: "\25BE"; font-size: 11px; opacity: .75; }
.topbar .empresa-sel > summary:hover { background: #ffffff2b; }
.topbar .empresa-sel .et { color: #7fa9bd; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; }
.topbar .empresa-sel.unica { display: inline-flex; align-items: center; gap: 8px; color: #fff; font-size: 13px; }

.topbar .empresa-panel {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: 1300;
  background: #fff; min-width: 300px; max-width: 380px;
  border-radius: 10px; box-shadow: 0 8px 24px rgba(1,58,82,.28); padding: 10px;
}
.topbar .empresa-panel input[type="search"] {
  width: 100%; margin: 0 0 8px; padding: 8px 10px; font-size: 14px;
  border: 1px solid var(--line); border-radius: 8px;
}
/* La lista tiene tope de altura y scroll propio: con muchas empresas, el panel
   no puede crecer hasta salirse de la pantalla. */
.empresa-lista { display: flex; flex-direction: column; gap: 2px; max-height: 320px; overflow-y: auto; }
.empresa-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; text-align: left; background: #fff; color: var(--txt);
  border: 0; border-radius: 7px; padding: 9px 11px; font-size: 14px; font-weight: 500; cursor: pointer;
}
.empresa-item:hover { background: #eef2f6; color: var(--navy); }
.empresa-item.actual { background: #e7f6f1; color: var(--green-d); font-weight: 700; }
.empresa-item .marca { color: var(--green-d); }
.empresa-vacio { margin: 8px 2px 0; font-size: 13px; color: var(--muted); }
.empresa-pie { margin: 8px 2px 0; font-size: 11px; color: var(--muted); text-align: right; }

@media (max-width: 720px) {
  /* En móvil el panel ocupa el ancho disponible y se centra bajo la barra. */
  .topbar .empresa-panel { position: static; min-width: 0; max-width: none; width: 100%; box-shadow: none; margin-top: 6px; }
}

/* ── Zona peligrosa (borrado de empresa) ──────────────────────────────────
   Separada visualmente del resto de la ficha: borde rojo y fondo tenue, para
   que nadie la confunda con los formularios de uso diario. */
.zona-peligro { border-color: #f0c9c9; background: #fffafa; }
.zona-peligro h2 { color: var(--danger); }
.lista-perdida { margin: 8px 0 14px; padding-left: 22px; font-size: 14px; }
.lista-perdida li { margin-bottom: 4px; }
.zona-peligro input { max-width: 380px; }

/* ── Bloque izquierdo de la barra: marca + selector de empresa (25/08) ─────
   El selector de empresa se movió aquí desde el <nav> de la derecha: es el
   contexto de trabajo y se debe leer antes que las opciones del menú. */
.topbar-izq { display: flex; align-items: center; gap: 16px; min-width: 0;
  /* nowrap: sin esto, si el nombre de la empresa es largo el selector se
     va a una segunda línea y queda DEBAJO del logo en vez de al lado. */
  flex-wrap: nowrap; }
.topbar-izq .brand { flex: 0 0 auto; }
@media (max-width: 720px) {
  /* En móvil la barra ya se apila en vertical: aquí solo se centra el bloque. */
  .topbar-izq { flex-direction: column; gap: 8px; width: 100%; }
  .topbar-izq .empresa-sel { width: 100%; text-align: center; }
  .topbar-izq .empresa-sel > summary { justify-content: center; width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MÓDULO 3 · PASARELA IA — burbujas del chat
   ---------------------------------------------------------------------------
   🧨 El motivo de que esto exista: las respuestas se salían del cuadro. Un
   <pre> NO parte las líneas largas por defecto, y la IA devuelve volcados de
   eventos con líneas larguísimas. La combinación que lo arregla es
   `white-space:pre-wrap` (respeta los saltos pero permite ajustar) +
   `overflow-wrap:anywhere` (parte una "palabra" interminable, como una ruta o
   un identificador) + `max-width:100%`.
   ⚠️ NO se toca el tamaño de la fuente: el texto técnico tiene que leerse.
   ═══════════════════════════════════════════════════════════════════════════ */
.salida{
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  word-break:break-word;
  max-width:100%;
  overflow-x:auto;
  margin:6px 0 0;
  padding:10px 12px;
  background:#f4f7f9;
  border:1px solid #e2e9ee;
  border-radius:8px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13.5px;
  line-height:1.45;
}

/* Cada intervención del chat. La del técnico y la de la IA se distinguen por
   color, no por alineación: con textos largos, alinear a la derecha se lee mal. */
.msg{ margin:14px 0; max-width:100%; }
.msg .muted{ font-size:12.5px; margin-bottom:2px; }
.msg-tecnico .salida{ background:#eef6fb; border-color:#d3e6f2; }
.msg-ia .salida{ background:#f6f9f4; border-color:#dfeadb; }

/* El contexto que se le pasó a la IA va plegado: es largo y casi nunca se
   necesita, pero tiene que poder consultarse para saber QUÉ vio la IA. */
.msg-sistema{ margin:14px 0; }
.msg-sistema>summary{ cursor:pointer; }
.msg-sistema .salida{ max-height:340px; overflow:auto; }

/* Caja de escritura */
.form-chat{ margin-top:16px; display:flex; flex-direction:column; gap:8px; }
.form-chat textarea{
  width:100%; box-sizing:border-box; resize:vertical;
  padding:10px 12px; border:1px solid #cfdae2; border-radius:8px;
  font-family:inherit; font-size:15px; line-height:1.4;
}
.form-chat button{ align-self:flex-start; }

/* Bloque «la IA está trabajando»: se distingue del texto normal para que se vea
   de un vistazo que aquello sigue en marcha y no es una respuesta. */
.salida.trabajando{ background:#fff8e8; border-color:#f0dcae; }
.girando{ display:inline-block; animation:girar 1.1s linear infinite; }
@keyframes girar{ to{ transform:rotate(360deg); } }

/* La IA ha pedido una acción: tiene que cantar, porque exige una decisión. */
.card.propuesta{ border-left:4px solid #e8a33d; background:#fffaf0; }
.card.propuesta code{ font-size:15px; background:#fff; padding:3px 8px;
  border:1px solid #e8dcc0; border-radius:6px; }

/* ═══════════════════════════════════════════════════════════════════════════
   HILO DEL CHAT CON SCROLL PROPIO  (25/08/2026)

   🧨 El motivo de que esto exista: la conversación crecía hacia abajo y hacía
   crecer la PÁGINA entera. Como al enviar un mensaje hay POST + redirección, y
   como mientras la IA trabaja la pantalla se recarga sola cada 3 s, en cada
   caso el navegador volvía al PRINCIPIO del documento: el último mensaje y la
   caja de escribir quedaban abajo, fuera de la vista, y había que desplazarse
   a mano una y otra vez.

   La solución es que el HILO tenga su propia ventana con scroll y arranque
   pegado abajo (lo hace el script del final de ia_chat.html). Así la caja de
   escribir está SIEMPRE en el mismo sitio de la pantalla.

   ⚠️ El max-height de aquí es solo el valor de partida, para que se vea bien
   incluso si el JS no llega a ejecutarse; el script lo recalcula con la altura
   real de la ventana y lo rehace al redimensionar.
   ⚠️ NO se toca el tamaño de la fuente (norma del usuario): esto va de alto y
   de scroll, no de encoger el texto.
   ═══════════════════════════════════════════════════════════════════════════ */
.chat-hilo{
  /* ⚠️ Valores SUBIDOS el 26/08: la consola salía diminuta («la pantalla es muy
     pequeña»). Esto es solo el punto de partida — el script de ia_chat.html
     recalcula el alto con la ventana real; pero si el JS no llega a correr,
     este es el tamaño que se ve, y 55vh/160px se quedaban muy cortos. */
  max-height:70vh;
  min-height:360px;
  overflow-y:auto;
  overscroll-behavior:contain;   /* al llegar al final NO arrastra la página */
  scrollbar-gutter:stable;       /* el ancho no baila al aparecer la barra */
  padding:2px 12px 2px 2px;
  margin-top:4px;
}
/* Contra los bordes del contenedor sobra el margen de 14px de .msg */
.chat-hilo>.msg:first-child,.chat-hilo>.msg-sistema:first-child{ margin-top:2px; }
.chat-hilo>.msg:last-child,.chat-hilo>.msg-sistema:last-child{ margin-bottom:2px; }

/* ═══════════════════════════════════════════════════════════════════════════
   CONSOLA DEL CHAT DE IA  (26/08/2026)

   PEDIDO DEL USUARIO: *«no veo la pantalla negra de consola. lo quiero todo
   ahí»*. El hilo era claro, con una cajita por mensaje, y solo la salida del
   equipo salía en oscuro. Ahora **TODO el hilo es una consola**: lo que escribe
   el técnico, lo que responde la IA, las acciones autorizadas y la salida del
   equipo, en el mismo flujo y con el mismo aspecto de terminal.

   CÓMO ESTÁ MONTADO
     · `.chat-hilo.consola` pinta el fondo negro y fija la fuente monoespaciada.
     · Dentro, `.salida` pierde su caja clara (fondo, borde y padding a cero):
       si no, quedaban recuadros blancos flotando sobre el negro.
     · Cada intervención lleva un PREFIJO de color (`.linea-prompt`) que dice de
       quién es, como el prompt de una shell. Se distingue por color, no por
       alineación: con textos largos, alinear a la derecha se lee fatal.

   ⚠️ Los colores son de la paleta oscura de GitHub, elegidos por CONTRASTE
      sobre #0c0c0c. Si se cambian, comprobar que siguen legibles.
   ⚠️ NO se toca el tamaño de la fuente (norma del usuario): esto va de color y
      de fondo, no de encoger el texto.
   ⛔ El ámbito es SIEMPRE `.consola …`: `.salida` se usa también en la ficha del
      equipo y en la pantalla de Hermes, que son claras y deben seguir siéndolo.
   ═══════════════════════════════════════════════════════════════════════════ */
.chat-hilo.consola{
  background:#0c0c0c;
  color:#d6d6d6;
  border:1px solid #2a2a2a;
  border-radius:8px;
  padding:12px 14px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}

/* Dentro de la consola, los bloques de texto son texto pelado: nada de cajas */
.consola .salida{
  background:transparent;
  border:0;
  border-radius:0;
  padding:0;
  margin:2px 0 0;
  color:inherit;
}
.consola .msg{ margin:0 0 12px; }
.consola .muted{ color:#8b949e; }

/* El prefijo de cada intervención: quién habla, como el prompt de una shell */
.consola .linea-prompt{
  font-size:12.5px;
  margin-bottom:2px;
  letter-spacing:.02em;
}
.consola .p-tecnico{ color:#7ee787; }   /* verde: lo que escribe la persona   */
.consola .p-ia     { color:#79c0ff; }   /* azul: lo que responde la IA        */
.consola .p-accion { color:#ffa657; }   /* naranja: una acción se ha lanzado  */
.consola .p-salida { color:#56d364; }   /* verde vivo: contestó el equipo     */
.consola .p-error  { color:#ff7b72; }   /* rojo: algo falló                   */

/* El texto de cada quién, con un matiz de color para seguir el hilo de un
   vistazo sin tener que leer los prefijos. */
.consola .msg-tecnico .salida{ color:#c9f5c9; }
.consola .msg-ia .salida     { color:#e6edf3; }
.consola .msg-salida .salida { color:#b9d0b9; }
.consola .msg-accion .salida { color:#ffd7a8; }

/* La salida del equipo va con una barra a la izquierda: es la única parte que
   NO la escribe nadie de la conversación, sino la máquina del cliente. */
.consola .msg-salida{
  border-left:3px solid #2ea043;
  padding-left:10px;
}
.consola .msg-accion{
  border-left:3px solid #d29922;
  padding-left:10px;
}

/* El contexto inicial sigue plegado, pero en versión oscura */
.consola .msg-sistema>summary{ color:#8b949e; font-size:12.5px; }
.consola .msg-sistema .salida{ color:#9aa4ad; max-height:340px; overflow:auto; }

/* «Trabajando»: en la consola no puede ser un recuadro amarillo claro */
.consola .salida.trabajando{
  background:transparent;
  border:0;
  color:#ffd7a8;
}

/* ⛶ CONSOLA AMPLIADA (26/08/2026)
   Pedido: «la pantalla es muy pequeña, hay que hacerla más grande». Con esto la
   consola ocupa casi toda la ventana y se trabaja en ella como en un terminal.
   El botón está en la barra de control y la preferencia se recuerda en el
   navegador (ver el script de ia_chat.html).
   ⚠️ `min-height` además de `max-height`: si no, con pocos mensajes la caja se
      encoge y el botón parece que no hace nada. */
.chat-hilo.ampliada{
  max-height:82vh;
  min-height:82vh;
}

/* ═══════════════════════════════════════════════════════════════════════════
   CONSOLA COMPACTA  (26/08/2026)

   PEDIDO: *«hay mucho espacio entre las preguntas y respuestas, eso hay que
   reducirlo para tener más visión»*.

   ⛔⛔ SE TOCAN SOLO MÁRGENES, PADDING E INTERLINEADO. **NO se baja el
   `font-size`** — es norma explícita del usuario, y en una consola de trabajo
   el texto técnico tiene que seguir leyéndose cómodo. Si algún día hace falta
   más densidad, se recorta más espaciado, NUNCA la letra.

   Va al final del fichero a propósito: así gana a las reglas de arriba sin
   tener que editarlas (y se ve de un vistazo qué se compactó y por qué).
   ═══════════════════════════════════════════════════════════════════════════ */

/* Entre una intervención y la siguiente: 12px → 6px. Es el hueco que más se
   notaba, porque se suma al del bloque de texto que va dentro. */
.consola .msg{ margin:0 0 6px; }
.consola .msg-sistema{ margin:0 0 6px; }

/* El prefijo (`usuario@pica:~$`) va pegado a su texto: son la misma cosa. */
.consola .linea-prompt{ margin-bottom:0; line-height:1.25; }

/* El bloque de texto ya no añade separación propia; el aire lo pone `.msg`.
   El interlineado baja de 1.45 a 1.35: se gana una línea cada siete y sigue
   siendo holgado para leer salidas de comandos. */
.consola .salida{ margin:0; line-height:1.35; }

/* Menos aire contra los bordes de la caja negra */
.chat-hilo.consola{ padding:8px 10px; }

/* Las barras laterales de acción y salida iban con 10px de sangría: con 8 se
   siguen distinguiendo igual de bien y se gana ancho útil. */
.consola .msg-salida,
.consola .msg-accion{ padding-left:8px; }

/* La actividad de la IA (traza de Hermes): plegada, en gris, para que se note
   que está pero no compita con la conversación. Ver `papel="traza"`. */
.consola .msg-traza{ margin:0 0 6px; }
.consola .msg-traza>summary{ list-style:none; }
.consola .msg-traza .salida{ color:#8b949e; border-left:2px solid #30363d; padding-left:8px; }

/* ═══════════════════════════════════════════════════════════════════════════
   CONSOLA · SEGUNDA PASADA DE COMPACTADO  (26/08/2026, tras probarlo el usuario)

   La primera pasada recortó márgenes e interlineado y el filtro `| compacto`
   quitó las líneas en blanco del texto… y AUN ASÍ se veía separado. Al mirar el
   HTML generado de verdad (en vez de seguir suponiendo) se vio por qué:
   **el bloque de la TRAZA se colaba entre la pregunta y la respuesta**. Ya se
   mueve detrás por código; aquí se le quita además todo el peso visual, para
   que sea una línea discreta y no un bloque más de la conversación.

   ⛔ SOLO espaciado. NADA de `font-size` (norma del usuario).
   ═══════════════════════════════════════════════════════════════════════════ */

/* La traza: una línea, sin aire alrededor. Es un «cómo se hizo», no un mensaje */
.consola .msg-traza{ margin:0 0 2px; }
.consola .msg-traza>summary{
  opacity:.55;             /* se ve que está, sin competir con la conversación */
  padding:0;
  margin:0;
}
.consola .msg-traza[open]>summary{ opacity:.85; margin-bottom:3px; }

/* Un punto menos de aire entre intervenciones (6px → 4px). Con el prompt
   pegado a su texto, 4px separa lo justo para distinguir de quién es cada cosa. */
.consola .msg{ margin:0 0 4px; }
.consola .msg-sistema{ margin:0 0 4px; }

/* El contexto inicial, plegado, tampoco necesita ocupar sitio */
.consola .msg-sistema>summary{ opacity:.55; padding:0; }

/* ═══════════════════════════════════════════════════════════════════════════
   .tblwrap — envoltorio de tablas anchas (27/08/2026)
   ---------------------------------------------------------------------------
   Vino con las plantillas portadas de Gest, donde SÍ estaba definida en su CSS.
   Aquí no existía, así que las tablas de cabeceras y ONTs se desbordaban y en
   el móvil la página aparecía CORTADA POR LA DERECHA (lo reportó el usuario).
   La tabla se desplaza DENTRO de su caja; la página nunca scrollea en horizontal.
   ═══════════════════════════════════════════════════════════════════════════ */
.tblwrap{
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;   /* inercia al arrastrar en iOS */
  max-width:100%;
}
/* En pantallas estrechas la tabla no se comprime hasta hacerse ilegible: se
   deja su ancho natural y se arrastra. */
@media (max-width:760px){
  .tblwrap > table{min-width:560px}
}

/* ═══════════════════════════════════════════════════════════════════════════
   MÓDULO 6 · MONITORIZACIÓN IA   (03/09/2026)
   ───────────────────────────────────────────────────────────────────────────
   Estilos del panel en vivo y del histórico. Se apoya en lo que ya existe
   (.card, .muted, .chip, .kpi…) y solo añade lo propio: las tarjetas de GPU,
   las barras de memoria y los lienzos de las gráficas.
   ⚠️ Al tocar este fichero hay que SUBIR el `?v=N` de base.html o el navegador
      seguirá pintando el CSS viejo (pasó ya con la consola del chat de IA).
   ═══════════════════════════════════════════════════════════════════════════ */
.mon-nodo { padding-bottom: 10px; }
.mon-cab { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.mon-cab h2 { margin: 0; }
.mon-sub { font-size: 13px; }
/* Punto de estado: verde si la máquina contesta, rojo si no. Es lo primero que
   se mira, así que va antes que el nombre. */
.mon-punto { width: 11px; height: 11px; border-radius: 50%; background: #cbd5dc; flex: 0 0 auto; }
.mon-punto.bien { background: #0f9d76; box-shadow: 0 0 0 3px rgba(15,157,118,.18); }
.mon-punto.malo { background: #c0392b; box-shadow: 0 0 0 3px rgba(192,57,43,.18); }

.mon-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px,1fr));
            gap: 12px; margin-bottom: 14px; }
.mon-kpi { background: #fafbfc; border: 1px solid var(--line); border-radius: 10px;
           padding: 12px; text-align: center; }
.mon-kpi .n { font-size: 26px; font-weight: 800; color: var(--navy); line-height: 1.1; }
.mon-kpi .l { font-size: 12px; color: var(--muted); margin-top: 3px; }

.mon-bloque { margin-bottom: 14px; }
.mon-tit { font-size: 12px; color: var(--muted); text-transform: uppercase;
           letter-spacing: .03em; font-weight: 600; margin-bottom: 6px; }
/* Altura fija: el canvas necesita un tamaño en CSS para que el JS pueda
   calcular su resolución real (devicePixelRatio). Sin altura, no se ve nada. */
.mon-canvas { width: 100%; height: 90px; display: block; }
.mon-canvas.alta { height: 170px; }

.mon-gpus { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px,1fr)); gap: 14px; }
.mon-gpu { border: 1px solid var(--line); border-radius: 10px; padding: 12px; background: #fff; }
.mon-gpu-cab { margin-bottom: 10px; font-size: 14px; }
.mon-gpu-cifras { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin-bottom: 10px; }
.mon-cifra { text-align: center; }
.mon-cifra .n { font-size: 16px; font-weight: 700; color: var(--navy); }
.mon-cifra .l { font-size: 11px; color: var(--muted); }

.mon-barra-caja { margin-bottom: 8px; }
.mon-barra-tit { font-size: 12px; color: var(--muted); display: flex;
                 justify-content: space-between; gap: 8px; margin-bottom: 3px; }
.mon-barra-val { color: var(--navy); font-weight: 600; }
.mon-barra { height: 9px; background: #eef2f5; border-radius: 5px; overflow: hidden; }
.mon-barra-int { height: 100%; width: 0; background: #0f9d76; border-radius: 5px;
                 transition: width .4s ease, background .4s ease; }

.mon-modelos { margin-bottom: 14px; }
/* 🌀 Ventiladores de las GPUs (30/09/2026): estado + botones de modo de ruido */
.mon-vent-estado { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.mon-vent-botones { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.mon-vent-botones .btn { font-size: 12px; padding: 5px 10px; }
.mon-vent-sel { padding: 4px 6px; border: 1px solid #d7dee5; border-radius: 8px; font-size: 12px; background: #fff; }
.mon-vent-num { width: 64px; padding: 4px 6px; border: 1px solid #d7dee5; border-radius: 8px; font-size: 12px; }
.mon-vent-aviso { font-size: 11px; margin-top: 6px; }
.mon-modelo { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
              padding: 5px 0; font-size: 13px; }

/* Leyenda de las gráficas del histórico: un cuadradito de color y el texto. */
.mon-leyenda { font-size: 12px; margin: -8px 0 8px; display: flex; gap: 14px; flex-wrap: wrap; }
.mon-leyenda .ll { display: inline-block; width: 10px; height: 10px; border-radius: 2px;
                   margin-right: 5px; vertical-align: middle; }
.mon-hist .card { margin-bottom: 16px; }

@media (max-width: 720px) {
  .mon-gpu-cifras { grid-template-columns: repeat(3, 1fr); }
  .mon-hist.grid2 { grid-template-columns: 1fr; }
}

/* Dos gráficas pequeñas lado a lado (CPU y tokens/s), 03/09/2026. Antes iban
   en un solo lienzo compartiendo eje, que era engañoso: % y tokens/s no son
   comparables. */
.mon-dos { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
@media (max-width: 720px) { .mon-dos { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════════════════════════════════════
   MÓDULO 7 · MONITORIZACIÓN (árbol tipo PRTG)   04/09/2026
   ───────────────────────────────────────────────────────────────────────────
   El árbol son <details> anidados: pliega sin JavaScript. La «bola» de color
   es lo que se lee de un vistazo, y sube desde el sensor hasta el grupo.
   ═══════════════════════════════════════════════════════════════════════════ */
.mon-bola { display:inline-block; width:11px; height:11px; border-radius:50%;
            vertical-align:middle; margin-right:7px; flex:0 0 auto; }
.mon-bola.verde { background:#0f9d76; box-shadow:0 0 0 3px rgba(15,157,118,.15); }
.mon-bola.ambar { background:#d98200; box-shadow:0 0 0 3px rgba(217,130,0,.15); }
.mon-bola.roja  { background:#c0392b; box-shadow:0 0 0 3px rgba(192,57,43,.18); }
.mon-bola.gris  { background:#cbd5dc; }

.mon-grupo, .mon-equipo { border-left:2px solid var(--line); margin:2px 0 2px 4px; padding-left:10px; }
.mon-grupo > summary, .mon-equipo > summary {
  cursor:pointer; padding:5px 4px; border-radius:6px; list-style:none;
  display:flex; align-items:center; gap:6px; flex-wrap:wrap; font-size:14px; }
.mon-grupo > summary::-webkit-details-marker,
.mon-equipo > summary::-webkit-details-marker { display:none; }
.mon-grupo > summary:hover, .mon-equipo > summary:hover { background:#f2f6f9; }
.mon-grupo > summary::before, .mon-equipo > summary::before {
  content:"\25B8"; color:var(--muted); font-size:11px; width:10px; }
.mon-grupo[open] > summary::before, .mon-equipo[open] > summary::before { content:"\25BE"; }
.mon-hijos { padding-left:8px; }
.mon-sensores { margin:4px 0 8px 18px; }
.mon-sensores td { padding:5px 8px; font-size:13px; }
.mon-pausado td { opacity:.55; }

/* 🔁 CORREGIDO EL 04/09/2026 — el usuario primero pidió «en verde todo lo que
   funcione» y al verlo lo matizó: «los textos de los dispositivos ponlos en
   negro; en color solo quiero el punto del estado». Y tiene razón: con 294
   equipos, el texto coloreado convertía el árbol en un semáforo ilegible.
   ⭐ El color queda SOLO en la bolita (.mon-bola) y en el chip del tiempo.
   Se conserva únicamente el NEGRITA en lo caído, que no es color pero ayuda. */
.mon-grupo > summary > strong, .mon-equipo > summary { color:var(--txt); }
.mon-equipo > summary.est-caido { font-weight:600; }
/* El tiempo de respuesta: legible pero sin robar protagonismo al nombre. */
.mon-ms { font-size:12px; font-weight:600; color:#0f7d5e; background:#e7f6f1;
          padding:1px 7px; border-radius:10px; white-space:nowrap; }
.mon-ms.malo { color:#c0392b; background:#fbe9e9; }

/* Aviso de caídas en la barra superior (04/09/2026). Parpadea suave para que se
   note sin ser insoportable; solo se pinta cuando hay algo caído. */
.topbar .mon-alarma { background:#c0392b; color:#fff; font-weight:700; font-size:13px;
  padding:3px 11px; border-radius:20px; animation:mon-late 2s ease-in-out infinite; }
.topbar .mon-alarma:hover { background:#a5301f; color:#fff; }
@keyframes mon-late { 0%,100%{opacity:1} 50%{opacity:.55} }

/* Mini gráfica desplegable junto a cada sensor y bloques de acciones (04/09/2026) */
.mon-mini { width:100%; height:70px; display:block; }
.mon-grafica-fila td { background:#fafbfc; padding:8px 12px !important; }
.mon-acciones { margin:6px 0 10px 18px; }
.mon-acciones > summary { cursor:pointer; font-size:12px; padding:3px 0; list-style:none; }
.mon-acciones > summary::-webkit-details-marker { display:none; }
.mon-acciones .row-form { gap:8px; align-items:flex-end; padding:8px 0; }
.mon-acciones input, .mon-acciones select { font-size:13px; padding:5px 8px; }
.mon-sensores td { vertical-align:middle; }
.mon-cog, .mon-add { padding:2px 8px !important; font-size:12px !important; }
.mon-add { margin:4px 0 8px 18px; }

/* El panel de acciones, cuando se coloca DENTRO del árbol (04/09/2026): se
   distingue del fondo pero sin parecer otra página. */
.mon-panel-inline { margin:8px 0 12px 18px !important; border-left:3px solid var(--green);
                    background:#fbfdfc; }
.mon-panel-inline h2 { font-size:15px; }

/* ⏱️ Latencia por encima del umbral: amarillo (petición del usuario, 04/09/2026).
   Se quita solo en cuanto el tiempo vuelve a bajar; no es una alerta, es un aviso
   visual — en una red de radio, alertar por cada subida sería ruido constante. */
.mon-ms.lento { color:#8a5a00; background:#fff3d6; }
.mon-enlace-eq { color:var(--txt); } .mon-enlace-eq:hover { color:var(--green-d); }

/* ═══════════════════════════════════════════════════════════════════════════
   PERMISOS AGRUPADOS POR MÓDULO (04/09/2026)
   ───────────────────────────────────────────────────────────────────────────
   Antes eran 43 casillas en una sola columna y había que bajar media página.
   Ahora: un bloque por módulo, y dentro las casillas en rejilla.
   ⚠️ No se toca el `font-size` (norma del usuario): lo que se compacta es el
      ESPACIADO y la disposición, no el tamaño de la letra.
   ═══════════════════════════════════════════════════════════════════════════ */
.perm-grupos { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
               gap: 10px; margin: 4px 0 10px; }
.perm-grupo { border: 1px solid var(--line); border-radius: 9px; padding: 8px 10px 10px;
              margin: 0; background: #fcfdfe; }
.perm-grupo legend { font-size: 12px; font-weight: 700; color: var(--navy); padding: 0 5px; }
.perm-grupo legend .muted { font-weight: 400; font-size: 11px; }
.perm-grupo.apagado { opacity: .5; }
.perm-lista { display: flex; flex-direction: column; gap: 3px; }
.perm-lista label { display: flex; align-items: center; gap: 7px; margin: 0; line-height: 1.35;
                    color: var(--txt); cursor: pointer; }
.perm-lista label:hover { color: var(--green-d); }
.perm-lista input[type=checkbox] { width: auto; margin: 0; flex: 0 0 auto; }

/* Bloque de permisos DEBAJO del usuario, a todo el ancho (04/09/2026).
   Antes vivía dentro de la última celda y salía estrecho y encajonado. */
.tbl tr.fila-detalle > td { background:#fafbfc; padding:6px 12px 12px !important;
                            border-bottom:2px solid var(--line); }
.tbl tr.fila-detalle summary { cursor:pointer; padding:6px 2px; list-style:none; }
.tbl tr.fila-detalle summary::-webkit-details-marker { display:none; }
.tbl tr.fila-detalle summary::before { content:"\25B8"; color:var(--muted); margin-right:6px; }
.tbl tr.fila-detalle details[open] > summary::before { content:"\25BE"; }
.tbl tr.fila-detalle:hover > td { background:#fafbfc; }
.bloque-usuario { margin-top:10px; max-width:420px; }


/* ─── Monitorización · bloques de borrado y KPI de PPPoE (04/09/2026) ───────
   Los dos bloques de borrado se separan VISUALMENTE porque borrar un sensor y
   borrar un equipo no son lo mismo: el segundo se lleva todos sus sensores. */
.bloque-borrar { border:1px solid #e6ecf1; border-radius:8px; padding:8px 10px; margin-top:10px; }
.bloque-borrar.peligro { border-color:#f3c6c6; background:#fff8f8; }
.bloque-borrar .bloque-titulo, .bloque-pausas .bloque-titulo { font-size:12px; text-transform:uppercase; letter-spacing:.4px;
  color:var(--muted); margin-bottom:6px; }
.bloque-borrar.peligro .bloque-titulo { color:#b03030; }
/* El recuadro de «usuarios PPPoE activos» es un enlace al RADIUS, pero tiene
   que verse exactamente como los demás KPI de la fila. */
a.kpi-enlace { text-decoration:none; color:inherit; }
a.kpi-enlace:hover { background:#eef2f6; }


/* ─── Monitorización · buscador de equipos del árbol (04/09/2026) ───────────
   Ocupa el sitio del antiguo título «Árbol», que el usuario pidió quitar. */
.mon-buscador { display:flex; gap:8px; align-items:center; margin-bottom:10px; }
.mon-buscador input[type="search"] { flex:1; max-width:420px; margin:0; }
.mon-buscador .muted { font-size:13px; }


/* ─── Monitorización · gráficas de tendencia y cifras (04/09/2026) ──────────
   Dos gráficas grandes arriba (dispositivos activos y PPPoE) y la línea de
   valores que acompaña a cada minigráfica de sensor. */
.mon-tendencias { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
@media (max-width:820px){ .mon-tendencias { grid-template-columns:1fr; } }
/* 🧨 08/09/2026: las dos gráficas no arrancaban a la misma altura porque la cabecera de
   PPPoE lleva el botón «ver conectados» y la otra no. Con la columna flex + el canvas
   pegado abajo, las dos quedan alineadas lleve la cabecera lo que lleve. */
.mon-tendencia { display:flex; flex-direction:column; }
/* 🧨🧨 SIN ESTA LÍNEA, `display:flex` GANA al atributo `hidden` y los recuadros de arriba
   dejan de poder apagar su gráfica (pasó el 08/09: apagabas una y se veían las dos). */
.mon-tendencia[hidden] { display:none; }
/* 🧨 La cabecera de PPPoE lleva el botón «ver conectados» y la de equipos no, así que sin
   `min-height` una mide 30 px y la otra 23, y todo lo de debajo se descuadra. */
.mon-tendencia-cab { display:flex; justify-content:space-between; align-items:center;
  gap:10px; margin-bottom:2px; flex-wrap:wrap; min-height:30px; }
.mon-tendencia-cab .muted { font-size:12px; }
/* 📐 08/09/2026 · La línea de cifras («ahora … · mín … · máx …») va FUERA de la cabecera, en
   su propio renglón, para que las dos gráficas la tengan exactamente a la misma altura. */
.mon-tendencia-datos { font-size:12px; min-height:18px; margin-bottom:4px; }

/* 🔎 DESPLEGABLE CON BUSCADOR (08/09/2026) — el filtro va ENCIMA de su <select>, en la
   misma columna, para que se lea como un solo control. El del equipo de un sensor se hace
   más ancho: ahí caben nombres largos («Serradilla › Ap CPD Serradilla (10.0.200.250)»). */
.sel-buscable { display:inline-flex; flex-direction:column; gap:3px; }
.sel-buscable .sel-filtro { font-size:12px; padding:2px 6px; }
.sel-buscable select { margin:0; }
.sel-buscable.ancho select, .sel-buscable.ancho .sel-filtro { min-width:260px; }
.mon-tendencia-cv { width:100%; height:120px; display:block; margin-top:auto; }
/* Cifras sobre la minigráfica de un sensor */
.mon-mini-datos { display:flex; flex-wrap:wrap; gap:4px 14px; font-size:12px;
  color:var(--muted); margin-bottom:4px; }
.mon-mini-datos .dato b { color:#1f2a33; }
.mon-mini-datos .dato.bien b, .mon-mini-datos .dato.bien { color:#0f9d76; }
.mon-mini-datos .dato.mal, .mon-mini-datos .dato.mal b { color:#c0392b; }

/* El «ahora» de las gráficas de tendencia: la cifra destaca sobre el resto.
   ⚠️ Antes este selector colgaba de `.mon-tendencia-cab`; al sacar las cifras de la cabecera
      dejaba de aplicar y el «ahora» perdía el destacado. */
.mon-tendencia-datos b, .mon-tendencia-cab .muted b { color:#1f2a33; font-size:14px; }


/* 📖 EXPLICACIONES PLEGABLES (09/09/2026) — «ocupan mucha pantalla y realmente se leen una
   sola vez». Conservan el color del aviso que ya tenían (`rn-aviso info`…): esta clase solo
   se ocupa del plegado, para no repetir paleta.
   ⛔ Esto NO es para los avisos de peligro ni de estado: ésos se ven siempre. */
details.explica { margin-bottom: 8px; }
details.explica > summary { cursor: pointer; font-weight: 700; list-style: none; }
details.explica > summary::-webkit-details-marker { display: none; }
details.explica > summary::after { content: " ▾"; opacity: .55; font-weight: 400; }
details.explica[open] > summary::after { content: " ▴"; }
details.explica .explica-cuerpo { margin-top: 6px; }


/* 🔧 09/09/2026 · Los iconos de cada servidor del Firewall, en la MISMA línea del nombre
   (patrón de Monitorización). Se separan del título y entre ellos sin ocupar una línea. */
.fw-acciones { margin-left: 10px; display: inline-flex; gap: 4px; vertical-align: middle; }


/* 📶 VELOCIDAD EN VIVO (09/09/2026) — arriba del todo en Monitorización. Números grandes,
   porque es lo que se mira de un vistazo desde lejos mientras pasa algo. */
/* 🔀 09/09/2026 · Vive DENTRO de `.page-head`, entre el título y los botones (antes era una
   tarjeta a lo ancho encima de todo). Por eso ya no lleva fondo de tarjeta ni margen inferior:
   el `gap` de la cabecera lo separa. */
/* ⭐ 09/09/2026 · `margin-left:auto` lo manda al extremo DERECHO de la cabecera. Vale para
   los dos casos: en escritorio comparte línea con el título, y en el MÓVIL, cuando la
   cabecera se parte, la velocidad baja pero sigue a la derecha (antes se quedaba pegada a
   la izquierda debajo del título, que es lo que pidió corregir). */
/* 🔀 09/09/2026 · El bloque de velocidad es además el INTERRUPTOR de la gráfica de los
   dos últimos días, así que se comporta como algo pulsable (cursor, foco visible) y
   pasa a `flex` para colocar el chevrón ▾ a la derecha de las cifras.
   🧨🧨 EL `[hidden]` DE ABAJO NO SOBRA: `display:flex` GANA al atributo `hidden`, y sin
      esa línea el widget se quedaría a la vista aunque no haya ningún sensor de tráfico
      (`traerVivo()` lo esconde con `caja.hidden = true`). Es el mismo tropiezo que ya
      costó una tarde con `.mon-tendencia[hidden]`. */
.mon-vivo { padding: 0; margin-left: auto; display: flex; align-items: center; gap: 10px;
  cursor: pointer; border-radius: 8px; }
.mon-vivo[hidden] { display: none; }
.mon-vivo:hover .mon-vivo-chev { color: var(--navy); }
.mon-vivo:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; }
/* El chevrón gira cuando la gráfica está desplegada: dice de un vistazo si hay algo
   abierto debajo, sin tener que mirar la página entera. */
.mon-vivo-chev { font-size: 14px; color: var(--muted); line-height: 1;
  transition: transform .15s ease; }
.mon-vivo.abierta .mon-vivo-chev { transform: rotate(180deg); }

/* 📈 GRÁFICA DE VELOCIDAD DE LOS DOS ÚLTIMOS DÍAS (09/09/2026).
   Más alta que las minigráficas del árbol (70 px) y que las de tendencia (120 px):
   aquí caben dos días de datos y hay que poder distinguir los picos. */
.mon-graf-trafico-cv { width: 100%; height: 180px; display: block; }
/* Leyenda de colores de la cabecera: un cuadradito y la palabra. */
.mon-leyenda { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  font-size: 12px; color: var(--muted); }
.mon-leyenda-it { display: inline-flex; align-items: center; gap: 5px; }
.mon-leyenda-it i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
/* 📚 09/09/2026 · Los botones de RANGO de la gráfica de tráfico (48 h · 7 d · 30 d · 1 año).
   Van pegados entre sí para que se lean como un solo control, y el activo se marca con
   la clase `navy` que ya usan el resto de botones seleccionados de PICA. */
.mon-rangos { display: inline-flex; gap: 4px; flex-wrap: wrap; }

/* 🔄 09/09/2026 · El botón de ACTUALIZAR de la cabecera de Monitorización.
   Pegado al título (`.page-head` es flex) y del tamaño de los demás botones pequeños.
   Gira mientras se recarga: la recarga tarda un momento y sin señal parece que el
   clic no ha hecho nada. */
.mon-refrescar { font-size: 14px; line-height: 1; padding: 5px 9px; }
.mon-refrescar.girando { animation: mon-girar .8s linear infinite; }
@keyframes mon-girar { to { transform: rotate(360deg); } }
/* Y en pantalla estrecha, sus cifras alineadas a la derecha también por dentro. */
@media (max-width: 720px) {
  .mon-vivo-fila { justify-content: flex-end; gap: 14px; }
  .mon-vivo-dato { text-align: right; }
}
.mon-vivo-fila { display: flex; gap: 18px; flex-wrap: wrap; align-items: baseline; }
/* ⭐ 09/09/2026 · MISMO TAMAÑO QUE LAS CIFRAS DE LOS RECUADROS (`.kpi .n`: 30 px / 800).
   🔀 Antes se igualó a los botones por una lectura mía equivocada de «el tamaño que sea el
      mismo el de los botones»: eran los RECUADROS con el número de PPPoE, equipos, etc.
      Encaja además con que los botones de acción ya no comparten esa fila. */
.mon-vivo-num { font-size: 30px; font-weight: 800; color: var(--navy); line-height: 1.15; }
.mon-vivo-uni { font-size: 13px; font-weight: 600; color: var(--muted); }
.mon-vivo-et  { font-size: 13px; color: var(--muted); }


/* 🔧 09/09/2026 · La fila de botones de Monitorización, encima del árbol (antes en la cabecera).
   🧨 SE LLAMA `mon-barra-acciones` Y NO `mon-acciones` PORQUE ESA YA EXISTE (línea ~743) para
      el <details> de acciones de dentro del árbol: al reusar el nombre, los botones heredaban
      su `margin-left` y se pegaban a la izquierda. Antes de inventar una clase, grep. */
.mon-barra-acciones { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
  margin: 0 0 10px; }


/* 📱 09/09/2026 · LOS CUATRO BOTONES DE MONITORIZACIÓN, EN UNA SOLA LÍNEA EN EL MÓVIL.
   ⛔ NO se toca el tamaño de letra (norma del usuario): se recorta SOLO el espaciado, y el
      último botón cambia «Mis avisos» por «Avisos», que es lo que él propuso. */
.solo-movil { display: none; }
@media (max-width: 720px) {
  .no-movil   { display: none; }
  .solo-movil { display: inline; }
  .mon-barra-acciones { flex-wrap: nowrap; justify-content: space-between; gap: 5px; }
  .mon-barra-acciones .btn { padding: 8px 8px; white-space: nowrap; }
}
/* Teléfonos pequeños: cae el «➕», que es decoración, ANTES que la palabra, que es lo que se
   lee. Medido: con las tres cruces fuera, los cuatro botones bajan de ~331 px a ~289 y caben
   incluso en una pantalla de 320. ⛔ La fuente sigue sin tocarse. */
@media (max-width: 400px) {
  .mon-barra-acciones .mas { display: none; }
  .mon-barra-acciones { gap: 4px; }
  .mon-barra-acciones .btn { padding: 8px 7px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   IPs PINCHABLES  ·  12/09/2026
   Las pinta el filtro `| enlaceip` de deps.py en /radius y /alertas. Se marcan
   con subrayado punteado en vez de con el azul de enlace: en una tabla llena de
   IPs, el azul chillaba demasiado. El puntero de mano ya avisa de que se puede
   pulsar; el subrayado sólido aparece solo al pasar por encima.
   ═══════════════════════════════════════════════════════════════════════════ */
a.ip-web {
  color: inherit;                      /* no roba protagonismo dentro de la tabla */
  text-decoration: underline dotted;
  text-underline-offset: 2px;
  cursor: pointer;
}
a.ip-web:hover,
a.ip-web:focus-visible {
  color: #0b63b8;                      /* el azul de acción del resto de la UI */
  text-decoration: underline solid;
}
