/* ============================================================
   CAPA DE DISEÑO — SystemTrack
   ============================================================
   Va DESPUÉS del <style> de index.html y solo reescribe la piel: espaciados,
   tipografía, bordes y color. No cambia ninguna estructura ni ningún
   identificador, así que todo el JavaScript que ya funcionaba sigue igual.

   El lenguaje visual sale de Tremor Blocks y shadcn/ui, que es lo que usan hoy
   los productos serios de tablero. Tres reglas lo gobiernan todo:

     1. El color SIGNIFICA algo. Verde reportando, ámbar sin reportar, rojo
        vencido. Nada de color decorativo: si todo resalta, no resalta nada.
     2. La jerarquía se hace con TAMAÑO y PESO, no con cajas y sombras.
     3. Los números van tabulares. En una columna de kilómetros, con dígitos de
        ancho variable las cifras bailan y no se pueden comparar de un vistazo.

   Los tokens de tema (--bg, --card, --line...) ya existían y se respetan; aquí
   solo se añaden los que faltaban.
   ============================================================ */

/* ---------- 1. TOKENS QUE FALTABAN ---------- */
:root, html[data-tema="oscuro"] {
  /* Escala de espaciado de 4 px. Tener una escala y no números sueltos es lo
     que hace que una pantalla se vea ordenada aunque nadie sepa explicar por qué. */
  --e1: 4px;  --e2: 8px;  --e3: 12px; --e4: 16px; --e5: 24px; --e6: 32px;
  --r-sm: 6px; --r: 10px; --r-lg: 14px; --r-full: 999px;

  /* Superficies: una sola sombra, muy suave. La sombra se reserva para lo que
     de verdad flota (menús, diálogos), no para cada tarjeta. */
  --elev: 0 1px 2px rgba(0,0,0,.28);
  --elev-alta: 0 12px 32px -8px rgba(0,0,0,.55);
  --anillo: rgba(56,189,248,.35);

  /* Estado. Un solo tono por significado, con su fondo tenue para las píldoras. */
  --ok: #22c55e;   --ok-suave: rgba(34,197,94,.14);
  --avi: #f59e0b;  --avi-suave: rgba(245,158,11,.14);
  --mal: #ef4444;  --mal-suave: rgba(239,68,68,.14);
  --info: #38bdf8; --info-suave: rgba(56,189,248,.14);
  --neutro-suave: rgba(148,163,184,.14);
}
html[data-tema="claro"] {
  --elev: 0 1px 2px rgba(15,23,42,.06), 0 1px 3px rgba(15,23,42,.05);
  --elev-alta: 0 12px 32px -8px rgba(15,23,42,.18);
  --anillo: rgba(2,132,199,.28);
  --ok: #16a34a;   --ok-suave: rgba(22,163,74,.10);
  --avi: #d97706;  --avi-suave: rgba(217,119,6,.10);
  --mal: #dc2626;  --mal-suave: rgba(220,38,38,.10);
  --info: #0284c7; --info-suave: rgba(2,132,199,.10);
  --neutro-suave: rgba(100,116,139,.10);
}

/* ---------- 2. BASE ---------- */
body {
  /* Inter si el sistema la tiene; si no, la del sistema. Sin descargar fuentes:
     una fuente web bloquea el primer pintado y aquí se abre la plataforma
     cincuenta veces al día. */
  font-family: Inter, "Segoe UI Variable", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "cv11", "ss01";
}
/* Todo lo que sea una cifra se alinea en columna. */
table td, table th, .kpi-num, .mono, code, input[type=number] { font-variant-numeric: tabular-nums; }

/* ---------- 3. TARJETAS ---------- */
/* Borde en vez de sombra: es lo que separa una tarjeta de 2016 de una de hoy. */
.card, .form {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--e4);
  box-shadow: var(--elev);
}
.card { margin-bottom: var(--e3); }
.card h2 {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--txt);            /* antes iba en color de acento: envejecía la pantalla */
  margin: 0 0 var(--e3);
  align-items: center;
  gap: var(--e2);
}
.card h2 > span:first-child { display: flex; align-items: center; gap: var(--e2); }
.form h3 {
  font-size: 13px; font-weight: 600; letter-spacing: -.01em;
  margin: 0 0 var(--e3); color: var(--txt);
}

/* Separador de sección: fino, en mayúsculas, sin gritar. */
.dsec {
  font-size: 10px; font-weight: 600; letter-spacing: .06em;
  color: var(--mut); text-transform: uppercase;
  margin: var(--e4) 0 var(--e2); padding-top: var(--e3);
  border-top: 1px solid var(--line);
}

/* ---------- 4. TABLAS ---------- */
table { font-size: 13px; }
th {
  font-size: 10.5px; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--mut);
  padding: var(--e2) var(--e3);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
td {
  padding: 10px var(--e3);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
tbody tr { transition: background .12s ease; }
tbody tr:hover td { background: var(--fila-hover); }
tbody tr:last-child td { border-bottom: 0; }

/* ---------- 5. BOTONES ---------- */
/* Tres niveles y no más. Si todo es un botón azul, nada es la acción principal. */
/* Solo el botón de enviar, no CUALQUIER botón del formulario. La regla sin
   excepciones pintaba de azul las cabeceras de sección (`.acc-head`), que son
   grises por diseño; al pasar el ratón recuperaban su gris y el texto blanco
   que les había puesto yo desaparecía. */
.form button:not(.acc-head):not(.btn-sm):not([style*="background"]), button.primario {
  background: var(--acc2); color: #fff;
  border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 9px var(--e4); font-size: 13px; font-weight: 500;
  transition: filter .12s ease, transform .04s ease;
}
.form button:not(.acc-head):not(.btn-sm):hover, button.primario:hover { filter: brightness(1.08); }
.form button:not(.acc-head):not(.btn-sm):active, button.primario:active { transform: translateY(.5px); }
.form button:disabled { opacity: .55; cursor: not-allowed; filter: none; }
/* La cabecera de sección conserva su gris propio y su texto normal. */
.form .acc-head { background: var(--btn) !important; color: var(--txt) !important; }
.form .acc-head:hover { background: var(--hover) !important; filter: none; }

.btn-sm {
  background: transparent; color: var(--txt);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 5px 10px; font-size: 12px; font-weight: 500;
  transition: background .12s ease, border-color .12s ease;
}
/* OJO con el hover: hay botones que traen su propio color de fondo escrito en
   el atributo style (las cabeceras azules de sección, Editar, Borrar). Si el
   hover les cambia el fondo a gris claro, el texto blanco queda sobre un fondo
   claro y desaparece. A esos se les aclara el color que ya tienen; el gris solo
   se le pone a los que no traen fondo propio. */
.btn-sm:not([style*="background"]):hover { background: var(--hover); }
.btn-sm[style*="background"]:hover { filter: brightness(1.08); }
.btn-sm:disabled { opacity: .5; cursor: not-allowed; filter: none; }

/* ---------- 6. CAMPOS ---------- */
.winp, .form input, .form select, .form textarea {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 8px 10px; font-size: 13px;
  background: var(--campo); color: var(--txt);
  transition: border-color .12s ease, box-shadow .12s ease;
}
.winp:focus, .form input:focus, .form select:focus, .form textarea:focus {
  outline: 0; border-color: var(--acc2);
  box-shadow: 0 0 0 3px var(--anillo);     /* el anillo de foco es accesibilidad, no adorno */
}
.winp::placeholder, .form input::placeholder, .form textarea::placeholder { color: var(--mut); opacity: .75; }
.form label { font-size: 11.5px; font-weight: 500; color: var(--mut); margin: var(--e3) 0 var(--e1); }

/* ---------- 7. PÍLDORAS DE ESTADO ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--r-full);
  font-size: 11px; font-weight: 500; line-height: 18px;
  background: var(--neutro-suave); color: var(--mut);
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.pill.ok  { background: var(--ok-suave);  color: var(--ok); }
.pill.avi { background: var(--avi-suave); color: var(--avi); }
.pill.mal { background: var(--mal-suave); color: var(--mal); }
.pill.info{ background: var(--info-suave);color: var(--info); }
.pill.plano::before { display: none; }

/* ---------- 8. TARJETAS DE INDICADOR ---------- */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--e2); }
.kpi {
  border: 1px solid var(--line); border-radius: var(--r);
  padding: var(--e3); background: var(--bg2);
}
.kpi-lbl {
  font-size: 11px; font-weight: 500; color: var(--mut);
  display: flex; align-items: center; gap: 5px; margin-bottom: 6px;
}
.kpi-num { font-size: 24px; font-weight: 650; letter-spacing: -.02em; line-height: 1.1; }
.kpi-sub { font-size: 11px; color: var(--mut); margin-top: 3px; }
.kpi.ok  .kpi-num { color: var(--ok); }
.kpi.avi .kpi-num { color: var(--avi); }
.kpi.mal .kpi-num { color: var(--mal); }

/* ---------- 9. ESTADO VACÍO ---------- */
/* Una tabla vacía con un "no hay datos" en gris no dice qué hacer. */
.vacio {
  text-align: center; padding: var(--e5) var(--e4); color: var(--mut);
}
.vacio .ic { width: 26px; height: 26px; opacity: .5; margin-bottom: var(--e2); }
.vacio b { display: block; color: var(--txt); font-size: 13px; font-weight: 550; margin-bottom: 3px; }
.vacio span { font-size: 12px; line-height: 1.5; display: block; max-width: 320px; margin: 0 auto; }

/* ---------- 10. ICONOS ---------- */
/* Trazo, no emoji. El emoji se ve distinto en cada sistema, no se puede colorear
   y es lo que primero delata una interfaz improvisada. */
.ic {
  width: 15px; height: 15px; flex: 0 0 auto;
  stroke: currentColor; fill: none;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  vertical-align: -2px;
}
.ic-lg { width: 18px; height: 18px; }
.ic-sm { width: 13px; height: 13px; }
/* Selector de icono del sensor (editor flotante) */
.snf-icos { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr)); gap: 4px; margin-top: 4px;
  max-height: 132px; overflow-y: auto; border: 1px solid var(--line); border-radius: 8px; padding: 6px; background: var(--fond); }
.snf-ico { display: inline-flex; align-items: center; justify-content: center; height: 30px; padding: 0;
  border: 1px solid transparent; border-radius: 7px; background: transparent; cursor: pointer; color: var(--tx); }
.snf-ico:hover { background: var(--hover); }
.snf-ico.sel { border-color: var(--acc2); background: rgba(59,130,246,.12); }
/* Los botones sueltos de un .form salen azules y a todo el ancho: estos son
   casillas de icono, así que se les gana con el id del contenedor. */
#sn-icos .snf-ico, #snf-icos .snf-ico {
  width: auto; margin: 0; padding: 0; border: 1px solid transparent; border-radius: 7px;
  background: transparent; color: var(--tx);
}
#sn-icos .snf-ico:hover, #snf-icos .snf-ico:hover { background: var(--hover); }
#sn-icos .snf-ico.sel, #snf-icos .snf-ico.sel { border-color: var(--acc2); background: rgba(59,130,246,.16); }
/* Iconos de la ficha del vehículo: trazo tenue al lado del valor, y en las filas del resumen */
.tile .tv .ic { opacity: .7; }
.rs-ico .ic { width: 14px; height: 14px; color: var(--mut); vertical-align: -3px; }

/* ---------- 11. NAVEGACIÓN DE PESTAÑAS ---------- */
/* Con catorce pestañas, dejarlas envolver parte los rótulos en dos líneas y
   descuadra la fila entera. Se quedan en una sola línea y se desplazan. */
#admin .subnav, .subnav {
  gap: var(--e1);
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  border-bottom: 1px solid var(--line);
  padding-bottom: 0; margin-bottom: var(--e4);
}
#admin .subnav::-webkit-scrollbar, .subnav::-webkit-scrollbar { display: none; }
#admin .subnav button, .subnav button {
  flex: 0 0 auto; white-space: nowrap;
  background: transparent; color: var(--mut);
  border: 0; border-bottom: 2px solid transparent; border-radius: 0;
  padding: 9px var(--e3); font-size: 13px; font-weight: 500;
  box-shadow: none;
  transition: color .12s ease, border-color .12s ease;
}
#admin .subnav button:hover, .subnav button:hover { color: var(--txt); background: transparent; }
/* La pestaña activa se marca con una línea bajo el rótulo, no con una caja:
   ocupa menos y se lee antes. */
#admin .subnav button.act, .subnav button.act {
  color: var(--txt); background: transparent;
  border-bottom-color: var(--acc2); box-shadow: none;
}

/* ---------- 11b. LA VISTA DE ALERTAS ---------- */
/* "Alertas" vuelve al menú lateral: en Administración se arma el catálogo de
   reglas y eventos, pero las alertas ya configuradas de cada vehículo las mira
   el usuario, y este botón es el único camino que tiene. La de IA de cámara ya
   NO se esconde por CSS: ahora es un servicio en la nube real y su visibilidad
   la controla comprobarIA() por el id #nav-ia (aparece solo si la cuenta tiene
   al menos una cámara con eventos). */
/* Alertas y Alertas de cámara vivían en el menú lateral y traían su propio
   título y su propio relleno de pantalla completa. Dentro de una pestaña eso
   sobra: la pestaña ya dice dónde estás, y repetirlo roba una franja entera. */
#rulv-alertas > .view > div, #rulv-ia > .view > div,
#rulv-alertas > div > div[id$="-wrap"], #rulv-ia > div > div[id$="-wrap"] { padding: 0 !important; }
#rulv-alertas h2[style*="var(--acc)"], #rulv-ia h2[style*="var(--acc)"] { display: none; }
#rulv-alertas #alerts-wrap, #rulv-ia > div { height: auto !important; overflow: visible !important; }

/* Botones de acción dentro de una tabla: uno solo destaca. Con Editar en azul
   sólido, Pausar gris y Borrar en rojo, la fila entera grita y el ojo no sabe
   dónde ir. Aquí el color se reserva para el que hace daño. */
#alerts-tb .btn-sm, #alerts-tb button {
  background: transparent !important; color: var(--txt) !important;
  border: 1px solid var(--line) !important; font-weight: 500;
}
#alerts-tb .btn-sm:hover, #alerts-tb button:hover { background: var(--hover) !important; }
#alerts-tb button[onclick*="orrar"], #alerts-tb button[onclick*="liminar"] {
  color: var(--mal) !important; border-color: transparent !important;
}
#alerts-tb button[onclick*="orrar"]:hover, #alerts-tb button[onclick*="liminar"]:hover {
  background: var(--mal-suave) !important;
}

/* ============================================================
   11c. COMPOSICIÓN — lo que de verdad estaba mal
   ============================================================
   Los tokens arreglan la piel; el problema era la DISPOSICIÓN. Esta sección
   corrige lo que se veía en la pantalla de Usuarios: un bloque de filtros con
   un hueco, una caja dentro de otra caja, y un formulario de diez campos en
   fila india sin ninguna jerarquía.
   ============================================================ */

/* --- Filtros en rejilla, no en flex con envoltura ---
   Con `flex-wrap` y anchos fijos, la última fila queda a medias y deja un vacío
   enorme; y como los campos tienen alturas distintas, nada se alinea. Una
   rejilla de columnas iguales llena el ancho siempre y alinea las etiquetas. */
.filtros {
  display: grid !important;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  align-items: start;
  gap: var(--e3);
  padding: var(--e3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  margin: 0 0 var(--e3);
}
/* Cada filtro es etiqueta + control, y la etiqueta ocupa SIEMPRE el mismo alto.
   Sin eso, un filtro con dos controles apilados empuja al vecino y la fila
   entera queda torcida: era lo que se veía en "Gerente superior". */
.filtros .campo {
  flex: none !important; max-width: none !important; min-width: 0;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.filtros .campo > span {
  font-size: 11px; font-weight: 500; color: var(--mut);
  height: 15px; line-height: 15px; margin-bottom: 5px;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.filtros .winp, .filtros input, .filtros select {
  width: 100% !important; height: 34px; padding: 0 9px;
}
.filtros .campo > div { display: flex; gap: 6px; }

/* --- El buscador sobrante ---
   El buscador automático se metía ENCIMA de la lista de gerentes: dos controles
   apilados donde los demás tienen uno, y por eso la fila quedaba desalineada.
   Dentro de un filtro sobra: un <select> ya busca solo al teclear. Se deja
   funcionando en las tablas, que es donde sí hace falta. */
.filtros .busca-caja { display: none !important; }

/* El botón de limpiar va al final de la fila, pegado a la derecha, no en una
   celda cualquiera en mitad de la rejilla. */
.filtros .campo:has(> button) { grid-column: -2 / -1; }
.filtros .campo > button { height: 34px; }
/* El contador cierra el bloque a la derecha. */
.filtros .cuenta { grid-column: 1 / -1; margin-left: 0; text-align: right; padding: 0; font-size: 11px; }

/* --- Formularios largos: agrupar en dos columnas ---
   Diez campos en una sola columna obligan a rodar la pantalla para crear un
   usuario. Con dos columnas cabe todo de un vistazo. */
.form.form-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--e3); align-items: start; }
.form.form-2col > h3, .form.form-2col > .dsec, .form.form-2col > button,
.form.form-2col > .msg, .form.form-2col > .ancho { grid-column: 1 / -1; }

/* --- Densidad de tabla ---
   Con filas de 60 px caben cuatro registros en pantalla. La consulta de una
   lista se hace comparando, y para comparar hay que ver varios a la vez. */
td { padding: 7px var(--e3); }
th { padding: 6px var(--e3); }
td b, td strong { font-weight: 550; }
/* La segunda línea gris de cada fila (nombre completo bajo el usuario) no
   necesita el mismo alto que la principal. */
td .k { line-height: 1.35; }

/* --- Panel de dos columnas: la lista manda ---
   Lista y formulario tenían casi el mismo ancho. La lista es lo que se consulta
   todos los días; el formulario se usa una vez por usuario nuevo. */
#admin .panel { display: grid; grid-template-columns: minmax(0, 1.9fr) minmax(300px, .85fr); gap: var(--e3); align-items: start; }
@media (max-width: 1100px) { #admin .panel { grid-template-columns: 1fr; } }

/* ---------- 11d. FORMULARIO EN VENTANA ---------- */
/* Ancha de verdad: dos columnas de campos caben sin apretarse, que es lo que
   no se podía hacer en la columna estrecha de al lado de la tabla. */
#form-vent {
  display: none; position: fixed; inset: 0; z-index: 4000;
  background: rgba(15,23,42,.55); backdrop-filter: blur(2px);
  align-items: flex-start; justify-content: center; padding: 4vh var(--e4);
  overflow: auto;
}
#form-vent .fv-caja {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--elev-alta);
  width: min(620px, 100%); max-height: 92vh; display: flex; flex-direction: column;
}
#form-vent .fv-top {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--e3) var(--e4); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--card); border-radius: var(--r-lg) var(--r-lg) 0 0;
}
#form-vent .fv-top b { font-size: 14px; font-weight: 600; }
#form-vent #fv-cuerpo { overflow: auto; padding: var(--e4); }
/* Dentro de la ventana el formulario ya no es una tarjeta: la ventana lo es. */
/* Una sola columna, con la etiqueta ENCIMA de su campo.
   Se probó a dos columnas y quedó mal: la rejilla reparte los hijos uno a uno,
   así que un texto de ayuda entre medias desplaza todo y las etiquetas dejan de
   corresponder con su campo. Agruparlas de verdad exigiría reescribir el HTML
   de cada formulario; no compensa para ganar una columna. */
#form-vent .form {
  border: 0 !important; box-shadow: none !important; padding: 0 !important;
  background: transparent !important;
}
#form-vent .form { display: block !important; }
#form-vent .form > h3 { display: none; }     /* el título ya está en la barra */
#form-vent .form label:first-of-type { margin-top: 0; }

/* Con el formulario fuera, la tabla se queda con todo el ancho. */
#admin .panel.panel-full { grid-template-columns: 1fr; }
/* El formulario descansa dentro de administración —de ahí lo toma la ventana y
   ahí vuelve al cerrarse—, pero NO debe verse: se llega a él por el botón de
   arriba. Dentro de la ventana sí se muestra, porque la ventana cuelga del
   cuerpo del documento y esta regla ya no le alcanza. */
#admin #form-usuario, #admin #form-objeto { display: none; }
/* Sin esto, una tabla ancha empuja su celda de la rejilla más allá del borde de
   la pantalla y se lleva por delante la cabecera de la tarjeta. El desborde
   tiene que quedarse DENTRO de la tabla, no salirse del panel. */
#admin .panel > * { min-width: 0; }
#admin .panel .card > div[style*="overflow"] { overflow-x: auto; }

/* ---------- 11e. ANCHO Y DESPLAZAMIENTO DE LA TABLA ---------- */
/* El problema no era el número de columnas sino que ninguna tenía tope: un
   correo largo estiraba su columna todo lo que hiciera falta y arrastraba la
   tabla entera fuera de la pantalla. Con un tope, el texto parte en dos líneas
   —no se oculta nada— y la tabla cabe. */
/* Se probó a limitar el ancho de cada celda y a dejar que el texto partiera:
   quedó peor. Un correo se rompía en tres trozos («ronaldm / ontoya99 /
   8@gmail.com») y el nombre de usuario igual. Un dato partido a mitad de
   palabra cuesta más de leer que uno que se sale.
   Ahora cada celda va en UNA línea y el ancho se gana por otro lado: menos
   relleno, la segunda línea más pequeña, y la barra de arriba para moverse. */
#admin td, #admin th { padding: 6px 10px; white-space: nowrap; }
/* La segunda línea de cada fila (el nombre completo bajo el usuario) es
   contexto, no dato principal: va más pequeña y no manda en el ancho. */
#admin td .k { font-size: 10.5px; }
/* OJO: aquí había un `display: block` para el texto gris de las celdas. Estaba
   pensado para la segunda línea bajo el nombre de usuario —que ya trae su
   propio <br>, así que no lo necesitaba— y de paso partía en tres renglones la
   celda de créditos: el número, el «/ 20» y el botón «+», cada uno en su línea.
   Un texto gris dentro de una celda va EN LÍNEA con lo que lo acompaña. */
#admin td .k { display: inline; }
/* La celda de créditos lleva número, total y botón: los tres en una línea y
   centrados entre sí. */
#admin td > b + .k { margin-left: 2px; }
#admin td .btn-sm { vertical-align: middle; }
/* El correo es lo más largo de la tabla y casi nunca se lee entero: se recorta
   con puntos suspensivos y se ve completo al pasar el ratón. Nada se pierde. */
#admin td.recorta, #admin td:nth-child(3) {
  max-width: 240px; overflow: hidden; text-overflow: ellipsis;
}
/* Celdas que SÍ pueden partirse en dos líneas: rótulos de dos palabras como
   «Créditos prepagados», que en una sola línea estiran su columna y desplazan
   toda la tabla a la derecha.

   OJO: aquí NO se puede usar `display:-webkit-box` para recortar a dos
   renglones. Un <td> con un display distinto de `table-cell` se sale de la
   tabla: la columna deja de alinearse con su cabecera y aparecen líneas
   sueltas. Basta con permitir el salto y poner un tope de ancho. */
#admin td.dos-lineas {
  white-space: normal;
  max-width: 130px;
  line-height: 1.3;
}

