:root {
  --bg: #0f141a; --panel: #182029; --line: #26313d; --txt: #e6edf3; --muted: #8b98a5;
  --primary: #2f81f7; --danger: #d1332e; --ok: #2ea043; --off: #6e7681;
}
* { box-sizing: border-box; }
body { margin: 0; font: 15px/1.45 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: var(--bg); color: var(--txt); }
h1 { font-size: 18px; margin: 0; } h1 small { color: var(--muted); font-weight: 400; }
h2 { font-size: 14px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 0 0 10px; }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 16px; }
.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.row.wrap { flex-wrap: wrap; justify-content: flex-start; margin-top: 10px; }
/* Sin `cursor:pointer`: la fila ya no es pulsable. La ficha se abre con su 🔧, y un cursor de mano sobre
   todo el renglón prometía un clic que no hace nada. */
.ep { display: flex; align-items: center; gap: 10px; padding: 10px; border: 1px solid var(--line); border-radius: 8px; margin-bottom: 8px; }
/* Equipo que solicitó soporte: card resaltada (parpadeo suave). Se limpia al hacer clic. */
.ep.support { border-color: var(--danger); background: #2a1416; animation: sos 1.4s ease-in-out infinite; }
@keyframes sos { 0%,100% { box-shadow: 0 0 0 0 rgba(209,51,46,.5); } 50% { box-shadow: 0 0 0 4px rgba(209,51,46,0); } }
@media (prefers-reduced-motion: reduce) { .ep.support { animation: none; } }
.ep .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--off); flex: 0 0 auto; }
.ep.online .dot { background: var(--ok); box-shadow: 0 0 8px var(--ok); }
.ep .name { font-weight: 600; } .ep .sub { color: var(--muted); font-size: 12px; }
.ep .meta { margin-left: auto; text-align: right; }
/* Identidad arriba (nombre + señales), datos abajo. `min-width:0` es lo que después permite recortar el
   nombre con elipsis; NO se le pone `flex:1 1 0`, que probado engorda las filas de 235 a 501px porque
   fuerza a envolver todo el contenido. */
