:root {
  --bg: #0d1117;
  --panel: #161b22;
  --panel2: #0d1117;
  --line: #21262d;
  --line-strong: #30363d;
  --text: #e6edf3;
  --muted: #8b949e;
  --accent: #2f81f7;
  --accent-ink: #6cb6ff;   /* acento legible como TEXTO sobre el fondo del tema */
  --fiber: #ff7b00;
  --ok: #3fb950;
  --ok-ink: #3fb950;
  --warn-ink: #d29922;
  --bad: #f85149;          /* rojo para FONDOS/sólidos */
  --bad-ink: #f85149;      /* rojo legible como TEXTO */
  --grad: linear-gradient(135deg,#2f81f7,#7c3aed); /* gradiente de marca (botones primarios) */
  --overlay: rgba(0,0,0,.55);
  --shadow-1: 0 2px 8px #0006;
  --shadow-2: 0 8px 28px #0008;
  --shadow-3: 0 12px 44px #000a;
}
/* Tema claro (DEFAULT; toggle 🌓 vuelve al oscuro; no afecta el mapa ni los
   colores semánticos). Tokens calibrados al tema claro frío de MAS-WISP. */
body.light {
  --bg: #eef3f7;
  --panel: #ffffff;
  --panel2: #f5f9fb;
  --line: #e3eaf0;
  --line-strong: #cfdbe4;
  --text: #14303a;
  --muted: #54707c;
  --accent: #2f9fe0;
  --accent-ink: #1a72ac;
  --fiber: #9a5a0e;        /* ámbar tinta: legible sobre claro (marca/realces de texto) */
  --ok: #14b8a6;
  --ok-ink: #0a766a;
  --warn-ink: #9a5a0e;
  --bad: #f0626d;
  --bad-ink: #c41f2d;
  --grad: linear-gradient(135deg,#14b8a6,#2f9fe0);
  --overlay: rgba(20,48,58,.38);
  --shadow-1: 0 2px 8px rgba(20,48,58,.12);
  --shadow-2: 0 8px 28px rgba(20,48,58,.16);
  --shadow-3: 0 12px 44px rgba(20,48,58,.20);
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; font-family: 'Hanken Grotesk', system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: var(--bg); color: var(--text); }
button, input, select, textarea { font-family: inherit; }
/* Tipografía de display (títulos) y técnica (mono), estilo MAS-WISP */
h1, h2, h3, h4, .brand, .login-card h1, .info-title, .edit-title {
  font-family: 'Bricolage Grotesque', 'Hanken Grotesk', system-ui, sans-serif; }
.menu-sec, .tools-cat-h, .acc-h, .kpi-l, .info-type, .info-actions-h, .trace-step .tag,
.tree-node .tag, .catalog-list th, .dash-card .lbl, .search-results .res .k,
.search-results .res-sep, .sidebar h3 {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .15em; }
.hidden { display: none !important; }

/* ---- Login ---- */
.login-screen { height: 100%; display: grid; place-items: center;
  background: radial-gradient(1200px 600px at 50% -10%, #14315c, var(--bg)); }
body.light .login-screen { background: radial-gradient(1200px 600px at 50% -10%, #d8ecf1, var(--bg)); }
.login-card { width: 320px; background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 28px; display: flex; flex-direction: column; gap: 12px; box-shadow: var(--shadow-2); }
.login-card h1 { margin: 0; font-size: 26px; letter-spacing: 1px; }
.login-card h1 span { color: var(--fiber); }
.subtitle { margin: 0 0 8px; color: var(--muted); font-size: 13px; }
.login-card input { background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding: 10px 12px; border-radius: 7px; font-size: 14px; }
.login-card button { background: var(--grad); color: white; border: 0; padding: 11px;
  border-radius: 7px; font-weight: 600; cursor: pointer; }
.error { color: var(--bad-ink); font-size: 13px; min-height: 16px; }

/* ---- App ---- */
.app { height: 100%; position: relative; display: grid;
  grid-template-columns: 240px 1fr; grid-template-rows: 56px 1fr;
  grid-template-areas: "top top" "side map"; }
.topbar { grid-area: top; display: flex; align-items: center; gap: 20px;
  padding: 0 18px; background: var(--panel); border-bottom: 1px solid var(--line); }
.brand { font-weight: 700; letter-spacing: 1px; }
.brand span { color: var(--fiber); }
.kpis { display: flex; gap: 18px; flex: 1; font-size: 13px; color: var(--muted); flex-wrap: wrap; }
.kpis b { color: var(--text); }
.ghost { background: transparent; border: 1px solid var(--line); color: var(--text);
  padding: 7px 14px; border-radius: 8px; cursor: pointer; }
.sidebar { grid-area: side; background: var(--panel); border-right: 1px solid var(--line);
  padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.sidebar h3 { margin: 0 0 6px; font-size: 13px; text-transform: uppercase; color: var(--muted); }
.sidebar label { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
.sidebar label .dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 10px; }
.hint { margin-top: auto; font-size: 12px; color: var(--muted); }
/* Fondo gris neutro mientras cargan los tiles */
#map { grid-area: map; background: #c8d2dc; }
/* Modo presentación: solo el mapa (demos / capturas) */
.app.presentation { grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "map"; }
.app.presentation .topbar, .app.presentation .sidebar, .app.presentation #tbToggles { display: none; }
#presExit { position: absolute; top: 10px; right: 10px; z-index: 1200; display: none;
  background: var(--panel); color: var(--text, #c9d1d9); border: 1px solid var(--line);
  border-radius: 7px; padding: 6px 10px; font-size: 12px; cursor: pointer; box-shadow: var(--shadow-1); }
.app.presentation #presExit { display: block; }
.maplibregl-map { background: #c8d2dc; }
/* Popups de MapLibre con tema oscuro */
.maplibregl-popup-content { background: var(--panel); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; font-size: 13px; padding: 10px 12px; }
.maplibregl-popup-anchor-top .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-right .maplibregl-popup-tip { border-bottom-color: var(--panel); }
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip { border-top-color: var(--panel); }
.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--panel); }
.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--panel); }
.maplibregl-popup-close-button { color: var(--text); font-size: 16px; padding: 0 6px; }
.maplibregl-popup-content button.trace-btn { margin-top: 8px; background: var(--accent);
  color: #fff; border: 0; padding: 6px 10px; border-radius: 6px; cursor: pointer; font-size: 12px; }

/* Barra de acciones del popup: botones de ícono en horizontal con tooltip */
.pop-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px;
  padding-top: 9px; border-top: 1px solid color-mix(in srgb, var(--line) 70%, transparent); }
.pop-btn { --pa: var(--accent); width: 36px; height: 36px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; font-size: 17px; line-height: 1;
  border: 1px solid color-mix(in srgb, var(--pa) 45%, transparent);
  background: color-mix(in srgb, var(--pa) 15%, var(--panel)); color: #fff;
  border-radius: 11px; cursor: pointer; box-shadow: 0 1px 2px #0003, inset 0 1px 0 #ffffff14;
  transition: transform .11s ease, box-shadow .16s ease, background .16s ease, border-color .16s ease; }
.pop-btn:hover { background: var(--pa); transform: translateY(-2px); border-color: var(--pa);
  box-shadow: 0 5px 14px color-mix(in srgb, var(--pa) 55%, transparent); }
.pop-btn:active { transform: translateY(0); box-shadow: 0 1px 3px #0005; }
.pop-btn:focus-visible { outline: 2px solid var(--pa); outline-offset: 2px; }
/* En claro el fondo del botón es casi blanco: el glifo/texto va en tinta, y
   vuelve a blanco cuando el hover pinta el fondo con el color pleno. */
body.light .pop-btn { color: var(--text); box-shadow: var(--shadow-1); }
body.light .pop-btn:hover { color: #fff; }
/* "⋯ Más" y el contenedor de acciones secundarias del popup */
.pop-more.open { background: var(--accent); color: #fff; border-color: var(--accent); }
.pop-extra { display: flex; flex-wrap: wrap; gap: 6px; width: 100%; margin-top: 8px; padding-top: 8px;
  border-top: 1px solid color-mix(in srgb, var(--line) 70%, transparent); }
.base-select { width: 100%; background: var(--bg); border: 1px solid var(--line);
  color: var(--text); padding: 7px 9px; border-radius: 7px; font-size: 13px; }
/* Selector con filtro activo (proyecto/zona): resaltado para no perder contexto */
.base-select.filtered { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--bg)); font-weight: 600; }
/* Iconos emoji en el mapa (Leaflet divIcon) */
.emoji-icon { background: none !important; border: none !important; }
.emoji-pin { font-size: 20px; line-height: 26px; display: block; text-align: center;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.5)); }
/* Planta interna de la manga/mufa */
.cl-form { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; margin-bottom: 12px; }
.cl-form-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: end; margin-top: 8px; }
.cl-form-row label { display: flex; flex-direction: column; font-size: 11px; color: var(--muted); gap: 2px; }
.cl-form-row select, .cl-form-row input { background: var(--bg); color: var(--text); border: 1px solid var(--line); border-radius: 6px; padding: 5px 6px; font-size: 12px; }
.cl-amp { align-self: center; font-size: 16px; color: var(--muted); }
.cl-tray { border: 1px solid var(--line); border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
.cl-tray-h { background: var(--panel); padding: 7px 10px; font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
.cl-tray-h small { color: var(--muted); font-weight: 400; }
.cl-splice { display: flex; align-items: center; gap: 5px; padding: 6px 10px; font-size: 13px; border-top: 1px solid var(--line); }
.cl-empty { padding: 6px 10px; color: var(--muted); font-size: 12px; }
.cl-x { margin-left: auto; background: transparent; border: 0; color: var(--bad-ink); cursor: pointer; font-size: 15px;
  border-radius: 7px; padding: 1px 5px; transition: background .12s; }
.cl-x:hover { background: color-mix(in srgb, var(--bad) 18%, transparent); }
/* Botones de los paneles internos (manga/NAP): look moderno + hover con acento */
.cl-mini { font-size: 11px; padding: 3px 8px; margin-left: 4px; cursor: pointer; border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--bg));
  color: var(--text); transition: background .13s, transform .1s, border-color .13s; }
.cl-mini:hover { background: var(--accent); color: #fff; border-color: var(--accent); transform: translateY(-1px); }
.cl-mini:active { transform: translateY(0); }
.cl-outs { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 10px; }
.cl-out { font-size: 11px; padding: 3px 7px; border-radius: 6px; border: 1px solid var(--line); background: var(--bg); display: inline-flex; align-items: center; gap: 3px; }
.cl-out.ocupado { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.cl-out.editing { background: var(--panel); }
.cl-out select { background: var(--bg); color: var(--text); border: 1px solid var(--line); border-radius: 5px; font-size: 11px; }
/* Pin de imagen (iconos subidos en un paquete) */
.img-pin { width: 26px; height: 26px; object-fit: contain; display: block;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
.img-icon { display: inline-block; }
.icon-pack-preview { margin-top: 10px; }
.icon-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.icon-grid label { flex-direction: row !important; align-items: center; gap: 6px; }
.icon-grid input { width: 46px; text-align: center; font-size: 16px; }
/* Paletas de color */
.palette-row { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.swatch { width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--line); cursor: pointer; }
.swatch:hover { border-color: var(--text); }
.theme-sw { width: 42px; height: 30px; border-radius: 6px; border: 2px solid var(--line); cursor: pointer; display: grid; place-items: center; padding: 0; }
.theme-sw:hover { outline: 2px solid var(--accent); }
.theme-sw span { width: 14px; height: 14px; border-radius: 50%; display: block; border: 1px solid #0006; }
/* Selector de iconos (paleta) */
.icon-picker { display: flex; flex-wrap: wrap; gap: 4px; max-height: 120px; overflow: auto;
  background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 6px; }
.icon-picker .ic { width: 30px; height: 30px; display: grid; place-items: center; font-size: 18px;
  border-radius: 6px; cursor: pointer; border: 2px solid transparent; }
.icon-picker .ic:hover { background: var(--line); }
.icon-picker .ic.sel { border-color: var(--accent); background: var(--line); }
.icon-lib-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.icon-lib-grid .ic { width: 34px; height: 34px; display: grid; place-items: center; font-size: 20px;
  border: 1px solid var(--line); border-radius: 6px; position: relative; }
.icon-lib-grid .ic button { position: absolute; top: -6px; right: -6px; background: var(--bad); color: #fff;
  border: 0; border-radius: 50%; width: 16px; height: 16px; font-size: 10px; cursor: pointer; line-height: 1; }

/* Panel de trazado / impacto */
.trace-panel { position: absolute; top: 100px; right: 14px; width: 320px; max-height: 78vh;
  overflow: auto; background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 16px; box-shadow: var(--shadow-2); z-index: 1000; }
.trace-close { position: absolute; top: 8px; right: 10px; background: transparent; border: 0;
  color: var(--muted); font-size: 20px; cursor: pointer; }
.trace-panel h4 { margin: 0 0 10px; font-size: 15px; }
.trace-step { display: flex; gap: 8px; align-items: baseline; padding: 6px 0; border-bottom: 1px solid var(--line); }
.trace-step .tag { font-size: 10px; text-transform: uppercase; color: var(--muted); min-width: 62px; }
.trace-step b { color: var(--text); }
.trace-step small { color: var(--muted); }
.impact-kpi { font-size: 13px; color: var(--muted); margin: 4px 0 10px; }
.impact-kpi b { color: var(--fiber); font-size: 18px; }
/* Campo obligatorio (asterisco rojo) */
.req { color: var(--bad-ink); font-weight: 700; }
/* Spinner de carga: agregar la clase "loading" a un elemento con texto de carga */
@keyframes dfm-spin { to { transform: rotate(360deg); } }
.loading::before { content: ''; display: inline-block; width: 12px; height: 12px; margin-right: 8px;
  vertical-align: -2px; border: 2px solid var(--line); border-top-color: var(--accent); border-radius: 50%;
  animation: dfm-spin .7s linear infinite; }
.impact-item { font-size: 13px; padding: 4px 0; border-bottom: 1px solid var(--line); }
.impact-item .st { float: right; font-size: 11px; color: var(--muted); }

/* ── Tarjeta de equipo del nodo: jerarquía visual de 3 niveles ────────────── */
/* --eq-c = color de la categoría (ancla visual por tipo de equipo). */
.eq-card { --eq-c: #64748b; background: var(--panel); border: 1px solid var(--line);
  border-left: 4px solid var(--eq-c); border-radius: 12px; padding: 16px 18px;
  margin-bottom: 12px; box-shadow: var(--shadow-1); }
.eq-head { display: flex; align-items: flex-start; gap: 13px; }
/* Iconografía de anclaje: icono representativo en la esquina superior izquierda. */
.eq-icon { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; font-size: 21px; line-height: 1;
  background: color-mix(in srgb, var(--eq-c) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--eq-c) 34%, transparent); }
.eq-titlewrap { flex: 1 1 auto; min-width: 0; }
/* Título: lectura rápida — negrita y mayor tamaño. */
.eq-title { font-size: 16px; font-weight: 700; color: var(--text); line-height: 1.25;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.eq-cat { font-size: 9.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  color: var(--eq-c); background: color-mix(in srgb, var(--eq-c) 15%, transparent);
  padding: 2px 7px; border-radius: 5px; }
/* Metadatos: detalle secundario en texto pequeño y gris. */
.eq-meta { margin-top: 5px; font-size: 11.5px; color: var(--muted); line-height: 1.5;
  display: flex; align-items: center; gap: 5px 9px; flex-wrap: wrap; }
.eq-meta .dot { opacity: .5; }
.eq-actions { flex: 0 0 auto; display: inline-flex; gap: 5px; }
.eq-toolrow { margin: 13px 0 0; display: flex; flex-wrap: wrap; gap: 6px; }
.eq-body { margin-top: 12px; }
.eq-body:empty { display: none; }

/* ── RackContainer: chasis vertical rígido de 19" con unidades de rack (U) ── */
.rack-wrap { display: inline-flex; flex-direction: column; gap: 8px; }
.rack-title { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 700; }
.rack-title small { color: var(--muted); font-weight: 500; }
.rack-face-sw { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 8px; overflow: hidden; }
.rack-face-sw button { border: 0; background: var(--panel2); color: var(--muted); font-size: 12px;
  padding: 4px 12px; cursor: pointer; font-weight: 600; }
.rack-face-sw button.on { background: var(--accent); color: #fff; }
/* El chasis: dos rieles metálicos + área de U. Ancho fijo (emula 19"). */
.rack-frame { position: relative; display: grid; grid-template-columns: 26px 1fr 26px;
  width: 320px; background: linear-gradient(180deg, #1a1f27, #10141a); border: 2px solid #3a4250;
  border-radius: 8px; padding: 8px 0; box-shadow: inset 0 0 22px #0009, 0 6px 20px #0006; }
.rack-frame.rear { background: linear-gradient(180deg, #201a1a, #171012); border-color: #4a3a3a; }
.rack-rail { display: flex; flex-direction: column; background: #0c0f14;
  border-left: 1px solid #ffffff10; border-right: 1px solid #ffffff10; }
.rack-rail.left { border-left: 0; } .rack-rail.right { border-right: 0; }
.rack-u-num { display: flex; align-items: center; justify-content: center; font-size: 8.5px;
  color: #6b7688; font-family: var(--mono, monospace); border-bottom: 1px solid #ffffff08; }
.rack-slots { position: relative; background:
  repeating-linear-gradient(180deg, #0d1117 0, #0d1117 calc(var(--u) - 1px), #ffffff10 var(--u)); }
.rack-eq { position: absolute; left: 3px; right: 3px; border-radius: 4px; overflow: hidden;
  cursor: pointer; box-shadow: 0 1px 3px #0008; border-left: 4px solid var(--eqc, #888); }
.rack-eq .re-label { font-size: 10px; color: #fff; font-weight: 600; padding: 2px 5px;
  display: flex; align-items: center; gap: 5px; white-space: nowrap; }
.rack-eq .re-label small { color: #b9c2d0; font-weight: 500; }
.rack-empty-note { font-size: 11.5px; color: var(--muted); }
.rack-u-free { position: absolute; left: 3px; right: 3px; border: 1px dashed #ffffff1a;
  border-radius: 3px; cursor: pointer; }
.rack-u-free:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: var(--accent); }

/* ── Faceplate del equipo: etiqueta arriba-izquierda + puertos debajo ──────── */
.fp { background: linear-gradient(180deg, #2b3038 0%, #1c2027 55%, #14171d 100%); min-height: 100%; width: 100%; }
/* Layout vertical: etiqueta (nombre+modelo) arriba, rejilla de puertos debajo. */
.fp-v { display: flex; flex-direction: column; overflow: visible; }
/* Equipo que CRECE con su contenido en modo panel (se muestra completo aunque
   exceda su altura en U; ej. EDFA con UPC/APC/PUMP apilados). */
.rack-eq.grow { overflow: visible; height: auto; }
.rack-eq.grow:hover { z-index: 6; }
.fp-label { flex: 0 0 auto; display: flex; align-items: baseline; gap: 5px; padding: 1px 6px;
  background: linear-gradient(180deg, #363d48, #2a303a); border-bottom: 1px solid #ffffff14; }
.fp-name { font-size: 9px; font-weight: 700; color: #eef2f7; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fp-model { font-size: 7px; color: #96a2b3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fp-ports-v { flex: 1 1 auto; display: flex; flex-wrap: wrap; align-items: flex-start; align-content: flex-start;
  gap: 3px 7px; padding: 3px 5px; }
/* Rejilla de puertos según el layout configurado en el área de equipos. */
.fp-grid { display: grid; gap: 1.5px; width: max-content; }
.fp-card { display: flex; flex-direction: column; gap: 1px; }
.fp-card-h { font-size: 7px; color: #8ea0b6; font-weight: 600; white-space: nowrap; line-height: 1.1; }
/* PDU: tomas con forma de enchufe + voltaje de salida en la etiqueta. */
.fp-pdu { display: flex; flex-direction: column; gap: 2px; }
.fp-outlets { display: flex; flex-wrap: wrap; gap: 5px; }
.pdu-outlet-wrap { display: flex; flex-direction: column; align-items: center; gap: 1px; }
.pdu-outlet { width: 15px; height: 15px; border-radius: 50%; border: 1.5px solid #b78a12; padding: 0;
  background: radial-gradient(circle at 50% 42%, #3a3320, #17140c); cursor: pointer; position: relative; }
/* Dos clavijas de la toma (tipo Schuko / NEMA). */
.pdu-outlet::before, .pdu-outlet::after { content: ''; position: absolute; top: 4px; width: 1.5px; height: 6px;
  background: #eab308; border-radius: 1px; }
.pdu-outlet::before { left: 5px; } .pdu-outlet::after { right: 5px; }
.pdu-outlet.linked { border-color: #22c55e; box-shadow: 0 0 5px #22c55e88; }
.pdu-outlet.linked::before, .pdu-outlet.linked::after { background: #22c55e; }
/* Toma que supera su límite físico seguro (80% de 10A/15A…): en rojo. */
.pdu-outlet.over { border-color: #ef4444; box-shadow: 0 0 6px #ef4444; }
.pdu-outlet.over::before, .pdu-outlet.over::after { background: #ef4444; }
.pdu-outlet:hover { outline: 1.5px solid #fff; }
.pdu-v { font-size: 6.5px; color: #d4b24a; font-weight: 700; line-height: 1; }
.pdu-v.over { color: #ef4444; }
/* Cabecera de una línea: dentro del rack el texto se recorta en vez de desbordar. */
.fp-1line { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* ── Fuente de poder (PSU) del equipo: entrada IEC que se enchufa a la PDU ── */
.fp-psu { margin-top: 2px; }
.fp-card-h.psu { color: #ff9a1f; }
.fp-psu-row { display: flex; gap: 3px; align-items: center; }
.psu-inlet { width: 13px; height: 9px; padding: 0; cursor: pointer; position: relative;
  border: 1.5px solid #9a5a12; border-radius: 2px 2px 4px 4px;
  background: linear-gradient(180deg, #3a2a14, #1a1209); }
/* Los tres contactos del conector IEC C14. */
.psu-inlet::before { content: ''; position: absolute; inset: 2px 2.5px; border-radius: 1px;
  background: repeating-linear-gradient(90deg, #c07818 0 1.5px, transparent 1.5px 4px); }
.psu-inlet.linked { border-color: #ff9a1f; box-shadow: 0 0 6px #ff9a1fbb; }
.psu-inlet.linked::before { background: repeating-linear-gradient(90deg, #ffb347 0 1.5px, transparent 1.5px 4px); }
.psu-inlet.sel { outline: 2px solid #fff; }
.psu-inlet:hover { outline: 1.5px solid #fff; }
.psu-add { font-size: 8px; line-height: 1; padding: 1px 3px; cursor: pointer; color: #ff9a1f;
  background: #ff9a1f14; border: 1px dashed #ff9a1f66; border-radius: 3px; }
.psu-add:hover { background: #ff9a1f2e; border-style: solid; }
/* Cable de energía en el rack: naranja luminoso (glow) sobre los de datos. */
.cable-pwr .cable-vis { filter: drop-shadow(0 0 3px #ff9a1fcc); }
.fp-port { width: 6px; height: 6px; padding: 0; border: 1px solid #00000066; border-radius: 1.5px;
  cursor: pointer; transition: outline .05s; }
.fp-port:hover { outline: 1.5px solid #fff; z-index: 3; position: relative; }
.fp-port.sel { outline: 2px solid #fff; }
/* Zoom cercano: puerto con número + borde por conector (UPC azul / APC verde). */
.fp-port-lg { width: 13px; height: 11px; border-width: 1.5px; border-radius: 2px; font-size: 7px;
  font-weight: 700; color: #fff; display: flex; align-items: center; justify-content: center;
  text-shadow: 0 1px 1px #0009; }

/* Barra de calor de ocupación del rack (visible sobre todo en zoom lejano). */
.rack-heat { display: flex; align-items: center; gap: 8px; margin: -2px 0 2px; max-width: 320px; }
.rack-heat-bar { flex: 1 1 auto; height: 7px; border-radius: 4px; background: #ffffff14; overflow: hidden; }
.rack-heat-bar span { display: block; height: 100%; border-radius: 4px; }
.rack-heat-txt { font-size: 10.5px; color: var(--muted); white-space: nowrap; }

/* ── Barra de energía del rack (horizontal, sobre el rack) ────────────────── */
.pwr-top { border: 1px solid var(--line-strong); border-radius: 10px; padding: 7px 10px;
  background: linear-gradient(180deg, #12161c, #0c0f14); display: flex; flex-direction: column;
  gap: 6px; margin: 3px 0 9px; }
.pwr-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pwr-h { font-size: 10px; font-weight: 700; color: var(--muted); text-transform: uppercase;
  letter-spacing: .3px; white-space: nowrap; }
/* Medidor horizontal: la marca del 80% es el límite seguro; la roja, el 95%. */
.pwr-hbar { position: relative; flex: 1 1 200px; min-width: 140px; height: 14px; border-radius: 8px;
  background: #0c0f14; border: 1px solid #2a3340; overflow: hidden; }
.pwr-hfill { display: block; height: 100%; border-radius: 7px 0 0 7px; transition: width .3s; }
.pwr-hmark { position: absolute; top: 0; bottom: 0; width: 0; border-left: 1px dashed #ffffff55; z-index: 2; }
.pwr-hmark.red { border-left-color: #ef4444aa; }
.pwr-pct { font-size: 15px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.pwr-state { font-size: 10.5px; font-weight: 700; white-space: nowrap; }
.pwr-none { font-size: 10.5px; color: #f59e0b; white-space: nowrap; }
.pwr-btn { font-size: 10px; color: var(--muted); background: transparent; border: 1px solid var(--line);
  border-radius: 6px; padding: 2px 7px; cursor: pointer; white-space: nowrap; margin-left: auto; }
.pwr-btn:hover { color: var(--text); border-color: var(--line-strong); }
.pwr-kpis { display: flex; flex-wrap: wrap; gap: 6px; }
.pwr-kpi { font-size: 10px; color: var(--muted); background: #ffffff0d; border: 1px solid var(--line);
  border-radius: 6px; padding: 2px 7px; white-space: nowrap; }
.pwr-kpi b { color: var(--text); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.pwr-redun { font-size: 10px; color: #f59e0b; line-height: 1.25; }
/* Sumatoria de watts equipo por equipo (los atenuados no están enchufados). */
.pwr-items { display: flex; flex-wrap: wrap; gap: 5px; border-top: 1px solid #ffffff14; padding-top: 6px; }
.pwr-item { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; color: var(--text);
  background: #ffffff0a; border: 1px solid var(--line); border-radius: 6px; padding: 2px 6px; white-space: nowrap; }
.pwr-item.off { opacity: .55; }
.pwr-item b { font-variant-numeric: tabular-nums; }
.pwr-item small { color: var(--muted); font-size: 8.5px; }
.pwr-item-total { background: #eab30818; border-color: #eab30855; color: #eab308; font-weight: 700; }
/* Desglose por PDU (cada barra = carga real enchufada a esa PDU). */
.pwr-pdus { display: flex; flex-wrap: wrap; gap: 10px; border-top: 1px solid #ffffff14; padding-top: 6px; }
.pwr-pdu { border-left: 3px solid #22c55e; padding-left: 6px; flex: 0 1 190px; min-width: 150px; }
.pwr-pdu-c { font-size: 10px; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pwr-pdu-bar { height: 5px; border-radius: 3px; background: #ffffff14; overflow: hidden; margin: 2px 0; }
.pwr-pdu-bar span { display: block; height: 100%; border-radius: 3px; }
.pwr-pdu-v { font-size: 9px; color: var(--muted); }
/* Parpadeo rojo de peligro (rack + dashboard cuando >95% de carga). */
.blink-red { animation: blinkRed 1s ease-in-out infinite; }
@keyframes blinkRed { 0%, 100% { box-shadow: 0 0 0 0 #ef444400, inset 0 0 22px #0009; } 50% { box-shadow: 0 0 14px 2px #ef4444cc, inset 0 0 22px #0009; border-color: #ef4444; } }

/* ── Panel de árbol (navegación rápida) + layout del rack ─────────────────── */
.rack-layout { display: grid; grid-template-columns: 214px 1fr; gap: 18px; align-items: start; }
.rack-tree { position: sticky; top: 0; align-self: start; border: 1px solid var(--line);
  border-radius: 10px; padding: 8px; background: var(--panel2); max-height: 78vh; overflow: auto; }
.rt-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); padding: 2px 4px 7px; }
.rt-rack-h { font-size: 12.5px; font-weight: 700; padding: 5px 4px; }
.rt-rack-h small { color: var(--muted); font-weight: 500; }
.rt-empty { font-size: 11px; color: var(--muted); padding: 2px 6px 6px; }
.rt-eq-h { display: flex; align-items: center; gap: 5px; font-size: 12px; padding: 4px 5px;
  border-left: 3px solid var(--eqc, #888); border-radius: 4px; cursor: pointer; }
.rt-eq-h:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.rt-eq-h b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-eq-h small { color: var(--muted); margin-left: auto; white-space: nowrap; }
.rt-tw { width: 12px; text-align: center; color: var(--muted); cursor: pointer; flex: 0 0 auto; }
.rt-ic { flex: 0 0 auto; }
.rt-ports { display: flex; flex-wrap: wrap; gap: 2px; padding: 3px 6px 5px 22px; }
.rt-port { min-width: 14px; height: 13px; padding: 0 2px; border-radius: 2px; font-size: 8px;
  color: #fff; display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  border: 1px solid #0006; text-shadow: 0 1px 1px #0008; }
/* Conexiones del rack: resaltan al pasar el cursor. El SVG no captura eventos,
   salvo el trazo "hit" transparente de cada cable. */
.rack-cables { pointer-events: none; }
.cable-hit { pointer-events: stroke; cursor: pointer; }
.cable-vis { transition: stroke-width .08s, opacity .08s; }
.cable-g:hover .cable-vis { stroke-width: 4 !important; opacity: 1 !important; filter: drop-shadow(0 0 4px var(--cc)); }
.cable-tip { pointer-events: none; }
.rack-eq-flash { animation: rackFlash 1.4s ease; }
@keyframes rackFlash { 0%, 100% { box-shadow: 0 1px 3px #0008; } 30% { box-shadow: 0 0 0 3px var(--accent), 0 0 18px var(--accent); } }

/* ── Mini-mapa flotante ───────────────────────────────────────────────────── */
.rack-minimap { position: fixed; right: 18px; bottom: 18px; z-index: 60; width: 74px;
  background: var(--panel); border: 1px solid var(--line-strong); border-radius: 8px;
  padding: 6px; box-shadow: 0 8px 24px #0007; }
.rmm-title { font-size: 9px; color: var(--muted); text-align: center; margin-bottom: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rmm-body { position: relative; width: 100%; background: #0d1117; border: 1px solid #ffffff14; border-radius: 4px; }
.rmm-view { position: absolute; left: 0; right: 0; background: color-mix(in srgb, var(--accent) 30%, transparent);
  border: 1.5px solid var(--accent); border-radius: 3px; pointer-events: none; }

/* Compatibilidad de conectores en la barra de conexión */
.cc-tag { font-size: 10px; font-weight: 700; color: var(--muted); background: color-mix(in srgb, var(--muted) 16%, transparent);
  padding: 1px 5px; border-radius: 4px; margin-left: 2px; }
.cc-chip { font-size: 11px; font-weight: 700; border: 1.5px solid; border-radius: 999px; padding: 2px 9px; margin: 0 4px; }

/* ── Modo Patchcord (conexión interactiva) ────────────────────────────────── */
body.patching { cursor: none; }
#patchCursor { position: fixed; z-index: 5001; pointer-events: none; font-size: 17px;
  transform: translate(-3px, -15px); filter: drop-shadow(0 1px 2px #000a); }
/* Puertos incompatibles / ocupados: atenuados y bloqueados para clics. */
.patch-block { opacity: .3; filter: grayscale(.55); pointer-events: none !important; }
/* Puerto destino compatible bajo el cursor: halo verde de confirmación. */
.patch-target { outline: 2px solid #22c55e !important; outline-offset: 1px;
  box-shadow: 0 0 9px #22c55e, 0 0 3px #22c55e inset !important; z-index: 8; position: relative; }
body.patching [data-pid].patch-ok { cursor: none; }

/* ── Menú contextual del puerto (clic derecho) ────────────────────────────── */
.port-ctx { position: fixed; z-index: 6000; min-width: 200px; background: var(--panel);
  border: 1px solid var(--line-strong); border-radius: 9px; padding: 4px; box-shadow: 0 10px 30px #000a; }
.port-ctx button { display: block; width: 100%; text-align: left; border: 0; background: none; color: var(--text);
  font-size: 13px; padding: 7px 10px; border-radius: 6px; cursor: pointer; }
.port-ctx button:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }

/* ── Panel de trazado de fibra (traceroute) ───────────────────────────────── */
.tr-panel { display: flex; flex-direction: column; padding: 4px 2px; }
.tr-step { display: flex; align-items: center; gap: 11px; padding: 9px 12px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--panel2); }
.tr-ic { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 9px; display: flex; align-items: center;
  justify-content: center; font-size: 18px; background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); }
.tr-t { font-size: 13.5px; font-weight: 700; color: var(--text); }
.tr-s { font-size: 11px; color: var(--muted); margin-top: 1px; }
.tr-rel { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--accent-ink); padding: 4px 0 4px 20px; }

/* ── Panel de resumen del rack (energía + ocupación) ──────────────────────── */
.rs-panel { display: flex; flex-direction: column; gap: 14px; }
.rs-rack { border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; background: var(--panel2); }
.rs-h { font-size: 14px; font-weight: 700; margin-bottom: 10px; }
.rs-h small { color: var(--muted); font-weight: 500; }
.rs-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-bottom: 12px; }
.rs-kpi { border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; background: var(--panel); }
.rs-n { font-size: 20px; font-weight: 800; color: var(--text); line-height: 1; }
.rs-n span { font-size: 11px; font-weight: 600; color: var(--muted); margin-left: 2px; }
.rs-l { font-size: 11px; color: var(--muted); margin-top: 3px; }
.rs-cols { max-height: 190px; overflow: auto; border: 1px solid var(--line); border-radius: 9px; }
.rs-tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.rs-tbl th { position: sticky; top: 0; background: var(--panel); text-align: left; font-size: 10.5px;
  text-transform: uppercase; letter-spacing: .3px; color: var(--muted); padding: 6px 10px; border-bottom: 1px solid var(--line); }
.rs-tbl td { padding: 5px 10px; border-bottom: 1px solid var(--line); }
.rs-tbl tr:last-child td { border-bottom: 0; }
.rs-pdu { margin-top: 9px; font-size: 12px; color: var(--muted); }

/* ── Catálogo de plantillas de equipos ────────────────────────────────────── */
.tpl-cat { max-height: 64vh; overflow: auto; }
.tpl-group { margin-bottom: 14px; }
.tpl-group-h { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); margin: 4px 2px 7px; }
.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 9px; }
.tpl-card { display: flex; align-items: center; gap: 10px; text-align: left; padding: 10px 11px;
  background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--eqc, #888);
  border-radius: 10px; cursor: pointer; transition: transform .06s, border-color .06s; }
.tpl-card:hover { transform: translateY(-1px); border-color: var(--eqc, var(--accent)); box-shadow: var(--shadow-1); }
.tpl-ic { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 8px; display: flex; align-items: center;
  justify-content: center; font-size: 18px; background: color-mix(in srgb, var(--eqc) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--eqc) 32%, transparent); }
.tpl-body { min-width: 0; }
.tpl-brand { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; color: var(--eqc, var(--muted)); }
.tpl-model { font-size: 13px; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tpl-ports { font-size: 10.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Port Channel (LACP) en la lista de conexiones */
.lag-box { border: 1px solid #7c3aed55; border-left: 3px solid #7c3aed; border-radius: 8px;
  padding: 4px 8px 6px; margin: 4px 0; background: color-mix(in srgb, #7c3aed 7%, transparent); }
.lag-h { font-size: 12px; font-weight: 700; color: #a78bfa; display: flex; align-items: center; gap: 6px; padding: 3px 0; }
.lag-h small { color: var(--muted); font-weight: 500; }
.lag-pick { display: flex; align-items: center; gap: 7px; font-size: 12.5px; padding: 3px 5px; border-radius: 5px; cursor: pointer; }
.lag-pick:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* Slot SFP vacío: rayado, indica que falta insertar módulo */
.fp-port.sfp-empty, .port-chip.sfp-empty { background-image: repeating-linear-gradient(45deg, #ffffff18 0 2px, transparent 2px 4px) !important; }
/* Puerto CONECTADO: un punto blanco en la esquina, SIN tapar el color de estado
   en vivo del fondo (verde activo / naranja sin clientes / rojo falla desde la OLT). */
.port-chip { position: relative; }
.port-chip.linked::after { content: ''; position: absolute; top: -2px; right: -2px; width: 7px; height: 7px;
  border-radius: 50%; background: #fff; border: 1.5px solid #0b0e13; box-shadow: 0 0 3px #000; }
.fp-port.linked::after { content: ''; position: absolute; top: -2px; right: -2px; width: 4px; height: 4px;
  border-radius: 50%; background: #fff; border: 1px solid #0b0e13; }
.fp-port.linked { position: relative; }
/* Dos LEDs verdes parpadeantes = actividad de red NATIVA (puerto emparejado en
   vivo con el equipo físico real, vía MAS-WISP). */
.live-act { position: relative; }
.fp-port.live-act::before, .port-chip.live-act::before {
  content: ''; position: absolute; bottom: 1px; left: 1.5px; width: 2px; height: 2px; border-radius: 50%;
  background: #4dff88; box-shadow: 3px 0 0 #4dff88, 0 0 3px #4dff88; animation: liveLeds .9s ease-in-out infinite; pointer-events: none; }
.port-chip.live-act::before { width: 3px; height: 3px; bottom: 2px; left: 3px; box-shadow: 5px 0 0 #4dff88, 0 0 3px #4dff88; }
@keyframes liveLeds { 0%, 100% { opacity: 1; } 50% { opacity: .2; } }

/* Presupuesto óptico GPON */
.ob-panel { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.ob-inputs { display: flex; flex-direction: column; gap: 8px; }
.ob-inp { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--muted); }
.ob-inp input, .ob-inp select { width: 100%; padding: 5px 8px; background: var(--panel2); color: var(--text);
  border: 1px solid var(--line-strong); border-radius: 7px; font-size: 13px; }
.ob-result { align-self: start; border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--panel2); }
.ob-line { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; padding: 3px 0; }
.ob-line span { color: var(--muted); }
.ob-total { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 6px; font-size: 14px; font-weight: 700; }
.ob-total span { color: var(--text); }
.ob-verdict { margin-top: 8px; text-align: center; font-weight: 700; font-size: 12.5px; padding: 6px; border: 1px solid; border-radius: 8px; }
.ob-panel .odf-note { grid-column: 1 / -1; }
@media (max-width: 560px) { .ob-panel { grid-template-columns: 1fr; } }

/* ── Tooltip flotante de puerto (carga cognitiva mínima) ──────────────────── */
.port-tip { position: fixed; z-index: 100000; display: none; min-width: 178px; max-width: 260px;
  background: #0d1117; color: #e6edf3; border: 1px solid #30363d; border-radius: 8px;
  padding: 8px 10px; box-shadow: 0 10px 30px #000a; font-size: 11.5px; pointer-events: none; }
.port-tip .pt-head { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 12px;
  padding-bottom: 5px; margin-bottom: 5px; border-bottom: 1px solid #ffffff14; }
.port-tip .pt-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.port-tip .pt-row { display: flex; justify-content: space-between; gap: 12px; line-height: 1.7; }
.port-tip .pt-k { color: #8b949e; white-space: nowrap; }
.port-tip .pt-v { color: #e6edf3; font-weight: 600; text-align: right; }

/* ── Esquema de Fusión de Extremo a Extremo (camino de la luz) ────────────── */
.e2e-head { display: grid; grid-template-columns: 1fr 34px 1.1fr 34px 1.2fr; gap: 4px;
  padding: 4px 10px; margin-bottom: 6px; }
.e2e-col-h { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); }
.e2e-col-h small { text-transform: none; font-weight: 500; letter-spacing: 0; }
.e2e-cable { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 10px; overflow: hidden; }
.e2e-cable-h { display: flex; align-items: center; gap: 6px; font-size: 13px; padding: 7px 12px;
  background: var(--panel2); border-bottom: 1px solid var(--line); }
.e2e-cable-h small { color: var(--muted); font-weight: 500; }
.e2e-row { display: grid; grid-template-columns: 1fr 34px 1.1fr 34px 1.2fr; gap: 4px; align-items: center;
  padding: 7px 12px; border-bottom: 1px solid var(--line); font-size: 12.5px; }
.e2e-row:last-child { border-bottom: 0; }
.e2e-row:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.e2e-in, .e2e-fuse, .e2e-out { display: flex; align-items: center; gap: 5px; min-width: 0; }
.e2e-in small, .e2e-fuse small { color: var(--muted); }
.e2e-link { display: flex; align-items: center; justify-content: center; gap: 2px; color: var(--muted); font-size: 11px; }
.e2e-line { height: 2px; width: 8px; background: color-mix(in srgb, var(--accent) 55%, transparent); border-radius: 2px; }
.e2e-dot { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.e2e-led { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; display: inline-block; }

/* Barra de edición */
/* Barra flotante horizontal de Objetos y Herramientas (íconos con tooltip) */
.edit-toolbar { position: absolute; top: 60px; left: 50%; transform: translateX(-50%); z-index: 1000;
  display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; gap: 8px;
  background: color-mix(in srgb, var(--panel) 92%, transparent); backdrop-filter: blur(8px);
  border: 1px solid var(--line); border-radius: 12px; padding: 7px 12px; width: auto; max-width: calc(100vw - 32px);
  box-shadow: var(--shadow-2); }
.edit-title { font-size: 10px; text-transform: uppercase; color: var(--muted); letter-spacing: .3px; }
.tb-icons { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tb-sep { width: 1px; align-self: stretch; background: var(--line); margin: 0 2px; }

/* Gestión de obra / As-built: letra más compacta para ver más en pantalla */
#workModal { font-size: 12.5px; }
#workBody { font-size: 12.5px; }
#workBody h4 { font-size: 13.5px; margin: 0 0 6px; }
#workBody table { font-size: 12px; }
#workBody .cl-card > div > div:first-child { font-size: 13px; }
#workModal .catalog-header h3 { font-size: 15px; }
#workTabs .tool-btn { font-size: 12px; padding: 4px 9px; }
.edit-toolbar button { background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding: 7px 9px; border-radius: 7px; cursor: pointer; font-size: 13px; text-align: left; }
.edit-toolbar button:hover { border-color: var(--accent); }
.edit-toolbar button.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.edit-hint { font-size: 11px; color: var(--fiber); line-height: 1.3; }
.edit-finish { background: var(--ok) !important; border-color: var(--ok) !important; color: #fff !important; }
.edit-cancel { background: transparent !important; color: var(--muted) !important; }
.map-editing { cursor: crosshair !important; }

/* Modal genérico */
.modal-overlay { position: absolute; inset: 0; z-index: 1100; background: var(--overlay);
  display: grid; place-items: center; padding: 18px; box-sizing: border-box; }
/* Modal chico (formularios simples): ancho responsivo + tope de alto con scroll
   para que el contenido largo NO se corte fuera de la pantalla. */
.modal-card { width: min(360px, 96vw); max-height: 92vh; overflow-y: auto; background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; padding: 22px; display: flex; flex-direction: column; gap: 12px; box-shadow: var(--shadow-3); }
.modal-card h4 { margin: 0; }
.modal-card label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.modal-card input, .modal-card select { background: var(--bg); border: 1px solid var(--line);
  color: var(--text); padding: 9px 10px; border-radius: 7px; font-size: 14px; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 4px; }
.modal-actions button { padding: 9px 16px; border-radius: 7px; border: 0; cursor: pointer;
  background: var(--grad); color: #fff; font-weight: 600; }
.modal-actions .ghost { background: transparent; border: 1px solid var(--line); color: var(--text); }

/* Catálogo */
.catalog-overlay { position: absolute; inset: 0; z-index: 1200; background: var(--overlay);
  display: grid; place-items: center; padding: 18px; box-sizing: border-box; }
/* Tarjeta estándar de modal: ancho responsivo, alto AUTO hasta 92vh (crece con el
   contenido y scrollea el cuerpo). Esquema común: header fijo arriba + cuerpo scrolleable. */
.catalog-card { width: min(820px, 96vw); height: auto; max-height: 92vh; background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; display: flex; flex-direction: column; overflow: hidden; box-shadow: var(--shadow-3); }
.catalog-header { display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px; border-bottom: 1px solid var(--line); }
.catalog-header h3 { margin: 0; font-size: 16px; }
.catalog-header .trace-close { position: static; }
.catalog-tabs { display: flex; gap: 6px; padding: 12px 18px 0; flex-wrap: wrap; }
.catalog-tabs button { background: var(--bg); border: 1px solid var(--line); color: var(--muted);
  padding: 8px 14px; border-radius: 8px 8px 0 0; cursor: pointer; font-size: 13px; }
.catalog-tabs button.active { background: var(--panel); color: var(--text); border-bottom-color: var(--panel); border-top: 2px solid var(--accent); }
.catalog-body { display: grid; grid-template-columns: 300px 1fr; gap: 16px; padding: 16px 18px;
  flex: 1; overflow: hidden; }
.catalog-form { display: flex; flex-direction: column; gap: 10px; overflow: auto; padding-right: 6px; }
.catalog-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.catalog-form input, .catalog-form select { background: var(--bg); border: 1px solid var(--line);
  color: var(--text); padding: 8px 10px; border-radius: 7px; font-size: 13px; }
.catalog-form button[type=submit] { background: var(--grad); color: #fff; border: 0;
  padding: 10px; border-radius: 7px; font-weight: 600; cursor: pointer; margin-top: 4px; }
.photo-preview { width: 100%; height: 110px; border: 1px dashed var(--line); border-radius: 8px;
  object-fit: contain; background: var(--bg); }
.catalog-list { overflow: auto; }
.catalog-list table { width: 100%; border-collapse: collapse; font-size: 13px; }
.catalog-list th { text-align: left; color: var(--muted); font-weight: 500; font-size: 11px;
  text-transform: uppercase; padding: 6px 8px; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--panel); }
.catalog-list td { padding: 7px 8px; border-bottom: 1px solid var(--line); }
.catalog-list img.thumb { width: 34px; height: 34px; object-fit: cover; border-radius: 5px; vertical-align: middle; }
.catalog-list .del { background: transparent; border: 0; color: var(--bad-ink); cursor: pointer; font-size: 16px; }
.catalog-error { color: var(--bad-ink); font-size: 12px; min-height: 14px; }

/* Búsqueda en topbar */
.search-box { position: relative; width: 280px; }
.search-box input { width: 100%; background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding: 8px 12px; border-radius: 8px; font-size: 13px; }
.search-results { position: absolute; top: 38px; left: 0; right: 0; background: var(--panel);
  border: 1px solid var(--line); border-radius: 8px; max-height: 320px; overflow: auto; z-index: 1500; }
.search-results .res { padding: 8px 12px; cursor: pointer; font-size: 13px; border-bottom: 1px solid var(--line); display: flex; gap: 8px; align-items: center; }
.search-results .res:hover { background: var(--bg); }
.search-results .res .k { font-size: 10px; text-transform: uppercase; color: var(--accent-ink); min-width: 50px; }

/* Botones de herramientas en sidebar */
.tool-btn { background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding: 8px 10px; border-radius: 7px; cursor: pointer; font-size: 13px; text-align: left; }
.tool-btn:hover { border-color: var(--accent); }
.tool-btn.active-tool { background: var(--ok); border-color: var(--ok); color: #fff; font-weight: 600; }

/* Dashboard */
/* Cuerpo genérico de modal: bloque scrolleable, mismo padding en todos.
   `min-height:0` + `flex:1` permiten que SCROLLEE dentro de la tarjeta (flex column)
   en vez de desbordarse y quedar cortado. */
.dash-body { padding: 16px 18px; overflow: auto; display: block; flex: 1 1 auto; min-height: 0; }
.dash-body > table, .modal-body > table { width: 100%; }
/* Grilla de tarjetas KPI (dashboard, mantenimiento): solo donde se quiere grid. */
.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px,1fr)); gap: 12px; }
.dash-card { background: var(--bg); border: 1px solid var(--line); border-radius: 10px; padding: 14px; }
.dash-card .big { font-size: 26px; font-weight: 700; color: var(--accent-ink); font-family: 'Bricolage Grotesque', 'Hanken Grotesk', sans-serif; }
.dash-card .lbl { font-size: 11px; text-transform: uppercase; color: var(--muted); margin-top: 4px; }
.dash-card.wide { grid-column: 1 / -1; }
.dash-bar { height: 8px; background: var(--line); border-radius: 4px; overflow: hidden; margin-top: 8px; }
.dash-bar > span { display: block; height: 100%; background: var(--accent); }
.dash-row { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 0; border-bottom: 1px solid var(--line); }
.dash-row b { color: var(--fiber); }

/* Árbol */
.tree-body { padding: 18px; overflow: auto; max-height: 76vh; font-size: 13px; }
.tree-node { margin-left: 16px; border-left: 1px solid var(--line); padding-left: 12px; }
.tree-node > .lbl { padding: 4px 0; }
.tree-node .tag { display: inline-block; font-size: 9px; text-transform: uppercase; padding: 1px 5px;
  border-radius: 4px; margin-right: 6px; background: var(--line); color: var(--text); }
.tag-olt { background: #f778ba33; color: #f778ba; }
.tag-splitter { background: #d2992233; color: var(--warn-ink); }
.tag-nap { background: #2f81f733; color: var(--accent-ink); }
.tag-cliente { background: #3fb95033; color: var(--ok-ink); }
/* Tintas legibles de los badges en tema claro */
body.light .tag-olt { background: #f778ba26; color: #b03579; }
body.light .tag-splitter { background: #f59e4226; }
body.light .tag-nap { background: #2f9fe022; }
body.light .tag-cliente { background: #14b8a622; }
.budget-box { background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 10px; margin-top: 10px; }
.budget-box .row { display: flex; justify-content: space-between; font-size: 12px; padding: 2px 0; }
/* Editor de empalmes */
.splice-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; padding: 0 18px; max-height: 40vh; overflow: auto; }
.strand-col { display: flex; flex-direction: column; gap: 3px; }
.strand { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border: 1px solid var(--line);
  border-radius: 6px; cursor: pointer; font-size: 12px; }
.strand:hover { border-color: var(--accent); }
.strand.sel { border-color: var(--accent); background: var(--line); }
.strand.used { opacity: .5; }
.strand .sw { width: 14px; height: 14px; border-radius: 3px; border: 1px solid #0006; flex: 0 0 14px; }
.splice-row { display: flex; justify-content: space-between; font-size: 12px; padding: 5px 0; border-bottom: 1px solid var(--line); }
.splice-row .del { background: transparent; border: 0; color: var(--bad-ink); cursor: pointer; }
.budget-status { padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
.bs-ok { background: #3fb95033; color: var(--ok-ink); }
.bs-ajustado { background: #d2992233; color: var(--warn-ink); }
.bs-critico { background: #f8514933; color: var(--bad-ink); }
body.light .bs-ok { background: #14b8a622; }
body.light .bs-ajustado { background: #f59e4226; }
body.light .bs-critico { background: #f0626d22; }

/* Planta interna del nodo: grilla de puertos */
.port-grid { display:flex; flex-wrap:wrap; gap:4px; margin-top:4px; }
.port-chip {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:24px; height:24px; padding:0 4px; border-radius:5px;
  color:#fff; font-size:11px; font-weight:600; cursor:pointer; user-select:none;
}
.port-chip:hover { filter:brightness(1.15); }

/* ---- Modal del nodo: botones y tarjetas más modernos ---- */
.node-btn { border:none; border-radius:6px; padding:3px 8px; background:var(--line); color:var(--text); cursor:pointer; font-size:12px; line-height:1; }
.node-btn:hover { filter:brightness(1.2); }
.node-btn.danger { background:color-mix(in srgb, var(--bad) 12%, transparent); }
.node-btn.danger:hover { background:var(--bad); color:#fff; }
#nodeBody .impact-item { background:var(--panel); border:1px solid var(--line); border-radius:10px; padding:10px 12px; box-shadow:var(--shadow-1); margin-bottom:8px; }
#nodeBody h4 { margin:12px 0 6px; font-size:13px; }
#nodeBody .trace-btn { background:var(--line); color:var(--text); border:none; border-radius:7px; padding:5px 11px; font-size:12px; cursor:pointer; margin:2px 4px 2px 0; transition:background .12s,color .12s; }
#nodeBody .trace-btn:hover { background:var(--accent); color:#fff; }
#nodeBody .trace-btn.active-tool { background:var(--accent); color:#fff; }
#nodeToolbar { background:var(--panel); border:1px solid var(--line); border-radius:10px; padding:8px 10px; }
#nodeToolbar select, #nodeToolbar input { border-radius:7px; padding:5px 8px; }
#nodeToolbar .trace-btn { background:var(--grad); color:#fff; font-weight:600; border:none; border-radius:7px; padding:6px 12px; cursor:pointer; }

/* ---- Barra superior compacta + menú agrupado + panel colapsable (estilo MAS-WISP) ---- */
.topbar { gap: 10px !important; }
.kpis { gap: 12px !important; font-size: 12px !important; }
#sideToggle { padding: 6px 11px; font-size: 16px; line-height: 1; }
.app-menu-wrap { position: relative; margin-left: auto; }
/* El menú se desplaza dentro de sí mismo cuando no cabe. Va `absolute` dentro
   de la barra, así que sin esto lo que sobresale queda literalmente fuera de
   alcance: no hay barra de desplazamiento que llegue hasta ahí. Con los cuatro
   módulos desplegados mide ~820 px, más que la altura útil de un portátil, y
   las últimas opciones (Configuración, Plataforma, ⌂ Suite) eran inalcanzables. */
/* Anclado a la VENTANA (fixed), no a la barra (absolute).
   La barra superior no cabe entera por debajo de ~1150 px de ancho: sus
   controles suman más que la pantalla y se sale por la derecha. Como el menú
   colgaba de ella con `right: 0`, se abría en el extremo derecho de la BARRA
   —fuera de la pantalla— y sólo asomaba un borde con las etiquetas cortadas.
   Desde fuera se veía como que el botón ☰ no hacía nada. Con `fixed` queda
   siempre pegado al borde derecho visible, quepa la barra o no. */
.app-menu { position: fixed; right: 10px; top: 46px; background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; padding: 8px; display: flex;
  flex-direction: column; gap: 4px; min-width: 210px; box-shadow: var(--shadow-2); z-index: 1500;
  max-height: calc(100vh - 56px); overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }
/* `overflow-x` va explícito: al fijar `overflow-y: auto` el eje horizontal deja
   de ser `visible` por regla de CSS y pasa a `auto` solo, con lo que el menú
   podía aparecer con desplazamiento lateral y las etiquetas cortadas
   («ACIÓN» en vez de «OPERACIÓN»). Nada del menú necesita ancho extra. */
.app-menu .ghost { text-align: left; border: none; padding: 8px 10px; border-radius: 7px; }
.app-menu .ghost:hover { background: var(--line); }
/* Encabezados de sección del menú ☰ (Operación / Análisis / Configuración / Plataforma) */
.menu-sec { font-size: 10px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted);
  font-weight: 700; padding: 9px 10px 3px; }
.menu-sec:first-child { padding-top: 3px; }
/* Módulos del menú ☰ como acordeón (clic en el encabezado despliega sus botones) */
.menu-sec-acc { cursor: pointer; display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px; border-radius: 7px; }
.menu-sec-acc:hover { background: var(--line); color: var(--text); }
.menu-sec-acc::after { content: '▸'; opacity: .7; transition: transform .15s ease; }
.menu-sec-acc.open::after { transform: rotate(90deg); }

/* Panel de Herramientas por categoría */
.tools-cat { margin-bottom: 16px; }
.tools-cat-h { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
  font-weight: 700; margin: 0 0 8px; }
.tools-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 8px; }
.tool-card { text-align: left; background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding: 10px 12px; border-radius: 9px; cursor: pointer; font-size: 13px; line-height: 1.25;
  transition: border-color .12s, background .12s; }
.tool-card:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--bg)); }
/* Tarjetas de acción de la bienvenida (título + subtítulo) */
.welcome-actions { display: flex; flex-direction: column; gap: 8px; margin: 4px 0; }
/* ---- Tarjeta de información del item (reemplaza el popup chico) ---- */
#infoSheet { position: fixed; top: 52px; right: 12px; width: 340px; max-width: calc(100vw - 24px);
  max-height: calc(100vh - 190px); overflow-y: auto; z-index: 1250;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow-3); animation: dfm-slide-r .28s cubic-bezier(.34,1.45,.42,1) both; }
.info-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--panel); z-index: 1; border-radius: 12px 12px 0 0; }
.info-icon { font-size: 26px; line-height: 1; flex: 0 0 auto; }
.info-titles { flex: 1; min-width: 0; }
.info-title { font-size: 18px; font-weight: 700; word-break: break-word; }
.info-type { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }
.info-close { background: transparent; border: 0; color: var(--muted); font-size: 24px; line-height: 1;
  cursor: pointer; padding: 0 4px; transition: color .12s ease, transform .12s ease; flex: 0 0 auto; }
.info-close:hover { color: var(--text); transform: scale(1.12); }
.info-body { padding: 12px 14px; display: flex; flex-direction: column; gap: 12px; }
.info-photo { width: 100%; max-height: 150px; object-fit: contain; border-radius: 8px; background: var(--bg); }
.info-rows { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 9px; overflow: hidden; }
.info-row { display: flex; justify-content: space-between; gap: 12px; padding: 10px 12px; font-size: 14.5px; background: var(--bg); }
.info-row:nth-child(even) { background: color-mix(in srgb, var(--text) 5%, var(--bg)); }
.info-k { color: color-mix(in srgb, var(--text) 62%, transparent); flex: 0 0 auto; }
.info-v { color: var(--text); font-weight: 700; text-align: right; word-break: break-word; }
.info-empty { color: var(--muted); font-size: 13px; padding: 2px; }
.info-actions { display: flex; flex-direction: column; gap: 6px; }
.info-actions-h { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin-bottom: 2px; }
.pop-btn.info-act { width: 100%; height: auto; justify-content: flex-start; gap: 10px; padding: 11px 13px;
  border-radius: 10px; font-size: 18px; color: var(--text); }
.pop-btn.info-act:hover { color: #fff; } /* al pasar el mouse el fondo es de color → texto blanco */
.info-act-l { font-size: 14px; font-weight: 600; line-height: 1.25; }

/* Lista de pasos de la guía de uso / tutorial */
.guide-list { margin: 4px 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.guide-list li { font-size: 12.5px; line-height: 1.4; color: var(--text); }
/* Tutorial completo */
.tutorial { max-height: 76vh; }
.tut-intro { font-size: 13px; color: var(--muted); margin: 0 0 4px; }
.tut-h { margin: 16px 0 2px; font-size: 14px; color: var(--accent-ink); }
.tut-h:first-of-type { margin-top: 2px; }
.tool-card.wc { display: flex; flex-direction: column; gap: 2px; }
.tool-card.wc span { font-size: 11px; color: var(--muted); }
/* Panel lateral colapsable: el mapa ocupa todo el ancho */
.app.side-collapsed { grid-template-columns: 0 1fr; }
.app.side-collapsed .sidebar { display: none; }

/* Menús Materiales/Herramientas: ancho acorde al texto (placeholder), no al más largo */
#editSelect { width: 170px !important; }
#toolSelect { width: 165px !important; }

/* Campana de alertas + badge */
.alerts-badge { position:absolute; top:-4px; right:-4px; min-width:16px; height:16px;
  padding:0 4px; border-radius:8px; background:var(--bad); color:#fff; font-size:10px;
  line-height:16px; text-align:center; font-weight:700; box-shadow:0 0 0 2px var(--bg); }
.alert-row { display:flex; gap:10px; align-items:flex-start; padding:10px 14px;
  border-bottom:1px solid var(--line); }
.alert-sev { width:8px; align-self:stretch; border-radius:4px; flex:0 0 8px; }
.alert-sev.alta { background:var(--bad); } .alert-sev.media { background:#f5c518; } .alert-sev.baja { background:#3fb950; }
body.light .alert-sev.media { background:#f59e42; } body.light .alert-sev.baja { background:#14b8a6; }
.alert-acts { margin-left:auto; display:flex; gap:6px; }
.alert-acts button { font-size:11px; padding:3px 8px; }

/* Continuidad / cortes a mitad de tramo */
.ms-paso { border:1px solid var(--line); border-radius:8px; padding:10px 12px; margin-bottom:12px; }
.ms-paso h4 { margin:0 0 6px; display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.ms-strand { display:flex; align-items:center; gap:8px; padding:5px 6px; border-bottom:1px solid var(--line); font-size:12px; }
.ms-line { flex:1; display:flex; align-items:center; gap:2px; min-width:120px; }
.ms-seg { height:4px; border-radius:2px; flex:1; }
.ms-cut { color:var(--bad-ink); font-weight:700; }
.ms-fuse { color:var(--warn-ink); font-weight:700; }
.ms-strand button { font-size:11px; padding:2px 7px; }
.ms-dot { width:12px; height:12px; border-radius:50%; border:1px solid #0006; flex:0 0 12px; }
.ms-branch { margin:4px 0 4px 26px; font-size:11px; color:var(--muted); display:flex; gap:6px; align-items:center; }
.ms-suger { background:#0891b21a; border:1px dashed #0891b2; border-radius:8px; padding:8px 10px; margin-bottom:10px; }

/* Toast efímero (deshacer/rehacer y avisos) */
.dfm-toast { position:fixed; bottom:24px; left:50%; transform:translateX(-50%) translateY(20px);
  background:var(--panel); color:var(--text); border:1px solid var(--line); border-radius:8px;
  padding:10px 16px; font-size:13px; box-shadow:var(--shadow-2); opacity:0; pointer-events:none;
  transition:opacity .2s, transform .2s; z-index:9999; }
.dfm-toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
#undoBtn:disabled, #redoBtn:disabled { opacity:.35; cursor:default; }

/* Ficha "Contenido" de la manga/caja */
.cl-stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(130px,1fr)); gap:8px; }
.cl-stat { background:var(--bg); border:1px solid var(--line); border-radius:8px; padding:8px 10px; }
.cl-stat-v { font-size:18px; font-weight:700; }
.cl-stat-l { font-size:11px; color:var(--text); opacity:.8; }

/* Separador de sección en resultados de búsqueda (objetos vs direcciones) */
.search-results .res-sep { font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); padding: 6px 12px 2px; border-top: 1px solid var(--line); background: var(--bg); }

/* Menú contextual del mapa (clic derecho) */
.map-ctx { position: fixed; z-index: 1500; min-width: 210px; background: var(--panel);
  border: 1px solid var(--line); border-radius: 10px; padding: 5px; box-shadow: var(--shadow-2);
  display: flex; flex-direction: column; }
.map-ctx button { text-align: left; background: transparent; border: 0; color: var(--text);
  padding: 8px 10px; border-radius: 7px; cursor: pointer; font-size: 13px; }
.map-ctx button:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }

/* Indicador de enganche (snap) al dibujar cables */
.snap-hint { width: 18px; height: 18px; border: 2.5px solid #3fb950; border-radius: 50%;
  background: rgba(63,185,80,.18); box-shadow: 0 0 0 2px #0006; pointer-events: none; }

/* ---- Impresión / exportación del plano del mapa ---- */
#printHeader { display: none; }
@media print {
  body * { visibility: hidden; }
  #map, #map * { visibility: visible; }
  #map { position: fixed; inset: 0; width: 100%; height: 100%; }
  /* ocultar UI no-mapa (pero conservar la barra de escala) */
  .sidebar, .topbar, .trace-panel, .catalog-overlay, .modal-overlay, .edit-toolbar,
  #toolBarIcons, #tbToggles, .map-ctx, .maplibregl-ctrl-zoom, .maplibregl-ctrl-attrib,
  .leaflet-control-zoom, .leaflet-control-attribution { display: none !important; }
  #printHeader { display: block; visibility: visible; position: fixed; top: 0; left: 0; right: 0;
    z-index: 99999; background: #fff; color: #111; border-bottom: 2px solid #111; padding: 8px 14px; }
  #printHeader * { visibility: visible; }
  #printHeader .ph-title { font-weight: 700; font-size: 16px; }
  #printHeader .ph-meta { font-size: 12px; color: #555; }
  #printHeader .ph-legend { display: flex; flex-wrap: wrap; gap: 12px; font-size: 11px; margin-top: 4px; }
  #printHeader .ph-legend span { display: inline-flex; align-items: center; gap: 4px; }
  #printHeader .ph-legend i { width: 11px; height: 11px; border-radius: 2px; display: inline-block; }
}

/* ---- Controles flotantes del mapa: leyenda + etiquetas ---- */
#mapControls { position: absolute; right: 14px; bottom: 40px; z-index: 700; display: flex; flex-direction: column; gap: 6px; }
#mapControls button { width: 38px; height: 38px; font-size: 18px; border-radius: 10px; cursor: pointer;
  background: var(--panel); color: var(--text); border: 1px solid var(--line); box-shadow: var(--shadow-1); }
#mapControls button.on { background: var(--accent); border-color: var(--accent); }
#mapLegend { position: absolute; right: 60px; bottom: 40px; z-index: 700; min-width: 160px; max-height: 50vh; overflow: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; box-shadow: var(--shadow-2); font-size: 12px; }
#mapLegend .lg-head { font-weight: 700; margin-bottom: 6px; font-size: 12px; }
#mapLegend .lg-row { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
#mapLegend .lg-ic { width: 16px; text-align: center; }
#mapLegend .lg-sw { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 12px; border: 1px solid #0006; }
/* Etiqueta sobre el mapa: siempre oscura (legible sobre cualquier mapa base),
   independiente del tema del chrome. */
.map-label { background: rgba(13,17,23,.85); color: #e6edf3; border: 1px solid rgba(255,255,255,.18);
  border-radius: 5px; padding: 1px 5px; font-size: 10px; font-weight: 600; box-shadow: none; }
.map-label::before { display: none; }
@media print { #mapControls, #mapLegend { display: none !important; } }

/* ---- Flecha norte (overlay permanente, también al imprimir) ---- */
#northArrow { position: absolute; top: 52px; right: 14px; z-index: 650; width: 34px; height: 40px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1;
  background: var(--panel); color: var(--text); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: var(--shadow-1); font-size: 18px; font-weight: 700; }
#northArrow span { font-size: 10px; margin-top: -2px; }
@media print {
  /* En el plano: mostrar barra de escala y flecha norte */
  .leaflet-control-scale, .maplibregl-ctrl-scale { display: block !important; visibility: visible !important;
    position: fixed !important; bottom: 12px !important; left: 12px !important; z-index: 99999 !important;
    background: #fff !important; color: #111 !important; }
  .leaflet-control-scale *, .maplibregl-ctrl-scale * { visibility: visible !important; color: #111 !important; }
  #northArrow { display: flex !important; visibility: visible !important; background: #fff !important; color: #111 !important;
    border: 1px solid #111 !important; top: 48px !important; }
  #northArrow * { visibility: visible !important; }
}

/* ============================================================
   UI compacta (~−25%) — chrome más chico sin tocar el mapa.
   ============================================================ */
.app { grid-template-columns: 184px 1fr; grid-template-rows: 44px minmax(0, 1fr); }
.topbar { gap: 8px !important; padding: 0 13px; }
.brand { font-size: 13px; display: flex; align-items: center; }
/* Logo de marca: sin texto al lado, aprovecha casi toda la altura de la barra. */
.brand-logo { height: 34px; width: auto; max-width: 190px; display: block; object-fit: contain; }
.ghost { padding: 5px 10px; font-size: 12px; border-radius: 7px; }
.kpis { gap: 9px !important; font-size: 10px !important; }
.search-box { width: 210px; }
.search-box input { font-size: 11px; padding: 6px 9px; }
.search-results .res { font-size: 12px; padding: 6px 10px; }
.base-select { font-size: 11px; padding: 5px 7px; }
.sidebar { padding: 11px 12px; gap: 5px; overflow-y: auto; min-height: 0; }
.sidebar label { font-size: 11px; gap: 6px; }
.sidebar label .dot { width: 8px; height: 8px; flex: 0 0 8px; }
.tool-btn { font-size: 11px; padding: 6px 8px; }
.trace-panel { width: 250px; padding: 12px; }
.trace-panel h4 { font-size: 13px; }
/* Barra a la derecha del botón flotante y en su misma línea; se desplaza con él
   al colapsar el panel (deja 56px a la derecha para la flecha norte). */
.edit-toolbar { padding: 5px 9px; gap: 6px; top: 52px; left: 292px; transform: none;
  width: auto; max-width: calc(100vw - 348px); flex-wrap: nowrap; overflow-x: auto;
  transition: left .18s ease, max-width .18s ease; }
.app.side-collapsed .edit-toolbar { left: 98px; max-width: calc(100vw - 154px); }
.edit-toolbar .tb-icons { flex-wrap: nowrap; }
.edit-toolbar .edit-title { white-space: nowrap; }
.edit-toolbar button { font-size: 11px; padding: 5px 7px; }
.pop-btn { width: 30px; height: 30px; font-size: 15px; }
#sideToggle { padding: 5px 9px; font-size: 14px; }
#mapControls button { width: 32px; height: 32px; font-size: 15px; }
#editSelect { width: 150px !important; }
#toolSelect { width: 150px !important; }

/* ============================================================
   Módulos del panel en acordeón (máx. 2 abiertos, controlado por JS).
   ============================================================ */
/* Tarjeta de resumen (KPIs) en el panel lateral, legible con número grande. */
.kpi-card { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 4px; }
.kpi { background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 6px 8px;
  display: flex; flex-direction: column; gap: 1px; }
.kpi-n { font-size: 17px; font-weight: 700; color: var(--accent-ink); line-height: 1.1; font-family: 'Bricolage Grotesque', 'Hanken Grotesk', sans-serif; }
.kpi-l { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: .2px; }

.acc { border-bottom: 1px solid var(--line); }
.acc-h { width: 100%; text-align: left; background: transparent; border: 0; cursor: pointer;
  color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .3px; font-weight: 600;
  padding: 8px 2px; display: flex; align-items: center; justify-content: space-between; }
.acc-h:hover { color: var(--text); }
.acc-h::after { content: '▸'; opacity: .7; transition: transform .15s ease; }
.acc.open > .acc-h::after { transform: rotate(90deg); }
.acc-b { display: none; flex-direction: column; gap: 6px; padding: 2px 0 9px; }
/* Control "Todas / Ninguna" de las capas */
.layers-toggle { display: flex; gap: 6px; margin-bottom: 2px; }
.layers-toggle button { font-size: 10px; padding: 2px 9px; background: var(--bg); border: 1px solid var(--line);
  color: var(--muted); border-radius: 6px; cursor: pointer; }
.layers-toggle button:hover { border-color: var(--accent); color: var(--text); }
.acc.open > .acc-b { display: flex; }

/* ============================================================
   Botón flotante para mostrar/ocultar la barra de objetos/herramientas.
   ============================================================ */
/* Botón flotante: por defecto a la DERECHA del panel (184 px + 10). Cuando el
   panel está colapsado, salta al borde izquierdo. Transición suave al moverse. */
#tbToggles { position: absolute; top: 52px; left: 204px; z-index: 1100; display: flex; gap: 6px;
  align-items: flex-start; transition: left .18s ease; }
/* Columna: el botón arriba y su lista vertical de iconos colgando debajo */
.tb-col { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.tb-vert { display: flex; flex-direction: column; gap: 5px; padding: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow-2);
  max-height: calc(100vh - 140px); overflow-y: auto; scrollbar-width: thin; }
.tb-vert.hidden { display: none; }
#tbToggles button { width: 32px; height: 32px; padding: 0;
  border-radius: 9px; cursor: pointer; font-size: 16px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-1);
  /* estado SECCIÓN VISIBLE (acento) */
  background: var(--accent); color: #fff; border: 1px solid var(--accent); }
/* estado SECCIÓN OCULTA (atenuado / neutro) */
#tbToggles button.off { background: var(--panel); color: var(--text); border-color: var(--line); }
#tbToggles button:hover { filter: brightness(1.08); }
/* Selector flotante de tipo de mapa (capa base) */
#basePopover { position: absolute; right: 58px; bottom: 40px; z-index: 760; background: var(--panel);
  border: 1px solid var(--line); border-radius: 10px; padding: 6px; box-shadow: var(--shadow-2);
  display: flex; flex-direction: column; gap: 3px; min-width: 150px; animation: dfm-pop .2s cubic-bezier(.34,1.45,.42,1) both; }
#basePopover button { text-align: left; background: transparent; border: 0; color: var(--text);
  padding: 9px 11px; border-radius: 7px; cursor: pointer; font-size: 13px; transition: background .12s ease; }
#basePopover button:hover { background: var(--line); }
#basePopover button.active { background: color-mix(in srgb, var(--accent) 22%, transparent); color: #fff; font-weight: 600; }
body.light #basePopover button.active { color: var(--accent-ink); }
.app.side-collapsed #tbToggles { left: 10px; }
.edit-toolbar.tb-hidden { display: none !important; }

/* ============================================================
   VISTA MÓVIL / TABLET (Bloque 1: layout + cajón lateral)
   Todo dentro de media queries → el escritorio no se altera.
   ============================================================ */
#sideBackdrop { display: none; }
@media (max-width: 820px) {
  /* El mapa ocupa todo; la barra superior arriba (override también side-collapsed).
     minmax(0,1fr) permite que la columna se achique al ancho real del celular. */
  .app, .app.side-collapsed { grid-template-columns: minmax(0, 1fr); grid-template-areas: "top" "map"; }
  /* Barra superior: compacta y con scroll horizontal si no entra todo */
  .topbar { gap: 6px !important; padding: 0 8px; overflow-x: auto; }
  .search-box { width: auto; flex: 1 1 100px; min-width: 90px; }
  /* !important: estos selects traen max-width inline (200/170/190px) que si no ganaría */
  #projectSelect, #zoneSelect, #viewSelect { max-width: 120px !important; }
  /* El menú ☰ y los resultados de búsqueda viven DENTRO de la topbar, que en móvil
     es un contenedor con overflow-x:auto (eso también recorta en Y). position:fixed
     los saca del recorte y los ancla al viewport. */
  .app-menu { position: fixed; top: 46px; right: 6px; width: min(300px, calc(100vw - 12px));
    max-height: calc(100vh - 56px); max-height: calc(100dvh - 56px); overflow-y: auto; }
  .search-results { position: fixed; top: 46px; left: 8px; right: 8px;
    max-height: 55vh; max-height: 55dvh; }
  /* Panel lateral = cajón deslizable sobre el mapa */
  .sidebar { position: fixed; top: 44px; left: 0; bottom: 0; width: min(280px, 82vw); z-index: 1300;
    display: flex; transform: translateX(0); transition: transform .22s ease;
    box-shadow: var(--shadow-3); overflow-y: auto; }
  .app.side-collapsed .sidebar { display: flex; transform: translateX(-100%); }
  /* Fondo oscuro cuando el cajón está abierto (tocar = cerrar) */
  .app:not(.side-collapsed) #sideBackdrop { display: block; position: fixed; inset: 44px 0 0 0;
    background: #00000066; z-index: 1290; }

  /* Bloque 3: paneles flotantes como hoja inferior + overlays reubicados */
  .trace-panel { top: auto; left: 8px; right: 8px; bottom: 8px; width: auto; max-height: 62vh; }
  /* Tarjeta de info: hoja inferior casi a pantalla completa (todos los botones visibles) */
  #infoSheet { top: auto; right: 0; left: 0; bottom: 0; width: 100%; max-width: none;
    max-height: 85vh; border-radius: 16px 16px 0 0;
    animation: dfm-pop .3s cubic-bezier(.34,1.45,.42,1) both; }
  .info-head { border-radius: 16px 16px 0 0; }
  .edit-toolbar, .app.side-collapsed .edit-toolbar { top: 90px; left: 6px; right: 6px; width: auto; max-width: none; }

  /* Botones flotantes 📦/🧰: en móvil el panel es un cajón overlay (no ocupa columna),
     así que siempre van pegados al borde izquierdo, esté o no colapsado el panel. */
  #tbToggles, .app.side-collapsed #tbToggles { left: 10px; }
  .tb-vert { max-height: calc(100vh - 160px); max-height: calc(100dvh - 160px); }

  /* Modales grandes (.catalog-card): hoja inferior a todo el ancho. !important porque
     varias tarjetas traen width/height inline (p. ej. height:min(720px,94vh)). */
  .catalog-overlay { padding: 0; place-items: end center; }
  .catalog-card { width: 100vw !important; max-width: 100vw; height: auto !important;
    max-height: 96vh !important; max-height: 96dvh !important; border-radius: 14px 14px 0 0; }
  /* Formulario + lista en una sola columna; scrollea el conjunto, la página nunca desborda */
  .catalog-body { grid-template-columns: 1fr !important; overflow: auto; }
  #invPreview { border-left: 0 !important; padding-left: 0 !important; }

  /* Modales chicos (.modal-card): a todo el ancho útil (algunos traen width inline 380/420px) */
  .modal-overlay { padding: 10px; }
  .modal-card { width: 100% !important; max-width: 100%; max-height: 94vh; max-height: 94dvh; }

  /* Hoja de info: dvh para que la barra del navegador móvil no tape el final */
  #infoSheet { max-height: 85dvh; }
  /* La leyenda flotante nunca más ancha que la pantalla */
  #mapLegend { max-width: calc(100vw - 76px); }

  /* Bloque 4: objetivos táctiles + tipografía (inputs ≥16px evita el zoom de iOS).
     !important para ganarle a los tamaños específicos de cada modal (13/14px). */
  .pop-btn { width: 40px; height: 40px; font-size: 19px; }
  .tool-btn, .ghost, .trace-btn { min-height: 38px; }
  input, select, textarea, .base-select { font-size: 16px !important; }
}

/* ============================================================
   Pantallas angostas (≤640px): liberar espacio en la topbar.
   La marca y el tutorial (🎓, duplicado en ☰ → Guía de uso) se ocultan.
   ============================================================ */
@media (max-width: 640px) {
  .brand { display: none; }
  #btnTutorial { display: none; }
}

/* ============================================================
   Ajustes responsive globales (seguros también en escritorio).
   ============================================================ */
/* Menú contextual del mapa: nunca se sale de la pantalla (scrollea si es muy alto) */
.map-ctx { max-width: calc(100vw - 12px); max-height: calc(100vh - 16px); overflow-y: auto; }
/* Toast: que el texto largo envuelva en vez de desbordar */
.dfm-toast { max-width: calc(100vw - 28px); }
/* Login usable en pantallas de 320px */
.login-card { width: min(320px, calc(100vw - 28px)); }
/* Pestañas de modales creadas con estilo inline sin flex-wrap */
#usersTabs, #pzTabs, #masterUsersTabs { flex-wrap: wrap; }

/* ============================================================
   MICRO-ANIMACIONES (leves) — solo chrome/UI, NO tocan los iconos del mapa.
   Respetan "prefers-reduced-motion".
   ============================================================ */
@keyframes dfm-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes dfm-pop { from { opacity: 0; transform: translateY(18px) scale(.93); } to { opacity: 1; transform: none; } }
@keyframes dfm-menu-in { from { opacity: 0; transform: translateY(-14px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes dfm-slide-r { from { opacity: 0; transform: translateX(28px); } to { opacity: 1; transform: none; } }

/* Aparición de overlays y tarjetas modales (con leve rebote) */
.modal-overlay, .catalog-overlay { animation: dfm-fade .26s ease both; }
.modal-card, .catalog-card { animation: dfm-pop .34s cubic-bezier(.34,1.45,.42,1) both; }
/* Panel de operación (recorrido/impacto) y menú desplegable */
.trace-panel { animation: dfm-slide-r .32s cubic-bezier(.34,1.45,.42,1) both; }
.app-menu:not(.hidden) { animation: dfm-menu-in .24s cubic-bezier(.34,1.45,.42,1) both; }
/* Cuerpo de acordeón al desplegar (panel izquierdo) */
.acc.open > .acc-b { animation: dfm-pop .28s cubic-bezier(.2,.85,.3,1) both; }

/* Micro-interacciones de hover (más marcadas) */
.ghost, .tool-btn { transition: transform .14s ease, border-color .14s ease, background .14s ease, color .14s ease; }
.ghost:hover, .tool-btn:hover { transform: translateY(-2px); }
.tool-card { transition: transform .14s ease, border-color .14s ease, background .14s ease, box-shadow .14s ease; }
.tool-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); }
.kpi { transition: transform .12s ease, border-color .12s ease; }
.kpi:hover { transform: translateY(-1px); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }
.trace-btn { transition: transform .1s ease, filter .12s ease; }
.trace-btn:hover { transform: translateY(-1px); }

/* Accesibilidad: si el usuario pide menos movimiento, se desactiva todo */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}

/* ============================================================================
   ESTILO GMAIL / MATERIAL DESIGN  (reemplaza el tema anterior)
   Superficies blancas planas, sombras de elevación sutiles (no relieve),
   navegación tipo "pill", barra de búsqueda tipo Gmail, acento azul Google.
   Cubre tema claro (Gmail light) y oscuro (Gmail dark).
   Además: barras de OBJETOS y HERRAMIENTAS reubicadas a la DERECHA.
   ==========================================================================*/
:root {
  /* Dark = Gmail dark */
  --bg: #131314;
  --panel: #1e1f20;
  --panel2: #2d2e30;
  --line: #444746;
  --line-strong: #5f6368;
  --text: #e3e3e3;
  --muted: #9aa0a6;
  --accent: #a8c7fa;
  --accent-ink: #a8c7fa;
  --grad: #8ab4f8;                 /* sin gradiente: azul sólido */
  --gm-search: #1e1f20;            /* barra de búsqueda */
  --gm-search-focus: #2d2e30;
  --gm-compose: #004a77;           /* botón de acción (Compose) */
  --gm-compose-ink: #c2e7ff;
  --gm-sel: #004a77;               /* nav seleccionado */
  --gm-sel-ink: #c2e7ff;
  --gm-hover: #2d2e30;             /* nav hover */
  --sh-1: 0 1px 2px rgba(0,0,0,.45), 0 1px 3px 1px rgba(0,0,0,.30);
  --sh-2: 0 1px 3px rgba(0,0,0,.45), 0 4px 8px 3px rgba(0,0,0,.30);
  --sh-3: 0 4px 8px 3px rgba(0,0,0,.35), 0 8px 24px 6px rgba(0,0,0,.40);
  --r-card: 12px;
  --r-dialog: 28px;
  --r-pill: 999px;
  --r-field: 8px;
}
body.light {
  /* Light = Gmail light */
  --bg: #f6f8fc;
  --panel: #ffffff;
  --panel2: #f0f4f9;
  --line: #dadce0;
  --line-strong: #c4c7c5;
  --text: #1f1f1f;
  --muted: #5f6368;
  --accent: #0b57d0;
  --accent-ink: #0b57d0;
  --grad: #0b57d0;
  --gm-search: #eaf1fb;
  --gm-search-focus: #ffffff;
  --gm-compose: #c2e7ff;
  --gm-compose-ink: #001d35;
  --gm-sel: #d3e3fd;
  --gm-sel-ink: #041e49;
  --gm-hover: #ebeef2;
  --sh-1: 0 1px 2px rgba(60,64,67,.30), 0 1px 3px 1px rgba(60,64,67,.15);
  --sh-2: 0 1px 3px rgba(60,64,67,.30), 0 4px 8px 3px rgba(60,64,67,.15);
  --sh-3: 0 4px 8px 3px rgba(60,64,67,.15), 0 8px 24px 6px rgba(60,64,67,.18);
}

/* ---- Superficies base: blancas, planas, radios Material ---- */
.topbar, .sidebar { box-shadow: none !important; }
.topbar { border-bottom: 1px solid var(--line); }
.sidebar { border-right: 1px solid var(--line); }
.login-card, .app-menu, .tools-cat, .search-results,
.cl-tray, .icon-picker, .map-ctx, .dfm-toast, #presExit {
  border: 1px solid var(--line) !important;
  border-radius: var(--r-card) !important;
  box-shadow: var(--sh-2) !important;
}
.login-screen { background: var(--bg) !important; }
body.light .login-screen { background: var(--bg) !important; }

/* ---- Barra de búsqueda estilo Gmail (pill gris que se aclara al enfocar) ---- */
.search-box { width: 360px; }
.search-box input {
  background: var(--gm-search) !important;
  border: 1px solid transparent !important;
  border-radius: var(--r-pill) !important;
  padding: 10px 16px !important;
  box-shadow: none !important;
  transition: background .15s, box-shadow .15s, border-color .15s;
}
.search-box input:hover { background: var(--gm-search-focus) !important; box-shadow: var(--sh-1) !important; }
.search-box input:focus {
  background: var(--gm-search-focus) !important;
  box-shadow: var(--sh-1) !important;
  border-color: transparent !important;
  outline: none !important;
}

/* ---- Navegación de la barra lateral: ítems tipo "pill" ---- */
.tool-card, .menu-sec-acc, .acc-h {
  border: 0 !important;
  border-radius: var(--r-pill) !important;
  background: transparent !important;
  transition: background .12s ease, color .12s ease;
}
.tool-card { padding: 9px 14px !important; }
.tool-card:hover, .menu-sec-acc:hover, .acc-h:hover {
  background: var(--gm-hover) !important;
  color: var(--text) !important;
}
.tool-card.active-tool, .tool-card.active, .acc.open > .acc-h {
  background: var(--gm-sel) !important;
  color: var(--gm-sel-ink) !important;
}
.sidebar label { border-radius: var(--r-pill); padding: 6px 12px; transition: background .12s; }
.sidebar label:hover { background: var(--gm-hover); }

/* ---- Botones: Material tonal / text ---- */
.ghost {
  background: var(--panel2) !important;
  border: 1px solid transparent !important;
  border-radius: var(--r-pill) !important;
  color: var(--text) !important;
  box-shadow: none !important;
  transition: background .12s, box-shadow .12s;
}
.ghost:hover { background: var(--gm-hover) !important; box-shadow: var(--sh-1) !important; }
.app-menu .ghost { background: transparent !important; border-radius: 8px !important; }
.app-menu .ghost:hover { background: var(--gm-hover) !important; box-shadow: none !important; }
.node-btn { border-radius: var(--r-pill) !important; }

/* Botón de acción principal (estilo "Compose" celeste) */
.login-card button, .edit-finish, .modal-actions button:first-child,
.trace-btn, .welcome-actions .ghost.primary, #btnNew, .btn-new {
  background: var(--gm-compose) !important;
  color: var(--gm-compose-ink) !important;
  border: 0 !important;
  border-radius: var(--r-pill) !important;
  box-shadow: var(--sh-1) !important;
  font-weight: 600;
  transition: box-shadow .12s, filter .12s;
}
.login-card button:hover, .edit-finish:hover, .trace-btn:hover,
.modal-actions button:first-child:hover { box-shadow: var(--sh-2) !important; filter: brightness(.98); }
.edit-finish { background: #1e8e3e !important; color: #fff !important; }  /* verde acción, plano */

/* ---- Inputs: Material outlined ---- */
.login-card input, .search-box input,
.modal-card input, .modal-card select,
.catalog-form input, .catalog-form select,
.cl-form-row input, .cl-form-row select, .cl-out select, .base-select {
  border-radius: var(--r-field) !important;
}
.login-card input, .modal-card input, .modal-card select,
.catalog-form input, .catalog-form select, .cl-form-row input, .cl-form-row select {
  background: var(--panel) !important;
  border: 1px solid var(--line) !important;
  box-shadow: none !important;
  transition: border-color .12s, box-shadow .12s;
}
.login-card input:focus, .modal-card input:focus, .modal-card select:focus,
.catalog-form input:focus, .catalog-form select:focus,
.cl-form-row input:focus, .cl-form-row select:focus {
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 1px var(--accent) !important;
  outline: none !important;
}

/* ---- KPIs / dash-cards: tarjetas blancas planas ---- */
.kpi, .kpi-card > *, .cl-stat {
  background: var(--panel) !important;
  border: 1px solid var(--line) !important;
  border-radius: var(--r-card) !important;
  box-shadow: var(--sh-1) !important;
}
.kpi:hover { box-shadow: var(--sh-2) !important; transform: none !important; }
.dash-card { border-radius: var(--r-card) !important; box-shadow: var(--sh-1) !important; border: 1px solid var(--line) !important; }
.dash-bar { border-radius: var(--r-pill) !important; overflow: hidden; }

/* ---- Chips / tags ---- */
.tree-node .tag, .trace-step .tag, .impact-kpi, .port-chip,
.alert-sev, .base-select, .catalog-tabs button {
  border-radius: var(--r-pill) !important;
}
.catalog-tabs button { border: 0 !important; }
.catalog-tabs button.active { background: var(--gm-sel) !important; color: var(--gm-sel-ink) !important; }

/* ---- Modales / diálogos Material ---- */
.modal-card, .catalog-card {
  border: 0 !important;
  border-radius: var(--r-dialog) !important;
  box-shadow: var(--sh-3) !important;
  background: var(--panel) !important;
}
.trace-panel { border-radius: var(--r-card) !important; box-shadow: var(--sh-2) !important; border: 1px solid var(--line) !important; }
.modal-overlay, .catalog-overlay { backdrop-filter: none; background: rgba(32,33,36,.5); }

/* ---- Popups del mapa ---- */
.maplibregl-popup-content, .leaflet-popup-content-wrapper {
  border: 1px solid var(--line) !important;
  border-radius: var(--r-card) !important;
  box-shadow: var(--sh-2) !important;
}

/* ---- Controles y botones de mapa: icon buttons Material ---- */
#mapControls button, .maplibregl-ctrl button {
  border-radius: var(--r-pill) !important;
  box-shadow: var(--sh-1) !important;
  border: 1px solid var(--line) !important;
}
#mapControls button:hover { background: var(--gm-hover) !important; }

/* =====================================================================
   BARRAS DE OBJETOS (📦) Y HERRAMIENTAS (🧰)
   En su sitio original (izquierda, junto a la barra lateral), pero
   desplegadas en HORIZONTAL: el toggle a la izquierda y los íconos en fila.
   (No se toca left/right → conserva el posicionamiento responsivo original.)
   ===================================================================*/
/* UNA SOLA PÍLDORA: el contenedor lleva el fondo/borde/sombra y envuelve
   ambas filas (objetos + herramientas). */
#tbToggles {
  flex-direction: row;
  align-items: center;
  background: var(--panel) !important;
  border: 1px solid var(--line) !important;
  border-radius: 28px !important;
  box-shadow: var(--sh-2) !important;
  padding: 6px !important;
  gap: 10px;
}
/* Cada sección: botón toggle a la izquierda + fila de íconos a su derecha */
.tb-col { flex-direction: row !important; align-items: center !important; gap: 6px; }
/* Los íconos en FILA, sin píldora propia (comparten la del contenedor) */
.tb-vert {
  flex-direction: row !important;
  flex-wrap: wrap;
  max-height: none !important;
  background: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  padding: 0 !important;
}
.tb-vert button { border-radius: var(--r-pill) !important; }

/* Toggles integrados en la píldora (conservan su color, sin recuadro propio) */
#tbToggles > .tb-col > button {
  border: 0 !important;
  box-shadow: none !important;
  border-radius: var(--r-pill) !important;
}
#tbToggles > .tb-col > button:hover { filter: brightness(1.06) !important; box-shadow: none !important; }

/* =====================================================================
   CAMPANA DE NOTIFICACIONES — estilo MAS-WISP
   El modal grande de alertas se muestra como un DROPDOWN anclado a la
   campana (arriba-derecha), con la lista tipo "notif-item".
   ===================================================================*/
#alertsModal { background: transparent; }
#alertsModal > .catalog-card {
  position: absolute !important; top: 52px !important; right: 14px !important; left: auto !important; margin: 0 !important;
  width: min(384px, 94vw) !important; max-height: 74vh; height: auto;
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0,0,0,.28);
}
#alertsModal .catalog-header { padding: 11px 14px; border-bottom: 1px solid var(--line); }
#alertsModal .catalog-header h3 { font-size: 15px; margin: 0; }
/* Filas de alerta como los items de la campana de MAS-WISP */
#alertsModal .alert-row { border-bottom: 1px solid var(--line); padding: 10px 14px; flex-wrap: wrap; }
#alertsModal .alert-row:last-child { border-bottom: 0; }
#alertsModal .alert-row:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
/* El mensaje ocupa toda la 1ª línea; las acciones bajan a una fila, como MAS-WISP */
#alertsModal .alert-row > div[style*="flex:1"] { flex: 1 1 100% !important; }
#alertsModal .alert-acts { margin-left: 18px; margin-top: 8px; flex: 0 0 auto; }
#alertsModal .alert-acts button { border-radius: 999px; }

/* =====================================================================
   OTDR PANTALLA COMPLETA — mapa al centro, información distribuida
   (reemplaza el panel emergente .trace-panel para la herramienta OTDR).
   ===================================================================*/
.app.otdr-mode { grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "map"; }
.app.otdr-mode .topbar, .app.otdr-mode .sidebar, .app.otdr-mode #tbToggles,
.app.otdr-mode .edit-toolbar, .app.otdr-mode #mapControls { display: none !important; }
#otdrFull { position: fixed; inset: 0; z-index: 1500; pointer-events: none; display: none; }
.app.otdr-mode #otdrFull { display: block; }
#otdrFull > * { pointer-events: auto; }
/* Barra superior */
#otdrFull .of-top { position: absolute; top: 0; left: 0; right: 0; height: 46px; display: flex;
  align-items: center; gap: 14px; padding: 0 16px; background: var(--panel); border-bottom: 1px solid var(--line); box-shadow: var(--shadow-1); }
#otdrFull .of-top .of-title { font-weight: 700; letter-spacing: .04em; font-size: 14px; }
#otdrFull .of-top .of-title span { color: var(--fiber); }
#otdrFull .of-spacer { flex: 1; }
#otdrFull .of-x { background: transparent; border: 1px solid var(--line); color: var(--text);
  border-radius: 8px; padding: 6px 12px; cursor: pointer; font-size: 13px; }
#otdrFull .of-x:hover { background: var(--line); }
/* Paneles laterales */
#otdrFull .of-left, #otdrFull .of-right { position: absolute; top: 54px; bottom: 214px; width: 310px;
  background: color-mix(in srgb, var(--panel) 92%, transparent); backdrop-filter: blur(8px);
  border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow-2); overflow-y: auto; padding: 14px; }
#otdrFull .of-left { left: 12px; }
#otdrFull .of-right { right: 12px; width: 340px; }
#otdrFull .of-h { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  font-family: 'JetBrains Mono', ui-monospace, monospace; margin: 2px 0 8px; }
#otdrFull label { display: block; font-size: 12px; opacity: .8; margin: 8px 0 3px; }
#otdrFull select, #otdrFull input { width: 100%; }
#otdrFull .of-btn { width: 100%; margin-top: 10px; background: var(--grad); color: #fff; border: 0;
  padding: 10px; border-radius: 8px; font-weight: 600; cursor: pointer; }
#otdrFull .of-btn.alt { background: transparent; border: 1px solid var(--line); color: var(--text); }
/* Franja inferior: traza reflectograma a lo ancho */
#otdrFull .of-bottom { position: absolute; left: 12px; right: 12px; bottom: 12px; height: 192px;
  background: color-mix(in srgb, var(--panel) 92%, transparent); backdrop-filter: blur(8px);
  border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow-2); padding: 10px 14px;
  display: grid; grid-template-columns: 1fr 320px; gap: 14px; }
#otdrFull .of-bottom .of-trace { min-width: 0; }
#otdrFull .of-bottom .of-events { overflow-y: auto; border-left: 1px solid var(--line); padding-left: 12px; }
#otdrFull .of-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; margin: 10px 0 2px; cursor: pointer; }
/* Veredicto de certificación */
#otdrFull .of-verdict { margin-top: 10px; border-radius: 10px; padding: 10px 12px; font-weight: 700; text-align: center; }
#otdrFull .of-verdict.pass { background: color-mix(in srgb, var(--ok) 18%, transparent); color: var(--ok-ink); }
#otdrFull .of-verdict.fail { background: color-mix(in srgb, var(--bad) 16%, transparent); color: var(--bad-ink); }
@media (max-width: 820px) {
  #otdrFull .of-left, #otdrFull .of-right { position: absolute; top: 54px; bottom: auto; width: auto; left: 8px; right: 8px; max-height: 34vh; }
  #otdrFull .of-right { top: 40vh; }
  #otdrFull .of-bottom { grid-template-columns: 1fr; height: 30vh; }
}

/* ─────────────────────────────────────────────────────────────
   Módulo Mapeo físico ODF & cross-connects (pantalla completa)
   ───────────────────────────────────────────────────────────── */
.app.odf-mode { grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "map"; }
.app.odf-mode .topbar, .app.odf-mode .sidebar, .app.odf-mode #tbToggles,
.app.odf-mode .edit-toolbar, .app.odf-mode #mapControls { display: none !important; }
#odfFull { position: fixed; inset: 0; z-index: 1500; display: none; background: var(--bg, #0b1326); }
.app.odf-mode #odfFull { display: grid; grid-template-rows: 48px 1fr; }
#odfFull .of-top { display: flex; align-items: center; gap: 8px; padding: 0 12px; background: var(--panel, #0f1830);
  border-bottom: 1px solid var(--line, #22314f); }
#odfFull .of-title { font-weight: 700; font-size: 15px; color: var(--text, #e8eefc); }
#odfFull .of-spacer { flex: 1; }
#odfFull .of-btn { background: var(--grad, linear-gradient(90deg,#0ea5e9,#2563eb)); color: #fff; border: 0;
  border-radius: 8px; padding: 7px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; }
#odfFull .of-btn.alt { background: transparent; border: 1px solid var(--line, #22314f); color: var(--text, #e8eefc); }
#odfFull .of-body { display: grid; grid-template-columns: 340px 1fr; overflow: hidden; }
#odfFull .odf-left { border-right: 1px solid var(--line, #22314f); overflow-y: auto; padding: 10px; }
#odfFull .odf-main { overflow-y: auto; padding: 16px 20px; }
#odfFull .odf-search { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--line, #22314f);
  background: var(--panel2, #0b1220); color: var(--text, #e8eefc); margin-bottom: 8px; }
#odfFull .odf-item { padding: 9px 10px; border-radius: 9px; border: 1px solid var(--line, #22314f);
  margin-bottom: 6px; cursor: pointer; background: var(--panel2, #0b1220); transition: border-color .12s; }
#odfFull .odf-item:hover { border-color: #0ea5e9; }
#odfFull .odf-item.sel { border-color: #0ea5e9; box-shadow: 0 0 0 1px #0ea5e9 inset; }
#odfFull .odf-item .lbl { font-weight: 600; font-size: 13px; color: var(--text, #e8eefc); }
#odfFull .odf-item .sub { font-size: 11.5px; color: var(--muted, #8aa0c6); margin-top: 2px; }
#odfFull .odf-rx { font-weight: 700; font-variant-numeric: tabular-nums; }
#odfFull .odf-rx.ok { color: #22c55e; } #odfFull .odf-rx.warn { color: #f59e0b; } #odfFull .odf-rx.bad { color: #ef4444; }
#odfFull .odf-chain { display: flex; flex-direction: column; gap: 0; max-width: 720px; }
#odfFull .odf-node { border: 1px solid var(--line, #22314f); border-radius: 12px; padding: 12px 14px;
  background: var(--panel, #0f1830); position: relative; }
#odfFull .odf-node h5 { margin: 0 0 6px; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted, #8aa0c6); }
#odfFull .odf-node .big { font-size: 15px; font-weight: 700; color: var(--text, #e8eefc); }
#odfFull .odf-node .kv { font-size: 12.5px; color: var(--text, #e8eefc); margin-top: 3px; }
#odfFull .odf-node .kv b { color: var(--muted, #8aa0c6); font-weight: 600; }
#odfFull .odf-link { width: 2px; height: 20px; background: #0ea5e9; margin: 0 auto; }
#odfFull .odf-node.miss { border-style: dashed; opacity: .7; }
#odfFull .odf-live { margin-top: 6px; border-top: 1px dashed var(--line, #22314f); padding-top: 6px; }
#odfFull .odf-badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; }
#odfFull .odf-badge.on { background: rgba(34,197,94,.16); color: #22c55e; }
#odfFull .odf-badge.off { background: rgba(239,68,68,.16); color: #ef4444; }
#odfFull .odf-badge.warn { background: rgba(245,158,11,.16); color: #f59e0b; }
#odfFull .odf-empty { color: var(--muted, #8aa0c6); font-size: 13px; padding: 30px 10px; text-align: center; }
#odfFull .odf-banner { background: rgba(14,165,233,.12); border: 1px solid #0ea5e9; border-radius: 8px;
  padding: 6px 10px; font-size: 12px; color: var(--text, #e8eefc); margin-bottom: 8px; }
.odf-form { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.odf-form label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--muted, #8aa0c6); }
.odf-form input, .odf-form select { padding: 7px 9px; border-radius: 7px; border: 1px solid var(--line, #22314f);
  background: var(--panel2, #0b1220); color: var(--text, #e8eefc); font-size: 13px; }
@media (max-width: 760px) { #odfFull .of-body { grid-template-columns: 1fr; } #odfFull .odf-left { max-height: 40vh; } }

/* ─────────────────────────────────────────────────────────────
   Módulo RED — réplica 1:1 del dashboard "Servidores y OLT" de MAS-WISP.
   Paleta clara teal scoped a #redFull (aunque DAS use tema oscuro).
   ───────────────────────────────────────────────────────────── */
.app.red-mode { grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "map"; }
.app.red-mode .topbar, .app.red-mode .sidebar, .app.red-mode #tbToggles,
.app.red-mode .edit-toolbar, .app.red-mode #mapControls { display: none !important; }
#redFull {
  --bg-0:#eef3f7; --bg-1:#f5f9fb; --bg-2:#fff; --bg-3:#eef4f8; --line:#e3eaf0; --line-bright:#cfdbe4;
  --tx-0:#14303a; --tx-1:#3a5560; --tx-2:#54707c; --tx-3:#6a828e;
  --signal:#14b8a6; --signal-ink:#0a766a; --signal-dim:#5ecfc0; --info:#2f9fe0; --info-ink:#1a72ac;
  --amber:#f59e42; --amber-ink:#9a5a0e; --danger:#f0626d; --danger-ink:#c41f2d;
  --grad:linear-gradient(135deg,#14b8a6,#2f9fe0); --radius:7px; --radius-lg:12px;
  --shadow:0 1px 2px rgba(20,48,58,.05), 0 16px 34px -22px rgba(20,48,58,.30);
  position: fixed; inset: 0; z-index: 1500; display: none; background: var(--bg-0);
  color: var(--tx-1); font-size: 14px;
}
.app.red-mode #redFull { display: grid; grid-template-rows: 52px auto 1fr; }
#redFull .of-top { display: flex; align-items: center; gap: 8px; padding: 0 18px; background: var(--bg-2); border-bottom: 1px solid var(--line); }
#redFull .of-title { font-weight: 600; font-size: 18px; color: var(--tx-0); letter-spacing: -.01em; }
#redFull .of-title span { color: var(--tx-2); font-size: 13px; font-weight: 400; }
#redFull .of-spacer { flex: 1; }
#redFull .of-btn { background: var(--grad); color: #fff; border: 0; border-radius: var(--radius); padding: 9px 15px; font-size: 13px; font-weight: 600; cursor: pointer; box-shadow: 0 4px 14px -4px rgba(20,184,166,.5); }
#redFull .of-btn.alt { background: var(--bg-3); border: 1px solid var(--line-bright); color: var(--tx-0); box-shadow: none; }
.red-tabs { display: flex; gap: 4px; padding: 0 18px; background: var(--bg-2); border-bottom: 1px solid var(--line); overflow-x: auto; }
.red-tab { background: transparent; border: 0; border-bottom: 2px solid transparent; color: var(--tx-2); padding: 11px 16px; font-size: 13.5px; font-weight: 600; cursor: pointer; white-space: nowrap; margin-bottom: -1px; }
.red-tab:hover { color: var(--tx-0); }
.red-tab.active { color: var(--signal-ink); border-bottom-color: var(--signal); }
.red-body { overflow-y: auto; padding: 20px; }
.red-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: 14px; }
.red-card { background: linear-gradient(180deg, var(--bg-2), var(--bg-1)); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow); display: flex; flex-direction: column; padding: 0; overflow: hidden; transition: box-shadow .22s, border-color .22s; }
.red-card.sel { box-shadow: 0 0 0 2px var(--signal); border-color: var(--signal); }
.red-card .rc-head { padding: 13px 16px; display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; border-bottom: 1px solid var(--line); }
.red-card .rc-name { font-weight: 600; font-size: 15.5px; color: var(--tx-0); line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.red-card .rc-sub { font-size: 11.5px; color: var(--tx-2); margin-top: 1px; }
.red-card .rc-body { padding: 16px; display: flex; flex-direction: column; align-items: center; gap: 14px; flex: 1; justify-content: center; }
.red-card .rc-foot { padding: 10px 14px; border-top: 1px solid var(--line); background: var(--bg-1); display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.red-led { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; }
.red-led.on { background: var(--signal); box-shadow: 0 0 8px var(--signal); } .red-led.off { background: var(--tx-3); }
.red-badge { display: inline-flex; align-items: center; gap: 6px; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; padding: 3px 8px; border-radius: 100px; border: 1px solid var(--line-bright); color: var(--tx-1); font-variant-numeric: tabular-nums; }
.red-badge.ok { color: var(--signal-ink); border-color: rgba(20,184,166,.28); background: rgba(20,184,166,.10); }
.red-badge.warn { color: var(--amber-ink); border-color: rgba(245,158,66,.30); background: rgba(245,158,66,.12); }
.red-badge.bad { color: var(--danger-ink); border-color: rgba(240,98,109,.30); background: rgba(240,98,109,.10); }
.red-badge.mute { color: var(--tx-2); background: var(--bg-3); border-color: var(--line); }
.red-counters { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 6px; width: 100%; }
.red-counter { text-align: center; padding: 5px 2px; border-radius: 8px; cursor: default; }
.red-counter .n { font-size: 19px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; color: var(--tx-0); }
.red-counter .l { font-size: 9.5px; color: var(--tx-2); text-transform: uppercase; letter-spacing: .04em; margin-top: 3px; white-space: nowrap; }
.red-rings { display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; }
.red-ring { display: flex; flex-direction: column; align-items: center; gap: 7px; }
.red-ring .rr-wrap { position: relative; width: 80px; height: 80px; }
.red-ring .rr-val { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 800; color: var(--tx-0); }
.red-ring .rr-lbl { font-size: 11px; font-weight: 700; color: var(--tx-2); text-transform: uppercase; letter-spacing: .06em; }
.red-ring .rr-sub { font-size: 10.5px; color: var(--tx-2); }
.red-chips { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.red-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: 999px; background: var(--bg-3); color: var(--tx-1); font-size: 12px; font-weight: 700; white-space: nowrap; }
.red-toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.red-search { padding: 8px 11px; border-radius: var(--radius); border: 1px solid var(--line-bright); background: var(--bg-2); color: var(--tx-0); min-width: 240px; font-size: 13px; }
.red-h { font-size: 13px; font-weight: 700; color: var(--tx-2); margin: 14px 0 8px; text-transform: uppercase; letter-spacing: .04em; }
.red-table-wrap { overflow-x: auto; background: linear-gradient(180deg, var(--bg-2), var(--bg-1)); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow); }
.red-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.red-table th { text-align: left; font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--tx-2); font-weight: 500; padding: 11px 16px; border-bottom: 1px solid var(--line); cursor: pointer; white-space: nowrap; position: sticky; top: 0; background: var(--bg-2); }
.red-table td { padding: 12px 16px; border-bottom: 1px solid var(--line); color: var(--tx-1); white-space: nowrap; }
.red-table tr:last-child td { border-bottom: 0; }
.red-table tbody tr { transition: background .12s; }
.red-table tbody tr:hover { background: var(--bg-3); }
.red-table td.num { font-variant-numeric: tabular-nums; color: var(--tx-0); text-align: right; }
.red-table td.strong { color: var(--tx-0); font-weight: 600; }
.red-table tr.click { cursor: pointer; }
.red-empty { color: var(--tx-3); text-align: center; padding: 36px; font-size: 12px; letter-spacing: .08em; }
.red-detail { margin-top: 16px; }
.red-metric { border: 1px solid var(--line); border-radius: 10px; padding: 12px; background: var(--bg-2); margin-bottom: 10px; }
.red-metric-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.red-metric-head .t { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--tx-2); }
.red-metric-head .v { font-size: 18px; font-weight: 600; color: var(--tx-0); }
.red-axis { display: flex; gap: 6px; }
.red-axis-y { position: relative; width: 40px; flex: none; }
.red-axis-y span { position: absolute; right: 0; font-size: 9.5px; color: var(--tx-2); font-variant-numeric: tabular-nums; }
.red-chart { width: 100%; display: block; }
.red-axis-x { position: relative; height: 14px; margin-left: 46px; }
.red-axis-x span { position: absolute; font-size: 9.5px; color: var(--tx-2); font-variant-numeric: tabular-nums; }
.red-note { font-size: 12px; color: var(--tx-2); }
.red-btn-xs { background: var(--bg-3); border: 1px solid var(--line-bright); color: var(--tx-0); border-radius: 6px; padding: 4px 9px; font-size: 11.5px; font-weight: 600; cursor: pointer; }
.red-btn-xs:hover { border-color: var(--signal-dim); }
.red-btn-xs.range { background: transparent; border-color: var(--line); color: var(--tx-2); }
.red-btn-xs.range.on { background: var(--grad); color: #fff; border-color: transparent; }
@media (max-width: 760px) { #redFull .red-cards { grid-template-columns: 1fr; } }

/* ─────────────────────────────────────────────────────────────
   Área de trabajo del NODO — full-screen moderno (planta + conexiones)
   ───────────────────────────────────────────────────────────── */
.catalog-overlay.node-fs { padding: 0; align-items: stretch; justify-content: stretch; }
.node-fs .catalog-card { width: 100% !important; height: 100% !important; max-width: none; max-height: none; border-radius: 0; display: flex; flex-direction: column; }
.node-fs .catalog-header { padding: 12px 22px; border-bottom: 1px solid var(--line, #22314f); background: var(--panel, #0f1830); }
.node-fs .catalog-header h3 { font-size: 17px; font-weight: 700; }
.node-fs .catalog-body { flex: 1; min-height: 0; padding: 0 22px 18px; grid-template-rows: auto 1fr; }
.node-fs #nodeToolbar { padding-top: 12px; }
.node-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line, #22314f); margin-bottom: 14px; }
.node-tab { background: transparent; border: 0; border-bottom: 2px solid transparent; color: var(--muted, #8aa0c6); padding: 10px 18px; font-size: 14px; font-weight: 600; cursor: pointer; white-space: nowrap; margin-bottom: -1px; }
.node-tab:hover { color: var(--text, #e8eefc); }
.node-tab.active { color: var(--text, #e8eefc); border-bottom-color: #0ea5e9; }
/* Vista de conexiones dentro del nodo (mapeo físico embebido) */
.node-conx { display: grid; grid-template-columns: 340px 1fr; gap: 16px; height: 100%; min-height: 0; }
.node-conx-left { border-right: 1px solid var(--line, #22314f); padding-right: 14px; overflow-y: auto; }
.node-conx-main { overflow-y: auto; }
.node-conx .odf-node { border: 1px solid var(--line, #22314f); border-radius: 12px; padding: 12px 14px; background: var(--panel, #0f1830); }
@media (max-width: 820px) { .node-conx { grid-template-columns: 1fr; } }

/* Puertos de fibra en vivo de una OLT emparejada al módulo RED (LED parpadeante) */
.olt-live { margin: 8px 0 4px; padding: 8px 10px; border: 1px solid var(--line, #22314f); border-radius: 10px; background: rgba(14,165,233,.06); }
.olt-live-h { font-size: 11.5px; font-weight: 700; color: #38bdf8; margin-bottom: 6px; }
.pon-ports { display: flex; flex-wrap: wrap; gap: 8px; }
.pon-port { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text, #e8eefc); padding: 3px 8px; border-radius: 7px; background: var(--panel2, #0b1220); border: 1px solid var(--line, #22314f); }
.pon-port small { color: var(--muted, #8aa0c6); }
.pon-led { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: none; }
.pon-led.active { background: #22c55e; animation: ponBlink 1.15s ease-in-out infinite; }
.pon-led.idle { background: #475569; }
.pon-led.warn { background: #f59e0b; animation: ponBlink 1.15s ease-in-out infinite; }
@keyframes ponBlink { 0%,100% { opacity: 1; box-shadow: 0 0 4px currentColor; } 50% { opacity: .3; box-shadow: 0 0 9px currentColor; } }
.pon-led.active { color: #22c55e; } .pon-led.warn { color: #f59e0b; }

/* Tarjetas/puertos en vivo de la OLT integrada — estado por color (verde/naranja/rojo) */
.olt-legend { display: flex; gap: 14px; margin-bottom: 8px; font-size: 11px; color: var(--muted, #8aa0c6); flex-wrap: wrap; }
.olt-legend span { display: inline-flex; align-items: center; gap: 5px; }
.olt-card { margin-top: 8px; padding: 7px 9px; border: 1px solid var(--line, #22314f); border-radius: 9px; background: var(--panel, #0f1830); }
.olt-card-h { font-size: 12px; font-weight: 700; color: var(--text, #e8eefc); margin-bottom: 6px; }
.olt-card-h small { color: var(--muted, #8aa0c6); font-weight: 400; }
.pon-led.idle-orange { background: #f59e0b; color: #f59e0b; animation: ponBlink 1.6s ease-in-out infinite; }
.pon-led.fault { background: #ef4444; color: #ef4444; animation: ponBlink .7s ease-in-out infinite; }

/* Puerto conectable del nodo emparejado a un PON real: parpadea con su estado en vivo */
.port-chip.pon-live { animation: ponBlink 1.4s ease-in-out infinite; box-shadow: 0 0 0 1px rgba(255,255,255,.15) inset; }

/* Tarjeta (slot) de una OLT con su propia grilla de puertos */
.olt-slot { border: 1px solid var(--line, #22314f); border-radius: 8px; padding: 6px 8px; background: rgba(0,0,0,.10); }
.olt-slot-h { font-size: 11px; font-weight: 600; color: var(--muted, #8aa0c6); margin-bottom: 4px; }
.olt-slot-h small { font-weight: 400; }

/* ═══ Planta del nodo (layout 2D del cuarto, vista desde arriba) ═══════════ */
.fl-bar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 8px 0 4px; }
.fl-h { font-size: 10px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .3px; }
.fl-tool { font-size: 11px; padding: 3px 8px; border-radius: 7px; cursor: pointer; white-space: nowrap;
  color: var(--text); background: #ffffff0d; border: 1px solid var(--line); }
.fl-tool:hover { border-color: var(--line-strong); background: #ffffff1a; }
.fl-tool.on { border-color: #7c3aed; color: #a78bfa; background: #7c3aed22; }
.fl-tool.danger { border-color: #ef444466; color: #ef4444; }
.fl-tool small { color: var(--muted); }
.fl-kpis { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 10px; }
.fl-kpi { font-size: 10.5px; color: var(--muted); background: #ffffff0d; border: 1px solid var(--line);
  border-radius: 7px; padding: 3px 9px; }
.fl-kpi b { color: var(--text); font-size: 12px; }
/* Lienzo: cuadrícula en metros con reglas en los bordes. */
.fl-canvas-wrap { overflow: auto; padding: 22px 0 6px 26px; }
.fl-canvas { position: relative; background:
  linear-gradient(90deg, #ffffff0f 1px, transparent 1px) 0 0 / var(--g) var(--g),
  linear-gradient(#ffffff0f 1px, transparent 1px) 0 0 / var(--g) var(--g),
  linear-gradient(180deg, #10141a, #0b0e13);
  border: 2px solid var(--line-strong); border-radius: 4px; box-shadow: inset 0 0 40px #0009; }
.fl-svg { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
.fl-route { pointer-events: auto; cursor: pointer; }
.fl-route.sel polyline:nth-child(2) { stroke-width: 12; stroke-opacity: 1; }
/* Objetos del cuarto. */
.fl-item { position: absolute; box-sizing: border-box; cursor: grab; user-select: none;
  border: 1.5px solid var(--fc); border-radius: 4px; background: color-mix(in srgb, var(--fc) 22%, #0b0e13);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  overflow: hidden; transition: box-shadow .15s; }
.fl-item:hover { box-shadow: 0 0 0 2px #ffffff44; }
.fl-item.sel { box-shadow: 0 0 0 2px #fff, 0 0 12px var(--fc); }
.fl-item.dragging { cursor: grabbing; opacity: .85; box-shadow: 0 6px 18px #000a; }
.fl-ic { font-size: 15px; line-height: 1; }
.fl-lbl { font-size: 8.5px; font-weight: 700; color: #e6edf3; text-align: center; line-height: 1.05;
  padding: 0 2px; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.fl-badge { font-size: 7.5px; color: var(--muted); }
/* Reglas (metros). */
.fl-ruler-x, .fl-ruler-y { position: absolute; color: var(--muted); font-size: 8px; }
.fl-ruler-x { top: -16px; left: 0; right: 0; height: 14px; }
.fl-ruler-y { left: -20px; top: 0; bottom: 0; width: 18px; }
.fl-ruler-x span { position: absolute; transform: translateX(-50%); }
.fl-ruler-y span { position: absolute; transform: translateY(-50%); right: 2px; }
.fl-selbar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 6px;
  padding: 5px 9px; border: 1px solid var(--line-strong); border-radius: 8px; background: #12161c; font-size: 11.5px; }
.fl-help { color: var(--muted); font-size: 11.5px; margin-top: 8px; }

/* ═══ Servicios / circuitos y análisis de impacto ═════════════════════════ */
.cir-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center;
  border-left: 3px solid var(--cc); background: #ffffff08; border-radius: 0 8px 8px 0;
  padding: 6px 10px; margin-bottom: 6px; }
.cir-main { font-size: 13px; grid-column: 1; grid-row: 1; }
.cir-meta { font-size: 11px; color: var(--muted); grid-column: 1; grid-row: 2; }
.cir-acts { grid-column: 2; grid-row: 1 / span 2; display: flex; gap: 4px; align-items: center; }
.cir-code { font-size: 10.5px; color: var(--muted); font-family: ui-monospace, monospace; }
.cir-badge { font-size: 9.5px; text-transform: uppercase; letter-spacing: .3px; color: #22c55e;
  border: 1px solid #22c55e66; border-radius: 5px; padding: 0 5px; }
.cir-badge.off { color: #f59e0b; border-color: #f59e0b66; }
.cir-detail { border: 1px solid var(--line-strong); border-radius: 10px; padding: 9px 12px; margin-top: 10px; background: #12161c; }
.cir-detail-h { font-size: 13px; font-weight: 700; margin-bottom: 6px; }
.cir-detail-h small { color: var(--muted); font-weight: 400; }
.cir-step { display: flex; align-items: center; gap: 6px; font-size: 11.5px; padding: 2px 0; }
.cir-step-n { width: 16px; height: 16px; border-radius: 50%; background: #7c3aed33; color: #a78bfa;
  font-size: 9px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
/* Modal de impacto. */
.imp-head { padding: 4px 2px 10px; border-bottom: 1px solid var(--line); margin-bottom: 10px; }
.imp-title { font-size: 13px; color: var(--muted); }
.imp-headline { font-size: 17px; font-weight: 800; color: #f59e0b; margin-top: 3px; line-height: 1.25; }
.imp-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.imp-chip { font-size: 10.5px; color: var(--muted); border: 1px solid var(--ic); border-radius: 7px;
  padding: 2px 8px; background: color-mix(in srgb, var(--ic) 12%, transparent); }
.imp-chip b { color: var(--text); font-size: 12.5px; }
.imp-sec { margin-top: 14px; }
.imp-sec h4 { font-size: 12px; margin: 0 0 6px; color: var(--text); }
.imp-row { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 3px 8px;
  border-left: 3px solid var(--cc); background: #ffffff08; border-radius: 0 6px 6px 0; margin-bottom: 3px; }
.imp-row small { color: var(--muted); font-size: 10.5px; }
.pwr-mini { font-size: 9px; padding: 0 3px; cursor: pointer; color: var(--muted);
  background: transparent; border: 1px solid var(--line); border-radius: 4px; }
.pwr-mini:hover { color: #f59e0b; border-color: #f59e0b66; }
/* Bastidor enfocado al llegar desde la planta del nodo. */
.rack-focus { animation: rackFocus 2.2s ease-out 1; }
@keyframes rackFocus { 0%, 60% { box-shadow: 0 0 0 3px #7c3aed, inset 0 0 22px #0009; } 100% { box-shadow: inset 0 0 22px #0009; } }

/* ═══ Disipación térmica y etiquetas QR ══════════════════════════════════ */
.thm-box { border: 1px solid var(--line-strong); border-radius: 10px; padding: 8px 11px;
  background: linear-gradient(180deg, #12161c, #0c0f14); display: flex; flex-direction: column;
  gap: 6px; margin: 4px 0 10px; }
.thm-msg { font-size: 12px; font-weight: 600; line-height: 1.3; }
/* Hoja de etiquetas QR (pensada para imprimir y recortar). */
.qr-help { font-size: 11.5px; color: var(--muted); margin-bottom: 10px; line-height: 1.4; }
.qr-sheet { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 10px; }
.qr-card { display: flex; gap: 9px; align-items: center; border: 1px dashed var(--line-strong);
  border-radius: 8px; padding: 8px; background: #fff; }
.qr-code { width: 84px; height: 84px; flex: 0 0 auto; }
.qr-code svg { width: 100%; height: 100%; display: block; }
.qr-info { min-width: 0; color: #111; }
.qr-kind { font-size: 9px; text-transform: uppercase; letter-spacing: .5px; color: #666; }
.qr-code-txt { font-size: 15px; font-weight: 800; line-height: 1.1; word-break: break-word; }
.qr-site { font-size: 9.5px; color: #444; margin-top: 2px; }
/* Aviso de "escaneaste esto" al entrar por QR. */
.scan-toast { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 9999;
  background: #12161c; border: 1px solid #7c3aed; color: var(--text); font-size: 13px;
  padding: 8px 14px; border-radius: 999px; box-shadow: 0 8px 24px #000a; }
@media print {
  body * { visibility: hidden; }
  #qrModal, #qrModal * { visibility: visible; }
  #qrModal { position: absolute; inset: 0; background: #fff; }
  #qrModal .catalog-card { width: 100%; max-height: none; border: none; box-shadow: none; }
  #qrModal .catalog-header, .no-print { display: none !important; }
  .qr-sheet { grid-template-columns: repeat(3, 1fr); }
  .qr-card { break-inside: avoid; }
}
/* Desglose de la carga térmica (una fila por fuente de calor). */
.thm-rows { display: flex; flex-direction: column; gap: 2px; margin-top: 4px;
  border-top: 1px solid #ffffff14; padding-top: 6px; }
.thm-row { display: grid; grid-template-columns: 1.4fr 2fr auto auto; gap: 8px; align-items: center; font-size: 11px; }
.thm-row.sub { opacity: .7; font-size: 10px; }
.thm-row.total { border-top: 1px solid #ffffff1a; padding-top: 4px; margin-top: 2px; font-weight: 700; color: #f59e0b; }
.thm-lbl { color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.thm-row.total .thm-lbl { color: #f59e0b; }
.thm-bar { height: 7px; border-radius: 4px; background: #ffffff10; overflow: hidden; }
.thm-bar i { display: block; height: 100%; border-radius: 4px; }
.thm-val { text-align: right; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.thm-w { text-align: right; color: var(--muted); font-size: 10px; min-width: 52px; font-variant-numeric: tabular-nums; }
/* Interruptor de cada aire acondicionado en el panel térmico. */
.hvac { display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; color: var(--muted);
  background: #ffffff0d; border: 1px solid var(--line); border-radius: 7px; padding: 2px 8px 2px 3px; }
.hvac b { color: var(--text); font-size: 11.5px; }
.hvac.on { border-color: #38bdf866; }
.hvac.off { opacity: .55; }
.hvac.off b { color: var(--muted); text-decoration: line-through; }
.hvac-sw { width: 18px; height: 18px; border-radius: 50%; cursor: pointer; font-size: 10px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; }
.hvac.on .hvac-sw { color: #0b0e13; background: #38bdf8; border: 1px solid #38bdf8; }
.hvac.off .hvac-sw { color: var(--muted); background: transparent; border: 1px solid var(--line-strong); }
.hvac-sw:hover { outline: 2px solid #ffffff44; }
.hvac-plan { font-size: 11.5px; color: #38bdf8; background: #38bdf814; border: 1px solid #38bdf833;
  border-radius: 7px; padding: 4px 9px; }
.hvac-plan.bad { color: #ef4444; background: #ef444414; border-color: #ef444440; }
.hvac-plan small { color: var(--muted); }
/* Objeto apagado en el plano. */
.fl-item.off { opacity: .45; border-style: dashed; }

/* ═══ Planta del nodo · rediseño: el plano manda, los datos acompañan ═════ */
.fl-top { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 8px; }
.fl-top .fl-tool.warn { border-color: #f59e0b66; color: #f59e0b; }
.fl-mode { font-size: 11px; color: #ffd166; background: #ffd16614; border: 1px solid #ffd16640;
  border-radius: 7px; padding: 2px 9px; }
.fl-zoom { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; font-size: 11px; color: var(--muted); }
.fl-zoom b { min-width: 34px; text-align: center; color: var(--text); }
/* Menú desplegable de la barra. */
.fl-menu { position: fixed; z-index: 10000; min-width: 210px; padding: 4px; border-radius: 9px;
  background: #12161c; border: 1px solid var(--line-strong); box-shadow: 0 10px 30px #000b;
  display: flex; flex-direction: column; gap: 1px; }
.fl-menu button { text-align: left; font-size: 12px; padding: 5px 9px; border-radius: 6px;
  background: transparent; border: none; color: var(--text); cursor: pointer; }
.fl-menu button:hover { background: #ffffff12; }
.fl-menu button small { color: var(--muted); }
.fl-menu-empty { font-size: 11px; color: var(--muted); padding: 6px 9px; }
/* Dos columnas: lienzo grande + panel lateral compacto. */
.fl-layout { display: grid; grid-template-columns: minmax(0, 1fr) 264px; gap: 14px; align-items: start; }
@media (max-width: 1100px) { .fl-layout { grid-template-columns: 1fr; } }
.fl-side { display: flex; flex-direction: column; gap: 10px; position: sticky; top: 0; }
.fl-card { border: 1px solid var(--line-strong); border-radius: 10px; padding: 8px 10px;
  background: linear-gradient(180deg, #12161c, #0c0f14); }
.fl-card-h { font-size: 10px; font-weight: 700; color: var(--muted); text-transform: uppercase;
  letter-spacing: .3px; margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
.fl-stat { display: flex; justify-content: space-between; gap: 8px; font-size: 11px; color: var(--muted); padding: 1px 0; }
.fl-stat b { color: var(--text); font-variant-numeric: tabular-nums; }
.fl-bar-mini { height: 5px; border-radius: 3px; background: #ffffff12; overflow: hidden; margin-top: 5px; }
.fl-bar-mini i { display: block; height: 100%; background: #7c3aed; border-radius: 3px; }
.fl-legend .fl-leg { display: flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--muted); padding: 1px 0; }
.fl-leg i { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.fl-leg i.line { height: 3px; border-radius: 2px; }
/* Tarjeta térmica compacta del panel. */
.thm-card .thm-head { display: flex; align-items: center; gap: 9px; margin-bottom: 5px; }
.thm-pct { font-size: 22px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.thm-headv { font-size: 12px; color: var(--muted); line-height: 1.25; }
.thm-headv b { color: var(--text); font-size: 14px; }
.thm-headv small { display: block; font-size: 9.5px; }
.thm-msg-sm { font-size: 10.5px; line-height: 1.3; margin: 5px 0; }
.thm-mini { margin-left: auto; font-size: 10px; padding: 0 5px; cursor: pointer; color: var(--muted);
  background: transparent; border: 1px solid var(--line); border-radius: 5px; }
.thm-mini:hover { color: var(--text); border-color: var(--line-strong); }
.hvac-list { display: flex; flex-direction: column; gap: 3px; margin: 5px 0; }
.hvac-list .hvac { width: 100%; justify-content: flex-start; }
.hvac-lbl { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thm-acts { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 6px; }
.thm-acts .fl-tool { font-size: 10.5px; padding: 2px 7px; }
/* El lienzo, con su propio scroll y más aire. */
.fl-canvas-wrap { overflow: auto; padding: 22px 8px 8px 26px; max-height: calc(100vh - 250px);
  border: 1px solid var(--line); border-radius: 10px; background: #0a0d12; }
/* Capacidad del cuarto: espacio vs clima, y el límite que manda. */
.fl-cap { margin-top: 6px; padding: 5px 8px; border-radius: 7px; font-size: 11px; color: var(--muted);
  background: #ffffff08; border: 1px solid var(--line); }
.fl-cap b { color: var(--text); font-size: 15px; margin-right: 3px; }
.fl-cap small { display: block; font-size: 9.5px; opacity: .8; line-height: 1.25; margin-top: 1px; }
.fl-cap.lim { border-color: #7c3aed66; background: #7c3aed14; }
.fl-cap.lim b { color: #a78bfa; }
.fl-cap.bad { border-color: #ef444466; background: #ef444414; }
.fl-cap.bad b { color: #ef4444; }
/* Huecos donde entra otro rack (vista fantasma sobre el plano). */
.fl-ghost { position: absolute; box-sizing: border-box; border: 1.5px dashed #7c3aed99;
  background: #7c3aed1f; border-radius: 4px; display: flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: 700; color: #a78bfa; pointer-events: none; }
/* Objeto que se está arrastrando a un lugar ocupado. */
.fl-item.bad { border-color: #ef4444 !important; background: #ef44443a !important;
  box-shadow: 0 0 0 2px #ef4444, 0 6px 18px #000a; }

/* ═══ Render cartográfico de fibra: selector de modo y leyenda ════════════ */
.fmode-bar { display: flex; flex-wrap: wrap; gap: 3px; margin: 4px 0 4px 22px; }
.fmode-bar button { font-size: 10px; padding: 2px 6px; border-radius: 6px; cursor: pointer;
  color: var(--muted); background: transparent; border: 1px solid var(--line); }
.fmode-bar button:hover { color: var(--text); border-color: var(--line-strong); }
.fmode-bar button.on { color: #a78bfa; border-color: #7c3aed; background: #7c3aed22; }
.fmode-legend { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 6px 22px; }
.fmode-legend .fl-leg { display: inline-flex; align-items: center; gap: 4px; font-size: 9.5px; color: var(--muted); }
.fmode-legend .fl-leg i { width: 14px; height: 4px; border-radius: 2px; display: inline-block; }
/* Badge de puertos ocupados/totales sobre la caja NAP. */
.pin-wrap { position: relative; display: inline-block; }
.nap-badge { position: absolute; top: -6px; right: -12px; font-size: 8.5px; font-weight: 800;
  color: #fff; padding: 0 3px; border-radius: 6px; border: 1px solid #0009; line-height: 1.35;
  text-shadow: 0 1px 1px #0008; white-space: nowrap; }
/* Corte transversal de la manguera (TIA-598-C). */
.tia-wrap { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.tia-svg { width: 280px; height: 280px; flex: 0 0 auto; }
.tia-side { flex: 1 1 200px; min-width: 190px; }
.tia-tubes { display: flex; flex-direction: column; gap: 6px; margin-top: 10px;
  max-height: 34vh; overflow: auto; }
.tia-tube { border: 1px solid var(--line); border-radius: 8px; padding: 5px 8px; background: #ffffff08; }
.tia-tube-h { display: flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 700; }
.tia-tube-h i { width: 11px; height: 11px; border-radius: 50%; border: 1px solid #0006; }
.tia-tube-h small { color: var(--muted); font-weight: 400; }
.tia-fibs { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 4px; }
.tia-fib { display: inline-flex; align-items: center; gap: 3px; font-size: 9.5px; color: var(--muted);
  border: 1px solid var(--line); border-radius: 5px; padding: 1px 4px; }
.tia-fib.used { border-color: #22c55e; color: #22c55e; }
.tia-fib i { width: 9px; height: 9px; border-radius: 50%; border: 1px solid #0006; }

/* ═══════════════════════════════════════════════════════════════════════════
   COMMAND CENTER — piel "Dark Sci-Fi Engineering" para el mapa GIS
   Fondo técnico slate-950 con rejilla, paneles glassmorphic y datos en mono.
   Solo aplica al tema oscuro: el claro (body.light) queda intacto.
   ═══════════════════════════════════════════════════════════════════════════ */
body:not(.light) {
  --cc-bg: #090d16;               /* slate-950 */
  --cc-line: #1e293b;             /* slate-800 */
  --cc-glass: rgba(15, 23, 42, .92);
  --cc-trunk: #a855f7;            /* dorsal   */
  --cc-dist: #3b82f6;             /* distribución */
  --cc-drop: #10b981;             /* acometida */
  --cc-alert: #ef4444;
  font-family: 'Inter', 'Hanken Grotesk', system-ui, sans-serif;
}
/* Rejilla técnica del lienzo: puntos de referencia + retícula de 32 y 64 px. */
/* Fondo del lienzo = gris pizarra, IGUAL en los dos motores.
   Antes era casi negro (slate-950) y, mientras los tiles nuevos cargaban en un
   zoom, ese negro asomaba y parecía que el mapa se trababa. Un gris medio hace
   la transición invisible: es el hueco de siempre, pero ya no salta a la vista. */
body:not(.light) #map,
body:not(.light) .leaflet-container,
body:not(.light) .maplibregl-map {
  background-color: #575d66;   /* valor inicial; setBase lo ajusta al mapa activo */
  background-image:
    radial-gradient(1400px 700px at 50% -20%, #10203c 0%, transparent 70%),
    linear-gradient(rgba(148, 163, 184, .045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 163, 184, .045) 1px, transparent 1px),
    linear-gradient(rgba(148, 163, 184, .028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 163, 184, .028) 1px, transparent 1px);
  background-size: 100% 100%, 64px 64px, 64px 64px, 32px 32px, 32px 32px;
}
/* Paneles flotantes: vidrio ahumado, borde fino y sombra profunda. */
body:not(.light) .sidebar,
body:not(.light) .topbar,
body:not(.light) .app-menu,
body:not(.light) .catalog-card,
body:not(.light) .info-sheet,
body:not(.light) .trace-panel,
body:not(.light) .fl-card,
body:not(.light) .fl-menu,
body:not(.light) .leaflet-popup-content-wrapper {
  background: var(--cc-glass) !important;
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-color: var(--cc-line) !important;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .75);
}
/* Datos técnicos (longitudes, coordenadas, dB/km) siempre en monoespaciada. */
body:not(.light) .mono, body:not(.light) .kpi-val, body:not(.light) .thm-val,
body:not(.light) .thm-w, body:not(.light) .fl-stat b, body:not(.light) .fl-cap b,
body:not(.light) .cir-code, body:not(.light) .imp-chip b, body:not(.light) .pwr-kpi b,
body:not(.light) .coord, body:not(.light) .tia-fib {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}

/* ── Fibra: jerarquía con resplandor y flujo de datos ───────────────────── */
/* El renderer SVG de Leaflet permite animar el trazo con CSS puro. */
/* El resplandor se reserva al cable SELECCIONADO: aplicarlo a todos exigía un
   <path> por cable (DOM pesado) y dejaba el mapa lento. */
path.fo-halo { filter: drop-shadow(0 0 8px currentColor); }
/* Tráfico activo: guiones claros corriendo por encima del cable. */
@keyframes flowSignal {
  0%   { stroke-dashoffset: 40; }
  100% { stroke-dashoffset: 0; }
}
path.fo-flow, .animated-fiber-signal {
  stroke-dasharray: 8, 8;
  animation: flowSignal 1.5s linear infinite;
}
/* Cable cortado o en avería: rojo latiendo. */
@keyframes pulseAlert {
  0%, 100% { opacity: 1; filter: drop-shadow(0 0 4px rgba(239, 68, 68, .9)); }
  50%      { opacity: .45; filter: drop-shadow(0 0 14px rgba(239, 68, 68, 1)); }
}
path.fo-alert { animation: pulseAlert 1.4s ease-in-out infinite; }

/* ── Marcador de falla / corte OTDR: neón + onda expansiva ─────────────── */
.fault-mark { position: relative; width: 30px; height: 30px; display: grid; place-items: center; }
.fault-mark .fx { position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--cc-alert); animation: faultWave 1.8s ease-out infinite; }
.fault-mark .fx:nth-child(2) { animation-delay: .6s; }
.fault-mark .fx:nth-child(3) { animation-delay: 1.2s; }
@keyframes faultWave {
  0%   { transform: scale(.35); opacity: .95; }
  100% { transform: scale(2.1);  opacity: 0; }
}
.fault-mark .ic { position: relative; font-size: 15px; line-height: 1;
  filter: drop-shadow(0 0 6px rgba(239, 68, 68, .95)); }

/* ── Simbología de nodos ───────────────────────────────────────────────── */
.sym { display: grid; place-items: center; }
/* Central / shelter: cuadro slate con borde azul y techo. */
.sym-shelter { width: 26px; height: 24px; }
/* Caja de empalme: círculos concéntricos púrpura. */
.sym-splice { width: 24px; height: 24px; }
/* NAP: caja verde compacta con borde blanco y badge de puertos. */
.sym-nap { position: relative; width: 22px; height: 18px; border-radius: 4px;
  background: linear-gradient(180deg, #10b981, #047857); border: 1.5px solid #fff;
  box-shadow: 0 0 8px rgba(16, 185, 129, .6), 0 2px 6px #000a;
  display: grid; place-items: center; font-size: 9px; color: #04231a; font-weight: 800; }
/* Cliente: círculo ámbar. */
.sym-client { width: 12px; height: 12px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fcd34d, #f59e0b);
  border: 1.5px solid #fff8; box-shadow: 0 0 7px rgba(245, 158, 11, .75); }

/* El basemap oscuro se funde con la paleta: menos saturación, más contraste. */
/* NO se filtran los tiles: un filter CSS por imagen obliga al navegador a
   recomponer cada tile en cada fotograma del zoom y tiraba la fluidez a la
   mitad. El basemap oscuro ya viene oscuro, y el satélite se lee mejor sin
   retocar. Si hace falta atenuar, se hace con una capa de color encima. */
/* Retícula técnica sobre el mapa (no intercepta el ratón). */
body:not(.light) .cc-grid {
  position: absolute; inset: 0; z-index: 350; pointer-events: none;
  background-image:
    radial-gradient(1200px 600px at 50% 0%, rgba(56, 189, 248, .07), transparent 65%),
    linear-gradient(rgba(148, 163, 184, .055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 163, 184, .055) 1px, transparent 1px),
    linear-gradient(rgba(148, 163, 184, .03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 163, 184, .03) 1px, transparent 1px);
  background-size: 100% 100%, 64px 64px, 64px 64px, 32px 32px, 32px 32px;
}
/* Aviso de capa recortada (demasiadas features para el zoom actual). */
.cut-warn { position: absolute; left: 50%; transform: translateX(-50%); bottom: 34px; z-index: 900;
  display: flex; gap: 10px; align-items: center; pointer-events: none;
  background: rgba(15,23,42,.94); border: 1px solid #f59e0b66; color: #f59e0b;
  font-size: 11.5px; padding: 4px 12px; border-radius: 999px; backdrop-filter: blur(8px);
  box-shadow: 0 8px 24px #0009; }
.cut-warn small { color: var(--muted); }
/* Burbuja de agrupación (vista alejada): cantidad de elementos por celda. */
.cl-bub { display: grid; place-items: center; border-radius: 50%; position: relative;
  background: color-mix(in srgb, var(--cb) 22%, rgba(11,14,19,.82));
  border: 2px solid var(--cb); color: #e6edf3; font-weight: 800;
  font-size: 10.5px; font-family: 'JetBrains Mono', ui-monospace, monospace;
  box-shadow: 0 0 12px color-mix(in srgb, var(--cb) 55%, transparent), 0 2px 8px #0009;
  cursor: pointer; }
.cl-bub i { position: absolute; top: -5px; right: -7px; font-style: normal; font-size: 8px;
  background: #ef4444; color: #fff; border-radius: 8px; padding: 0 3px; border: 1px solid #0008; }

/* Mientras el mapa se mueve, se apaga todo lo que no aporta durante el gesto:
   la retícula técnica (5 degradados a pantalla completa) y los marcadores DOM
   (cientos de <div> que Leaflet reposiciona en cada fotograma). Vuelven al
   soltar. Es lo que separa un zoom fluido de uno a tirones. */
.map-moviendo .cc-grid { display: none; }
.map-moviendo .leaflet-marker-pane,
.map-moviendo .cut-warn { visibility: hidden; }
.leaflet-marker-pane { will-change: transform; }
/* Modo rendimiento: se quitan las sombras y resplandores, que son lo más caro
   de recomponer cuando el mapa se mueve. Los colores y las formas se mantienen. */
body.perf-on .cc-grid { display: none; }
body.perf-on .cl-bub { box-shadow: none; }
body.perf-on .sym-nap, body.perf-on .sym-client { box-shadow: none; }
body.perf-on .nap-badge { text-shadow: none; }
body.perf-on path.fo-halo { filter: none; }
#perfBtn.on { color: #22c55e; }

/* ═══════════════════════════════════════════════════════════════════════════
   ACCESIBILIDAD
   Lo que hace falta para usar el mapa sin ratón y sin ver la pantalla.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Saltar la barra superior (~20 controles) e ir directo al mapa. Sólo se ve
   cuando recibe el foco, así que no altera el diseño para quien usa ratón. */
.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 10000;
  padding: .55rem .9rem;
  border-radius: 8px;
  background: var(--accent, #1f6feb);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  transition: top .15s ease;
}
.skip-link:focus { top: 8px; }

/* Foco visible en TODO lo que se puede enfocar. Antes sólo lo tenían algunos
   controles sueltos, así que al tabular se perdía de vista dónde estabas.
   :focus-visible no lo muestra al hacer clic con el ratón, sólo con teclado. */
:focus-visible {
  outline: 2px solid var(--accent, #1f6feb);
  outline-offset: 2px;
  border-radius: 4px;
}
/* Sobre superficies oscuras el azul se pierde: se refuerza con un halo. */
.modal-overlay :focus-visible,
.catalog-overlay :focus-visible,
.sidebar :focus-visible {
  box-shadow: 0 0 0 4px rgb(31 111 235 / .35);
}

/* Texto sólo para lectores de pantalla (contexto que no debe ocupar espacio). */
.solo-lectores {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Quien haya pedido menos animación en su sistema no debería recibir las
   transiciones del mapa ni de los paneles: en algunas personas provocan mareo. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   DENTRO DE LA APLICACIÓN DE ANDROID
   La misma web, pero sin lo que sólo tiene sentido con teclado y pantalla
   grande. En un teléfono la barra superior llevaba dieciséis controles y se
   desbordaba casi setecientos píxeles: había que arrastrarla para llegar a la
   mitad de las cosas, y eso es exactamente lo que hace que una aplicación
   parezca «una página web metida en una ventana».
   Se activa sola: la app se identifica y app.js marca el <body>.
   ══════════════════════════════════════════════════════════════════════════ */
body.en-app .topbar {
  gap: 8px !important;
  padding: 0 10px;
  overflow: visible;            /* ya no hace falta arrastrar: nada se sale */
}

/* Lo que se va: el reloj (el teléfono ya tiene uno), deshacer/rehacer (la
   edición fina no se hace en la calle), el tutorial, el medidor de
   rendimiento, el cambio de tema y el selector de zona/vista/proyecto — todo
   eso vive en ☰ Menú, que sigue a un toque. */
body.en-app #topClock,
body.en-app #btnTutorial,
body.en-app #perfBtn,
body.en-app #themeToggle,
body.en-app #undoBtn,
body.en-app #redoBtn,
body.en-app #manageProjects,
body.en-app #projectSelect,
body.en-app #zoneSelect,
body.en-app #viewSelect { display: none !important; }

/* El buscador toma el espacio que dejaron: es lo que más se usa en campo. */
body.en-app .topbar > div:has(#searchInput),
body.en-app .search-wrap { flex: 1 1 auto; min-width: 0; }

/* Objetivos táctiles de verdad: con guantes, 44 px es el mínimo que se acierta. */
body.en-app .topbar button,
body.en-app .sidebar button { min-height: 40px; min-width: 40px; }

/* El enlace a la Suite sobra: dentro de la aplicación de DAS FIBER, mandar al
   panel de aplicaciones es sacar al usuario de donde quiso entrar. */
body.en-app #suite-link,
body.en-app .nav-foot .suite { display: none !important; }

/* Las ventanas emergentes ocupan la pantalla, como en una aplicación, en vez
   de quedar como un panel flotante pensado para un monitor. */
body.en-app .trace-panel,
body.en-app #traceModal .modal-box {
  max-width: 100% !important;
  width: 100% !important;
  border-radius: 14px 14px 0 0;
}