/* --- Barra de desplazamiento ARRIBA ---
   La barra del navegador queda al fondo de la tabla: con treinta filas hay que
   bajar hasta abajo para poder moverse a los lados. Esta es un espejo de la de
   abajo, puesta encima, y las dos van sincronizadas. */
.scroll-espejo {
  overflow-x: auto; overflow-y: hidden;
  height: 12px; margin-bottom: 2px;
}
.scroll-espejo > div { height: 1px; }
.scroll-espejo::-webkit-scrollbar { height: 9px; }
.scroll-espejo::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: var(--r-full);
}
.scroll-espejo::-webkit-scrollbar-thumb:hover { background: var(--mut); }
.scroll-espejo::-webkit-scrollbar-track { background: transparent; }

/* ---------- 12. AVISOS ---------- */
.msg { font-size: 12px; line-height: 1.5; }
.msg.ok  { color: var(--ok); }
.msg.err { color: var(--mal); }

/* ---------- 13. FILAS DE LISTA ---------- */
.row { padding: var(--e2) 0; font-size: 12.5px; }
.row:last-child { border-bottom: 0; }

/* ---------- 14. DETALLE ---------- */
code, .mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 11.5px; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--line); border-radius: var(--r-full); border: 3px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--mut); background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

/* ===== 11f · LA FILA DE HERRAMIENTAS DE OBJETOS =====
   El buscador, los tres botones y los dos avisos ocupaban cuatro franjas
   apiladas: más de un tercio del alto útil antes de empezar la tabla. Van en
   una sola línea, con las acciones a la izquierda y los avisos a la derecha.
   Ojo: `.filtros` es `display:grid !important`, así que esto tiene que ser
   igual de terminante o la caja se sigue repartiendo en columnas de 180 px
   y todo se envuelve. */
#atab-devices .busca-caja {
  display: flex !important;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 10px;
}
#atab-devices .busca-caja > .campo { flex: 0 0 240px; order: 1; }
#atab-devices .busca-caja > #devs-barra { order: 2; }
#atab-devices .busca-caja > #devs-avisos { order: 3; margin-left: auto; }
#atab-devices .busca-caja > .cuenta { order: 4; margin-left: 8px; }

#devs-barra, #devs-avisos {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0;
}
/* Las etiquetas de aviso son botones: se les quita el aire de botón y se les
   deja el de etiqueta, que es lo que son hasta que se pulsan. */
#devs-avisos .pill { border: 0; cursor: pointer; font: inherit; }
#devs-avisos .pill:hover { filter: brightness(1.12); }

/* ===== 11g · NOTIFICACIONES: CANALES, PRUEBA Y DÍA A DÍA =====
   Eran ocho tarjetas apiladas y había que rodar la pantalla entera para
   responder la única pregunta que trae aquí a la gente: ¿está todo puesto? */

/* Los tres canales, misma anchura y misma altura de cabecera. */
#atab-notify .canales {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--e3);
  margin-bottom: var(--e3);
  /* Las tres del mismo alto, y el botón pegado abajo. Rellenar con espacios
     alinearía solo con los textos de hoy: en cuanto uno cambie, se tuerce
     otra vez. Con la tarjeta en columna y la explicación estirándose, el
     botón queda a la misma altura diga lo que diga cada canal. */
  align-items: stretch;
}
#atab-notify .canales > .card {
  margin: 0; display: flex; flex-direction: column;
}
#atab-notify .canales > .card > .canal-sub { flex: 1; }
#atab-notify .canales h2 { display: flex; align-items: center; gap: 8px; }
/* La explicación es contexto, no el contenido: dos líneas y a otra cosa. */
#atab-notify .canal-sub {
  font-size: 11.5px; line-height: 1.5; margin-bottom: 10px;
}

/* El formulario de cada canal vive en una ventana, no desplegándose hacia
   abajo: al abrirse empujaba la página entera y las tres tarjetas dejaban de
   estar alineadas, que era justo lo que se ganaba poniéndolas en fila.
   Se esconde por identificador de ancestro (#admin), no por clase suelta:
   la ventana cuelga de <body>, fuera de #admin, así que allí sí se ve. */
#admin .canal-form { display: none; }
/* Dentro de la ventana ya hay una caja; otra dentro sería caja sobre caja. */
#fv-cuerpo > .canal-form { border: 0; padding: 0; background: none; }
#fv-cuerpo > .canal-form > .form { margin-top: 12px; }

/* La prueba: una línea, no una tarjeta con título. */
#atab-notify .barra-prueba {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: var(--e3);
}
#atab-notify .barra-prueba .bp-ico { display: flex; color: var(--mut); }
#atab-notify .barra-prueba .bp-tit { font-size: 13px; font-weight: 500; }
#atab-notify .barra-prueba select { width: 120px; flex: 0 0 auto; }
#atab-notify .barra-prueba input { flex: 1; min-width: 180px; }
#atab-notify .barra-prueba .msg { flex: 1 0 100%; margin: 0; }
#atab-notify .barra-prueba .msg:empty { display: none; }

/* Abajo: la lista corta a la izquierda, la tabla que sí se mira a la derecha. */
#atab-notify .notif-abajo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: var(--e3);
}
#atab-notify .notif-abajo > .card { margin: 0; }
/* Mi empresa › Perfil: logo y QR en marcos de 120 px con botón debajo; sin imagen
   se ve "Sin logo" en vez del texto alternativo roto. */
.ep-imgs { display: flex; gap: 16px; flex-wrap: wrap; }
.ep-img-caja { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1 1 140px; }
.ep-marco { position: relative; width: 120px; height: 120px; border: 1px dashed var(--line); border-radius: 10px; overflow: hidden; background: rgba(127,127,127,.05); display: grid; place-items: center; }
.ep-marco img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ep-marco img:not([src]), .ep-marco img[src=""] { display: none; }
.ep-marco .ep-vacio { position: absolute; font-size: 11px; color: var(--mut); }
.ep-marco img[src]:not([src=""]) ~ .ep-vacio { display: none; }

/* Menú flotante de acciones de una fila (teléfono; lo abre el "＋" de Objetos) */
#acc-pop { position: fixed; z-index: 6000; background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0,0,0,.25); padding: 6px; min-width: 210px; max-width: 92vw; }
#acc-pop .acc-tit { font-size: 12px; font-weight: 700; color: var(--acc); padding: 6px 8px 4px; border-bottom: 1px solid var(--line); margin-bottom: 4px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 260px; }
#acc-pop .acc-item { display: flex; align-items: center; gap: 10px; padding: 5px 8px; border-radius: 8px; font-size: 13px; cursor: pointer; color: var(--tx); }
#acc-pop .acc-item:active { background: var(--bg2); }
#acc-pop .acc-item button { flex: 0 0 auto; margin: 0; }
.acc-mas { display: none; }
/* Integraciones: con método GT06 (TCP) se esconden headers/body y sale la nota */
#integ-editor:not(.gt06) .solo-gt06, #integ-plantilla-editor:not(.gt06) .solo-gt06 { display: none !important; }
#integ-editor.gt06 .solo-http, #integ-plantilla-editor.gt06 .solo-http { display: none !important; }
/* Integraciones de CONSUMO: no envian, traen. Sobran el modo de envio, los
   reintentos, el body con variables y la lista de objetos — esa no manda
   objetos, los da de alta. Se queda el nombre, la URL, el intervalo y la llave. */
#integ-editor.consumo .no-consumo { display: none !important; }
#integ-editor:not(.consumo) .solo-consumo { display: none !important; }

@media (max-width: 760px) { .acc-mas { display: inline-flex; align-items: center; justify-content: center; } }

@media (max-width: 760px) {
  #atab-notify .notif-abajo { grid-template-columns: 1fr; }

  /* ---- Usuarios y Objetos: LISTA con desplazamiento lateral ----
     En vez de tarjetas: la tabla conserva sus columnas y se desliza con el dedo.
     La cabecera se queda pegada arriba y la primera columna con dato (Usuario /
     Placa) pegada a la izquierda, para saber de quién es la fila mientras se
     desplaza. */
  .lista-movil { overflow-x: auto !important; -webkit-overflow-scrolling: touch; max-width: 100%; }
  .lista-movil table { width: max-content; min-width: 100%; font-size: 12px; }
  .lista-movil th, .lista-movil td { white-space: nowrap; padding: 6px 8px; }
  .lista-movil thead th { position: sticky; top: 0; z-index: 2; background: var(--campo); }
  #atab-users .lista-movil th:nth-child(2), #atab-users .lista-movil td:nth-child(2),
  #u-devices-card .lista-movil th:first-child, #u-devices-card .lista-movil td:first-child,
  #atab-devices .lista-movil th:nth-child(2), #atab-devices .lista-movil td:nth-child(2) {
    position: sticky; left: 0; z-index: 1; background: var(--campo);
    box-shadow: 2px 0 0 var(--line);
  }
  #atab-users .lista-movil thead th:nth-child(2), #atab-devices .lista-movil thead th:nth-child(2),
  #u-devices-card .lista-movil thead th:first-child { z-index: 3; }
  /* La columna fija medía 290 de los 411 px (usuario + nombre completo): al
     deslizar, las demás columnas quedaban escondidas detrás (Pixel, 8-sep-2026).
     Se acota a 124 px con puntos suspensivos, y las demás celdas a 170. */
  #atab-users .lista-movil td:first-child, #atab-users .lista-movil th:first-child,
  #atab-devices .lista-movil td:first-child, #atab-devices .lista-movil th:first-child { width: 26px; padding: 3px 4px; }
  #atab-users .lista-movil td:nth-child(2), #atab-users .lista-movil th:nth-child(2),
  #atab-devices .lista-movil td:nth-child(2), #atab-devices .lista-movil th:nth-child(2),
  #u-devices-card .lista-movil td:first-child, #u-devices-card .lista-movil th:first-child {
    max-width: 124px; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-users .lista-movil td:nth-child(2) > b, #atab-users .lista-movil td:nth-child(2) > .k,
  #atab-devices .lista-movil td:nth-child(2) > b, #atab-devices .lista-movil td:nth-child(2) > .k {
    display: block; max-width: 116px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  #atab-users .lista-movil td:nth-child(2) > br, #atab-devices .lista-movil td:nth-child(2) > br { display: none; }
  #atab-users .lista-movil td, #atab-devices .lista-movil td, #u-devices-card .lista-movil td {
    max-width: 170px; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-users .lista-movil td.acciones, #atab-devices .lista-movil td.acciones { max-width: none; overflow: visible; }
  /* Objetos: la celda fija lleva el nombre arriba y, debajo, los botones de
     acción de la fila (antes estaban al final, había que deslizar a buscarlos). */
  #atab-devices .lista-movil td:nth-child(2), #atab-devices .lista-movil th:nth-child(2) { max-width: 118px; white-space: normal; padding: 4px 6px; }
  #atab-devices .lista-movil td:nth-child(2) .acc-nombre { max-width: 106px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
  #atab-devices .lista-movil td:nth-child(2) .acc-sub { margin-top: 2px; }
  #atab-devices .lista-movil td:nth-child(2) .acc-sub .btn-sm.ic-b.mb-obj { height: 20px; min-height: 0; min-width: 0; margin: 0; padding: 0 5px; font-size: 10px; white-space: nowrap; display: inline-flex; align-items: center; gap: 2px; border-radius: 6px; }
  #atab-devices .lista-movil td:nth-child(2) .acc-sub .ic { width: 11px; height: 11px; margin: 0; }
  /* 3ª columna: 6 botones parejos en una fila (160 px), a la vista sin deslizar */
  #atab-devices .lista-movil td.acc-fila { white-space: nowrap; padding: 4px 3px; max-width: none; overflow: visible; }
  #atab-devices .lista-movil td.acc-fila > .btn-sm.ic-b {
    height: 24px; min-height: 0; min-width: 0; margin: 0 2px 0 0; padding: 0; width: 24px; border-radius: 6px;
    display: inline-flex; align-items: center; justify-content: center; line-height: 1; vertical-align: middle;
  }
  #atab-devices .lista-movil td.acc-fila > .btn-sm.ic-b:last-child { margin-right: 0; }
  #atab-devices .lista-movil td.acc-fila .ic { width: 12px; height: 12px; margin: 0; }
  /* Barra de herramientas de Objetos en el teléfono: buscador a todo el ancho
     (sin rótulo ni botón "Filtros"), debajo Columnas · Exportar · Importar parejos,
     y los avisos y el conteo en su propia fila. */
  #atab-devices .busca-caja { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; align-items: stretch; }
  #atab-devices .busca-caja > .campo { grid-column: 1 / -1; }
  #atab-devices .busca-caja > .campo > span { display: none; }
  #atab-devices .busca-caja > .campo > input { width: 100% !important; min-height: 40px; }
  #atab-devices .busca-caja > #devs-barra { grid-column: 1 / -1; display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  #atab-devices .busca-caja > #devs-barra > * { width: 100%; min-width: 0; }
  #atab-devices .busca-caja > #devs-barra button { width: 100%; min-height: 36px; white-space: nowrap; }
  #atab-devices .busca-caja > #devs-avisos { grid-column: 1 / -1; margin: 0; justify-content: flex-start; }
  #atab-devices .busca-caja > .cuenta { grid-column: 1 / -1; margin: 0; text-align: right; }
  #atab-devices .busca-caja .fl-plegar { display: none !important; }
  /* Usuarios: las acciones en la 3ª columna (tras el usuario fijo), una fila de
     botones parejos; el botón de equipos lleva el conteo. */
  /* 7 botones en 202 px: con el usuario (124) y la casilla (26) caben en los 356 de la caja */
  #atab-users .lista-movil td.acc-fila { white-space: nowrap; padding: 4px 3px; max-width: none; overflow: visible; }
  #atab-users .lista-movil td:nth-child(2), #atab-users .lista-movil th:nth-child(2) { max-width: 118px; }
  #atab-users .lista-movil td:nth-child(2) > b, #atab-users .lista-movil td:nth-child(2) > .k { max-width: 108px; }
  #atab-users .lista-movil td.acc-fila > .btn-sm.ic-b {
    height: 24px; min-height: 0; min-width: 0; margin: 0 2px 0 0; padding: 0; width: 24px; border-radius: 6px;
    display: inline-flex; align-items: center; justify-content: center; line-height: 1; vertical-align: middle;
  }
  #atab-users .lista-movil td.acc-fila > .btn-sm.ic-b:last-child { margin-right: 0; }
  #atab-users .lista-movil td.acc-fila > .btn-sm.ic-b.us-eq { width: auto; padding: 0 4px; font-size: 10px; gap: 2px; }
  #atab-users .lista-movil td.acc-fila .ic { width: 12px; height: 12px; margin: 0; }
  /* El de borrar, rojo pleno como en Objetos (el fondo --no-bg es pálido y el icono blanco casi no se veía) */
  #atab-users .lista-movil td.acc-fila > .btn-sm.ic-b[style*="no-bg"] { background: #ef4444 !important; color: #fff !important; }
  /* Buscador sin rótulo, como en Objetos */
  #atab-users .filtros > .campo:first-of-type > span { display: none; }
  #atab-users .filtros > .campo:first-of-type > input { min-height: 40px; }

  /* "Equipos del usuario": en el teléfono quedaba como tarjeta al FINAL de la
     lista de 84 usuarios y había que ir a buscarla. Ahora es una hoja que sube
     desde abajo, encima de la lista, con su ✕ (Pixel, 8-sep-2026). */
  #u-devices-card {
    position: fixed !important; left: 0; right: 0; bottom: 0; top: auto;
    max-height: 72vh; overflow: auto; z-index: 5000; margin: 0 !important;
    border-radius: 16px 16px 0 0; box-shadow: 0 -10px 30px var(--sombra);
    border-bottom: 0; padding-bottom: calc(10px + env(safe-area-inset-bottom));
  }
  #u-devices-card > h2 { position: sticky; top: 0; left: 0; z-index: 4; background: var(--card); padding-bottom: 6px; }

  /* Barra del diálogo de objetos (Buscar · Marcar visibles · Desmarcar · conteo):
     el buscador a todo el ancho y los dos botones en una fila, parejos. */
  #modal #m-busca-caja { grid-template-columns: 1fr 1fr; padding: 0 0 8px; border-bottom: 1px solid var(--line); margin-bottom: 8px; }
  #modal #m-busca-caja > .campo:first-child { grid-column: 1 / -1; }
  #modal #m-busca-caja > .campo:first-child > span { display: none; }
  #modal #m-busca-caja > button { min-height: 34px; font-size: 12px; }
  #modal #m-busca-caja > .cuenta { grid-column: 1 / -1; text-align: right; }
  /* Tags: la misma lista, con la columna "Tag" pegada a la izquierda. */
  #tg-lista.lista-movil th:nth-child(2), #tg-lista.lista-movil td:nth-child(2) {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line);
  }
  #tg-lista.lista-movil thead th:nth-child(2) { z-index: 3; }

  /* ---- Tags: filtros plegados detrás de un botón ----
     En el Pixel la barra medía 325 px de 809 y a la lista le quedaban 125. Ahora
     arriba solo van el botón "Filtros", las fichas de conteo y el total; los campos
     se despliegan al tocarlo, a dos por fila. */
  #tg-barra { gap: 8px 10px; padding: 8px 10px; }
  #tg-barra > div { flex: 1 1 45%; min-width: 0; }
  #tg-barra > div > .winp { width: 100%; min-width: 0 !important; margin: 0; }
  #tg-barra > button { flex: 1 1 30%; min-width: 0; white-space: nowrap; }
  #tg-barra #tg-info { flex: 1 1 100%; margin-left: 0 !important; order: 2; }
  #tg-barra #tg-plegar { flex: 1 1 100%; order: 1; min-height: 36px; display: inline-flex; align-items: center; justify-content: center; }
  #tg-barra.tg-plegada > div, #tg-barra.tg-plegada > button:not(#tg-plegar) { display: none; }
  #tg-info .tg-ficha { font-size: 11px; padding: 3px 8px; }

  /* ---- Chat con clientes: dos pantallas, no dos columnas ----
     En el Pixel la lista medía 276 px y a los mensajes les quedaban 102 px
     (8-sep-2026). En el teléfono se ve UNA de las dos: la lista a todo el ancho
     y, al tocar una conversación, los mensajes a todo el ancho con el botón ‹
     para volver. */
  #view-chat > div { padding: 8px !important; gap: 8px !important; }
  #view-chat > div > h2 { font-size: 16px; }
  #chat-cols { flex-direction: column; min-height: 0 !important; }
  #chat-col-hilos { width: auto !important; flex: 1 1 auto; min-height: 0; }
  #chat-col-msgs { display: none !important; }
  #view-chat.chat-abierto #chat-col-hilos { display: none !important; }
  #view-chat.chat-abierto #chat-col-msgs { display: flex !important; flex: 1 1 auto; min-height: 0; }
  #view-chat.chat-abierto #chat-volver {
    display: inline-flex !important; align-items: center; gap: 4px;
    min-height: 34px; padding: 4px 12px; font-size: 13px; font-weight: 700;
    color: var(--acc); border: 1px solid var(--acc); border-radius: 999px; background: transparent;
    white-space: nowrap; flex: 0 0 auto;
  }
  #view-chat.chat-abierto #chat-col-msgs > h2 { flex-wrap: nowrap; }
  #view-chat.chat-abierto #chat-tit { text-align: right; font-size: 13.5px; }
  #chat-hilos .row { padding: 10px 6px; font-size: 14px; }
  #chat-msgs > div { max-width: 88% !important; }
  #chat-reply .winp { font-size: 15px; }

  /* ---- Eventos de cámara: lista con desplazamiento lateral ----
     Cada evento salía como tarjeta de 5 renglones (dos de ellos en "—") y cabían
     3 en pantalla. Tabla con la fecha pegada a la izquierda, y la lista con tope
     de alto para que la evidencia de abajo quede al alcance. */
  #view-ia .ia-wrap { padding: 8px 8px !important; }
  #view-ia .ia-cab { gap: 8px; }
  #view-ia .ia-cab #ia-dev { max-width: none !important; flex: 1 1 100%; }
  #view-ia #ia-lista { max-height: 52vh !important; }
  #view-ia #ia-lista.lista-movil th:first-child, #view-ia #ia-lista.lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line);
  }
  #view-ia #ia-lista.lista-movil thead th:first-child { z-index: 3; }
  #view-ia #ia-lista.lista-movil td { font-size: 12px; }

  /* ---- Contable: Cuentas de cobro y Planes como lista ----
     81 cuentas en tarjetas medían 12.384 px de alto. La tarjeta entera desliza de
     lado; la columna Usuario / ID queda fija. */
  #view-billing #billing-wrap { padding: 8px !important; }
  #view-billing .card.lista-movil { padding: 8px 6px; }
  #view-billing .card.lista-movil > h2 { position: sticky; left: 0; }
  #view-billing .lista-movil th:first-child, #view-billing .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line);
  }
  #view-billing .lista-movil thead th:first-child { z-index: 3; }
  #view-billing .lista-movil td { font-size: 12px; }
}

/* La ventana de Firebase lleva una tabla de cinco columnas; en 620 px sale
   con desplazamiento lateral desde el primer proyecto. */
#form-vent:has(#form-canal-push) .fv-caja { width: min(880px, 100%); }

/* ===== 11h · LA VENTANA DE UN CANAL, POR DENTRO =====
   La de Firebase no es solo un formulario: es una lista y además un alta. Sin
   separarlas, la tabla y los campos parecían la misma cosa y no se entendía
   qué estabas mirando. */

/* Rótulo de sección. No se usa <h3> porque dentro de la ventana los <h3> están
   escondidos a propósito (el título ya está en la barra de arriba), y aquí sí
   hacen falta dos rótulos: uno para la lista y otro para el alta. */
#fv-cuerpo .sec {
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  color: var(--mut); text-transform: uppercase;
  margin: 0 0 8px;
}
/* El alta va en su propia caja, separada de la lista por una línea. */
#fv-cuerpo .bloque-alta {
  margin-top: var(--e4); padding-top: var(--e4) !important;
  border-top: 1px solid var(--line) !important;
}

/* Un <textarea> sin ancho cae en su `cols` de fábrica —veinte caracteres— y
   sale como una rendija al lado de campos de ochocientos píxeles. */
.form textarea, #fv-cuerpo textarea { width: 100%; box-sizing: border-box; }
#fv-cuerpo textarea.mono { font-size: 12px; line-height: 1.45; }

/* El texto de ayuda es ayuda, no un párrafo más del formulario. */
#fv-cuerpo .ayuda {
  font-size: 11px; line-height: 1.5; color: var(--mut); margin-top: 5px;
}

/* Casilla y su explicación: el cuadro arriba a la izquierda, el texto al lado
   sin saltar de línea por debajo del cuadro. */
#fv-cuerpo .casilla {
  align-items: flex-start; gap: 9px; margin-top: var(--e3);
  cursor: pointer; font-weight: 400;
}
#fv-cuerpo .casilla > input { width: 15px; height: 15px; margin: 2px 0 0; flex: 0 0 auto; }
#fv-cuerpo .casilla > span { font-size: 12.5px; }
#fv-cuerpo .casilla .k { display: block; font-size: 11px; margin-top: 2px; }

/* Guardar no es una franja azul de lado a lado: es un botón, y va a la derecha
   donde acaba la lectura del formulario. */
#fv-cuerpo .acciones {
  display: flex; justify-content: flex-end; gap: var(--e2); margin-top: var(--e4);
}
/* Los botones del formulario llevan `width:100%` de base —tienen sentido
   cuando son el único elemento de la columna—, pero en una fila de acciones
   eso los convierte en una franja de lado a lado. */