.ep .epinfo { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ep .eptop { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.ep .pills { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
/* A partir de tablet el nombre se recorta en vez de envolver, y "en línea" deja de partirse en dos
   renglones cuando el alias es largo. Recortar SOLO el nombre: hacerlo también en .sub escondía el final
   del renglón, que es justo donde vivían las señales antes de ser pastillas. */
@media (min-width: 761px) {
  .ep .meta { white-space: nowrap; flex: 0 0 auto; }
  .ep .eptop .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  /* El renglón chico también se recorta. Esto NO se podía hacer antes: era donde vivían las señales, y la
     elipsis se comía justo el final —«MODO KIOSCO aplicad…»—. Ahora solo lleva identidad, y el código y el
     hostname, que es lo que se busca, van al principio. La ficha tiene el detalle completo. */
  .ep .epinfo .sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* `nowrap` es lo que hace útil la elipsis: con `wrap`, flexbox PRIMERO envuelve —mandando las pastillas
     al renglón de abajo— y solo DESPUÉS encoge el nombre, así que se pagaba el recorte Y el renglón extra
     (65 → 110px entre 762 y 1200). Ahora el nombre cede y las pastillas se quedan a su lado. */
  .ep .eptop { flex-wrap: nowrap; }
  /* `0 0 auto`: las pastillas no ceden ni una pizca. Con `0 1 auto` se comprimían y, al tener wrap propio,
     se APILABAN — dos señales dejaban la fila en 96px. El que tiene que encoger es el nombre, que para eso
     lleva la elipsis. */
  .ep .eptop .pills { flex: 0 0 auto; flex-wrap: nowrap; }
}
/* En el teléfono la fila envuelve en tres bloques (identidad, estado, botones) y llegaba a 241px. Con
   `align-items:center` la casilla se centraba en esa altura, o sea a más de 100px del nombre al que
   pertenece: se alinea arriba, junto a lo que marca. Y el alias se acota a dos renglones — el completo
   sigue en el title — para que un nombre largo no estire la fila sin límite. */
@media (max-width: 760px) {
  .ep { align-items: flex-start; }
  .ep .rowchk, .ep .dot { margin-top: 2px; }
  .ep .eptop .name { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
}
textarea { width: 100%; background: #0d1117; color: var(--txt); border: 1px solid var(--line); border-radius: 8px; padding: 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; resize: vertical; }
select { background: #0d1117; color: var(--txt); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font: inherit; }
/* Los inputs no tenían estilo: salían blancos del navegador sobre el panel oscuro. */
input[type=text], input[type=email], input[type=password], input[type=search], input:not([type]) {
  background: #0d1117; color: var(--txt); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font: inherit;
}
input::placeholder { color: var(--muted); }
:where(button, .btn, input, select):focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
button.ghost:hover, .btn.ghost:hover { background: #1e2732; border-color: #3a4756; }
/* Targets táctiles en móvil: 44px mínimo para todos los botones (design-review). Solo afecta <=760px. */
/* `.epinfo` y no `.ep > span:not([class])`: al darle clase al contenedor de la identidad, esa regla dejó
   de coincidir con nada. Con base 120px la identidad reclama sitio en la MISMA línea de flex que la
   casilla, en vez de caer al renglón siguiente y dejar a la casilla sola 37px por encima del nombre al
   que pertenece. 120 y no 140: a 320px vuelve a romperse. */
@media (max-width: 760px) { button { min-height: 44px; } .ep { flex-wrap: wrap; } .ep .epinfo { flex: 1 1 120px; min-width: 0; } }
button { border: 0; border-radius: 8px; padding: 9px 14px; font-weight: 600; cursor: pointer; color: #fff; }
button.primary { background: var(--primary); } button.danger { background: var(--danger); }
button.ghost { background: transparent; border: 1px solid var(--line); color: var(--txt); padding: 6px 10px; }
button:disabled { opacity: .45; cursor: not-allowed; }
.muted { color: var(--muted); }
.pill { font-size: 12px; padding: 3px 9px; border-radius: 999px; white-space: nowrap; }
.pill.on { background: #0d2a17; color: #4ac26b; } .pill.off { background: #2a1414; color: #e5837f; }
/* Tonos de las señales de la fila. No se inventan colores: son las mismas familias que ya usa la consola
   — ámbar el del borde de "sin grupo" y de los avisos parciales, azul el de kiosco, y gris para lo que
   solo informa. Cada uno sobre su propio fondo, así el contraste no depende de la fila. */
.pill.warn { background: #2a1f14; color: #e0b341; }
.pill.info { background: #14202f; color: #7fb2f7; }
.pill.dim { background: #1e2732; color: #9fb0c0; }
/* El borde es lo que hace que la pastilla siga siendo una pastilla sobre CUALQUIER fila. Sin él se
   disolvía justo donde más importa: el fondo de .pill.info es idéntico al de .ep.viewing (#14202f) y el de
   .pill.off casi el de .ep.support (#2a1414 vs #2a1416), así que en la fila de SOS «reinstalar» salía como
   texto suelto mientras «kiosco» era el único chip visible — la gravedad al revés. */
.pill.on, .pill.off, .pill.warn, .pill.info, .pill.dim { border: 1px solid rgba(255,255,255,.10); }
.results { margin-top: 12px; display: grid; gap: 8px; }
.res { border: 1px solid var(--line); border-radius: 8px; padding: 10px; }
.res .head { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; }
.res pre { margin: 8px 0 0; white-space: pre-wrap; word-break: break-word; font-size: 12px; color: #cdd9e5; max-height: 260px; overflow: auto; }
.res.ok { border-left: 3px solid var(--ok); } .res.error, .res.timeout { border-left: 3px solid var(--danger); }
.res.skipped { border-left: 3px solid var(--off); }

/* Checkbox "instalar como usuario conectado" */
.chk { display: inline-flex; align-items: center; gap: .5rem; margin-top: .5rem; font-size: .9rem; color: var(--muted, #64748b); cursor: pointer; min-height: 32px; }
.chk input { width: 18px; height: 18px; }

/* Caja de URL para instalar desde enlace */
#dlUrl { width: 100%; box-sizing: border-box; margin-bottom: .5rem; }

/* Plantillas de instalación */
#tplSelect { max-width: 260px; }
#tplEditor { margin-top: .5rem; padding: .6rem; border: 1px dashed var(--line); border-radius: 8px; }
#tplEditor input, #tplEditor textarea, #tplEditor select { width: 100%; box-sizing: border-box; margin-bottom: .4rem; }
#tplEditor .chk { width: auto; }
#tplEditor .chk input { width: 18px; height: 18px; }

/* Vista Plantillas: botones genéricos .btn (catálogo) */
.btn { background: var(--primary); }
.btn.ghost { background: transparent; border: 1px solid var(--line); color: var(--txt); padding: 6px 10px; }
.btn.sm { padding: 5px 10px; font-size: 13px; border: 1px solid transparent; } /* alinea altura con .btn.sm.ghost */
.btn.sm.ghost { border-color: var(--line); } /* recupera el borde: .btn.sm iba después y lo dejaba transparente */

/* Badge de estatus de instalación por equipo */
.ep .inst { margin-top: .35rem; font-size: .82rem; font-weight: 600; padding: .15rem .45rem; border-radius: 6px; display: inline-block; }
.ep .inst.running { color: #92600a; background: #fef3c7; }
.ep .inst.ok { color: #065f46; background: #d1fae5; }
.ep .inst.error { color: #991b1b; background: #fee2e2; }
.res details { margin-top: .4rem; } .res details summary { cursor: pointer; color: #64748b; font-size: .85rem; }

/* Botones editar/borrar por equipo */
.epact { display: inline-flex; gap: .25rem; margin-left: .4rem; }
.epact .mini { min-height: 32px; min-width: 32px; padding: .2rem .4rem; border-radius: 6px; border: 1px solid var(--line); background: transparent; cursor: pointer; font-size: .85rem; line-height: 1; }
.epact .mini:hover { background: rgba(148,163,184,.18); }
/* En táctil, 44px. Va AQUÍ y no en el bloque de móvil de arriba: `.epact .mini` tiene la misma
   especificidad que la regla de 32px, así que puesto antes perdía por orden de fuente — el `button
   { min-height:44px }` global tampoco le gana, por ser menos específico. */
@media (max-width: 760px) { .epact .mini { min-height: 44px; min-width: 44px; } }

/* Shell: menú lateral + vistas (Task 2) */
.app { display: grid; grid-template-columns: 220px 1fr; min-height: 100vh; align-content: start; }
.side { background:#0c1116; border-right:1px solid var(--line); padding:16px 12px; display:flex; flex-direction:column; gap:4px; }
.brand { display:flex; align-items:center; gap:9px; padding:6px 8px 14px; font-weight:700; font-size:17px; }
.brand .logo { width:26px; height:26px; border-radius:7px; background:linear-gradient(135deg,var(--primary),#1e5fc0); display:flex; align-items:center; justify-content:center; }
.nav { display:flex; align-items:center; gap:11px; padding:10px 11px; border-radius:9px; color:var(--muted); cursor:pointer; font-weight:500; min-height:44px; }
.nav[hidden] { display: none; }
.nav:hover { background:#141c25; color:var(--txt); }
.nav.active { background:#14202f; color:var(--txt); box-shadow:inset 3px 0 0 var(--primary); }
.nav .ic { width:20px; text-align:center; }
.nav .badge { margin-left:auto; background:var(--danger); color:#fff; font-size:11px; font-weight:700; border-radius:999px; padding:1px 7px; }
.side .foot { margin-top:auto; padding:8px; display:flex; flex-direction:column; gap:8px; }
main { padding:22px 26px 60px; overflow:auto; }
.head { display:flex; align-items:center; gap:12px; margin-bottom:18px; flex-wrap:wrap; }
.head h1 { font-size:20px; margin:0; } .head .sub { color:var(--muted); font-size:13px; }
.view { display:none; } .view.on { display:block; }
@media (max-width:860px) {
  .app { grid-template-columns:1fr; }
  .side { flex-direction:row; position:sticky; top:0; z-index:5; padding:8px; gap:2px; overflow-x:auto; border-right:0; border-bottom:1px solid var(--line); align-items:center; }
  .brand .t, .nav .lbl { display:none; }
  .side .foot { margin-left:auto; margin-top:0; flex-direction:row; }
  .nav { min-height:44px; }
  button, .btn { min-height:44px; }
  .head .sub { flex-basis:100%; }
  .ep .rowchk { width:24px; height:24px; }
}

/* Vista Panel: tarjetas de contadores + listas (Task 3) */
.stats { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin-bottom:18px; }
.stat { background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:15px 16px; }
.stat .k { color:var(--muted); font-size:12px; text-transform:uppercase; letter-spacing:.04em; }
.stat .v { font-size:28px; font-weight:700; margin-top:5px; font-variant-numeric:tabular-nums; }
.stat.alert .v { color:var(--danger); } .stat.warn .v { color:#d1a32e; }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.card { background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:16px; }
.card h2 { font-size:13px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); margin:0 0 12px; }
.pline { display:flex; align-items:center; gap:10px; padding:8px 0; border-bottom:1px solid var(--line); flex-wrap:wrap; }
.pline:last-child { border-bottom:0; } .pline .r { margin-left:auto; color:var(--muted); font-size:13px; }
.pline .muted { overflow-wrap: anywhere; }
@media (max-width:860px) { .stats { grid-template-columns:1fr 1fr; } .grid2 { grid-template-columns:1fr; } .pline { flex-direction:column; align-items:flex-start; } .pline .r { margin-left:0; } }

/* Vista Equipos: lista de ancho completo, agrupada por grupo; la ficha abre en modal */
.col-list { background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:12px; }
#epGroupFilter { background:var(--bg); color:var(--txt); border:1px solid var(--line); border-radius:8px; padding:0 10px; max-width:220px; }
.selbar { background:#14202f; border:1px solid var(--primary); border-radius:10px; padding:9px 12px; display:flex; align-items:center; gap:10px; margin-bottom:8px; font-size:13px; flex-wrap:wrap; }
/* En escritorio los cinco botones caben de sobra en un renglón: el `margin-right:auto` los empuja a la
   derecha sin que el contador rompa línea (darle 100% de base costaba 36px de barra a 1440). */
.selbar .selcount { flex:1 1 auto; margin-right:auto; color:var(--muted); }
.selbar .selcount.hay { color:var(--txt); } /* con selección, el contador es el dato que importa */
.selbar .btn[disabled] { opacity:.45; cursor:not-allowed; }
/* En pantalla estrecha la barra se ancla abajo, al alcance del pulgar: está siempre presente, y en el
   flujo normal se iba de la pantalla en cuanto se bajaba por la lista. No sirve position:sticky — `main`
   es overflow:auto y se vuelve el contenedor del sticky, mientras el que desplaza es el body.
   860 y no 760: el layout se reacomoda a 860, y entre medias la barra volvía a irse de la pantalla. */
@media (max-width:860px) {
  .selbar { position:fixed; left:8px; right:8px; bottom:8px; margin:0; z-index:45; box-shadow:0 6px 20px rgba(0,0,0,.5); }
  /* Aquí sí: envuelta en tres renglones, el contador aislado arriba evita que el primer botón quede solo. */
  .selbar .selcount { flex:1 1 100%; margin-right:0; }
  /* Holgura sobre los 147px que mide la barra llena (5 botones, super-admin): que no tape el último
     equipo si el contador llegara a envolver. */
  .col-list { padding-bottom:170px; }
}

/* Jerarquía de la lista: organización › grupo › equipo. El nivel de organización se dibuja SIEMPRE, aunque
   solo haya una, para que la vista tenga una única forma para el super-admin y para el admin de un cliente.
   Se distingue del grupo por peso y por el borde de acento a la izquierda, no por otro color de fondo:
   añadir un tercer tono habría hecho ruido con las pastillas de las filas. */
.orgsec { border:1px solid var(--line); border-left:3px solid var(--primary); border-radius:10px; margin-bottom:12px; overflow:hidden; }
.orgsec > summary { list-style:none; cursor:pointer; display:flex; align-items:center; flex-wrap:wrap; gap:10px; padding:11px 12px; background:#111823; font-size:15px; min-height:44px; }
.orgsec > summary::-webkit-details-marker { display:none; }
.orgsec > summary::before { content:'▸'; color:var(--txt); transition:transform .12s; }
.orgsec[open] > summary::before { transform:rotate(90deg); }
.orgsec .orgnm { font-weight:700; letter-spacing:.01em; }
/* Los grupos van dentro, con sangría pequeña: a 360px cada píxel de indentación se lo quita al nombre del
   equipo, que es lo que se lee. Sin borde propio arriba para que no se vean dos marcos pegados. */
.orgsec > .grpsec { margin:0 8px 8px; }
.orgsec > .grpsec:first-of-type { margin-top:8px; }

/* Secciones colapsables por grupo */
.grpsec { border:1px solid var(--line); border-radius:10px; margin-bottom:10px; overflow:hidden; }
/* wrap: la cabecera (casilla + nombre + contador) debe poder bajar de línea en móvil en vez de
   desbordarse fuera de la tarjeta. Ya no lleva botones: las acciones viven en la barra de arriba. */
.grpsec > summary { list-style:none; cursor:pointer; display:flex; align-items:center; flex-wrap:wrap; gap:10px; padding:10px 12px; background:#141b23; font-size:14px; min-height:44px; }
.grpsec > summary::-webkit-details-marker { display:none; }
.grpsec > summary::before { content:'▸'; color:var(--txt); transition:transform .12s; }
.grpsec[open] > summary::before { transform:rotate(90deg); }
/* Mismo tamaño que la casilla de cada equipo: son la misma acción a dos alturas, y una casilla de grupo
   más grande sugeriría que hace algo distinto. El blanco táctil de 44px lo da el <label> de alrededor;
   los márgenes negativos lo consiguen sin engordar la cabecera.
   Sin prefijo de sección: la casilla es idéntica en organización y en grupo porque hace lo mismo a dos
   alturas, y duplicar la regla por nivel era invitar a que se separaran. */
.grpchkwrap { flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  min-width:44px; min-height:44px; margin:-8px -6px -8px -10px; cursor:pointer; }
.grpchk, .orgchk { width:18px; height:18px; flex:0 0 auto; cursor:pointer; accent-color:var(--primary); }
/* Sección sin equipos: el <input> se ve gris por el render nativo del disabled, pero el <label> conservaba
   el cursor de mano y prometía una acción que no existe. */
.grpchkwrap:has(:disabled), .grpchk:disabled, .orgchk:disabled { cursor:default; }
.grpsec .grpnm { font-weight:600; }
/* Sin prefijo de sección, igual que la casilla: el contador es metadato en los DOS niveles. Prefijado por
   `.grpsec` no alcanzaba al de la organización, que heredaba sus 15px y el color de texto pleno — el ojo
   caía en el metadato y no en el nombre del cliente, con la jerarquía al revés.
   `pointer-events:none` porque no es interactivo: a ≤600px ocupa su propio renglón pintado sobre la mitad
   inferior del label de 44px, y un toque ahí plegaba la sección en vez de marcar. */
.grpcount { margin-left:auto; color:var(--muted); font-size:12px; pointer-events:none; }
.grpsec.nogroup > summary { border-left:3px solid #d1a32e; }
/* El único control de acción que queda en el <summary>: el ⚙️ de políticas del grupo. Registro secundario
   (contorno, no relleno) para no competir con el nombre del grupo, que es lo que se escanea.
   Los márgenes negativos son los mismos que usa .grpchkwrap y por lo mismo: el blanco táctil son 44px,
   pero sin compensarlos esa caja arrastraba la cabecera de 48 a 64px en TODOS los anchos.
   El -4px de la derecha además recorta la franja de <summary> pulsable que quedaba a su lado, donde un
   toque plegaba la sección en vez de abrir las políticas. */
.grpact { flex:0 0 auto; white-space:nowrap; font-size:14px; line-height:1; min-width:44px; min-height:44px;
  margin:-8px -4px -8px 0;
  background:transparent; border:1px solid var(--line); border-radius:8px; color:var(--txt); cursor:pointer; }
.grpact:hover { background:#1e2732; border-color:#3a4756; }
/* Destructiva: distinta por color Y por relleno, sin leer la etiqueta. #e5837f (el rojo claro que ya usa
   .pill.off) da 6.5:1 sobre #141b23; var(--danger) como texto se quedaría en 3.5:1. Vale para la barra
   de acciones (Energía) y para los modales (Quitar política): antes .del solo existía para .grpact y las
   acciones destructivas se veían idénticas a las demás. */
.btn.del, button.ghost.del { background:#2a1414; border-color:#5e2320; color:#e5837f; }
.btn.del:hover, button.ghost.del:hover { background:#3a1a19; border-color:var(--danger); }
.grpsec ul { list-style:none; margin:0; padding:10px; }

/* Modal de ficha */
.modal { position:fixed; inset:0; z-index:50; display:flex; align-items:center; justify-content:center; padding:16px; }
.modal[hidden] { display:none; } /* .modal { display:flex } gana sobre el [hidden] del user-agent; hay que anularlo explícito */
.modal-backdrop { position:absolute; inset:0; background:rgba(0,0,0,.6); }
/* padding-right reserva el carril del ✕ en TODA la altura. Con `sticky + float` el hueco solo existía en
   el primer renglón, así que al desplazar un modal largo (Políticas, que ahora lleva también Perfil) el
   botón opaco se quedaba encima del texto. El margen negativo lo devuelve a la esquina. */
.modal-card { position:relative; background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:16px 56px 16px 16px; width:min(560px,100%); max-height:88vh; overflow:auto; }
.modal-close { position:sticky; top:0; float:right; margin:-4px -44px 0 8px; background:var(--bg); color:var(--txt); border:1px solid var(--line); border-radius:8px; width:44px; height:44px; cursor:pointer; font-size:16px; line-height:1; }
/* En móvil el carril de 56px cuesta demasiado: se comía 40 de 294px útiles y hacía envolver a tres
   renglones la casilla de "sin contraseña". Aquí el ancho completo vuelve al contenido y el ✕ se apoya en
   una franja opaca pegajosa: el texto pasa por DEBAJO de una cabecera, que es el patrón normal, en vez de
   chocar con un botón suelto. Sin la franja el botón seguía tapando el final de las líneas al desplazar
   (`position:sticky` no reserva espacio fuera de su renglón original), y dejarlo `static` obligaba a
   subir hasta arriba para cerrar un modal de 1600px.
   La franja NO es pulsable a propósito: a 360 provocaría cierres accidentales al desplazar. */
/* A 360 el ⚙️ no cabe en la misma línea que nombre + contador (hacen falta ~308px de 256), y envolvía a
   un segundo renglón dejando la cabecera en 102px y el botón en una posición impredecible: a la
   izquierda con nombres cortos, a la derecha con largos. Se le da su orden explícito y el contador baja
   a su propio renglón, que es el dato menos urgente de los tres. */
@media (max-width:600px) {
  .grpsec > summary, .orgsec > summary { row-gap:2px; }
  .grpchkwrap { order:1; }
  .grpsec .grpnm, .orgsec .orgnm { order:2; }
  .grpsec .grpact { order:3; margin-left:auto; }
  .grpcount { order:4; flex:1 0 100%; margin-left:0; }
  /* Sin sangría a este ancho: los 8px de cada lado se los quita al nombre del equipo, que es lo que se
     lee. El borde de acento de la organización ya dice de quién es el bloque. */
  .orgsec > .grpsec { margin-left:0; margin-right:0; }
}
@media (max-width:600px) {
  .modal-card { padding:16px; }
  /* La franja pegajosa del ✕ cortaba la línea de mayúsculas del título (se comía el acento de
     «Políticas»): el encabezado necesita despegarse de su borde inferior.
     `padding` y no `margin`: un margin-top aquí COLAPSA con el margin-bottom:8px del ✕ (gana el mayor,
     8px) y no movía el título ni un píxel. El padding no colapsa, así que además es inmune a que
     alguien cambie los márgenes del botón. */
  .modal-card > .fhead { padding-top:10px; }
  /* 60 = los 44 del botón + los 16 de padding del card. El `top:0` del botón se resuelve contra la caja
     de CONTENIDO y el de la franja contra la de relleno, así que el botón queda 16px más abajo: con una
     franja de 44 asomaba por debajo y volvía a tapar texto al desplazar. */
  .modal-card::before { content:''; position:sticky; top:0; z-index:1; display:block; height:60px; margin:-16px -16px 0; background:var(--panel); }
  .modal-close { float:none; display:block; z-index:2; margin:-44px 0 8px auto; }
}
#reload { min-width:44px; min-height:44px; }
.ep .rowchk { width:18px; height:18px; flex:0 0 auto; cursor:pointer; accent-color:var(--primary); }
/* Acciones de una fila (grupo/usuario): con 4 botones deben poder bajar de línea o "Borrar" se sale de la tarjeta. */
.pline .r { display:flex; gap:8px; flex-wrap:wrap; }
.updinfo { font-size:12px; color:var(--muted); margin-top:6px; }
.warnbox { background:#2a1f14; border:1px solid #6b4f1f; border-radius:8px; padding:8px 10px; font-size:12px; margin:0 0 10px; }
/* Aviso de versión nueva: flota sobre la vista, sin empujar el contenido ni tapar la barra lateral.
   Reusa .warnbox (ámbar = "atención") en vez de inventar un color; solo aporta la posición. */
.newver { position:fixed; left:50%; transform:translateX(-50%); bottom:16px; z-index:60; margin:0;
  display:flex; align-items:center; gap:10px; max-width:calc(100% - 32px);
  box-shadow:0 6px 20px rgba(0,0,0,.45); }
.lvl { font-size:11px; border:1px solid currentColor; border-radius:999px; padding:0 6px; }
.lvl.super { color:var(--primary); } .lvl.admin { color:var(--muted); }
.orggrp { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); margin:8px 0 2px; }
/* Lista de usuarios: una fila por usuario, escaneable. El detalle se edita en un modal. */
.urow { display:flex; align-items:center; gap:12px; flex-wrap:wrap; padding:10px 0; border-bottom:1px solid var(--line); }
.urow:last-child { border-bottom:0; }
.urow .who { display:flex; align-items:center; gap:8px; flex-wrap:wrap; min-width:200px; }
.urow .accwrap { margin-left:auto; display:flex; min-width:0; }
.urow .acc { display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; min-width:0; }
.urow button { min-height:44px; }
/* max-width + elipsis: hay clientes con nombres largos que si no se salen de la tarjeta. */
.chip { font-size:11px; border-radius:999px; padding:2px 8px; border:1px solid currentColor; white-space:nowrap; max-width:100%; overflow:hidden; text-overflow:ellipsis; }
.chip.org { color:var(--primary); }
.chip.extra { color:var(--muted); }
.chip.all { color:var(--ok); }
.chip.none { color:#d1a32e; }
/* Modal de acceso: cada organización es una tarjeta con SUS grupos dentro (jerarquía visible). */
.orgcard { border:1px solid var(--line); border-radius:10px; padding:8px 10px; margin-bottom:8px; }
.orgcard.on { border-color:var(--primary); background:#14202f; }
.orghead { display:flex; align-items:center; gap:8px; flex-wrap:wrap; min-height:44px; cursor:pointer; }
/* El control de la organización manda sobre los de sus grupos: debe verse MÁS grande, no menos. */
.orghead input { width:20px; height:20px; flex:0 0 auto; accent-color:var(--primary); }
.orgkids { display:flex; flex-direction:column; gap:2px; margin:4px 0 0 24px; }
.orgkids .chk.kid { margin-top:0; min-height:36px; display:flex; align-items:center; gap:8px; }
#uAcc input[type=checkbox], #userList input[type=checkbox] { accent-color:var(--primary); }
.modal .fsec .row.wrap #uDel { margin-left:auto; }
#v-admin .row.wrap button, .modal .fsec .row.wrap button { min-height:44px; }
.modal-close { font-family: inherit; } /* Arial no trae el glifo ✕ y salía como caja */
@media (max-width:600px) {
  /* Fila compacta: el botón se ancla a la derecha en vez de caer solo en una línea vacía. */
  .urow { position:relative; padding-right:104px; }
  .urow .accwrap { margin-left:0; width:100%; }
  .urow .acc { justify-content:flex-start; }
  .urow > button { position:absolute; right:0; top:10px; }
  .orghead input { width:24px; height:24px; }
  .orgkids .chk.kid { min-height:44px; }
  .modal .fsec .row.wrap #uDel { width:100%; margin-top:8px; }
}
@media (max-width:860px) { .pline .r { width:100%; row-gap:8px; } .ep .rowchk, .grpchk, .orgchk { width:24px; height:24px; } }
.ep.viewing { border-color:var(--primary); background:#14202f; }
/* Equipo con modo kiosco puesto: su cuenta limitada arranca sin escritorio. Se marca porque no se
   distingue de uno normal hasta que alguien inicia sesion, y eso se descubre tarde. */
.ep.kiosco { border-left:3px solid var(--primary); }
/* El renglón chico ya NO se pinta de azul: teñir la línea entera hacía que la señal de kiosco se
   disolviera justo en lo que quería destacar. La lleva su pastilla, y la identidad se queda gris. */
.ep.nogroup { border-left:3px solid #d1a32e; }
/* Sordo: reporta presencia pero no recibe comandos. El punto NO puede quedarse verde: sería mentir. */
.ep.sordo { border-left:3px solid #d1a32e; }
.ep.sordo .dot, .ep.sordo.online .dot { background:#d1a32e; box-shadow:none; }
.ep.sordo .meta { color:#d1a32e; }
.fhead { display:flex; align-items:center; gap:10px; margin-bottom:16px; flex-wrap:wrap; }
.fhead .fnm { font-size:17px; font-weight:700; }
.fhead .fst { font-size:13px; font-weight:600; }
.fsec { margin-bottom:18px; padding-bottom:16px; border-bottom:1px solid var(--line); }
.fsec:last-child { border-bottom:0; margin-bottom:0; padding-bottom:0; }
.fsec h3 { font-size:13px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); margin:0 0 8px; display:flex; align-items:center; gap:8px; }
/* Secciones plegables de la ficha: mismo idioma que los grupos de la lista (▸ que gira, toque de 44px). */
.fdet { border-top:1px solid var(--line); }
.fdet > summary { list-style:none; cursor:pointer; display:flex; align-items:center; gap:8px; min-height:44px;
  font-size:13px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); }
.fdet > summary::-webkit-details-marker { display:none; }
.fdet > summary::before { content:'▸'; color:var(--txt); transition:transform .12s; }
.fdet[open] > summary::before { transform:rotate(90deg); }
.fdet > summary:focus-visible { outline:2px solid var(--primary); outline-offset:2px; border-radius:4px; }
.fdet .fdbody { padding:2px 0 16px; }
@media (prefers-reduced-motion: reduce) { .fdet > summary::before { transition:none; } }
@media (max-width:860px) { .modal-card { width:100%; max-height:92vh; } .grpcount { font-size:11px; } }

/* Menú de acciones de un grupo (Energía). Vive en <body> con position:fixed para que no lo recorte el
   <details> ni el desplazamiento de la lista; se posiciona desde el botón que lo abre. */
.gmenu { position:fixed; z-index:70; background:var(--panel); border:1px solid var(--line); border-radius:10px;
  /* Techo propio: la nota del pie no envuelve, así que el shrink-to-fit estiraba el menú a 487px cuando
     el botón dejaba de estar pegado al borde derecho (que era lo único que lo acotaba antes). */
  padding:6px; min-width:210px; max-width:min(320px, calc(100vw - 16px)); display:flex; flex-direction:column; gap:2px;
  box-shadow:0 8px 24px rgba(0,0,0,.5); }
.gmenu-tit { color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:.04em; padding:4px 12px 6px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.gmenu-it { background:transparent; border:0; color:var(--txt); text-align:left; padding:10px 12px;
  border-radius:8px; min-height:44px; font-weight:500; cursor:pointer; }
.gmenu-it:hover { background:#1e2732; }
/* Apagar en rojo claro: #e5837f da 6.5:1 sobre el panel; var(--danger) como texto se queda en 3.5:1. */
.gmenu-it.del { color:#e5837f; }
.gmenu-it.del:hover { background:#3a1a19; }
.gmenu-nota { color:var(--muted); font-size:11px; padding:6px 12px 2px; }