#fv-cuerpo .acciones button { width: auto !important; flex: 0 0 auto; padding: 8px 18px; }

/* ===== 11i · LA MATRIZ DE PERMISOS =====
   Permisos en filas, grupos en columnas. Lo que importa aquí es poder seguir
   una fila con el ojo sin perderla: por eso la columna de nombres se queda
   fija al desplazar y las filas se resaltan al pasar por encima. */

#pm-tabla { width: auto; min-width: 100%; border-collapse: separate; border-spacing: 0; }

/* La columna del permiso no se va con el desplazamiento lateral. Sin esto, con
   ocho grupos dejas de saber qué permiso estás marcando. */
#pm-tabla th.pm-h-perm, #pm-tabla td.pm-perm, #pm-tabla tr.pm-mod > td:first-child {
  position: sticky; left: 0; z-index: 2;
  background: var(--card);
  text-align: left; white-space: nowrap;
  min-width: 220px; padding: 6px 14px 6px 10px;
}
#pm-tabla tr.pm-mod > td:first-child { background: var(--hover); }
#pm-tabla td.pm-perm { font-size: 12.5px; }
#pm-tabla td.pm-perm .ic { width: 13px; height: 13px; opacity: .5; vertical-align: -2px; }

/* Cabecera de columna: nombre, a cuánta gente afecta, y qué se puede hacer con
   ella. Eso era todo lo que aportaba la tabla de grupos que se quitó. */
#pm-tabla th.pm-col {
  min-width: 128px; padding: 8px 10px 7px; text-align: center; vertical-align: bottom;
}
#pm-tabla th.pm-col .pm-col-tit { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
#pm-tabla th.pm-col .k { font-size: 10.5px; font-weight: 400; }
#pm-tabla th.pm-col .pm-defecto { font-size: 10.5px; font-weight: 500; color: var(--ok); }
#pm-tabla th.pm-col .pm-col-acc {
  display: flex; justify-content: center; gap: 4px; margin-top: 6px; min-height: 22px;
  align-items: center;
}
#pm-tabla th.pm-col .pm-col-acc .btn-sm { padding: 2px 6px; }

/* Fila de módulo: es un encabezado, no un permiso más. */
#pm-tabla tr.pm-mod > td {
  background: var(--hover);
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  padding: 6px 10px; border-top: 1px solid var(--line);
}
#pm-tabla tr.pm-mod > td .k { font-weight: 400; text-transform: none; letter-spacing: 0; }

#pm-tabla tbody td { text-align: center; padding: 5px 10px; border-top: 1px solid var(--line); }
#pm-tabla tbody tr:not(.pm-mod):hover > td { background: var(--fila-hover); }
#pm-tabla tbody tr:not(.pm-mod):hover > td.pm-perm { background: var(--fila-hover); }
#pm-tabla input[type=checkbox] { width: 16px; height: 16px; cursor: pointer; margin: 0; }
#pm-tabla input[type=checkbox]:disabled { cursor: not-allowed; opacity: .3; }

/* El pie solo grita cuando hay algo que perder. */
#pm-pie {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--e3);
  margin-top: var(--e3); padding-top: var(--e3); border-top: 1px solid var(--line);
}
#pm-pie #pm-aviso:empty { display: none; }
#pm-pie #pm-guardar { width: auto !important; padding: 8px 18px; }
#pm-pie #pm-guardar:disabled { opacity: .45; cursor: not-allowed; }

/* El formulario de alta descansa dentro de administración —de ahí lo toma la
   ventana— pero no debe verse ahí. Igual que #form-usuario y #form-objeto. */
#admin #form-grupo { display: none; }

/* El buscador automático se cuela aquí porque la matriz es una tabla de más de
   ocho filas, pero filtrar por texto la rompe: esconde permisos y deja las
   cabeceras de módulo colgando sobre nada. Y su contador dice "28 filas",
   sumando las ocho cabeceras a los veinte permisos. */
#atab-permisos .busca-caja { display: none !important; }

/* El botón azul se quedaba en blanco al pasar por encima y su texto —blanco—
   desaparecía. La culpa era de la regla genérica `.btn-sm:not([style])::hover`,
   que pinta el fondo de gris: tiene tres clases de especificidad y le ganaba a
   `button.primario:hover`, que solo ajustaba el brillo. Se iguala aquí. */
.btn-sm.primario, button.primario { background: var(--acc2); color: #fff; border-color: transparent; }
.btn-sm.primario:hover { background: var(--acc2); color: #fff; filter: brightness(1.1); }
.btn-sm.primario:disabled, .btn-sm.primario:disabled:hover { filter: none; }

/* ===== 11j · MEMBRESÍAS: PRECIOS CON LA CUENTA HECHA =====
   La tabla de créditos y la de niveles eran dos tarjetas separadas, y para saber
   lo que paga un gerente había que multiplicar de cabeza saltando entre ambas.
   Aquí el peso va debajo del crédito, en gris, para que se lea sin cruzar nada. */
#atab-membresias .mb-nivel {
  background: var(--fond); border: 1px solid var(--line); border-radius: var(--r);
  padding: var(--e3); margin-bottom: var(--e3);
}
/* Los seis niveles de un vistazo: sustituyen a la tabla que estaba aparte.
   Se comparan sin abrir nada y el que se pulsa es el que se edita abajo. */
#atab-membresias .mb-tira {
  display: flex; flex-wrap: wrap; gap: var(--e2); margin-bottom: var(--e3);
}
#atab-membresias .mb-nv {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  min-width: 118px; padding: 8px 12px; text-align: left; cursor: pointer;
  background: var(--card); color: var(--txt);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  width: auto; font-size: 13px; transition: border-color .12s ease, background .12s ease;
}
#atab-membresias .mb-nv:hover { border-color: var(--acc2); background: var(--card); }
#atab-membresias .mb-nv.act {
  border-color: var(--acc2); background: var(--card);
  box-shadow: inset 0 -2px 0 var(--acc2);
}
#atab-membresias .mb-nv.tocada { border-color: var(--avi); }
#atab-membresias .mb-nv-n { font-size: 11px; color: var(--mut); }
#atab-membresias .mb-nv-v { font-size: 15px; font-weight: 650; letter-spacing: -.02em; }
#atab-membresias .mb-nv-p {
  font-size: 10.5px; color: var(--mut); max-width: 150px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

#atab-membresias .mb-nivel-edit {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--e3);
  border-top: 1px solid var(--line); padding-top: var(--e3);
}
#atab-membresias .mb-nivel-edit .campo { margin: 0; }
#atab-membresias .mb-nivel-edit .campo label {
  display: block; font-size: 11.5px; font-weight: 500; color: var(--mut); margin-bottom: var(--e1);
}
#atab-membresias .mb-equiv {
  margin-left: auto; align-self: center; font-size: 13px; color: var(--mut);
}
#atab-membresias .mb-equiv b { color: var(--txt); font-size: 15px; }

/* Estos campos viven fuera de un `.form`, así que no les llegaba ninguna de las
   reglas de la sección 6 y salían con el borde gris de fábrica del navegador. */
#atab-membresias .mb-nivel-edit input,
#atab-membresias .mb-cr input {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 8px 10px; font-size: 13px;
  background: var(--campo); color: var(--txt);
  transition: border-color .12s ease, box-shadow .12s ease;
}
#atab-membresias .mb-nivel-edit input:focus,
#atab-membresias .mb-cr input:focus {
  outline: 0; border-color: var(--acc2); box-shadow: 0 0 0 3px var(--anillo);
}
#atab-membresias .mb-nivel-edit input::placeholder { color: var(--mut); opacity: .75; }
#atab-membresias .mb-nivel-edit .campo input { width: 190px; margin: 0; }

#atab-membresias .mb-celda { white-space: nowrap; }
#atab-membresias .mb-cr { display: flex; align-items: center; gap: 6px; }
#atab-membresias .mb-cr input { width: 84px; margin: 0; }
/* El equivalente en pesos es consecuencia, no dato que se teclee: va debajo,
   más pequeño y apagado, para que no compita con la casilla editable. */
#atab-membresias .mb-eq { font-size: 11.5px; color: var(--mut); margin-top: 3px; }

/* Ámbar en el borde izquierdo = esta celda cambió y todavía no se ha guardado. */
#atab-membresias .mb-celda.tocada { box-shadow: inset 3px 0 0 var(--avi); }
#atab-membresias .mb-celda.tocada .mb-cr input { border-color: var(--avi); }

#mb-pend:empty { display: none; }
#mb-guardar { width: auto !important; padding: 8px 18px; }
#mb-guardar:disabled { opacity: .45; cursor: not-allowed; }

/* Vive dentro de administración —de ahí lo toma la ventana— pero no se ve ahí.
   Igual que #form-grupo y #form-usuario. */
#admin #form-inventario { display: none; }

/* La fila de herramientas del inventario, igual que la de Objetos (11f): el
   buscador que inyecta el observador a la izquierda y los filtros a la derecha.
   Dentro del <h2> los selects se estiraban a la altura del título. */
#mbv-inventario .busca-caja {
  display: flex !important; align-items: flex-end; flex-wrap: wrap; gap: 10px;
}
#mbv-inventario .busca-caja > .campo { flex: 0 0 220px; order: 1; }
#mbv-inventario .busca-caja > .cuenta { order: 2; }
/* Los controles acaban siendo hijos directos de la caja (ver mbJuntarBarra). */
/* El `width: auto` va con !important a la fuerza: la caja lleva también la clase
   `filtros`, y `.filtros select { width: 100% !important }` (sección 10) le gana
   a cualquier regla sin marcar por especificidad que tenga. */
#mbv-inventario .busca-caja > .mb-ctl {
  margin: 0; width: auto !important; flex: 0 0 auto; height: 34px;
  font-size: 13px; order: 3;
}
#mbv-inventario .busca-caja > select.mb-ctl { min-width: 165px; }
#mbv-inventario .busca-caja > select.mb-ctl:first-of-type { margin-left: auto; }
#mbv-inventario .busca-caja > button.mb-ctl {
  width: auto !important; height: 34px; padding: 0 14px; white-space: nowrap; order: 4;
}
#mb-barra:empty { display: none; }

/* La tabla de precios tiene cuatro filas: un buscador ahí sobra y su contador
   ("4 filas") no le dice nada a nadie. */
#mbv-precios .busca-caja { display: none !important; }

/* ===== 11k · COMANDOS E INTEGRACIONES: LA TABLA MANDA =====
   Los dos tenían la lista a media pantalla y, al lado, un formulario permanente
   que solo se usa al crear o editar. Mismo tratamiento que Usuarios y Objetos:
   el formulario se muda a la ventana y la lista se queda con todo el ancho. */
#admin #form-comando, #admin #integ-editor { display: none; }
/* Dentro de la ventana ya no son una tarjeta: el marco lo pone la ventana. */
#fv-cuerpo > #integ-editor { border: 0; padding: 0; background: none; }
#form-vent:has(#integ-editor) .fv-caja { width: min(820px, 100%); }
/* Los botones del pie van en fila, no como barras apiladas de ancho completo. */
#fv-cuerpo > #integ-editor > div[style*="flex-wrap"] button { width: auto !important; }
#admin #form-informe, #admin #form-envio { display: none; }
#admin #form-sensor { display: none; }
/* El editor de sensores es largo (plantilla, tipo, origen, bits, calibración):
   con el ancho de una columna estrecha no cabía nada. */
#form-vent:has(#form-sensor) .fv-caja { width: min(760px, 100%); }

/* Sensores: los grupos a la izquierda, sus sensores a la derecha. Va con clase
   propia porque `.grid2` no tiene ninguna regla —se usa en seis sitios y no hace
   nada—, y darle estilo cambiaría de golpe otras cinco pantallas. */
.sen-2col {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--e3); align-items: start;
}
.sen-2col > * { min-width: 0; margin: 0; }
@media (max-width: 1100px) { .sen-2col { grid-template-columns: 1fr; } }
/* Veintidós grupos son mil quinientos píxeles de alto: la segunda columna
   quedaba fuera de la pantalla y había que rodar para saber que existía. */
#sg-lista, #sl-lista { max-height: 420px; overflow: auto; }
/* El observador engancha un buscador a los <select> largos. Dentro de la ventana
   sale con su propia etiqueta "Buscar" en medio del formulario, partiendo la
   pareja etiqueta–campo. Un <select> nativo ya busca al teclear. */
#fv-cuerpo .busca-caja { display: none !important; }

/* ===== 11l · ADMINISTRACIÓN EN COLUMNA =====
   Diecinueve pestañas en fila no caben en ningún monitor: se cortaban "Sistema"
   e "Iconos", y ya se habían escondido cuatro tras "Técnico" sin que bastara.
   En columna la etiqueta larga no compite por espacio y el panel puede crecer.
   No cambia ninguna ruta: showAdmin() y #admin/<tab> siguen igual. */
#admin {
  display: grid; grid-template-columns: 190px minmax(0, 1fr);
  gap: var(--e4); align-items: start;
}
/* Los dos modales de crédito son `position: fixed`: fuera del flujo, no ocupan
   celda. El resto de pestañas comparte la segunda columna; solo una se ve. */
#admin > .atab { grid-column: 2; }

#admin > .subnav {
  flex-direction: column; gap: 0; margin: 0;
  position: sticky; top: 0;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); padding: 6px;
  /* Dieciséis entradas más los rótulos pasan del alto de un portátil: el menú
     rueda por dentro en vez de empujar el contenido. */
  max-height: calc(100vh - 36px); overflow-y: auto;
}
#admin > .subnav button {
  width: 100%; text-align: left; padding: 6px 10px;
  font-size: 12.5px; line-height: 1.35; border-radius: var(--r-sm); background: none;
}
#admin > .subnav button:hover { background: var(--hover); color: var(--txt); }
/* La pestaña abierta se marca con fondo, no con el subrayado de la barra: en
   columna un `inset 0 -2px` queda debajo del texto y no se lee como selección. */
#admin > .subnav button.act {
  background: var(--acc2); color: #fff; box-shadow: none;
}
#admin > .subnav button.act:hover { background: var(--acc2); color: #fff; }

/* Rótulos de sección: dicen dónde buscar, no se pulsan. */
#admin > .subnav .sn-sec {
  font-size: 10px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--mut); padding: 9px 10px 2px;
}
#admin > .subnav .sn-sec:first-child { padding-top: 4px; }

#nav-tecnico {
  margin-top: 6px; border-top: 1px solid var(--line) !important;
  border-radius: 0 !important; padding-top: 9px !important;
}
#nav-tecnico::before { content: "▸ "; }
#admin > .subnav.tec-abierto #nav-tecnico::before { content: "▾ "; }
#admin > .subnav.tec-abierto .tec-item.permitido { display: block; }
#nav-tecnico.act { box-shadow: none; }

/* En pantalla estrecha no hay ancho que ceder: vuelve a ser una fila que rueda,
   igual que antes. */
@media (max-width: 900px) {
  #admin { display: block; }
  #admin > .subnav {
    flex-direction: row; position: static; padding: 0; border: 0;
    background: none; gap: 6px; margin-bottom: 8px;
  }
  #admin > .subnav button { width: auto; }
  #admin > .subnav .sn-sec { display: none; }
  #nav-tecnico { margin-top: 0; border-top: 0 !important; padding-top: 9px !important; }
}

/* Comandos: seis columnas cortas repartidas a mano. Sin anchos, las cuatro
   primeras se apelotonaban a la izquierda y las acciones quedaban a mil
   píxeles, con un desierto en medio. La descripción se lleva el sobrante. */
.cc-tabla { table-layout: fixed; width: 100%; }
.cc-tabla th:nth-child(1), .cc-tabla td:nth-child(1) { width: auto; }
.cc-tabla th:nth-child(2), .cc-tabla td:nth-child(2) { width: 110px; }
.cc-tabla th:nth-child(3), .cc-tabla td:nth-child(3) { width: 160px; }
.cc-tabla th:nth-child(4), .cc-tabla td:nth-child(4) { width: 180px; }
.cc-tabla th:nth-child(5), .cc-tabla td:nth-child(5) { width: 150px; }
.cc-tabla th:nth-child(6), .cc-tabla td:nth-child(6) { width: 96px; text-align: right; }
.cc-tabla code { word-break: break-all; }
.ig-tabla { table-layout: fixed; width: 100%; }
.ig-tabla th:nth-child(1), .ig-tabla td:nth-child(1) { width: auto; }
.ig-tabla th:nth-child(2), .ig-tabla td:nth-child(2) { width: 110px; }
.ig-tabla th:nth-child(3), .ig-tabla td:nth-child(3) { width: 90px; }
.ig-tabla th:nth-child(4), .ig-tabla td:nth-child(4) { width: 130px; }
.ig-tabla th:nth-child(5), .ig-tabla td:nth-child(5) { width: 80px; }
.ig-tabla th:nth-child(6), .ig-tabla td:nth-child(6) { width: 120px; }
.ig-tabla th:nth-child(7), .ig-tabla td:nth-child(7) { width: 60px; }
.ig-tabla td:nth-child(1) span { word-break: break-all; }

/* Informes: encendidos y apagados separados. Antes iban mezclados y lo único
   que distinguía a un apagado era medio punto de opacidad en el nombre. */
#ri-lista .ri-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer;
  font-weight: 400; margin: 0;
}
#ri-lista .ri-item:hover { background: var(--fila-hover); }
#ri-lista .ri-item input { width: 15px; height: 15px; margin-top: 3px; flex: 0 0 auto; }
#ri-lista .ri-txt { flex: 1; min-width: 0; font-size: 12.5px; }
#ri-lista .ri-acc { flex: 0 0 auto; white-space: nowrap; }
#ri-lista .ri-grupo.apagado .ri-txt b { color: var(--mut); font-weight: 400; }
#ri-lista .ri-sep {
  font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--mut); margin: 14px 0 4px; padding-top: 10px;
  border-top: 1px solid var(--line);
}
#ri-lista .ri-sep .k { text-transform: none; letter-spacing: 0; }

/* Sensores: el grupo abierto se marcaba con un azul translúcido en línea que en
   modo oscuro casi no se veía. Y las cifras iban en texto gris corrido. */
#sg-lista .sg-item {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 8px 10px; border-radius: var(--r-sm);
  border-left: 3px solid transparent;
}
#sg-lista .sg-item:hover { background: var(--fila-hover); }
#sg-lista .sg-item.sel { background: var(--fila-hover); border-left-color: var(--acc2); }
#sg-lista .sg-txt { flex: 1; min-width: 0; cursor: pointer; font-size: 12.5px; line-height: 1.5; }
#sg-lista .sg-acc { flex: 0 0 auto; white-space: nowrap; }

/* Reglas de alerta: una sola vista. Los dos formularios —regla y evento— vivían
   fijos al lado de sus tablas; ahora se abren en ventana como los demás. */
#admin #form-regla, #admin #form-evento { display: none; }
#form-vent:has(#form-evento) .fv-caja { width: min(720px, 100%); }
/* Las secciones de la pestaña se separan entre sí, que ya no las divide nada. */
#atab-rules > #rulv-reglas > .card { margin-top: 14px; }

/* Reglas y eventos en una sola lista y un solo formulario. */
#admin #form-rul { display: none; }
#form-vent:has(#form-rul) .fv-caja { width: min(760px, 100%); }
#ru-bloque-evento { display: none; }
.rul-tabla { table-layout: fixed; width: 100%; }
.rul-tabla th:nth-child(1), .rul-tabla td:nth-child(1) { width: auto; }
.rul-tabla th:nth-child(2), .rul-tabla td:nth-child(2) { width: 118px; }
.rul-tabla th:nth-child(3), .rul-tabla td:nth-child(3) { width: 220px; }
.rul-tabla th:nth-child(4), .rul-tabla td:nth-child(4) { width: 190px; }
.rul-tabla th:nth-child(5), .rul-tabla td:nth-child(5) { width: 120px; }
.rul-tabla th:nth-child(6), .rul-tabla td:nth-child(6) { width: 110px; }
.rul-tabla th:nth-child(7), .rul-tabla td:nth-child(7) { width: 96px; text-align: right; }
.rul-tabla td:nth-child(4) .pill { margin: 1px 2px 1px 0; }
/* El bloque de cada tipo, dentro de la ventana, no vuelve a enmarcarse. */
#fv-cuerpo #ru-bloque-regla, #fv-cuerpo #ru-bloque-evento { border: 0; padding: 0; }

/* Las condiciones, dentro de la ventana. El observador engancha un buscador al
   <input list="ev-taglist"> y ese intruso, sin `flex`, se comía el reparto: los
   tres campos quedaban en veinte píxeles y el botón de borrar en una barra. */
#ev-conds .busca-caja, #ev-conds .campo > .winp { display: none !important; }
#ev-conds .ev-cond { display: flex !important; gap: 6px; align-items: center; margin-bottom: 6px; }
#ev-conds .ev-c-tag { flex: 1 1 30% !important; width: auto !important; min-width: 0; margin: 0; }
#ev-conds .ev-c-op  { flex: 1 1 34% !important; width: auto !important; min-width: 0; margin: 0; }
#ev-conds .ev-c-val { flex: 1 1 30% !important; width: auto !important; min-width: 0; margin: 0; }
#ev-conds .ev-cond > button { flex: 0 0 auto !important; width: auto !important; padding: 6px 9px; }

/* Dentro de la ventana, un botón secundario no es una barra de ancho completo:
   "+ Añadir condición" y "Probar" son acciones menores, no la principal. */
#fv-cuerpo #form-rul button.btn-sm { width: auto !important; }
#fv-cuerpo #form-rul .acciones button { padding: 8px 18px; }
#fv-cuerpo #form-rul .sec:first-child { margin-top: 0; }
/* El probador va en una línea: campo ancho, botón al lado. */
#form-rul #ev-probar-no { flex: 1 1 auto; min-width: 0; }
/* Una sola acción principal por ventana: Cancelar no puede ir del mismo azul. */
.btn-sm.secundario, .btn-sm.secundario:hover {
  background: var(--card); color: var(--mut); border: 1px solid var(--line);
}
.btn-sm.secundario:hover { color: var(--txt); background: var(--hover); }

/* Las etiquetas que manda el equipo, a la vista. El <datalist> las escondía: no
   se sabía que existían treinta y siete, y Chrome no despliega nada cuando lo
   escrito ya coincide entero. */
.ev-tags-caja {
  background: var(--fond); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 9px 10px; margin: 8px 0 4px;
}
.ev-tags-tit {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  font-size: 11.5px; color: var(--mut); margin-bottom: 7px;
}
.ev-tags-tit input { width: 130px !important; margin: 0; padding: 5px 8px; font-size: 12px; }
#ev-tags { display: flex; flex-wrap: wrap; gap: 5px; max-height: 132px; overflow-y: auto; }
#ev-tags button.ev-tag {
  width: auto !important; padding: 4px 9px; font-size: 11.5px;
  font-family: ui-monospace, monospace;
  background: var(--card) !important; color: var(--txt) !important;
  border: 1px solid var(--line); border-radius: var(--r-full); cursor: pointer;
  font-weight: 400;
}
#ev-tags button.ev-tag:hover {
  border-color: var(--acc2); color: var(--acc) !important;
  background: var(--card) !important; filter: none;
}
/* Con 258 protocolos, el filtro y el contador de marcados no son adorno. */
.ev-p-tit { display: flex; justify-content: space-between; align-items: baseline;
  font-size: 11.5px; font-weight: 500; color: var(--mut); margin: var(--e3) 0 var(--e1); }
#ev-puerto-q { margin-bottom: 5px !important; }
#ev-puerto optgroup { font-style: normal; font-weight: 500; color: var(--mut); }
#ev-puerto option { padding: 3px 6px; color: var(--txt); }

/* Condiciones de una regla: mismo reparto que las de trama cruda. */
#rl-conds .rl-cond { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }
#rl-conds .rl-c-var { flex: 1 1 34% !important; width: auto !important; min-width: 0; margin: 0; }
#rl-conds .rl-c-op  { flex: 1 1 32% !important; width: auto !important; min-width: 0; margin: 0; }
#rl-conds .rl-c-val, #rl-conds .rl-c-val2 { flex: 1 1 22% !important; width: auto !important; min-width: 0; margin: 0; }
#rl-conds .rl-c-del { flex: 0 0 auto !important; width: auto !important; padding: 6px 9px;
  background: var(--no-bg) !important; color: var(--no-tx) !important; }
#rl-conds .busca-caja { display: none !important; }
/* Cuantas veces se ha visto la etiqueta: separa la que llega siempre de la que
   aparecio seis veces en un mes, que suele ser justo la que hay que nombrar. */
#ev-tags .ev-tag-n { margin-left: 5px; font-size: 10px; color: var(--mut); }
#ev-tags button.ev-tag.fija { border-style: dashed; }

/* ============================================================
   12 · TELÉFONO — SEGUNDA PASADA (operar todo desde el celular)
   ============================================================
   La primera pasada (bloque TELEFONO de index.html) arregló blancos de toque,
   tablas-tarjeta y ventanas como hoja inferior. Esta cubre lo que faltaba:

   1. El menú lateral se vuelve BARRA INFERIOR: en 375 px, la columna de 56
      robaba el 15% del ancho y quedaba lejos del pulgar. Abajo, el contenido
      recupera todo el ancho y las pestañas quedan donde llega el dedo.
   2. Geocercas NO TENÍA ninguna regla móvil: el panel de 340 px dejaba 35 px
      de mapa. Ahora mapa arriba, panel abajo, como Monitoreo.
   3. Historial: la fila del medio (mapa + resumen de 240 px + clips de 300 px)
      se apila en columna; los laterales fijos se comían el mapa entero.
   4. El formulario en ventana (#form-vent) pasa a hoja inferior, como ya
      hacían los demás modales.
   5. Los paneles flotantes de Monitoreo (herramientas) dejan de abrirse en
      x=222 con 330 px de ancho: se anclan a los bordes.
   Va aquí y no en index.html porque esta hoja carga última y a igual
   especificidad gana; varias de estas reglas corrigen a las secciones 11x. */

@media (max-width: 760px) {

  /* ---- 12a. BARRA DE NAVEGACIÓN ABAJO ---- */
  /* html:has(#leftnav.mini) vale (1,1,1) y le ganaba al :root de aquí: si el
     usuario dejó el menú contraído en escritorio, --nav-w volvía a 56. */
  :root, html:has(#leftnav.mini) { --nav-w: 0px; }
  #app { flex-direction: column; }
  #body { min-height: 0; }
  #leftnav, #leftnav.mini {
    order: 2; width: 100%; flex: 0 0 auto;
    flex-direction: row; align-items: stretch;
    border-right: 0; border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
  }
  #leftnav .ln-logo, #ln-version, .ln-toggle, #leftnav .ln-txt { display: none; }
  #leftnav nav {
    flex-direction: row; flex: 1 1 auto; min-width: 0;
    overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; padding: 4px 6px; gap: 2px;
  }
  #leftnav nav::-webkit-scrollbar { display: none; }
  #leftnav nav button {
    flex: 1 0 auto; min-width: 48px; min-height: 46px;
    justify-content: center; padding: 0;
  }
  #leftnav nav button i { font-size: 20px; width: auto; }
  #leftnav .ln-foot {
    display: flex; align-items: center; gap: 2px;
    border-top: 0; border-left: 1px solid var(--line); padding: 4px 6px;
  }
  /* El width:100% en línea de "Mi vendedor" aplastaría a los demás en la fila.
     Solo hijos directos: el botón de "Volver a mi cuenta" vive DENTRO de
     #imp-barra y necesita su relleno propio. */
  #leftnav .ln-foot > button, #leftnav .ln-user {
    width: auto !important; flex: 0 0 auto;
    min-width: 44px; min-height: 46px; justify-content: center;
    padding: 0 !important;
  }
  /* El aviso de sesión prestada flota ENCIMA de la barra: dentro de la fila
     sería una tira ilegible de veinte píxeles. */
  #leftnav #imp-barra {
    position: absolute; bottom: 100%; left: 8px; right: 8px;
    margin-bottom: 6px; z-index: 1300;
  }

  /* ---- 12b. GEOCERCAS: MAPA ARRIBA, PANEL ABAJO ---- */
  /* El mapa se lleva el 58%: es donde se DIBUJA la geocerca, que es a lo que
     se viene. La lista y el formulario ruedan por dentro en su 42%. */
  #gmap { right: 0; bottom: 42%; }
  #gpanel {
    width: auto; left: 0; top: 58%;
    border-left: 0; border-top: 2px solid var(--line);
  }

  /* ---- 12c. HISTORIAL EN COLUMNA ---- */
  #h-medio { flex-direction: column; }
  #hmap { min-height: 170px; }
  /* Los laterales llevan su ancho en línea: sin !important no hay quien gane. */
  #h-summary {
    width: auto !important; flex: 0 0 auto; max-height: 128px;
    border-left: 0 !important; border-top: 1px solid var(--line);
  }
  #h-sd-side {
    width: auto !important; flex: 0 0 auto; max-height: 40%;
    border-left: 0 !important; border-top: 1px solid var(--line);
  }
  /* La barra de consulta: cada campo a media fila, sin mínimos de escritorio. */
  #h-barra > div { flex: 1 1 46%; min-width: 0; }
  #h-barra .winp { width: 100%; min-width: 0 !important; }
  #h-barra > button { flex: 1 1 auto; }
  #h-playbar { flex-wrap: wrap; }
  /* La franja de Registro/Viajes/Clips: al 42% dejaba el mapa en 170 px,
     porque la barra de consulta ya se lleva 200 arriba. */
  #view-historial > div > div:last-child { height: 34% !important; min-height: 150px !important; }

  /* ---- 12d. MONITOREO: PANELES FLOTANTES A LOS BORDES ---- */
  #tool-panel {
    left: 10px !important; right: 10px !important; width: auto !important;
    top: 10px !important; max-height: calc(50% - 20px) !important;
  }
  /* Compartir enlace (Ronald, 9-sep-2026: "optimízalo"): el mapa no hace falta
     mientras se arma el enlace, así que el panel sube al 72 %; todo más apretado:
     sin el texto de intro, "Toda la flota" y el buscador en una fila, lista de
     objetos más baja, nombre y caducidad lado a lado, enlaces creados compactos. */
  #tool-panel[data-tool="compartir"] { max-height: calc(72% - 20px) !important; padding: 10px 12px; }
  #tool-panel[data-tool="compartir"] #tool-body > .k:first-child { display: none; }
  #tool-panel[data-tool="compartir"] .sh-caja { padding: 6px; display: grid; grid-template-columns: auto 1fr; gap: 4px 8px; align-items: center; }
  #tool-panel[data-tool="compartir"] .sh-caja .sh-todos { padding: 3px 2px; }
  #tool-panel[data-tool="compartir"] .sh-caja #sh-buscar { margin: 0 !important; min-height: 32px; }
  #tool-panel[data-tool="compartir"] .sh-caja #sh-lista, #tool-panel[data-tool="compartir"] .sh-caja #sh-cuenta { grid-column: 1 / -1; }
  #tool-panel[data-tool="compartir"] #sh-lista { max-height: 118px; }
  #tool-panel[data-tool="compartir"] #sh-cuenta { margin-top: 0 !important; }
  #tool-panel[data-tool="compartir"] .sh-item { padding: 2px 4px; font-size: 12px; }
  #tool-panel[data-tool="compartir"] .sh-dos { display: flex; gap: 8px; }
  #tool-panel[data-tool="compartir"] .sh-dos > div { flex: 1 1 50%; min-width: 0; }
  #tool-panel[data-tool="compartir"] .sh-dos .winp { width: 100%; }
  #tool-panel[data-tool="compartir"] #tool-body > button.btn-sm { margin-top: 8px !important; padding: 8px !important; }
  #tool-panel[data-tool="compartir"] #tool-body > h4 { margin: 10px 0 4px !important; }
  #tool-panel[data-tool="compartir"] .sh-enlace { padding: 6px 8px !important; margin-bottom: 5px !important; }
  #tool-panel[data-tool="compartir"] .sh-enlace-cab { gap: 6px; }
  #tool-panel[data-tool="compartir"] .sh-enlace-cab b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #tool-panel[data-tool="compartir"] .sh-enlace-cab .k { white-space: nowrap; font-size: 10.5px; }
  #tool-panel[data-tool="compartir"] .sh-enlace-btns { display: grid !important; grid-template-columns: 1fr 1fr auto auto; gap: 4px !important; }
  #tool-panel[data-tool="compartir"] .sh-enlace-btns .btn-sm { min-height: 30px; padding: 4px 8px; white-space: nowrap; }
  #tools-menu {
    max-height: calc(50% - 70px) !important; overflow: auto !important;
  }

  /* ---- 12e. VENTANA DE FORMULARIO COMO HOJA INFERIOR ---- */
  /* Centrada, los botones de guardar caían bajo la barra del navegador. */
  #form-vent { align-items: flex-end; padding: 0; }
  #form-vent .fv-caja {
    width: 100% !important; max-width: none;
    max-height: 94vh; max-height: 94dvh;
    border-radius: 16px 16px 0 0;
  }
  #form-vent #fv-cuerpo { padding-bottom: calc(16px + env(safe-area-inset-bottom)); }

  /* ---- 12f. RELLENOS: 18 px de marco no caben en 375 ---- */
  #admin { padding: 10px; }
  #alerts-wrap, #billing-wrap { padding: 10px !important; }
  #mpanel { padding: 8px; }
  .card, .form { padding: var(--e3); }

  /* ---- 12g. FORMULARIOS Y REJILLAS A UNA COLUMNA ---- */
  .form.form-2col { grid-template-columns: 1fr; }
  #al-filtergeos { grid-template-columns: 1fr !important; }
  .kpis { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); }

  /* ---- 12h. TABLAS: lo que la tarjeta-móvil no cubría ---- */
  /* Las de columnas fijas (Comandos, Integraciones, Reglas) conservaban sus
     anchos de 110-220 px dentro de la tarjeta y partían cada renglón. */
  table.tabla-movil th, table.tabla-movil td {
    width: auto !important; max-width: none;
  }
  /* #admin td lleva nowrap por identificador: en la tarjeta parte el layout. */
  #admin table.tabla-movil td { white-space: normal; max-width: none; }
  /* La matriz de permisos se queda de tabla (sticky), pero su columna fija de
     220 px dejaba 100 px para las casillas: se estrecha y el rótulo parte. */
  #pm-tabla th.pm-h-perm, #pm-tabla td.pm-perm, #pm-tabla tr.pm-mod > td:first-child {
    min-width: 132px; max-width: 150px; white-space: normal;
  }
  /* La matriz se queda de MATRIZ en el teléfono (antes el barrido la volvía
     tarjetas y cada permiso salía con los 4 grupos apilados, Pixel 8-sep-2026):
     tabla con desplazamiento lateral, permiso fijo a la izquierda, columnas de
     grupo compactas y cabecera pegada arriba. */
  #pm-envoltorio.lista-movil { margin: 0 -6px; }
  #pm-tabla { font-size: 12px; }
  #pm-tabla th.pm-col { min-width: 84px; padding: 6px 6px 5px; }
  #pm-tabla th.pm-col .pm-col-tit { font-size: 12px; }
  #pm-tabla th.pm-col .pm-col-acc .btn-sm { padding: 1px 5px; font-size: 11px; }
  #pm-tabla thead th { position: sticky; top: 0; z-index: 3; background: var(--card); }
  #pm-tabla thead th.pm-h-perm { z-index: 4; }
  #pm-tabla td.pm-perm { font-size: 12px; padding: 6px 8px 6px 8px; }
  #pm-tabla tbody td { padding: 5px 6px; }
  #pm-tabla input[type=checkbox] { width: 20px; height: 20px; }
  #atab-permisos .card > .k { font-size: 11px; }

  /* ---- Membresías en el teléfono ----
     Inventario: el buscador del selector de gerentes salía como SEGUNDA caja
     "Buscar…" debajo de la de la tabla; fuera. Y las dos tablas (precios e
     inventario) como lista con scroll lateral y la primera columna fija. */
  #mbv-inventario #mb-barra .busca-caja > .campo, #mbv-inventario #mb-barra .busca-caja > .cuenta { display: none !important; }
  #mbv-inventario #mb-barra .busca-caja { padding: 0 !important; border: 0 !important; margin: 0 !important; }
  #mbv-inventario .mb-ctl { width: 100% !important; margin: 0 !important; }
  #mbv-inventario .lista-movil { max-height: 60vh !important; }
  #atab-membresias .lista-movil th:first-child, #atab-membresias .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line); max-width: 150px;
    overflow: hidden; text-overflow: ellipsis;
  }
  #atab-membresias .lista-movil thead th:first-child { z-index: 3; }
  #atab-membresias .lista-movil td { font-size: 12px; }
  #mb-matriz-tb input[type=number] { width: 58px !important; }
  /* Los campos de Inventario, ordenados en rejilla: buscador a todo el ancho con
     el conteo debajo a la derecha, el gerente a todo el ancho, y estado +
     "Importar" repartidos en una fila (pedido de Ronald, 8-sep-2026). */
  /* La caja del buscador (input + conteo) y la barra de controles (gerente,
     estado, Importar) son dos bloques distintos en el teléfono: cada uno en
     rejilla de dos columnas y con el mismo aire. */
  #mbv-inventario .busca-caja {
    display: grid !important; grid-template-columns: 1fr auto; gap: 4px 8px; align-items: center;
    padding: 0 0 6px !important; margin: 0 0 6px !important;
  }
  #mbv-inventario .busca-caja > .campo { flex: none !important; order: 0; }
  #mbv-inventario .busca-caja > .campo > span { display: none; }
  #mbv-inventario .busca-caja > .cuenta { order: 1; text-align: right; font-size: 11px; margin: 0 !important; white-space: nowrap; }
  #mbv-inventario #mb-barra {
    display: grid !important; grid-template-columns: 1fr 1fr; gap: 8px; align-items: end;
    margin: 0 0 8px; padding: 0;
  }
  #mbv-inventario #mb-barra #mb-filtro-ger { grid-column: 1 / -1; }
  #mbv-inventario #mb-barra button.mb-ctl { min-height: 38px; white-space: nowrap; }
  #mbv-inventario #mb-barra > * { min-width: 0; }
  #mbv-inventario #mb-barra .busca-caja { display: contents !important; }
  #mbv-inventario .busca-caja > .campo { display: block !important; width: 100% !important; min-width: 100% !important; max-width: none !important; }
  #mbv-inventario .busca-caja > .campo input { display: block; width: 100% !important; min-width: 0 !important; box-sizing: border-box; }
  /* Precios: la fila "Nombre del plan / Valor del crédito" y la equivalencia,
     con aire parejo. */
  #mbv-precios .mb-nivel .campo { min-width: 0; }
  #mbv-precios .mb-nivel .campo input { width: 100%; }

  /* ---- 12i. CONDICIONES DE REGLAS: QUE ENVUELVAN ---- */
  /* Tres selectores en una fila de 340 px quedaban de 90 px cada uno. */
  #ev-conds .ev-cond, #rl-conds .rl-cond { flex-wrap: wrap; }
  #ev-conds .ev-c-tag, #ev-conds .ev-c-op { flex: 1 1 44% !important; }
  #ev-conds .ev-c-val { flex: 1 1 70% !important; }
  #rl-conds .rl-c-var, #rl-conds .rl-c-op { flex: 1 1 44% !important; }
  #rl-conds .rl-c-val, #rl-conds .rl-c-val2 { flex: 1 1 30% !important; }

  /* ---- 12j. BARRAS DE HERRAMIENTAS DE OBJETOS/INVENTARIO ---- */
  #atab-devices .busca-caja > .campo,
  #mbv-inventario .busca-caja > .campo { flex: 1 1 100%; }
  #mbv-inventario .busca-caja > select.mb-ctl:first-of-type { margin-left: 0; }
  #atab-membresias .mb-nivel-edit .campo { flex: 1 1 46%; }
  #atab-membresias .mb-nivel-edit .campo input { width: 100%; }
  #atab-membresias .mb-equiv { margin-left: 0; flex: 1 1 100%; }
  /* Precios: las tarjetas de nivel a DOS columnas ocupando todo el ancho de la
     caja (medían 118 px fijos y sobraba espacio a la derecha; Ronald, 8-sep). */
  #atab-membresias .mb-nivel { padding: 10px; }
  #atab-membresias .mb-tira { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  #atab-membresias .mb-nv { width: 100%; min-width: 0; box-sizing: border-box; padding: 9px 12px; }
  #atab-membresias .mb-nv-p { max-width: 100%; }
  #atab-membresias .mb-nv-v { font-size: 16px; }

  /* ---- Reglas de alerta: lista con scroll lateral ----
     11 reglas en tarjetas de 6 renglones medían 2.624 px. Tabla con el nombre
     fijo a la izquierda; los anchos fijos de escritorio se dejan pero más
     estrechos, y el texto de la condición se parte en dos líneas. */
  #atab-rules .lista-movil .rul-tabla { table-layout: auto; width: max-content; min-width: 100%; }
  #atab-rules .rul-tabla th:nth-child(1), #atab-rules .rul-tabla td:nth-child(1) {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line);
    min-width: 120px; max-width: 140px; white-space: normal;
  }
  #atab-rules .rul-tabla thead th:nth-child(1) { z-index: 3; }
  #atab-rules .rul-tabla th:nth-child(3), #atab-rules .rul-tabla td:nth-child(3) { width: 170px; white-space: normal; }
  #atab-rules .rul-tabla th:nth-child(4), #atab-rules .rul-tabla td:nth-child(4) { width: 120px; white-space: normal; }
  #atab-rules .rul-tabla th:nth-child(2), #atab-rules .rul-tabla td:nth-child(2),
  #atab-rules .rul-tabla th:nth-child(5), #atab-rules .rul-tabla td:nth-child(5),
  #atab-rules .rul-tabla th:nth-child(6), #atab-rules .rul-tabla td:nth-child(6) { width: 92px; }
  #atab-rules .rul-tabla td { font-size: 12px; padding: 6px 8px; }
  #atab-rules .card > .k { font-size: 11px; }

  /* ---- Flota en el teléfono ----
     Las cuatro tablas (conductores, gastos, llamadas, tareas) como lista con
     scroll lateral, y los formularios de alta plegados detrás de un botón
     "＋ Nuevo…": abiertos, la pantalla medía 4.100 px. */
  #atab-flota .lista-movil th:first-child, #atab-flota .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line);
    max-width: 140px; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-flota .lista-movil thead th:first-child { z-index: 3; }
  #atab-flota .lista-movil td { font-size: 12px; }
  #atab-flota .form.fl-form-plegado, #atab-empresa .form.fl-form-plegado { display: none; }
  #atab-flota .fl-form-btn, #atab-empresa .fl-form-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 100%; min-height: 38px; margin-top: 8px; font-weight: 600;
  }
  #atab-flota .card > .k { font-size: 11.5px; }

  /* ---- Mi empresa en el teléfono ----
     Tablas como lista (primera columna fija), y en la cabecera solo el selector
     de empresa a todo el ancho: el nombre salía dos veces, en el rótulo y en el
     selector. */
  #atab-empresa .lista-movil th:first-child, #atab-empresa .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--campo); box-shadow: 2px 0 0 var(--line);
    max-width: 140px; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-empresa .lista-movil thead th:first-child { z-index: 3; }
  #atab-empresa .lista-movil td { font-size: 12px; }
  #atab-empresa > .card:first-child > h2 > span:first-child { display: none; }
  #atab-empresa > .card:first-child > h2 > #em-sel { width: 100% !important; flex: 1 1 100%; }
  #atab-empresa .subnav { padding-bottom: 4px; }
  #atab-empresa .subnav button { flex: 0 0 auto; min-height: 36px; }
  /* Calendario: dos días por pantalla, con tope al pasar el dedo */
  #em-cal-cont .em-cal-dias { scroll-snap-type: x mandatory; gap: 6px; margin: 0 -4px; padding: 0 4px 6px; }
  #em-cal-cont .em-cal-dia { flex: 0 0 47% !important; min-width: 0 !important; scroll-snap-align: start; }
  #em-cal-cont > div:first-child select.winp { margin-left: 0 !important; flex: 1 1 100%; width: 100% !important; order: 10; }
  #em-cal-cont > div:first-child > button:last-of-type { margin-left: auto; }
  /* Mensajes automáticos: cada plantilla plegada tras su título */
  #em-mensajes-cont .pl-card { padding: 10px 12px; margin-bottom: 8px !important; }
  #em-mensajes-cont .pl-card h2 { margin-bottom: 0 !important; }
  #em-mensajes-cont .pl-titulo { cursor: pointer; flex: 1; }
  #em-mensajes-cont .pl-titulo::before { content: '▸'; display: inline-block; width: 14px; color: var(--mut); }
  #em-mensajes-cont .pl-card.abierta .pl-titulo::before { content: '▾'; }
  #em-mensajes-cont .pl-card:not(.abierta) .pl-cuerpo { display: none; }
  #em-mensajes-cont .pl-card.abierta h2 { margin-bottom: 6px !important; }
  /* Perfil: los dos marcos lado a lado */
  #emv-perfil .ep-imgs { gap: 10px; }
  #emv-perfil .ep-img-caja { flex: 1 1 45%; }
  /* Conciliación: movimientos como lista con scroll lateral, fecha fija */
  #atab-balance .lista-movil { max-height: 60vh !important; }
  #atab-balance .lista-movil th:first-child, #atab-balance .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--card); box-shadow: 1px 0 0 var(--line);
  }
  #atab-balance .lista-movil thead th:first-child { z-index: 3; }
  #atab-balance .lista-movil td { font-size: 12px; max-width: 170px; overflow: hidden; text-overflow: ellipsis; }
  #atab-balance .lista-movil td:last-child { max-width: none; }
  #atab-balance .card > h2 { flex-wrap: wrap; gap: 6px; }
  #atab-balance #bal-filtro { flex: 1 1 100%; width: 100% !important; }
  #atab-balance .grid2 { grid-template-columns: 1fr 1fr !important; gap: 8px !important; }
  #atab-balance .grid2 .card { padding: 10px; }
  #atab-balance .grid2 .card div[style*="font-size:18px"] { font-size: 16px !important; }
  /* Integraciones: las dos tablas como lista con scroll lateral, nombre fijo */
  #atab-integraciones .lista-movil .ig-tabla { table-layout: auto; width: max-content; min-width: 100%; }
  #atab-integraciones .lista-movil .ig-tabla th, #atab-integraciones .lista-movil .ig-tabla td { width: auto; }
  #atab-integraciones .lista-movil th:first-child, #atab-integraciones .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--card); box-shadow: 1px 0 0 var(--line);
    max-width: 140px; white-space: normal; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-integraciones .lista-movil thead th:first-child { z-index: 3; }
  #atab-integraciones .lista-movil td:first-child > .k { display: none; }
  #atab-integraciones .lista-movil td:first-child > br { display: none; }
  #atab-integraciones .lista-movil td { font-size: 12px; max-width: 170px; overflow: hidden; text-overflow: ellipsis; }
  #atab-integraciones .lista-movil td:last-child { max-width: none; }
  #atab-integraciones .lista-movil td[style*="word-break"] { word-break: normal !important; white-space: nowrap; }
  #atab-integraciones .card > h2 { flex-wrap: wrap; gap: 6px; }
  /* Editor de plantilla: los 4 campos (método, cuándo, segundos, reintentos) a 2 por fila */
  #integ-plantilla-editor > div[style*="display:flex"] { flex-wrap: wrap; }
  #integ-plantilla-editor > div[style*="display:flex"] > div { flex: 1 1 45% !important; min-width: 0; }
  /* ---- Técnico: bitácora, tráfico de puertos, protocolos, bloqueados ---- */
  #atab-audit .lista-movil th:first-child, #atab-audit .lista-movil td:first-child,
  #atab-traffic .lista-movil th:first-child, #atab-traffic .lista-movil td:first-child,
  #atab-protocols .lista-movil th:first-child, #atab-protocols .lista-movil td:first-child,
  #atab-bloqueados .lista-movil th:first-child, #atab-bloqueados .lista-movil td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--card); box-shadow: 1px 0 0 var(--line);
    max-width: 130px; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-audit .lista-movil thead th:first-child, #atab-traffic .lista-movil thead th:first-child,
  #atab-protocols .lista-movil thead th:first-child, #atab-bloqueados .lista-movil thead th:first-child { z-index: 3; }
  #atab-audit .lista-movil td, #atab-traffic .lista-movil td, #atab-protocols .lista-movil td, #atab-bloqueados .lista-movil td {
    font-size: 12px; max-width: 170px; overflow: hidden; text-overflow: ellipsis;
  }
  #atab-audit .lista-movil td:last-child, #atab-traffic .lista-movil td:last-child,
  #atab-protocols .lista-movil td:last-child, #atab-bloqueados .lista-movil td:last-child { max-width: none; }
  #atab-audit .lista-movil, #atab-traffic .lista-movil, #atab-protocols .lista-movil, #atab-bloqueados .lista-movil { max-height: 60vh !important; }
  #atab-audit .card > h2, #atab-traffic .card > h2, #atab-protocols .card > h2, #atab-bloqueados .card > h2 { flex-wrap: wrap; gap: 6px; }
  /* Bitácora: los dos filtros a todo el ancho */
  /* Bitácora y Tráfico: celda fija con dos líneas (+ botones en Tráfico), como Objetos */
  #atab-audit .lista-movil td:first-child, #atab-audit .lista-movil th:first-child,
  #atab-traffic .lista-movil td:first-child, #atab-traffic .lista-movil th:first-child { max-width: 200px; white-space: normal; padding: 4px 6px; }
  #atab-audit .lista-movil td:first-child .acc-nombre, #atab-traffic .lista-movil td:first-child .acc-nombre {
    max-width: 188px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; font-size: 12px;
  }
  #atab-traffic .lista-movil td:first-child .acc-fila { display: flex; flex-wrap: nowrap; gap: 3px; margin-top: 3px; }
  #atab-traffic .lista-movil td:first-child .acc-fila .btn-sm { padding: 2px 6px; min-height: 0; min-width: 0; margin: 0; font-size: 11px; white-space: nowrap; }
  #atab-traffic .lista-movil td:first-child .acc-fila .ic { width: 13px; height: 13px; }
  #atab-audit #au-accion, #atab-audit #au-usuario { width: 100% !important; flex: 1 1 100%; }
  /* Tráfico: el formulario del puerto (5 campos + botón) a 2 por fila */
  #atab-traffic .card > div[style*="align-items:flex-end"] { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  #atab-traffic .card > div[style*="align-items:flex-end"] > div { min-width: 0; }
  #atab-traffic .card > div[style*="align-items:flex-end"] input,
  #atab-traffic .card > div[style*="align-items:flex-end"] select { width: 100% !important; }
  #atab-traffic .card > div[style*="align-items:flex-end"] > button { grid-column: 1 / -1; }
  /* Protocolos: buscador y estado a 2 columnas; selector de equipo al ancho */
  #atab-protocols .card > div[style*="align-items:flex-end"] { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  #atab-protocols .card > div[style*="align-items:flex-end"] > div { min-width: 0; }
  #atab-protocols .card > div[style*="align-items:flex-end"] input,
  #atab-protocols .card > div[style*="align-items:flex-end"] select { width: 100% !important; }
  #atab-protocols #hm-dev { max-width: 100% !important; width: 100%; }
  #atab-protocols .hm-prop { flex-wrap: wrap; }

  /* ---- 12k. TECLEAR SIN QUE EL iPHONE HAGA ZOOM ---- */
  /* Safari amplía la página entera al tocar un campo con letra menor de 16 px,
     y luego no vuelve solo. Los clientes finales entran desde iPhone. */
  input[type=text], input[type=password], input[type=email], input[type=number],
  input[type=search], input[type=date], input[type=time], input[type=datetime-local],
  select:not([multiple]), textarea { font-size: 16px !important; }

  /* ---- 12l. MONITOREO EN EL TELÉFONO: LA HOJA ----
     (Los botones flotantes y las pastillas son comunes a escritorio y celular
     y viven en la sección 13.) Estados en #view-monitoring: hoja-oculta (solo
     mapa) · hoja-lista (buscador + lista) · hoja-ficha (solo la ficha) ·
     hoja-llena (ficha alta). */

  /* La hoja: tarjeta flotante con aire a los lados, como FichaMapa. */
  #view-monitoring #mpanel {
    top: auto; left: 10px; right: 10px; bottom: 10px; width: auto;
    height: auto; max-height: 56%;
    background: var(--card); border: 1px solid var(--line);
    border-radius: 16px; box-shadow: 0 8px 28px var(--sombra);
    z-index: 1250; padding: 2px 10px 8px;
  }
  /* Doble id a propósito: la regla que la esconde en escritorio va al final de
     la hoja y a igual especificidad ganaría también aquí. */
  #view-monitoring #mpanel .hoja-asa {
    flex: 0 0 auto; display: flex; flex-direction: column; align-items: center;
    padding: 4px 0 2px; cursor: pointer; touch-action: none; gap: 1px;
  }
  #view-monitoring #mpanel .hoja-asa span {
    width: 40px; height: 4px; border-radius: 999px; background: var(--line);
  }
  #view-monitoring #mpanel .hoja-asa i {
    font-style: normal; color: var(--mut); font-size: 14px; line-height: 12px; font-weight: 700;
  }
  /* Oculta = se esconde el CONTENIDO, no el panel: la tarjeta de cámara
     (#cam-card) vive dentro y es position:fixed — con display:none en el
     ancestro desaparecía también, y en el teléfono no había video. */
  #view-monitoring.hoja-oculta #mpanel {
    background: transparent; border: 0; box-shadow: none;
    padding: 0; height: auto; pointer-events: none;
  }
  #view-monitoring.hoja-oculta #mpanel > *:not(#cam-card) { display: none; }
  #view-monitoring.hoja-oculta #cam-card { pointer-events: auto; }
  /* Lista: vehículos (+ alertas). La ficha no. */
  #view-monitoring.hoja-lista #panel-bottom { display: none; }
  /* ---- LISTA DE VEHÍCULOS = PANEL LATERAL, como en la app (Ronald, 10-sep-2026).
     Entra deslizándose por la izquierda y ocupa todo el alto del mapa: cabecera
     con el conteo, buscador, filtros de estado y la lista.
     11-sep: la barra se abrió A LA MITAD (iba al 86% y tapaba el mapa entero,
     que es justo lo que uno quiere seguir viendo mientras elige) y VOLVIÓ el
     buscador — con la barra angosta caben menos vehículos de un vistazo, así que
     escribir dos letras es más rápido que desplazar la lista. */
  #view-monitoring.hoja-lista #mpanel {
    left: 0; right: auto; top: 0; bottom: 0;
    width: 50%; min-width: 186px; max-width: 260px; max-height: none;
    border-radius: 0 16px 16px 0; border-left: 0;
    padding: 8px 8px 10px; gap: 6px;
    animation: veh-entra .18s ease-out;
  }
  @keyframes veh-entra { from { transform: translateX(-102%); } to { transform: none; } }
  /* El buscador ocupa el ancho del panel; el rótulo largo lo acorta hojaPoner(). */
  #view-monitoring.hoja-lista #veh-search {
    display: block; width: 100%; box-sizing: border-box;
    font-size: 11px; padding: 5px 8px;
  }
  #view-monitoring.hoja-lista #mpanel .hoja-asa { display: none; }
  #view-monitoring.hoja-lista #veh-list { flex: 1 1 auto; min-height: 0; }
  #view-monitoring.hoja-lista #veh-cab {
    display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
    padding: 2px 2px 7px; border-bottom: 1px solid var(--line);
  }
  #view-monitoring.hoja-lista #veh-cab b { font-size: 13px; color: var(--acc); }
  #view-monitoring.hoja-lista #veh-cab .k { font-size: 11.5px; }
  #view-monitoring.hoja-lista #veh-cab button { margin-left: auto; padding: 3px 9px; }
  /* Los filtros de estado sí se ven aquí (en la hoja de antes no cabían) */
  #view-monitoring.hoja-lista #fl-chips {
    display: flex; flex-wrap: wrap; gap: 4px; flex: 0 0 auto; margin: 0;
  }
  #view-monitoring.hoja-lista #fl-chips button { font-size: 10px; padding: 3px 6px; }
  /* Velo: el mapa se ve detrás pero tocarlo cierra el panel */
  #view-monitoring.hoja-lista #veh-velo {
    display: block; position: absolute; inset: 0; z-index: 1240;
    background: rgba(0, 0, 0, .35);
  }
  /* Con el panel abierto, la barra de capas del mapa estorba: se montaba encima
     de la cabecera "Vehículos". Vuelve sola al cerrar el panel. */
  #view-monitoring.hoja-lista #map-toolbar { display: none; }
  /* Ficha: SOLO el vehículo elegido, como la tarjeta de la app. */
  #view-monitoring.hoja-ficha #mpanel { max-height: 46%; }
  #view-monitoring.hoja-ficha #veh-search,
  #view-monitoring.hoja-ficha #veh-list,
  #view-monitoring.hoja-ficha #alertas-panel { display: none; }
  #view-monitoring.hoja-ficha #panel-bottom,
  #view-monitoring.hoja-llena #panel-bottom {
    border-top: 0; padding-top: 0; max-height: none; flex: 1 1 auto; overflow-y: auto;
  }
  #view-monitoring.hoja-llena #mpanel { max-height: calc(100% - 20px); }
  /* Ficha CHICA de verdad (Ronald, 9-sep-2026: "muy grande, me quita mucho
     espacio"): solo cabecera + resumen (dirección, última conexión, geocerca,
     marcha, km de hoy), apretados. Sensores, recorrido y demás aparecen al
     desplegar con el asa (estado llena). Pasa de 42 % de la pantalla a ~25 %. */
  #view-monitoring.hoja-ficha #mpanel { max-height: 34%; padding: 0 10px 6px; }
  #view-monitoring.hoja-ficha #dev-detail > *:not(.dh):not(#dev-resumen) { display: none !important; }
  #view-monitoring.hoja-ficha #dev-detail .dh { margin-bottom: 2px; }
  #view-monitoring.hoja-ficha #dev-resumen .rs-fila { padding: 1px 0; font-size: 11px; line-height: 1.3; align-items: center; }
  #view-monitoring.hoja-ficha #dev-resumen .rs-ico { font-size: 11px; }
  #view-monitoring.hoja-ficha #dev-resumen .rs-txt br { display: none; }
  #view-monitoring.hoja-ficha #dev-resumen .rs-link { margin-left: 6px; font-size: 10px; }
  #view-monitoring.hoja-ficha #mpanel .hoja-asa { padding: 3px 0 0; }
  /* Desplegada: el resumen también va apretado, los sensores y el resto normales */
  #view-monitoring.hoja-llena #dev-resumen .rs-fila { padding: 1.5px 0; font-size: 11px; }
  #view-monitoring.hoja-llena #veh-search,
  #view-monitoring.hoja-llena #veh-list,
  #view-monitoring.hoja-llena #alertas-panel { display: none; }
  /* Los avisos flotantes bajan un poco y dejan libre la esquina de los
     botones (#avisos es hijo de <body>, no de la vista). */
  body:has(#view-monitoring.act) #avisos { top: 66px; right: 68px; }

  /* ---- 12m. ADMINISTRACIÓN COMO MENÚ DE LISTA ---- */
  /* En 375 px la fila de pestañas no cabe ni deslizando con gracia. En el
     celular: primero un MENÚ vertical con todos los módulos; al tocar uno se
     abre a pantalla completa con su barra de volver (la pone movilInit). */
  #admin.mv-menu > .subnav {
    display: flex; flex-direction: column; position: static;
    border: 0; background: none; overflow: visible;
    margin: 0; padding: 0; gap: 0; max-height: none;
  }
  #admin.mv-menu > .subnav button {
    width: 100%; text-align: left; flex: 0 0 auto;
    padding: 13px 14px; min-height: 48px; font-size: 14px;
    background: var(--card); border: 0; border-bottom: 1px solid var(--line);
    border-radius: 0; color: var(--txt); white-space: normal;
  }
  #admin.mv-menu > .subnav button::after { content: '›'; float: right; color: var(--mut); font-size: 16px; }
  #admin.mv-menu > .subnav button.act { background: var(--card); color: var(--txt); box-shadow: none; }
  #admin.mv-menu > .subnav .sn-sec { display: block; padding: 16px 12px 6px; }
  /* El grupo Técnico sigue plegable, pero como fila del menú. */
  #admin.mv-menu #nav-tecnico { margin-top: 0; border-top: 0 !important; padding-top: 13px !important; border-radius: 0 !important; }
  /* Menú a la vista = ningún módulo a la vista, y al revés. */
  #admin.mv-menu > .atab { display: none !important; }
  #admin.mv-menu.mv-abierto > .atab.act { display: block !important; }
  #admin.mv-menu.mv-abierto > .subnav { display: none; }
  #admin.mv-abierto #mv-volver {
    display: flex; align-items: center; gap: 10px;
    position: sticky; top: -10px; z-index: 60;
    background: var(--bg); padding: 10px 0;
    margin: -10px 0 8px; border-bottom: 1px solid var(--line);
  }
  #mv-volver button { flex: 0 0 auto; }
  #mv-volver b { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* ---- 12n. PROPORCIONES: EL DATO MANDA, EL ADORNO CEDE ----
     En cada módulo de datos lo que se viene a mirar es LA TABLA o LA LISTA.
     Los KPIs y los filtros son contexto: útiles, pero en un celular no pueden
     quedarse con la mitad del alto antes de que empiece el contenido. */

  /* KPIs: de rejilla (2-3 filas apiladas) a UNA tira que se desliza de lado.
     Cuatro tarjetas medían ~200 px de alto; ahora son 64. */
  /* (8-sep-2026) La tira dejaba la tercera tarjeta cortada al borde ("Sin
     reporta…", "Ya activad…"). Ahora rejilla de DOS columnas a todo el ancho,
     como pidió Ronald para las tarjetas de nivel. */
  .kpis {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--e2); padding-bottom: 0;
  }
  .kpi { flex: none; min-width: 0; }
  .kpi-num { font-size: 20px; }

  /* Filtros: dos columnas compactas. La rejilla de 180 px dejaba UNA columna
     y cada filtro era una franja; a dos columnas el bloque mide la mitad. */
  .filtros {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--e2); padding: var(--e2);
  }
  .filtros .campo:has(> button) { grid-column: auto; }
  .filtros .cuenta { grid-column: 1 / -1; }
  /* Usuarios: los 9 filtros medían 390 px y la lista empezaba a mitad de la
     pantalla (Pixel, 8-sep-2026). Plegados detrás de "🔍 Filtros": queda a la
     vista solo el buscador y el conteo; el resto aparece al tocar el botón. */
  .filtros .fl-plegar { grid-column: 1 / -1; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; }
  .filtros.fl-plegada .campo:not(:first-of-type) { display: none; }
  .filtros.fl-plegada .campo:first-of-type { grid-column: 1 / -1; }

  /* Tarjetas más pegadas: el aire entre tarjetas no informa nada. */
  .card { margin-bottom: var(--e2); }

  /* La ficha del vehículo se lee A LA DISTANCIA DEL DEDO: los 10.5 px de
     escritorio en el teléfono son ilegibles. Es el contenido principal de la
     hoja: letra de verdad. */
  .drow { font-size: 12px; padding: 3.5px 0; }
  .tile .tv { font-size: 14px; }
  .tile .tl { font-size: 9.5px; }
  .st-pill { font-size: 10px; }
  #dev-detail .dh b { font-size: 13px; }
  #alertas-lista .al-item { font-size: 12px; }
  #alertas-lista .al-item .meta { font-size: 10.5px; }

  /* El registro de datos del recorrido: 9.5 px era letra de lupa. */
  #day-grid table, #h-grid table { font-size: 11px; }
  #day-grid td { padding: 2px 6px; }

  /* Historial: la barra de consulta se usa UNA vez por consulta; el mapa y los
     datos se miran todo el tiempo. Tras consultar se pliega sola (lo hace
     movilInit) y queda el botón para reabrirla. */
  /* Doble id: la regla que lo esconde en escritorio va al final de la hoja. */
  #h-barra #h-plegar { display: inline-flex; flex: 1 1 100% !important; min-height: 40px; align-items: center; justify-content: center; }
  #h-barra.h-plegada { padding: 6px 10px; }
  #h-barra.h-plegada > * { display: none !important; }
  #h-barra.h-plegada > #h-plegar { display: inline-flex !important; flex: 1 1 auto !important; }
  #h-barra.h-plegada > #h-status { display: inline !important; }

  /* ---- 12o. HISTORIAL CON EL DISEÑO DE LA APP ----
     Copiado de src/pantallas/Historial.js: arriba una tira de pastillas con
     los últimos días —del más viejo a Hoy, que el presente va al final— y el
     chip 📅 Rango que abre la consulta completa. El resumen deja de ser una
     lista de renglones y pasa a las métricas de la app: el valor en negrita
     arriba y la etiqueta chiquita debajo, cuatro por fila. */
  #view-historial #h-dias {
    display: flex; gap: 6px; overflow-x: auto; flex: 0 0 auto;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    padding: 7px 10px; background: var(--bg2); border-bottom: 1px solid var(--line);
  }
  #view-historial #h-dias::-webkit-scrollbar { display: none; }
  #view-historial #h-dias button {
    flex: 0 0 auto; min-height: 34px; padding: 0 13px;
    background: var(--card); border: 1px solid var(--line); color: var(--txt);
    border-radius: 999px; font-size: 12px; font-weight: 600;
  }
  #view-historial #h-dias button.act { background: var(--acc2); border-color: var(--acc); color: #fff; }
  #view-historial #h-dias button.cal { border-color: var(--acc); }

  /* La columna de objetos trae su display:flex EN LÍNEA: la regla vieja de
     index.html sin !important nunca pudo esconderla de verdad. */
  #view-historial #h-flota-wrap { display: none !important; }
  /* El resumen sale sobrando en el teléfono (Ronald, 2026-08-09): la mitad de
     sus casillas llegaban en "—" y el bloque entero le robaba el alto al mapa
     y al registro. En escritorio sigue. */
  #view-historial #h-summary { display: none !important; }
  #view-historial #h-summary h3 { display: none; }
  #view-historial #h-summary .row {
    width: 25%; border-bottom: 0; padding: 2px 4px 2px 0;
    display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: 0;
  }
  #view-historial #h-summary .row b { font-size: 11.5px; line-height: 1.25; }
  #view-historial #h-summary .row .k {
    font-size: 8.5px; line-height: 1.2; letter-spacing: .04em; text-transform: uppercase;
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  }
  /* Inicio y Fin traen fecha y hora completas: a un cuarto no caben. */
  #view-historial #h-summary .row:nth-child(6),
  #view-historial #h-summary .row:nth-child(7) { width: 50%; }

  /* La consulta abierta, ordenada: el dispositivo a lo ancho (compartía fila
     con las fechas y quedaba desfasado), y las dos cajas de fecha se van — en
     el teléfono el rango se elige en UN calendario, como en la app. */
  #view-historial #h-barra { align-items: stretch !important; }
  #h-barra > .busca-no { flex: 1 1 100%; }
  #h-barra > div:has(> #h-from), #h-barra > div:has(> #h-to) { display: none; }
  #h-barra #h-cal-abrir {
    display: inline-flex; align-items: center; justify-content: center;
    flex: 1 1 46%; min-height: 42px;
  }
  /* Antes de la primera consulta, ni el resumen vacío ni las pestañas sin
     datos aportan nada: el mapa se queda con todo el alto. */
  #view-historial:not(.h-con-datos) > div > div:last-child { display: none !important; }
  #view-historial:not(.h-con-datos) #h-summary { display: none !important; }
  /* Cada registro del historial, como una tabla de verdad y no como 22
     renglones sueltos: dos columnas parejas de filas ETIQUETA→valor, cada
     fila con su línea, la etiqueta gris a la izquierda y el valor en negrita
     pegado a la derecha. La rejilla alinea las dos columnas entre sí, que era
     lo que el reparto suelto no lograba. Las casillas vacías ya las esconde
     el observador (clase "vacia"). */
  #h-grid table.tabla-movil tr, #day-grid table.tabla-movil tr {
    display: grid; grid-template-columns: 1fr 1fr;
    column-gap: 16px; align-items: stretch; padding: 6px 12px 8px;
  }
  #h-grid table.tabla-movil td, #day-grid table.tabla-movil td {
    width: auto !important; display: flex; flex-wrap: nowrap;
    justify-content: space-between; align-items: baseline; gap: 8px;
    border: 0; border-bottom: 1px solid var(--line);
    padding: 5.5px 0; font-size: 12.5px; font-weight: 600;
    min-width: 0;
  }
  #h-grid table.tabla-movil td::before, #day-grid table.tabla-movil td::before {
    flex: 0 0 auto; font-size: 9px; font-weight: 500; letter-spacing: .04em;
    text-transform: uppercase; color: var(--mut);
  }
  /* El valor, pegado a la derecha y sin desbordar la celda. */
  #h-grid table.tabla-movil td, #day-grid table.tabla-movil td { text-align: right; }
  #h-grid table.tabla-movil td > *, #day-grid table.tabla-movil td > * { min-width: 0; overflow-wrap: anywhere; }
  /* La hora del equipo encabeza la tarjeta, a lo ancho y en color de acento. */
  #h-grid table.tabla-movil td:first-child, #day-grid table.tabla-movil td:first-child {
    grid-column: 1 / -1; font-size: 13px; color: var(--acc);
    justify-content: flex-start; border-bottom: 1px solid var(--line);
  }

  /* Los módulos de Administración (Usuarios, Objetos, Inventario…): las
     tarjetas con la MISMA tabla estructurada del historial — dos columnas
     parejas de filas etiqueta→valor con su línea. */
  #admin table.tabla-movil tr {
    display: grid; grid-template-columns: 1fr 1fr;
    column-gap: 16px; padding: 6px 12px 8px; position: relative;
  }
  #admin table.tabla-movil td {
    width: auto !important; display: flex;
    justify-content: space-between; align-items: baseline; gap: 8px;
    border: 0; border-bottom: 1px solid var(--line);
    padding: 5.5px 0; font-size: 12.5px; font-weight: 600;
    min-width: 0; text-align: right;
  }
  #admin table.tabla-movil td::before {
    flex: 0 0 auto; font-size: 9px; font-weight: 500; letter-spacing: .04em;
    text-transform: uppercase; color: var(--mut); text-align: left;
  }
  #admin table.tabla-movil td > * { min-width: 0; overflow-wrap: anywhere; }
  /* La primera columna con rótulo encabeza la tarjeta, a lo ancho y en acento. */
  #admin table.tabla-movil td:first-child:not([data-etq=""]),
  #admin #users-tb td:nth-child(2) {
    grid-column: 1 / -1; font-size: 13px; color: var(--acc);
    justify-content: flex-start; text-align: left;
  }
  /* Los botones van al pie, en su propia franja sin línea. */
  #admin table.tabla-movil td.acciones {
    grid-column: 1 / -1; justify-content: flex-start; flex-wrap: wrap;
    border-bottom: 0; padding-top: 8px; gap: 6px; text-align: left;
  }
  /* La casilla de selección (celda sin rótulo) flota arriba a la derecha. */
  #admin table.tabla-movil td[data-etq=""]:first-child {
    position: absolute; top: 9px; right: 12px;
    border: 0; padding: 0; grid-column: auto;
  }

  /* El buscador que el observador le engancha a tablas y selectores largos:
     en el teléfono roba una franja entera cada vez que aparece, y aparece en
     cualquier lista de más de ocho filas. Fuera TODOS en el celular; en
     escritorio siguen estando, que ahí sí filtran mil filas con el teclado. */
  .busca-caja { display: none !important; }
  /* MENOS las de Objetos e Inventario: ahí la caja ES la barra de herramientas
     — juntarBarraObjetos y mbJuntarBarra le meten los botones dentro — y
     esconderla se llevaría las acciones enteras del módulo. */
  #mbv-inventario .busca-caja { display: flex !important; }
  #atab-devices .busca-caja { display: grid !important; }
}

/* ---- El calendario de rango del Historial, copiado de RangoFechas de la app:
   se toca la fecha de inicio y luego la de fin, el tramo queda pintado de azul
   y el futuro no se puede tocar. Vive fuera de la consulta de medios porque lo
   crea app.js al vuelo, pero solo el teléfono lo abre. ---- */
#h-cal { position: fixed; inset: 0; z-index: 6000; display: none; align-items: center; justify-content: center; padding: 16px; }
#h-cal .hc-velo { position: absolute; inset: 0; background: rgba(0,0,0,.6); }
#h-cal .hc-caja {
  position: relative; width: min(452px, 100%);
  background: var(--bg2); border: 1px solid var(--line);
  border-radius: 16px; padding: 12px;
}
/* Dos columnas: el calendario a la izquierda y las HORAS a la derecha. En
   pantallas angostas la de horas baja debajo del calendario. */
#h-cal .hc-cuerpo { display: flex; gap: 12px; flex-wrap: wrap; }
#h-cal .hc-fechas { flex: 1 1 260px; min-width: 0; }
/* El <input type=time> trae un ancho mínimo propio de ~130px que empujaba la
   columna a la fila de abajo: se fija la base y se anula el mínimo. */
#h-cal .hc-horas {
  flex: 0 0 128px; min-width: 0; display: flex; flex-direction: column; gap: 10px;
  justify-content: center; border-left: 1px solid var(--line); padding-left: 12px;
}
#h-cal .hc-horas input { min-width: 0; }
#h-cal .hc-horas label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--mut);
}
#h-cal .hc-horas input {
  border: 1px solid var(--line); border-radius: 8px; padding: 8px;
  background: var(--campo); color: var(--txt); font-size: 14px; width: 100%;
}
@media (max-width: 480px) {
  /* En el teléfono la fila de horas va HORIZONTAL debajo del calendario. Ojo: el
     flex:0 0 128px de escritorio se le quedaba puesto, los dos campos sumaban 242 px
     en una caja de 128 y el "justify-content:center" empujaba "Hora inicio" fuera
     del diálogo (visto en el Pixel, 8-sep-2026). Aquí ocupa todo el ancho. */
  #h-cal .hc-horas {
    border-left: 0; border-top: 1px solid var(--line); padding: 10px 0 0;
    flex: 0 0 auto; width: 100%; flex-direction: row; justify-content: space-between; gap: 10px;
  }
  #h-cal .hc-horas label { flex: 1 1 0; min-width: 0; }
}
#h-cal .hc-cab { display: flex; align-items: center; justify-content: space-between; padding-bottom: 8px; }
#h-cal .hc-cab b { font-size: 14.5px; }
#h-cal .hc-cab button {
  width: 38px; height: 38px; background: none; border: 0; padding: 0;
  color: var(--acc); font-size: 24px; font-weight: 800;
}
#h-cal .hc-sem { display: flex; }
#h-cal .hc-sem span { flex: 1; text-align: center; color: var(--mut); font-size: 10px; font-weight: 700; }
#h-cal .hc-rej { display: flex; flex-wrap: wrap; margin-top: 4px; }
#h-cal .hc-celda {
  width: 14.28%; height: 38px; display: flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; color: var(--txt); font-size: 12.5px;
}
#h-cal .hc-celda .d { width: 30px; height: 30px; border-radius: 15px; display: flex; align-items: center; justify-content: center; }
#h-cal .hc-celda.dentro { background: rgba(2,132,199,.27); }
#h-cal .hc-celda.dentro .d { color: var(--acc); font-weight: 700; }
#h-cal .hc-celda.extremo .d { background: var(--acc2); color: #fff; font-weight: 800; }
#h-cal .hc-celda.futuro { color: var(--line); }
#h-cal .hc-etq { color: var(--acc); font-size: 12px; font-weight: 700; text-align: center; margin-top: 8px; min-height: 15px; }
#h-cal .hc-pie { display: flex; gap: 8px; margin-top: 12px; }
#h-cal .hc-pie button { border-radius: 12px; min-height: 42px; font-size: 13px; font-weight: 700; }
#h-cal .hc-no { flex: 1; background: none; border: 1px solid var(--line); color: var(--mut); }
#h-cal .hc-si { flex: 1.4; background: var(--acc2); border: 0; color: #fff; font-weight: 800; }
#h-cal .hc-si:disabled { opacity: .4; }

/* El botón que abre el calendario solo existe en el teléfono. */
#h-cal-abrir { display: none; }

/* ============================================================
   13 · MONITOREO "TODO FLOTANTE" — escritorio y celular
   ============================================================
   Opción B elegida por Ronald (2026-08-09): el mapa ocupa el 100% en todas
   las pantallas y todo lo demás flota encima, como Google Maps. En el
   celular la lista/ficha es la hoja inferior (sección 12l); en escritorio la
   lista es un panel flotante a la izquierda que se pliega a solo el buscador
   y la ficha del vehículo es una tarjeta flotante abajo a la derecha. */

#view-monitoring #map { right: 0; bottom: 0; }

/* La columna de botones circulares translúcidos (la crea flotInit). Mismos
   valores que la app: se leen sobre calles y sobre satélite. */
#mv-flot {
  position: absolute; top: 10px; right: 10px; z-index: 1260;
  display: flex; flex-direction: column; gap: 10px; align-items: flex-end;
}
#mv-flot > button {
  width: 48px; height: 48px; border-radius: 24px;
  background: rgba(15,23,42,.55); backdrop-filter: blur(2px);
  border: 1px solid rgba(255,255,255,.22); color: #fff;
  font-size: 20px; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 5px rgba(0,0,0,.35); padding: 0; cursor: pointer;
}
#mv-flot > button.on { background: var(--acc2); border-color: var(--acc); }

/* Los toggles del mapa (Ver todos, Agrupar, Rastro, Nombres, Mapa…) viven
   SIEMPRE a la vista como iconos redondos translúcidos, en fila arriba —igual
   que quedaron en la app (pedido Jeferson 27-ago). Ya no hay barra plegable:
   el 🧰 del stack abre directo la lista de herramientas. El texto del botón se
   esconde con font-size:0 y el icono (.mt-em) recupera su tamaño. */
#view-monitoring #map-toolbar {
  display: flex; flex-direction: row; align-items: center; gap: 10px;
  left: auto; right: 70px; top: 10px; bottom: auto;
  max-height: none; overflow: visible;
  background: transparent; padding: 0; box-shadow: none; border-radius: 0;
  z-index: 1260;
}
/* La rueda y el pellizco acercan y alejan: los botones de zoom sobran. */
#view-monitoring #map-toolbar .mt-zoom { display: none; }
#view-monitoring #map-toolbar button.mt-txt {
  font-size: 0; gap: 0; width: 44px; height: 44px; min-height: 0; padding: 0;
  border-radius: 22px; display: flex; align-items: center; justify-content: center;
  background: rgba(15,23,42,.55); backdrop-filter: blur(2px);
  border: 1px solid rgba(255,255,255,.22); color: #fff;
  box-shadow: 0 2px 5px rgba(0,0,0,.35);
}
#view-monitoring #map-toolbar button.mt-txt .mt-em { font-size: 18px; width: auto; flex: none; }
#view-monitoring #map-toolbar button.on {
  background: var(--acc2); border-color: var(--acc); color: #fff;
}
/* En el teléfono la fila va ARRIBA, pegada a la izquierda y hasta el stack de
   la derecha. Los seis botones del superadmin (Ver todos, Agrupar, Nombres,
   Tags, Eventos, Mapa) a 44 px no cabían en 390: se achican a 40 y, si aun
   así no caben, la fila se desliza de lado. El `bottom:auto` lleva !important
   por si a alguien se le ocurre volver a escribirle un bottom en línea: con
   top y bottom a la vez la barra se estiraba y quedaba a MEDIA pantalla. */
@media (max-width: 760px) {
  #view-monitoring #map-toolbar {
    left: 10px; right: 68px; top: 10px; bottom: auto !important;
    gap: 8px; justify-content: flex-start; padding: 0;
    overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  #view-monitoring #map-toolbar::-webkit-scrollbar { display: none; }
  #view-monitoring #map-toolbar button.mt-txt {
    width: 40px; height: 40px; flex: 0 0 40px; border-radius: 20px;
  }
  #view-monitoring #map-toolbar button.mt-txt .mt-em { font-size: 16px; }
}

/* El menú de Herramientas se ancla junto a la pastilla que lo abre y caía
   ENCIMA de los botones circulares. Va a su izquierda, con !important porque
   anclarEnMapa le escribe la posición en línea. */
/* El top NO va fijo: toggleToolsMenu lo mide en vivo bajo el stack (campana
   incluida) — regla de oro: nada anclado bajo #mv-flot con top fijo. */
#view-monitoring #tools-menu {
  left: auto !important; right: 66px !important;
  bottom: auto !important;
  max-height: calc(100% - 20px) !important; overflow-y: auto !important;
}

/* Chips de estado DENTRO del panel, bajo el buscador: solo el punto de color
   y la cifra. Informan Y filtran — un toque deja en la lista solo los de ese
   estado; otro toque lo quita. */
#fl-chips {
  display: flex; gap: 5px; flex-wrap: wrap; flex: 0 0 auto;
  margin: 0; padding: 0;
}
#fl-chips button {
  display: inline-flex; align-items: center; gap: 6px; flex: 1 1 0;
  justify-content: center;
  background: var(--card); color: var(--txt);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 4px 8px; font-size: 11.5px; font-weight: 600; cursor: pointer;
}
#fl-chips button .pt { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
#fl-chips button.on { border-color: var(--acc); background: var(--acc2); color: #fff; }
@media (max-width: 760px) { #fl-chips { display: none; } }

/* Selectores de icono con el catálogo grande (4.600+): rejilla con tope de
   alto y scroll, para que no hagan una página infinita. La carga diferida
   (loading=lazy en los <img>) evita bajar todas las imágenes de una. */
#d-icons, #fo-icons {
  display: flex; flex-wrap: wrap; gap: 5px;
  max-height: 240px; overflow-y: auto; align-content: flex-start;
  padding: 4px; border: 1px solid var(--line); border-radius: var(--r-sm);
}
#ic-lista { max-height: 60vh; overflow-y: auto; }

/* El asa de la ficha solo existe en escritorio; en el teléfono la ficha vive
   dentro de la hoja, que ya tiene la suya. */
#fi-asa { display: none; }

/* ---- El menú principal, más discreto ----
   Letra e iconos bajados bastante (pedido de Ronald): el menú se usa dos
   veces por sesión y estaba gritando al lado de un mapa que es lo que
   importa. En el teléfono la barra inferior conserva sus tamaños de dedo. */
@media (min-width: 761px) {
  #leftnav .ln-logo { font-size: 12.5px; padding: 10px 12px; gap: 7px; }
  #leftnav nav { gap: 1px; padding: 7px 6px; }
  #leftnav nav button {
    font-size: 11.5px; padding: 6.5px 9px; gap: 8px; border-radius: 7px;
  }
  #leftnav nav button i { font-size: 12px; width: 15px; }
  #leftnav .ln-user { font-size: 10.5px; padding: 5px 9px; }
  #leftnav .ln-user b { font-size: 11px; }
  #leftnav #logout, #leftnav #tema-btn, #leftnav #credito-barra { font-size: 11.5px; padding: 6px 9px; }
  /* "Mi vendedor" trae tamaño y relleno en línea. */
  #leftnav #btn-mi-gerente { font-size: 11.5px !important; padding: 6px 9px !important; }
  #leftnav #logout i, #leftnav #tema-btn i, #leftnav #credito-barra i,
  #leftnav #btn-mi-gerente i { font-size: 12px !important; width: 15px !important; }
}

/* La fila de cada vehículo: nombre que puede partir en dos renglones y la
   última conexión siempre a la derecha, en gris. */
#veh-list .vitem { align-items: center; }
#veh-list .vitem .v-nom { flex: 1 1 auto; min-width: 0; line-height: 1.25; overflow-wrap: anywhere; }
#veh-list .vitem .v-hora {
  flex: 0 0 auto; margin-left: auto; padding-left: 6px;
  font-size: 10px; color: var(--mut); white-space: nowrap;
}
/* Acceso rápido a comandos (⋮) al final de cada fila, para emergencias. */
#veh-list .vitem .vkebab {
  flex: 0 0 auto; margin-left: 4px; padding: 2px 5px; border-radius: 6px;
  font-size: 17px; line-height: 1; font-weight: 800; color: var(--mut); cursor: pointer;
}
#veh-list .vitem .vkebab:hover { background: rgba(128,128,128,.2); color: var(--acc); }
/* En escritorio la lista baja de letra: 13px era tamaño de dedo, no de ratón.
   Compacta (fuentes E iconos más chicos): quien quiera verla grande, hace zoom
   del navegador. El icono de cada fila es un emoji que escala con la fuente, así
   que bajar el font-size achica letra e icono a la vez. */
@media (min-width: 761px) {
  #veh-list .vitem { font-size: 8px; padding: 3px 8px; gap: 4px; margin-bottom: 1px; }
  #veh-list .vitem .dot { width: 5.5px; height: 5.5px; }
  #veh-list .vitem .v-nom { line-height: 1.2; }
  #veh-list .vitem .v-hora { font-size: 7px; }
  #veh-list .vitem .vkebab { font-size: 12px; padding: 1px 4px; }
  #veh-search { font-size: 11px; padding: 6px 9px; }
}

/* Las banderitas con el nombre sobre cada vehículo, más chicas: a tamaño
   grande, con veinte carros el mapa era puro letrero. */
.leaflet-tooltip.veh-eti { font-size: 9.5px; padding: 1px 6px; border-radius: 5px; }

/* ---- Escritorio: lista flotante a la izquierda, ficha flotante a la derecha ---- */
@media (min-width: 761px) {
  #view-monitoring #mpanel {
    top: 12px; left: 12px; bottom: auto; right: auto; width: 296px;
    max-height: calc(100% - 24px);
    background: var(--flot); border: 1px solid var(--line);
    border-radius: 14px; box-shadow: 0 8px 28px var(--sombra);
    padding: 10px; z-index: 1250;
  }
  /* Plegado: queda solo el buscador, como la caja de búsqueda de Google Maps.
     Se pliega con el botón 🚚 y se abre al tocar el buscador. */
  /* Oculta DEL TODO el panel de la lista (tercer estado del 🚚). */
  #view-monitoring.fl-oculta #mpanel { display: none; }
  #view-monitoring.fl-plegada #veh-list,
  #view-monitoring.fl-plegada #fl-chips,
  #view-monitoring.fl-plegada #alertas-panel { display: none; }
  /* La ficha ya no vive dentro del panel: es su propia tarjeta flotante,
     abajo a la derecha, y solo existe con un vehículo elegido. Igual que
     FichaMapa en la app, arranca MINIMIZADA —cabecera y resumen— para no
     robarle el mapa; el asa la despliega y al cambiar de vehículo se vuelve
     a encoger. */
  #view-monitoring #panel-bottom {
    display: none;
    position: fixed; right: 12px; bottom: 12px; width: 330px;
    max-height: 248px; overflow: hidden;
    background: var(--flot); border: 1px solid var(--line);
    border-radius: 14px; box-shadow: 0 10px 30px var(--sombra);
    padding: 2px 10px 10px; margin: 0; border-top: 1px solid var(--line); z-index: 1300;
  }
  #view-monitoring.sel-activa #panel-bottom { display: block; }
  /* La ficha, otro punto más chica (pedido de Ronald): es tarjeta de consulta
     rápida, no una página. */
  #panel-bottom .rs-fila { font-size: 10.5px; padding: 2.5px 0; }
  #panel-bottom .rs-ico { font-size: 11.5px; }
  #panel-bottom .rs-link { font-size: 9.5px; }
  #panel-bottom #dev-detail .dh b { font-size: 11px; }
  #panel-bottom .st-pill { font-size: 8px; padding: 2px 6px; }
  #panel-bottom .drow { font-size: 10px; }
  #panel-bottom .tile .tv { font-size: 11.5px; }
  #panel-bottom .tile .tl { font-size: 7.5px; }
  #view-monitoring.fi-abierta #panel-bottom {
    max-height: 62vh; max-height: 62dvh; overflow-y: auto;
  }
  #view-monitoring #fi-asa {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    cursor: pointer; padding: 3px 0 4px;
  }
  #view-monitoring #fi-asa span { width: 40px; height: 4px; border-radius: 999px; background: var(--line); }
  #view-monitoring #fi-asa i { font-style: normal; color: var(--mut); font-size: 13px; line-height: 11px; font-weight: 700; }
  /* Con las cámaras abiertas —la banda de abajo o la tarjeta suelta— la lista
     de vehículos se ENCOGE a la mitad de arriba y rueda por dentro: antes
     seguía de largo y quedaba tapada debajo del video. */
  #view-monitoring:has(#cam-grid:not(.en-ficha)[style*="flex"]) #mpanel,
  #view-monitoring:has(#cam-card[style*="block"]) #mpanel {
    max-height: calc(46% - 20px);
  }
  /* La tarjeta de cámara suelta comparte esquina con la lista: se queda con
     la mitad de abajo (salvo en pantalla completa). */
  #view-monitoring #cam-card:not(.fs) { max-height: calc(52% - 20px); }

  /* Los avisos y las bandas flotantes se referían al panel derecho de 340 px
     que ya no existe: se corren a los nuevos bordes. */
  #avisos { right: 70px; }
  #day-panel { right: 404px; }
  #cmd-panel { right: 404px; }
  #cmd-panel.solo { right: 404px; }
  /* Las cámaras se topan ANTES de la ficha del vehículo (#panel-bottom: abajo-
     derecha, 330px). Así el mosaico no tapa la info del vehículo; al estirarlo,
     camGridLimites() lo frena en el mismo borde. */
  #cam-grid { right: 354px; }
  #cam-grid.con-comandos { right: 744px; }
  #pip { right: 70px; }
  /* Cero superposición: el menú de herramientas y su panel se abrían a la
     izquierda, encima del panel flotante de la lista. Se corren al lado
     derecho (junto a los botones circulares), sin taparla. */
  #view-monitoring #tools-menu { left: auto !important; right: 68px !important; top: 12px !important; }
  #view-monitoring #tool-panel { left: auto !important; right: 68px !important; top: 12px !important; max-height: calc(100% - 24px); }
}

/* ============================================================
   14 · HISTORIAL "TODO FLOTANTE" EN ESCRITORIO (opción A)
   ============================================================
   El mapa ocupa todo. La consulta es una tarjeta flotante a la izquierda con
   la lista de objetos dentro (la mueve histFlotInit); los días van arriba al
   centro; los datos son una hoja inferior plegable cuya cabecera lleva el
   asa, las métricas y las pestañas. Sin datos no hay hoja. */
@media (min-width: 761px) {
  #view-historial > div { position: relative; }
  /* La consulta: tarjeta flotante arriba a la izquierda. Sus estilos vienen
     EN LÍNEA (fondo, borde, radio): !important o no cambia nada. */
  /* La tarjeta al 75% del ancho anterior, en REJILLA de 3 columnas: el
     dispositivo, el contador y los objetos van de lado a lado; las tres
     acciones (Fechas · Buscar · CSV) comparten UNA fila de botones chicos.
     El display y align-items vienen EN LÍNEA de la barra vieja: !important. */
  #view-historial #h-barra {
    position: absolute; top: 10px; left: 10px; z-index: 1210; width: 250px;
    display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px 6px; align-items: stretch !important;
    background: var(--flot) !important; border: 1px solid var(--line) !important;
    border-radius: 14px !important; box-shadow: 0 8px 28px var(--sombra);
    padding: 10px !important; max-height: calc(100% - 20px);
    overflow-y: auto; overflow-x: hidden;
  }
  #h-barra > * { grid-column: 1 / -1; min-width: 0; margin: 0; }
  #h-barra > div:has(> #h-from), #h-barra > div:has(> #h-to) { display: none; }
  /* El buscador que inyecta el observador sobra dentro de la tarjeta. */
  #h-barra .busca-caja { display: none !important; }
  #h-barra #h-dev { width: 100%; min-width: 0 !important; }
  /* Las tres acciones, chicas y en una fila. */
  #h-barra > #h-cal-abrir,
  #h-barra > button[onclick="runHistorial()"],
  #h-barra > button[onclick="exportHistCsv()"] {
    grid-column: auto; width: 100%; min-height: 34px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 6px 4px !important; font-size: 11.5px; white-space: nowrap;
  }
  #h-barra #h-status { margin-left: 0 !important; text-align: center; }
  #h-barra #h-flota-wrap { min-width: 0; overflow: hidden; }
  /* Los objetos, dentro de la tarjeta, alineados con el resto. */
  #h-barra #h-flota-wrap {
    display: flex !important; width: auto !important;
    border-right: 0 !important; border-top: 1px solid var(--line);
    padding-top: 4px; max-height: 30vh; background: transparent !important;
  }
  #h-barra #h-flota-wrap > div:first-child { padding: 2px 0 2px !important; }
  /* La lista de objetos se dimensiona a SU contenido pero con TOPE y scroll, para
     no salir "extra grande". OJO: NO usar flex:1 aquí — el contenedor no tiene alto
     fijo (max-height por contenido), así que flex-grow con base 0 la colapsaría a 0
     y la lista desaparecía. Con max-height + overflow crece hasta el tope y rueda. */
  #h-barra #h-flota { padding: 0 0 4px !important; flex: 0 0 auto !important; max-height: 26vh !important; overflow-y: auto !important; }
  /* El contador pegajoso traía el fondo de la página; sobre la tarjeta
     flotante se veía como una franja gris de lado a lado. */
  #h-barra #h-flota > .k:first-child { background: var(--flot) !important; padding: 2px 0 7px !important; }
  #h-barra .h-flo-item { padding: 8px 9px; font-size: 12.5px; }
  /* La tira de días, arriba al centro del mapa. */
  #view-historial #h-dias {
    display: flex; position: absolute; left: 50%; top: 10px; transform: translateX(-50%);
    z-index: 1205; gap: 6px; max-width: 48%; overflow-x: auto;
    background: transparent; border: 0; padding: 0; scrollbar-width: none;
  }
  #view-historial #h-dias::-webkit-scrollbar { display: none; }
  #view-historial #h-dias button {
    flex: 0 0 auto; min-height: 34px; padding: 0 13px; cursor: pointer;
    background: var(--flot); color: var(--flot-txt);
    border: 1px solid var(--line); border-radius: 999px;
    font-size: 12px; font-weight: 600; box-shadow: 0 2px 8px var(--sombra);
  }
  #view-historial #h-dias button.act { background: var(--acc2); border-color: var(--acc); color: #fff; }
  #view-historial #h-dias button.cal { border-color: var(--acc); }
  /* La hoja inferior: plegada muestra asa + métricas + pestañas + reproductor. */
  #view-historial > div > div:last-child {
    position: absolute; left: 10px; right: 10px; bottom: 10px;
    height: auto !important; min-height: 0 !important; z-index: 1215;
    background: var(--flot); border: 1px solid var(--line);
    border-top: 1px solid var(--line) !important;
    border-radius: 14px; box-shadow: 0 10px 30px var(--sombra);
    padding: 2px 10px 8px;
  }
  /* Abierta ocupa poco más de un cuarto: los registros se recorren con su
     scroll interno y el mapa sigue mandando. */
  #view-historial.h-hoja-abierta > div > div:last-child { height: 28% !important; min-height: 210px !important; }
  /* REGLA 2: con la pestaña de Clips activa la hoja sube hasta el borde de los objetos
     (media pantalla). El alto lo calcula histTab en --sd-alto según dónde termina la
     tarjeta de objetos; si no, cae a 46%. Va DESPUÉS de .h-hoja-abierta para ganarle. */
  #view-historial.h-sd-activo > div > div:last-child { height: var(--sd-alto, 46%) !important; min-height: 300px !important; }
  #view-historial:not(.h-hoja-abierta) #h-grid,
  #view-historial:not(.h-hoja-abierta) #h-trips,
  #view-historial:not(.h-hoja-abierta) #h-sd { display: none !important; }
  /* Sin datos, ni hoja ni métricas: el mapa entero. */
  #view-historial:not(.h-con-datos) > div > div:last-child { display: none; }
  #view-historial #h-hoja-asa {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    cursor: pointer; padding: 3px 0 2px;
  }
  #view-historial #h-hoja-asa span { width: 40px; height: 4px; border-radius: 999px; background: var(--line); }
  #view-historial #h-hoja-asa i { font-style: normal; color: var(--mut); font-size: 13px; line-height: 11px; font-weight: 700; }
  /* Las métricas del resumen, en UNA línea en la cabecera de la hoja. */
  #view-historial #h-summary {
    display: flex !important; flex-wrap: nowrap; gap: 4px; overflow-x: auto;
    width: auto !important; border: 0 !important; background: transparent !important;
    padding: 0 4px !important;
  }
  #view-historial #h-summary h3 { display: none; }
  #view-historial #h-summary .row {
    display: flex; flex-direction: column-reverse; justify-content: flex-end;
    border: 0; padding: 0 12px 0 0; flex: 0 0 auto;
  }
  #view-historial #h-summary .row b { font-size: 10px; line-height: 1.2; }
  #view-historial #h-summary .row .k {
    font-size: 6.5px; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
  }
  /* Todo a media letra y sin aire de sobra (pedido de Ronald): la hoja es de
     barrido, cada punto de relleno le roba una fila al registro. */
  #view-historial > div > div:last-child { padding: 0 8px 4px; }
  #view-historial #h-hoja-asa { padding: 1px 0 0; }
  #view-historial #h-hoja-asa span { height: 3px; width: 34px; }
  #view-historial #h-hoja-asa i { font-size: 10px; line-height: 8px; }
  #view-historial .subnav { margin-bottom: 2px; padding: 2px 4px 0 !important; }
  #view-historial .subnav button {
    font-size: 10.5px !important; padding: 4px 9px !important; min-height: 0 !important;
  }
  #h-grid table { font-size: 9px; }
  #h-grid th { font-size: 7px; padding: 2px 6px; }
  #h-grid td { font-size: 9px; padding: 1.5px 6px; }
  #h-trips .row { font-size: 9.5px; padding: 1px 6px; }
  #h-grid tr.h-trama td { font-size: 8px; }
  #view-historial #h-hoja-cerrar { min-height: 22px; padding: 0 8px; font-size: 11px; }
  /* La ✕ dentro de la fila de pestañas: un botón redondo a la derecha, sin
     estirar la fila (las pestañas siguen con su alto de 4px+texto). */
  #view-historial .subnav #h-hoja-cerrar {
    flex: 0 0 auto; margin-left: auto; align-self: center;
    width: 26px; min-height: 26px !important; padding: 0 !important; border-radius: 50%;
    font-size: 12px !important; line-height: 1;
  }
  #view-historial #h-playbar #h-playbtn { min-height: 26px; padding: 2px 10px; }
  #view-historial #h-playbar #h-speed { padding: 2px 4px !important; font-size: 11px; }
  #view-historial #h-playbar #h-seeklbl { font-size: 9.5px; }
  /* La cabecera de la hoja en una fila: métricas · reproductor · ✕. */
  #view-historial #h-hoja-cab { display: flex; align-items: center; gap: 16px; }
  #view-historial #h-hoja-cab #h-summary { flex: 0 1 auto; }
  #view-historial #h-hoja-cab #h-playbar {
    flex: 1 1 auto; min-width: 220px; margin: 0;
    border: 0 !important; background: transparent !important; padding: 0 !important;
  }
  #view-historial #h-hoja-cab #h-hoja-cerrar {
    flex: 0 0 auto; min-height: 28px; padding: 2px 10px; align-self: flex-start;
  }
  /* El lateral de clips, como tarjeta flotante a la derecha. */
  #view-historial #h-sd-side {
    position: absolute; right: 10px; top: 10px; width: 300px !important;
    max-height: calc(46% - 20px); z-index: 1210;
    background: var(--flot) !important; border: 1px solid var(--line) !important;
    border-radius: 14px;
  }
}
/* El asa de la hoja del historial no aparece en el teléfono. */
#h-hoja-asa { display: none; }

/* Los buscadores inyectados del Historial y del Recorrido de hoy: fuera en
   TODOS los anchos (Ronald, 2026-08-09). OJO: el observador NO siempre mete la
   caja dentro de #h-grid — cuando el contenedor tiene scroll la deja como
   HERMANA, fuera de él. Por eso se tapa por vista entera, no por tabla. */
#view-historial .busca-caja, #day-panel .busca-caja { display: none !important; }

/* La tira de días de los clips de la SD, con las mismas pastillas del
   Historial: un toque pone la fecha y le pregunta a la tarjeta. */
#sdc-dias {
  display: flex; gap: 5px; overflow-x: auto; flex: 0 0 auto;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
  padding: 4px 10px 2px;
}
#sdc-dias::-webkit-scrollbar { display: none; }
#sdc-dias button {
  flex: 0 0 auto; min-height: 24px; padding: 0 10px; cursor: pointer;
  background: var(--card); color: var(--txt);
  border: 1px solid var(--line); border-radius: 999px;
  font-size: 11px; font-weight: 600;
}
#sdc-dias button.act { background: var(--acc2); border-color: var(--acc); color: #fff; }

/* Compactación del panel de Clips SD (16-ago): el timeline ya navega el día, así
   que la fila de botones de HORA sobra; y las barras de control se aprietan para
   no comerse media pantalla. */
#sdc-horas { display: none !important; }
#day-sd #sdc-bar { padding: 4px 10px; gap: 5px; }
#sdc-timeline { padding: 5px 10px 2px !important; }
#tl-track { height: 34px !important; }
.tl-ticks { height: 12px !important; margin-top: 2px !important; }

/* La trama del registro (doble clic en una fila del historial): todas las
   etiquetas tal como llegaron, en letra pequeñita y de máquina. */
#h-grid tr.h-trama td {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 9px; line-height: 1.7; color: var(--mut);
  white-space: normal; word-break: break-word;
  background: var(--bg2); padding: 4px 10px;
}
#h-grid tr.h-trama td b { color: var(--acc); font-weight: 600; }
#h-grid tr.h-trama:hover td { background: var(--bg2); }
#h-grid tr.fila-sel td { background: var(--fila-hover); }

/* El navegador de clips DENTRO de la ficha: mismo contenido, a la medida de
   la tarjeta. Miniaturas CUADRADAS con su hora debajo; la rejilla rueda. */
#sd-clips-host #day-sd { display: flex; flex-direction: column; max-height: 420px; }
#sd-clips-host #sdc-bar { padding: 6px 0; border-bottom: 1px solid var(--line); }
#sd-clips-host #sdc-dias { padding: 4px 0 2px; }
#sd-clips-host #sdc-grid {
  max-height: 260px; padding: 8px 0;
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
}
#sd-clips-host .sdc-mini { aspect-ratio: 1 / 1 !important; min-height: 92px; }
#sd-clips-host .sdc-pie { padding: 3px 5px; font-size: 10px; }
#sd-clips-host .sdc-pie b { font-size: 11px; }
#sd-clips-host #sdc-horas { padding: 6px 0; }

/* Reproductor de la grabación, ahora integrado en la pestaña de clips (ya no en
   un panel flotante aparte). En el Historial el panel es ancho, así que se acota
   para que el video no salga gigante; en la ficha/sidebar ya es angosto. */
#sd-player-box #sd-video { max-height: 42vh; }
#h-sd #sd-player-box { max-width: 640px; margin: 8px auto 4px; }
/* Botón "generar miniaturas": ahora icónico y discreto (era una frase larga que
   amontonaba la barra). Las miniaturas igual se generan solas al abrir la hora. */
#sdc-bar .sdc-sec { opacity: .85; }

/* Rejilla de miniaturas: SOLO 2 filas a la vista y el resto con scroll (pedido de
   Ronald). Se fija el alto de fila para que "2 filas" sea exacto sin depender del
   ancho de cada cuadro. #sdc-grid siempre vive dentro de #day-sd (en la ficha, en
   el panel del día y en el Historial), así que este selector cubre los tres. */
#day-sd #sdc-grid {
  flex: none;
  overflow-y: auto;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  grid-auto-rows: 112px;
  max-height: 254px;         /* 10 (pad) + 112 + 10 (gap) + 112 + 10 (pad) */
}
#day-sd .sdc-clip { display: flex; flex-direction: column; }
#day-sd .sdc-mini { flex: 1 1 auto; aspect-ratio: auto; min-height: 0; }
#day-sd .sdc-pie { flex: 0 0 auto; }
/* En el Historial, day-sd se reubica en #h-sd con display:flex pero SIN dirección:
   sus hijos (barra, horas, reproductor, rejilla) salían en fila y las miniaturas
   se veían como "líneas". Se fuerza columna para que se apilen y la rejilla mande. */
#h-sd #day-sd { display: flex; flex-direction: column; flex: 1; min-height: 0; }
#h-sd #sdc-grid { max-height: none; flex: 1; }

/* --- Clips de la SD en el PANEL INFERIOR (pestaña "Clips de la SD"): al abrirla la
   ventana se AGRANDA y las miniaturas cargan en su PROPORCIÓN REAL 16:9 (grandes, no
   "líneas chiquitas"), en una tira de 2 FILAS que se navega con SCROLL LATERAL. Pedido
   de Ronald (15-ago-2026). Solo aplica al panel del día; ficha/historial/ventana
   ampliada conservan su propio layout. --- */
#day-panel.con-clips { max-height: 72%; }
#day-panel.con-clips #day-sd #sdc-grid {
  display: grid !important;
  grid-auto-flow: column !important;
  grid-template-columns: none !important;
  grid-template-rows: repeat(2, min-content) !important;
  grid-auto-columns: 240px !important;
  gap: 12px !important;
  overflow-x: auto !important;
  overflow-y: hidden !important;
  max-height: none !important;
  padding: 12px !important;
  align-content: start !important;
}
#day-panel.con-clips #day-sd .sdc-clip { display: flex; flex-direction: column; }
#day-panel.con-clips #day-sd .sdc-mini { aspect-ratio: 16 / 9 !important; min-height: 0 !important; flex: none !important; height: auto !important; }
#day-panel.con-clips #day-sd .sdc-pie { flex: 0 0 auto; }
/* En pantallas medianas (tablet), miniaturas algo más pequeñas para que quepan 2 filas. */
@media (max-width: 760px) and (min-width: 601px) {
  #day-panel.con-clips #day-sd #sdc-grid { grid-auto-columns: 200px !important; }
}
/* Pantalla MUY pequeña (teléfono): UNA SOLA fila de miniaturas con scroll lateral,
   para no comerse el alto. Pedido de Ronald. */
@media (max-width: 600px) {
  #day-panel.con-clips #day-sd #sdc-grid {
    grid-template-rows: min-content !important;   /* 1 fila */
    grid-auto-columns: min(80vw, 260px) !important;
    flex: 0 0 auto !important;                     /* que la tira no crezca: el panel se ajusta a 1 fila */
  }
  #day-panel.con-clips { max-height: 66%; }
}

/* --- BARRA INFERIOR DE ANCHO COMPLETO Y BAJA (16-ago-2026, pedido de Ronald) ---
   El timeline ya navega el día, así que el panel deja de ser un bloque alto que
   tapa medio mapa: va de ancho COMPLETO (right:0), más bajo, con las miniaturas en
   UNA sola fila y el video a su lado (no apilado). --- */
#day-panel { right: 0 !important; }
#day-panel.con-clips { max-height: 46% !important; }
#sd-lower { display: flex; gap: 10px; padding: 0 10px 8px; align-items: flex-start; }
#day-panel.con-clips #day-sd #sd-player-box { flex: 0 0 clamp(260px, 34vw, 420px); max-width: 420px; margin: 8px 0 0; }
#day-panel.con-clips #day-sd #sd-video { max-height: 34vh; }
/* Las miniaturas se ocultan: el timeline ya es la navegación (como TrackSolid), así
   navegar el día deja el panel bajito. Se pueden mostrar con el botón 🖼️ de la barra. */
#day-panel.con-clips.sin-minis #day-sd #sdc-grid { display: none !important; }
#day-panel.con-clips #day-sd #sdc-grid {
  grid-template-rows: min-content !important;      /* UNA sola fila (antes 2) */
  grid-auto-columns: 170px !important;
  flex: 1 1 auto !important; min-width: 0 !important;
  margin: 0 !important;
}
@media (max-width: 760px) {
  #sd-lower { flex-direction: column; }
  #day-panel.con-clips #day-sd #sd-player-box { flex: none; max-width: none; width: 100%; }
  #day-panel.con-clips { max-height: 52% !important; }
}

/* ---- CLIPS DE LA SD EN VENTANA AMPLIA ----
   Al abrir "Grabaciones de la SD", el panel deja de ser la tarjeta angosta de la
   ficha y se abre como ventana flotante centrada: miniaturas 16:9 REALES en una
   rejilla de varias columnas, con scroll vertical cómodo (no la tira chiquita). */
#sd-veil { display: none; position: fixed; inset: 0; background: var(--velo); z-index: 10055; }
#sd-panel.sd-expandido {
  position: fixed; z-index: 10060;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(1040px, 95vw); max-height: 90vh; margin: 0 !important;
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 24px 70px var(--sombra); padding: 12px 16px; overflow: hidden;
}
#sd-panel.sd-expandido > .dh { margin-bottom: 10px; }
#sd-panel.sd-expandido #sd-clips-host { flex: 1; min-height: 0; display: flex; overflow: hidden; }
#sd-panel.sd-expandido #sd-clips-host #day-sd { max-height: none !important; flex: 1; min-height: 0; }
#sd-panel.sd-expandido #sd-clips-host #sdc-grid {
  max-height: none !important; padding: 10px 2px;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)) !important;
}
#sd-panel.sd-expandido #sd-clips-host .sdc-mini { aspect-ratio: 16 / 9 !important; min-height: 0 !important; }
#sd-panel.sd-expandido #sd-clips-host .sdc-pie { padding: 5px 8px; font-size: 11.5px; }
#sd-panel.sd-expandido #sd-clips-host .sdc-pie b { font-size: 12px; }
/* el reproductor del clip queda abajo, sin comerse la rejilla */
#sd-panel.sd-expandido #sd-player-box { max-height: 46vh; overflow: auto; margin-top: 10px; }
#sd-panel.sd-expandido #sd-player-box video { max-height: 36vh; }
@media (max-width: 760px) {
  #sd-panel.sd-expandido { width: 96vw; max-height: 92vh; padding: 10px 12px; }
  #sd-panel.sd-expandido #sd-clips-host #sdc-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)) !important; }
}

/* La tira de días del historial solo existe en el teléfono. */
#h-dias { display: none; }

/* El botón de plegar la consulta solo existe en el teléfono. */
#h-plegar { display: none; }

/* La barra de volver y el asa del modo móvil no existen en escritorio. Van
   fuera de la consulta de medios porque los elementos quedan en el DOM si la
   ventana se agranda después. */
#mv-volver { display: none; }
#mpanel .hoja-asa { display: none; }
#veh-cab, #veh-velo { display: none; }

/* ---------- CÁMARAS DENTRO DE LA TARJETA DEL VEHÍCULO ----------
   Pedido de Ronald (10-sep-2026): las cámaras se abren DENTRO de la tarjeta
   donde están los sensores, no como una banda aparte sobre el mapa, y con su
   propio scroll para no comerse la pantalla. El mosaico a pantalla completa
   (.mfs, botón ▦) y el canal ampliado siguen saliendo por encima de todo. */
#cam-grid.en-ficha:not(.mfs) {
  position: relative; left: auto; right: auto; top: auto; bottom: auto;
  z-index: auto; pointer-events: auto;
  max-height: 46vh; overflow-y: auto; overflow-x: hidden;
  margin: 8px 0 2px; padding: 5px 5px 6px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--fond);
}
#cam-grid.en-ficha:not(.mfs) > * { pointer-events: auto; }
/* Una cámara por fila y a todo el ancho de la tarjeta: el cálculo automático
   (columnas de ~230 px pensadas para la banda sobre el mapa) dejaba un hueco. */
#cam-grid.en-ficha:not(.mfs) #cam-grid-body {
  padding: 0; margin: 0; justify-content: stretch;
  grid-template-columns: 1fr !important; grid-auto-rows: auto !important;
}
#cam-grid.en-ficha:not(.mfs) .cam-mini { width: auto !important; max-width: none; min-width: 0; }
#cam-grid.en-ficha:not(.mfs) #cam-grid-grip,
#cam-grid.en-ficha:not(.mfs) #cam-grid-restore { display: none !important; }
#cam-grid.en-ficha:not(.mfs) #cam-mosaic-bar {
  display: flex; align-items: center; gap: 6px; padding: 2px 2px 6px;
  border-bottom: 1px solid var(--line); margin-bottom: 6px; background: transparent;
}
#cam-grid.en-ficha:not(.mfs) #cam-mosaic-bar b { font-size: 12px; }
#cam-grid.en-ficha:not(.mfs) #mos-open { position: static; margin-left: auto; }

/* ============================================================
   15 · ALTA DENSIDAD — Usuarios y Objetos (modo gerente)
   ============================================================
   Menos texto, más iconos. Botones de acción icónicos con tooltip, tabla
   compacta y fuentes reducidas para que quepa más sin rodar la pantalla. */

/* Botón icónico: cuadrado, compacto, solo el trazo. El tooltip lo da title. */
.btn-sm.ic-b {
  padding: 4px 6px; min-height: 0; line-height: 0;
  display: inline-flex; align-items: center; justify-content: center;
  margin: 1px; vertical-align: middle;
}
.btn-sm.ic-b .ic { width: 15px; height: 15px; vertical-align: middle; }
.btn-sm.ic-b.mb-obj { line-height: 1; font-size: 11px; padding: 3px 7px; }
/* Acciones en UNA sola línea. Antes iba con flex-wrap:wrap y los 6 botones se
   envolvían en varias filas, estirando el renglón ~75px de alto. En nowrap se
   quedan en línea (la tabla ya tiene scroll horizontal si no caben). */
#atab-users td.acciones, #atab-devices td.acciones { white-space: nowrap; }
#atab-users td.acciones > *, #atab-devices td.acciones > * { display: inline-flex; vertical-align: middle; }
/* Filas parejas y bajas: el contenido centrado, sin aire de más. */
#atab-users td, #atab-devices td { vertical-align: middle; }

/* Densidad de las tablas de Usuarios y Objetos: fuente y relleno reducidos. */
#atab-users table, #atab-devices table { font-size: 11px; }
#atab-users th, #atab-devices th { font-size: 9px; padding: 4px 6px; }
#atab-users td, #atab-devices td { padding: 3px 6px; }
#atab-users td .k, #atab-devices td .k { font-size: 9.5px; }
#atab-users td b, #atab-devices td b { font-weight: 600; }

/* Popup de la membresía fusionada: desglose del costo con cámaras incluidas. */
#mb-fusion {
  display: none; position: fixed; z-index: 9600; right: 24px; bottom: 24px;
  width: 260px; background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--elev-alta); padding: 10px 12px;
}
#mb-fusion .mbf-h { display: flex; align-items: center; justify-content: space-between; font-weight: 600; font-size: 13px; margin-bottom: 8px; }
#mb-fusion .mbf-x { background: none; border: 0; color: var(--mut); font-size: 15px; cursor: pointer; padding: 0 2px; }
#mb-fusion .mbf-l { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; padding: 3px 0; color: var(--mut); }
#mb-fusion .mbf-l b { color: var(--txt); }
#mb-fusion .mbf-sep { font-size: 10px; letter-spacing: .03em; text-transform: uppercase; color: var(--mut); margin: 8px 0 6px; padding-top: 6px; border-top: 1px solid var(--line); }
#mb-fusion .mbf-op { width: 100%; display: flex; justify-content: space-between; align-items: center; margin-top: 6px; padding: 8px 10px; }
#mb-fusion .mbf-op b { color: var(--acc); }

/* SEGUIR: mini-mapa flotante y arrastrable. Cero superposición con el resto:
   flota por encima y se mueve donde el gerente lo deje. */
#seguir-box {
  display: none; position: fixed; z-index: 9500; right: 24px; top: 90px;
  width: 300px; height: 280px; flex-direction: column;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--elev-alta); overflow: hidden;
}
#seguir-cab {
  display: flex; align-items: center; gap: 7px; padding: 7px 10px;
  border-bottom: 1px solid var(--line); cursor: move; user-select: none;
}
#seguir-cab .mira { display: flex; color: var(--acc); }
#seguir-cab b { flex: 1; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#seguir-x { background: none; border: 0; color: var(--mut); font-size: 15px; cursor: pointer; padding: 0 2px; }
#seguir-map { flex: 1; min-height: 0; }
#seguir-pie { padding: 5px 10px; border-top: 1px solid var(--line); font-size: 11px; }

/* Sensores: el observador le pega un buscador al selector de vehículo, y ahí
   no sirve (el desplegable ya busca al teclear). Fuera. Y el selector de
   vehículo se ve como lo que es: la entrada principal de la pantalla. */
#atab-sensores .busca-caja { display: none !important; }
#atab-sensores #sl-dev { margin-top: 2px; }
#atab-sensores label[class="k"] { margin-top: 10px; }


/* ---------- CRM ---------- */
.crm-tab.act { background: var(--acc) !important; color: #fff !important; border-color: var(--acc) !important; }
#crm-lista .crm-item { padding: 9px 10px; border-bottom: 1px solid var(--line); cursor: pointer; }
#crm-lista .crm-item:hover { background: var(--hover); }
#crm-lista .crm-item.sel { background: var(--hover); border-left: 3px solid var(--acc); }
/* La burbuja: entrada a la izquierda, salida a la derecha, como cualquier chat.
   Sin esto no se distingue de un vistazo quien dijo que. */
.crm-burb { max-width: 74%; padding: 7px 10px; border-radius: 12px; margin-bottom: 7px; font-size: 13px; line-height: 1.45; word-break: break-word; }
.crm-burb.ent { background: var(--card); border: 1px solid var(--line); margin-right: auto; border-bottom-left-radius: 3px; }
.crm-burb.sal { background: #14532d; color: #eafff1; margin-left: auto; border-bottom-right-radius: 3px; }
.crm-burb .hora { font-size: 10px; opacity: .65; display: block; margin-top: 3px; }
/* Tablero del embudo: columnas que se desplazan de lado, no se apilan. */
.crm-cols { display: flex; gap: 10px; align-items: flex-start; overflow-x: auto; padding-bottom: 8px; }
.crm-col { flex: 0 0 230px; background: var(--fond); border: 1px solid var(--line); border-radius: 10px; padding: 8px; }
.crm-col h4 { margin: 0 0 6px; font-size: 12.5px; display: flex; align-items: center; gap: 6px; }
.crm-tarjeta { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 8px; margin-bottom: 6px; font-size: 12px; }
@media (max-width: 900px) {
  #crm-p-bandeja { flex-direction: column; }
  #crm-p-bandeja > div { flex: 1 1 auto !important; }
  #crm-ficha { display: none; }
}

/* Arrastrar en el embudo: la columna destino se marca y la tarjeta en vuelo se
   atenua, para que se vea donde va a caer antes de soltar. */
.crm-col .crm-drop { min-height: 40px; }
.crm-col.encima { border-color: var(--acc); box-shadow: inset 0 0 0 2px var(--acc); }
.crm-tarjeta[draggable] { cursor: grab; }
.crm-tarjeta.arrastrando { opacity: .45; cursor: grabbing; }


/* ---------- CRM: bandeja estilo chat ----------
   En escritorio son tres columnas a la vista. En el telefono NO caben, y
   apilarlas obliga a bajar media pantalla para leer: se navega como en
   WhatsApp — lista, entras al chat, vuelves con la flecha. */
.crm-pane { display:flex; flex-direction:column; min-height:0; overflow:hidden;
  border:1px solid var(--line); border-radius:10px; background:var(--card); }
.crm-pane-lista { flex:0 0 300px; }
.crm-pane-chat  { flex:1 1 auto; min-width:0; }
.crm-pane-ficha { flex:0 0 250px; overflow:auto; padding:10px; }

.crm-cab { display:flex; align-items:center; gap:9px; padding:8px 10px;
  border-bottom:1px solid var(--line); background:var(--fond); flex:0 0 auto; }
.crm-volver { display:none; background:none; border:0; color:var(--acc);
  font-size:22px; line-height:1; cursor:pointer; padding:0 4px; width:auto; margin:0; }
.crm-cab-tx { flex:1 1 auto; min-width:0; }
.crm-cab-tx b { display:block; font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.crm-cab-tx span { font-size:11px; color:var(--mut); }

/* Avatar con iniciales: distingue de un vistazo sin cargar ninguna imagen. */
.crm-ava { flex:0 0 auto; width:38px; height:38px; border-radius:50%;
  display:grid; place-items:center; font-size:14px; font-weight:700; color:#fff; }
.crm-item .crm-ava { width:34px; height:34px; font-size:12.5px; }
.crm-item { display:flex; gap:9px; align-items:flex-start; }
.crm-item > div:last-child { flex:1 1 auto; min-width:0; }

/* Separador de dia, como en cualquier chat: sin esto un hilo largo es un muro. */
.crm-dia { text-align:center; margin:10px 0 8px; }
.crm-dia span { background:var(--card); border:1px solid var(--line); color:var(--mut);
  font-size:10.5px; padding:3px 10px; border-radius:10px; }

@media (max-width: 900px) {
  .crm-bandeja { gap:0 !important; }
  .crm-pane { border:0; border-radius:0; }
  .crm-pane-lista { flex:1 1 auto !important; }
  .crm-pane-chat, .crm-pane-ficha { display:none; }
  .crm-bandeja.chat .crm-pane-lista { display:none; }
  .crm-bandeja.chat .crm-pane-chat  { display:flex; flex:1 1 auto !important; }
  .crm-bandeja.ficha .crm-pane-lista, .crm-bandeja.ficha .crm-pane-chat { display:none; }
  .crm-bandeja.ficha .crm-pane-ficha { display:block; flex:1 1 auto !important; }
  .crm-volver { display:block; }
}


/* ---------- CRM: medios en el hilo ----------
   Una foto tiene que VERSE en la conversacion. Cuando solo se mostraba un
   enlace, quien atendia tenia que abrir una pestana por cada imagen para saber
   que le habian mandado, y en la practica no las miraba. */
.crm-burb img.crm-img { display:block; max-width:min(260px, 60vw); max-height:280px;
  width:auto; height:auto; border-radius:8px; cursor:zoom-in; background:var(--fond); }
.crm-burb video.crm-vid { display:block; max-width:min(260px, 60vw); border-radius:8px; }
.crm-burb audio.crm-aud { display:block; width:min(240px, 58vw); margin:2px 0; }
.crm-burb .crm-doc { display:inline-flex; align-items:center; gap:6px; color:inherit;
  text-decoration:none; padding:6px 8px; border-radius:8px; background:rgba(127,127,127,.14); }
.crm-burb .crm-pie { display:block; margin-top:4px; }

/* Lo que se va a mandar, a la vista antes de mandarlo: sin esto se adjunta a
   ciegas y el archivo equivocado se descubre cuando ya salio. */
.crm-adj { display:flex; align-items:center; gap:8px; margin-bottom:6px; padding:6px 8px;
  border:1px solid var(--line); border-radius:8px; background:var(--fond); }
.crm-adj img { width:44px; height:44px; object-fit:cover; border-radius:6px; }
.crm-adj .nom { flex:1 1 auto; min-width:0; font-size:12px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.crm-adj .qt { background:none; border:0; color:var(--mut); font-size:18px; cursor:pointer; padding:0 4px; }

/* Visor a pantalla completa al tocar una imagen. */
.crm-visor { position:fixed; inset:0; z-index:9999; background:rgba(0,0,0,.9);
  display:grid; place-items:center; cursor:zoom-out; }
.crm-visor img { max-width:94vw; max-height:92vh; border-radius:6px; }


/* Etiqueta de casa en la bandeja unida. Discreta a proposito: la conversacion es
   lo que se lee, la casa solo hace falta para saber por que linea contestar. */
.crm-casa-tag { display:inline-block; font-size:9.5px; letter-spacing:.4px;
  text-transform:uppercase; padding:1px 6px; border-radius:8px; margin-top:3px;
  background:rgba(127,127,127,.16); color:var(--mut); }


/* Cuentas con acceso a una casa del CRM. */
.crm-chip { display:inline-flex; align-items:center; gap:6px; font-size:12px;
  padding:3px 9px; border-radius:12px; background:rgba(127,127,127,.16); }
.crm-chip a { color:var(--mut); text-decoration:none; font-size:14px; line-height:1; }
.crm-chip a:hover { color:#f87171; }


/* El motivo completo de un envio fallido, desplegable dentro de la burbuja. */
.crm-burb .crm-err { display:block; margin-top:4px; font-size:10.5px; opacity:.85;
  word-break:break-word; white-space:pre-wrap; max-height:200px; overflow:auto; }


/* Sticker en el hilo: tamano de sticker, sin marco ni fondo. */
.crm-burb img.crm-stk { display:block; width:140px; height:140px; object-fit:contain; background:transparent; }

/* Bandeja del CRM: filtro "solo sin leer". Encendido = rojo como el globo de no leidos. */
.crm-noleidos { white-space:nowrap; font-size:11.5px; }
.crm-noleidos.on { background:#ef4444; color:#fff; border-color:#ef4444; }

/* Logo de la marca en el login. Un archivo por instancia en /marca/logo.png;
   si la instancia no tiene, el <img> se quita solo y queda solo el nombre. */
.lbox h1 .marca-logo { height:46px; width:auto; vertical-align:middle; margin-right:10px; }
@media (max-width:480px) { .lbox h1 .marca-logo { height:38px; margin-right:7px; } }

/* El logo tambien en la barra lateral, al lado del nombre. */
.ln-logo .marca-logo-mini { height:22px; width:auto; vertical-align:middle; margin-right:2px; }

/* ---------- FILTROS COMPACTOS (una sola fila) — SOLO ESCRITORIO ----------
   Ocupaban tres filas porque cada control llevaba su etiqueta encima. Los
   selectores ya se nombran solos ("Todos los roles"), asi que la etiqueta sobra.
   Los rangos de creditos si la conservan —un "min/max" suelto no dice nada— y
   por eso viven detras del embudo.

   VA DENTRO DE min-width:761px A PROPOSITO: en el celular estos mismos filtros
   YA se pliegan con otro mecanismo (movilInit + .fl-plegada, hecho el 8-sep).
   Como `.filtros.compacta` tiene mas especificidad que `.filtros`, sin esta
   frontera le ganaria a las reglas moviles y desarmaria esa pantalla. */
@media (min-width: 761px) {
  .filtros.compacta {
    display: flex !important; flex-wrap: wrap; align-items: center;
    gap: 8px; padding: 10px var(--e3);
  }
  /* 130 de base: con la caja en ~1110 px entran los siete en UNA fila
     (7x130 + 6 huecos = 958). Mas ancho y el ultimo se cae al renglon de abajo. */
  .filtros.compacta .campo { flex: 1 1 130px !important; max-width: 230px !important; }
  .filtros.compacta .campo > span { display: none; }          /* la etiqueta */
  .filtros.compacta .winp,
  .filtros.compacta input,
  .filtros.compacta select { height: 34px; font-size: 13px; }

  /* Lupa dentro de los buscadores, como en la plataforma de referencia. */
  .filtros.compacta input[type="search"] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: 10px center; background-size: 15px;
    padding-left: 32px;
  }

  /* Los avanzados aparecen solo con el embudo encendido, y ahi si con etiqueta. */
  .filtros.compacta .campo.avanzado { display: none; }
  .filtros.compacta.con-avanzados .campo.avanzado { display: flex; }
  .filtros.compacta.con-avanzados .campo.avanzado > span { display: block; }
  /* El conteo se muestra arriba, junto al titulo: aqui ocupaba un renglon entero. */
  .filtros.compacta .cuenta { display: none; }
}

.btn-filtro {
  width: auto !important; padding: 0 10px;
  display: inline-flex !important; align-items: center; gap: 3px;
}
.btn-filtro [data-ico] svg { width: 15px; height: 15px; }
.btn-filtro.abierto { background: var(--line); }
/* El numerito: cuantos filtros hay puestos, contando los escondidos. */
.filtro-pin { display: none; }
.btn-filtro.con-filtros { background: var(--acc, #38bdf8); color: #06131f; font-weight: 700; }
.btn-filtro.con-filtros .filtro-pin {
  display: inline-block; font-size: 11px; font-weight: 700; background: #06131f; color: #fff;
  border-radius: 9px; padding: 0 6px; line-height: 16px; margin-left: 3px;
}

/* En el celular manda el plegado de siempre (movilInit + .fl-plegada): este
   boton sobraria. Va al FINAL del archivo a proposito: tiene la misma
   especificidad que la regla que lo pinta y ambas llevan !important, asi que
   decide el orden. Puesta antes, no escondia nada. */
@media (max-width: 760px) { .btn-filtro { display: none !important; } }

/* ============================================================
   CHAT CON CLIENTES — apariencia de app de mensajeria
   Las reglas del telefono (lista y conversacion como dos pantallas) siguen mas
   arriba en este mismo archivo, colgadas de #chat-cols / .chat-abierto: por eso
   el HTML conserva esos ids.
   ============================================================ */
#view-chat.act { display: flex; padding: 12px; box-sizing: border-box; }
#chat-cols { display: flex; gap: 10px; flex: 1; min-height: 0; width: 100%; }

/* ---------- columna de conversaciones ---------- */
#chat-col-hilos {
  width: 300px; flex: 0 0 300px; display: flex; flex-direction: column;
  min-height: 0; padding: 0; overflow: hidden;
}
.chat-lista-top { padding: 10px 10px 8px; border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.chat-lista-top h2 { margin: 0 0 8px; font-size: 15px; }
.chat-lista-top input { margin: 0; height: 32px; font-size: 13px; }
#chat-hilos { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.chat-lista-pie { flex: 0 0 auto; border-top: 1px solid var(--line); padding: 8px; max-height: 45%; overflow: auto; }

/* Cada conversacion: foto, nombre, hora, adelanto y no leidos. */
.hilo {
  display: flex; gap: 9px; align-items: center; padding: 9px 10px; cursor: pointer;
  border-bottom: 1px solid var(--line);
}
.hilo:hover { background: rgba(127,127,127,.09); }
.hilo.act { background: rgba(56,189,248,.14); }
.hilo .ava { flex: 0 0 38px; }
.hilo-tx { flex: 1 1 auto; min-width: 0; }
.hilo-l1 { display: flex; justify-content: space-between; gap: 6px; align-items: baseline; }
.hilo-l1 b { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hilo-l1 span { font-size: 10.5px; color: var(--mut); white-space: nowrap; }
.hilo-l2 {
  display: flex; justify-content: space-between; gap: 6px; align-items: center;
  font-size: 12px; color: var(--mut); margin-top: 2px;
}
.hilo-l2 .prev { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hilo.sin-leer .hilo-l1 b, .hilo.sin-leer .hilo-l2 .prev { color: var(--tx, inherit); font-weight: 600; }
.chat-pin {
  background: #ef4444; color: #fff; border-radius: 10px; font-size: 10px; font-weight: 700;
  padding: 1px 6px; flex: 0 0 auto;
}

/* Foto redonda con iniciales: el mismo truco de la bandeja del CRM. */
.ava {
  width: 38px; height: 38px; border-radius: 19px; display: flex; align-items: center;
  justify-content: center; color: #fff; font-weight: 700; font-size: 14px; flex: none;
}

/* ---------- la conversacion ---------- */
#chat-col-msgs {
  flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0;
  padding: 0; overflow: hidden; position: relative;
}
#chat-cab {
  display: flex; align-items: center; gap: 9px; padding: 9px 12px;
  border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
#chat-cab-tx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
#chat-cab-tx b { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#chat-sub { font-size: 11px; }
#chat-volver { display: none; }

#chat-busca-hilo { display: none; gap: 6px; align-items: center; padding: 7px 10px; border-bottom: 1px solid var(--line); }
#chat-busca-hilo.abierto { display: flex; }
#chat-busca-hilo input { margin: 0; height: 30px; font-size: 13px; flex: 1; }
#chat-qh-n { font-size: 11px; white-space: nowrap; }

#chat-msgs {
  flex: 1 1 auto; overflow-y: auto; min-height: 0;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 3px;
  scroll-behavior: smooth;
}

/* ---------- burbujas ---------- */
.msg { max-width: 74%; align-self: flex-start; position: relative; }
.msg.mio { align-self: flex-end; }
.msg .glob {
  background: rgba(127,127,127,.16); border-radius: 12px 12px 12px 3px;
  padding: 7px 10px; font-size: 13.5px; line-height: 1.42; word-break: break-word;
}
.msg.mio .glob { background: var(--acc2); color: #fff; border-radius: 12px 12px 3px 12px; }
/* Mensajes seguidos del mismo lado: se pegan, como en cualquier mensajero. */
.msg.sigue { margin-top: -1px; }
.msg.sigue .glob { border-radius: 12px; }
.msg .txt { white-space: pre-wrap; }
.msg .meta {
  display: flex; align-items: center; justify-content: flex-end; gap: 4px;
  font-size: 9.5px; opacity: .72; margin-top: 3px;
}
.msg .tick { font-size: 11px; letter-spacing: -2px; }
.msg .tick.visto { color: #7dd3fc; }
.msg.mio .tick.visto { color: #d9f2ff; }
.msg.enviando .glob { opacity: .6; }
.msg.fallo .glob { outline: 1px solid #ef4444; }
.msg.borrado .glob { font-style: italic; opacity: .6; }

/* cita del mensaje al que se responde */
.msg .cita {
  border-left: 3px solid rgba(255,255,255,.55); padding: 3px 7px; margin-bottom: 5px;
  background: rgba(0,0,0,.14); border-radius: 5px; font-size: 12px; opacity: .92; cursor: pointer;
}
.msg:not(.mio) .cita { border-left-color: var(--acc); background: rgba(127,127,127,.14); }
.msg .cita b { display: block; font-size: 11px; }
.msg .cita span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* adjuntos */
.msg img.foto { max-width: 100%; max-height: 260px; border-radius: 9px; display: block; cursor: zoom-in; }
.msg audio { max-width: 232px; display: block; }
.msg video { max-width: 100%; max-height: 260px; border-radius: 9px; display: block; }
.msg .doc { display: flex; align-items: center; gap: 7px; font-weight: 600; text-decoration: none; color: inherit; }

/* acciones de cada mensaje (responder / eliminar), a la vista solo al pasar por encima */
.msg .acc {
  position: absolute; top: 2px; display: none; gap: 2px;
  background: var(--card); border: 1px solid var(--line); border-radius: 7px; padding: 1px;
}
.msg.mio .acc { left: -58px; }
.msg:not(.mio) .acc { right: -58px; }
.msg:hover .acc { display: flex; }
.msg .acc button {
  background: none; border: 0; cursor: pointer; font-size: 12px; padding: 2px 4px;
  color: var(--tx, inherit); line-height: 1;
}
.msg .acc button:hover { color: var(--acc); }

/* separador de dia y de mensajes nuevos */
.msg-dia { align-self: center; font-size: 10.5px; color: var(--mut); background: rgba(127,127,127,.13);
  padding: 2px 10px; border-radius: 10px; margin: 8px 0 4px; }
.msg-nuevos { align-self: stretch; text-align: center; font-size: 10.5px; color: #ef4444;
  border-top: 1px solid #ef4444; margin: 8px 0 2px; }
.msg-nuevos span { background: var(--card); padding: 0 8px; position: relative; top: -8px; }

/* resaltado al buscar dentro de la conversacion */
.msg mark { background: #fde047; color: #111; border-radius: 2px; }
.msg.coincide .glob { outline: 2px solid var(--acc); }

/* "escribiendo…" */
.escribiendo { display: inline-flex; align-items: center; gap: 3px; }
.escribiendo i { width: 4px; height: 4px; border-radius: 2px; background: currentColor; display: inline-block;
  animation: chatpunto 1.1s infinite; }
.escribiendo i:nth-child(2) { animation-delay: .18s; }
.escribiendo i:nth-child(3) { animation-delay: .36s; }
@keyframes chatpunto { 0%,60%,100% { opacity: .25 } 30% { opacity: 1 } }

/* bajar al ultimo */
#chat-abajo {
  position: absolute; right: 16px; bottom: 74px; z-index: 5;
  background: var(--acc); color: #06131f; border: 0; border-radius: 16px;
  padding: 5px 11px; font-size: 12px; font-weight: 700; cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,.35);
}

/* barra de "respondiendo a" */
#chat-citando {
  display: flex; align-items: center; gap: 8px; padding: 6px 12px;
  border-top: 1px solid var(--line); background: rgba(127,127,127,.08); flex: 0 0 auto;
}
#chat-citando .cita-tx { flex: 1; min-width: 0; border-left: 3px solid var(--acc); padding-left: 8px; font-size: 12px; }
#chat-citando .cita-tx b { display: block; font-size: 11px; color: var(--acc); }
#chat-citando .cita-tx span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--mut); }

/* ---------- barra de escritura ---------- */
#chat-reply {
  display: flex; align-items: flex-end; gap: 6px; padding: 9px 10px;
  border-top: 1px solid var(--line); flex: 0 0 auto;
}
#chat-reply textarea {
  flex: 1; margin: 0; resize: none; max-height: 132px; min-height: 36px;
  line-height: 1.4; font-size: 13.5px; padding: 8px 10px; overflow-y: auto;
}
.chat-ico {
  width: 36px !important; height: 36px; flex: 0 0 36px; border-radius: 18px;
  padding: 0 !important; font-size: 16px; display: flex; align-items: center; justify-content: center;
}
.chat-enviar { background: var(--acc); color: #06131f; font-weight: 700; }
#chat-btn-voz.grabando { background: #ef4444; color: #fff; animation: chatlatido 1s infinite; }
@keyframes chatlatido { 50% { opacity: .55 } }

/* panel de emojis */
#chat-emojis {
  position: absolute; bottom: 62px; left: 10px; z-index: 20; width: 268px; max-height: 210px;
  overflow-y: auto; background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 7px; box-shadow: 0 6px 20px rgba(0,0,0,.4);
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px;
}
#chat-emojis button { background: none; border: 0; font-size: 19px; cursor: pointer; padding: 3px; border-radius: 6px; }
#chat-emojis button:hover { background: rgba(127,127,127,.2); }

/* visor de imagenes */
#chat-visor {
  position: fixed; inset: 0; z-index: 99999; background: rgba(0,0,0,.9);
  display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 12px; cursor: zoom-out;
}
#chat-visor img { max-width: 92vw; max-height: 82vh; border-radius: 8px; }

/* ---------- telefono ---------- */
@media (max-width: 760px) {
  #view-chat.act { padding: 0; }
  #chat-cols { gap: 0; }
  #chat-col-hilos { width: 100%; flex: 1 1 auto; border-radius: 0; }
  #chat-col-msgs { border-radius: 0; }
  #view-chat.chat-abierto #chat-volver { display: flex !important; }
  .msg { max-width: 86%; }
  .msg .acc { position: static; display: flex; margin-top: 3px; background: none; border: 0; padding: 0; opacity: .65; }
  #chat-emojis { width: calc(100vw - 20px); }
}
