/* ===========================================================================
   KW — identidad de la web (kwapp.co)
   ---------------------------------------------------------------------------
   Producto propio: negro + azul eléctrico, Sora para texto y JetBrains Mono
   para todo lo que sea número. NADA se hereda de TGData: esta app no comparte
   ni hoja de estilos ni layout.

   ⚠️ EL COLOR NO VIVE ACÁ. Está en `kw-tokens.css`, que declara los dos temas
   (oscuro y claro) y se carga ANTES que esta hoja. Regla dura:

       en este archivo no se escribe ni un #hex, ni un rgb(), ni un rgba()
       con números de color adentro.

   Un color escrito a mano acá queda MUDO en claro —la regla sigue aplicando,
   pero con el valor del tema oscuro— y eso no rompe nada, no da error y no lo
   ve nadie hasta que alguien mira la pantalla. Las únicas excepciones, todas
   marcadas donde están: el blanco sobre un relleno de color de marca, el botón
   de Google (su color es de ellos), el negro de la cámara, y los alfas puros
   (`rgba(0,0,0,.45)` de una sombra que va sobre el MAPA, no sobre la app).
   =========================================================================== */

*{box-sizing:border-box}

/* ===========================================================================
   ⚠️⚠️ EL TAMAÑO DE TODA LA APP SE CONTROLA DESDE ACÁ
   ---------------------------------------------------------------------------
   Todas las medidas de esta hoja están en `rem`, no en px. Eso no es un
   capricho de estilo: es lo que permite que la app ESCALE. Con px, un teléfono
   de 6,8 pulgadas muestra el texto exactamente del mismo tamaño que un monitor
   de 27 — y eso fue exactamente lo que pasó.

   Medido el 2026-10-06 en el viewport de un Xiaomi 15T (412 px CSS), antes de
   este cambio: **el 98 % del texto visible estaba por debajo de 16 px**, con los
   rótulos de las tarjetas en 9,5 px y la barra de abajo en 10. Reportado como
   «hay que acercar mucho para ver las cosas», y con razón: 16 px es el mínimo
   de cuerpo en un teléfono y el tamaño por omisión de cualquier navegador.

   ⚠️⚠️ Va en PORCENTAJE y no en px, y la diferencia no es cosmética: `100%` es
   **el tamaño que la persona tenga configurado** en su navegador o en su
   teléfono (16 px si no tocó nada). Escrito como `font-size:16px`, a quien
   configuró letra grande porque no ve bien se le ignora la preferencia y se le
   impone nuestro tamaño. Con `%`, su ajuste se respeta y el nuestro se suma.

   Y como el valor por omisión es 16, en escritorio la conversión a rem no
   cambió absolutamente nada: lo que cambia es que ahora existe una sola palanca.

   ⚠️⚠️ Y no se escala solo el texto: al estar TODO en rem —paddings, gaps,
   altos, radios—, la caja crece junto con su contenido. Subir solo la letra es
   lo que hace que el texto se desborde de su botón.

   ⚠️ Los bordes (1px, 2px, 3px) se quedaron en px a propósito: un borde en rem
   escalado se vuelve una línea borrosa. Y los breakpoints de `@media` también:
   miden el DISPOSITIVO, no el contenido — en rem, subir el root cambiaría el
   breakpoint y la app entraría en modo teléfono en un monitor.
   =========================================================================== */
html{font-size:100%}

@media (max-width:940px){
  /* El mismo umbral que la barra de abajo: es donde esto deja de ser
     escritorio.

     ⚠️ ESTE NÚMERO SE CALIBRÓ DOS VECES, Y LA SEGUNDA CORRIGIÓ UNA MEDICIÓN
     QUE SE HIZO CONTRA LA PANTALLA EQUIVOCADA.
     Arrancó en +25 % (cuerpo de 17,5 px) para responder a «hay que acercar
     mucho para ver las cosas». Resultó que ese teléfono tenía prendido «Sitio
     para computadores», o sea que **nunca estaba viendo esta disposición**:
     Chrome le mentía el ancho (980 px), este `@media` no aplicaba y el cuerpo
     le llegaba a ~6,7 px reales. La letra chica no la causaba el 100 %; la
     causaba el modo escritorio. Apagado el modo, el +25 % quedó pasado y se
     reportó al revés: «muy grande».
     +12,5 % es el punto medio medido: el cuerpo queda en **15,8 px** —contra
     los 14 de escritorio y los 17,5 que sobraban— y el rótulo más chico sube
     de 9,5 a 10,7. La lección es la del proyecto: una medición vale lo que
     valga la pantalla donde se tomó.

     ⚠️ 2026-10-28: el usuario la pidió un 30 % más chica («bajémosle la
     resolución… quizás al 30 %»): 112,5 % × 0,7 = 78,75 %. Ese mismo día la
     subió a solo un 20 % menos («déjela al 20 %»): 112,5 % × 0,8 = **90 %**.
     El cuerpo queda en ~12,6 px y el rótulo más chico en ~8,6. Queda POR
     DEBAJO del escritorio (100 %) a propósito, por decisión suya. Vale igual
     para la web en el teléfono y para el APK, que carga esta misma hoja. Los
     campos en iPhone no cambian: la regla de iOS de más abajo los deja en
     16 px. */
  html{font-size:90%}
}

html,body{margin:0;padding:0}
body{
  background:var(--ground);color:var(--ink);font-family:var(--sora);
  font-size:0.875rem;line-height:1.5;-webkit-font-smoothing:antialiased;
  min-height:100vh;display:flex;flex-direction:column;
}
a{color:var(--led-bright);text-decoration:none}
a:hover{text-decoration:underline}
/* ⭐ EL FOCO DE TECLADO, EN TODA LA APP (2-nov-2026): el halo de doble anillo de la
   casa, un aro del fondo y uno azul por fuera, que se ve sobre cualquier color.
   Antes los enlaces y las casillas mostraban el anillo del navegador
   (`outline:auto 1px`) y los botones, otro. `:where()` pesa cero: toda regla de foco
   propia de un componente sigue ganando. Los campos de texto no entran: su foco es
   el borde azul (`.campo input:focus`). */
:where(a,button,summary,select,[tabindex],input[type="checkbox"],input[type="radio"]):focus-visible{
  outline:2px solid var(--led-bright);outline-offset:2px;box-shadow:0 0 0 2px var(--ground);
}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}

/* ⚠️ SIN TRANSICIÓN ENTRE PANTALLAS (quitada el 2-nov-2026). Desde el 28-oct cada
   navegación desvanecía la pantalla vieja (140 ms) y deslizaba la nueva (240 ms).
   Es lo que más veces se ve en la app, cientos al día, y cada vez la demoraba casi
   un cuarto de segundo: se sentía lenta, no «de app» (pulir-pantallas, animar.md
   §3-bis, rama 2). No se repone sin medir que no se sienta así. */

/* ------------------------------- navbar ---------------------------------- */
.nav{
  height:var(--nav);flex:none;display:flex;align-items:center;gap:1.125rem;
  padding:0 1.25rem;background:var(--nav-bg);backdrop-filter:blur(0.625rem);
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:900;
}
/* ⚠️ El logotipo son DOS letras. El tamaño y el tracking que servían para
   "kilowatt" (8 letras, peso 300, tracking -.04em) dejan a "kw" flaco y sin
   presencia: con menos letras hace falta más cuerpo, no el mismo. */
/* ⚠️⚠️ EL LOGOTIPO ES UN DIBUJO, NO TEXTO.
   Acá había `font-size:1.6875rem` sobre las letras «kw» escritas en Sora, con un
   degradado recortado encima. Eso NO es la marca: el monograma de KW es un
   trazo de grosor uniforme donde la `k` es una barra más un chevron `<` que la
   toca, y la `w` es un zigzag redondeado suelto (2026-10-21). Sora tiene
   vértices, brazos separados y remates rectos — era otra cosa que casualmente
   decía lo mismo. Ver `docs/marca.md §2` y `templates/_kw.svg`.

   El alto reemplaza al `font-size`: el trazo es proporcional, así que el
   monograma se escala con una sola medida. 19 px de alto da el mismo peso
   visual en la barra que tenían los 27 px de tipografía. */
.logo{
  display:flex;align-items:center;flex:none;text-decoration:none;line-height:0;
}
.logo:hover{text-decoration:none}
.kw-mono{height:1.1875rem;width:auto;display:block}
/* El color del trazo lo pone el tema, no el archivo. En oscuro, el degradado
   de marca; en claro se PLANCHA al azul profundo, porque el degradado muere en
   blanco y sobre fondo claro se comería media palabra (`docs/marca.md §8.1`).
   No es un ajuste estético: es la regla que impone el propio manual. */
.kw-mono path{stroke:var(--deep)}
[data-tema="oscuro"] .kw-mono path{stroke:url(#kw-degradado)}
.logo:hover .kw-mono{opacity:.85}
/* ⚠️⚠️ LA BARRA SE DESBORDABA TAMBIÉN POR ENCIMA DE 940 px, Y ES EL MISMO
   DEFECTO QUE YA SE ARREGLÓ DOS VECES.
   `.nav` tiene alto FIJO (`var(--nav)`), así que unos enlaces que se envuelven
   NO ensanchan la barra: se salen de ella. Con una cuenta de operador —ocho
   enlaces— se caían fuera desde ~1.250 px, y con los siete que había antes,
   desde ~1.150: comprobado midiendo, el enlace sobrante quedaba flotando
   debajo de la línea divisoria.
   El arreglo de «el navbar se pisaba» ya existía, pero **encerrado en
   `@media (max-width:940px)`** — la misma forma de fallar que cuando estuvo
   encerrado en `max-width:40rem` y abrió la franja de 641 a 900. Acá va en la
   regla base: por encima de 940 no cambia nada mientras quepan, y cuando no
   quepan se arrastran en vez de romperse.
   ⚠️ `flex:none` en cada enlace: sin eso, `nowrap` los exprime y les parte el
   texto en dos líneas, que en una barra de alto fijo es el mismo desborde por
   otro camino. */
.nav-links{
  display:flex;gap:0.25rem;flex:1;min-width:0;flex-wrap:nowrap;
  overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;
}
.nav-links::-webkit-scrollbar{display:none}
.nav-links a{flex:none}
.nav-links a{
  color:var(--ink-2);font-size:0.8125rem;font-weight:500;padding:0.4375rem 0.75rem;
  border-radius:0.5rem;white-space:nowrap;
}
.nav-links a:hover{background:var(--surface-2);color:var(--ink);text-decoration:none}
.nav-links a.on{background:rgba(var(--led-rgb),.16);color:var(--led-bright)}
.nav-yo{display:flex;align-items:center;gap:0.625rem;flex:none}

/* ------------------------- el botón del tema ----------------------------- */
/* 34 px y no 28 como los otros botones chicos de la barra: este se aprieta con
   el pulgar y no tiene rótulo que agrande el blanco de toque. */
.tema-btn{
  flex:none;width:2.125rem;height:2.125rem;border-radius:0.5625rem;cursor:pointer;
  display:grid;place-items:center;padding:0;
  background:var(--surface-2);border:1px solid var(--line);color:var(--ink-2);
  transition:border-color .15s,color .15s,background .15s;
}
.tema-btn:hover{border-color:var(--led);color:var(--led-bright)}
.tema-btn:focus-visible{outline:2px solid var(--led-bright);outline-offset:2px}
/* ⚠️⚠️ CUARTA vez con este patrón en este archivo (van: el emergente de Leaflet,
   los créditos, los +/- táctiles, y esto). Acá el `display:block` estaba en la
   regla de arriba, `.tema-btn svg`, que pesa (0,1,1) — UNA clase y UN elemento —
   y le ganaba a `.tema-sol{display:none}`, que pesa (0,1,0). Resultado: en claro
   se veían EL SOL Y LA LUNA a la vez, uno encima del otro dentro del botón de
   34 px. En oscuro no pasaba, porque ahí quien esconde la luna es
   `[data-tema="oscuro"] .tema-luna` y eso pesa (0,2,0) y sí gana.
   O sea: el defecto solo existía en UNO de los dos temas — que es exactamente
   la clase de cosa que este trabajo puede introducir sin que nada falle.
   El `display` NO va en la regla genérica. */
.tema-btn svg{width:1.0625rem;height:1.0625rem}
/* Se muestra el ícono de LO QUE VA A PASAR, no el del estado actual: estando en
   oscuro el botón ofrece el sol. Al revés —mostrar la luna cuando ya está
   oscuro— se lee como un indicador y nadie lo aprieta. */
.tema-sol,.tema-luna{display:none}
[data-tema="oscuro"] .tema-sol{display:block}
[data-tema="claro"]  .tema-luna{display:block}
.avatar{
  width:2rem;height:2rem;border-radius:50%;flex:none;display:grid;place-items:center;
  background:linear-gradient(140deg,var(--deep),var(--led));
  font-size:0.7188rem;font-weight:600;color:#fff;overflow:hidden;
}
.avatar img{width:100%;height:100%;object-fit:cover}
.nav-yo .quien{font-size:0.75rem;line-height:1.25;text-align:right}
.nav-yo .quien b{display:block;font-size:0.7812rem;color:var(--ink);font-weight:600}
/* Tope al nombre (2-nov-2026): sin él, un nombre largo («María Fernanda de los
   Ángeles Restrepo Villegas») encogía la fila de enlaces a 296 px a 1024 y
   escondía «Cuenta» y «Ayuda y PQR». El nombre entero va en el `title` del enlace. */
.nav-yo .quien{min-width:0;max-width:12rem}
.nav-yo .quien b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Y por debajo de 1.100 px el nombre se va (queda el avatar, con el nombre en su
   `title`): a 1024, aun cortado, le quitaba a la fila el sitio de «Ayuda y PQR»,
   que la Ley 1480 art. 50 pide a la vista. */
@media (max-width:1100px){ .nav-yo .quien{display:none} }
/* Quedan enlaces a la derecha (cuenta de operador en una ventana angosta): el borde
   de la fila se desvanece (`base.html` pone `hay-mas` midiendo). La máscara solo
   usa la opacidad del color, así que el token da igual. */
.nav.hay-mas .nav-links{
  -webkit-mask-image:linear-gradient(90deg,var(--ink) calc(100% - 2.5rem),transparent);
  mask-image:linear-gradient(90deg,var(--ink) calc(100% - 2.5rem),transparent);
}
.nav-yo .quien span{color:var(--ink-3);font-size:0.6562rem}

/* El bloque para teléfono vive al FINAL del archivo — ver "TELÉFONO" allá. */

/* -------------------------------- botones -------------------------------- */
.btn{
  font-family:var(--sora);font-size:0.8438rem;font-weight:600;border-radius:0.5625rem;
  padding:0.625rem 1rem;border:1px solid transparent;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:0.5rem;
  transition:filter .15s,transform .06s,border-color .15s,background .15s;
  text-decoration:none;line-height:1.2;
}
.btn:hover{text-decoration:none}
.btn:active:not(:disabled){transform:translateY(1px)}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn:focus-visible{outline:2px solid var(--led-bright);outline-offset:2px}
/* `#fff` literal y a propósito: es texto BLANCO SOBRE EL RELLENO AZUL, no
   texto sobre el fondo de la app. El relleno es el mismo en los dos temas
   (medido: blanco sobre #0D6BFF da 4,60:1 y sobre #085CF0 da 5,53:1), así que
   este blanco no se tematiza — se quedaría en negro sobre azul. */
.btn.pri{background:linear-gradient(180deg,var(--led),var(--led-hondo));color:#fff}
.btn.pri:hover:not(:disabled){filter:brightness(1.12)}
.btn.gh{background:var(--surface-2);border-color:var(--line);color:var(--ink)}
.btn.gh:hover:not(:disabled){border-color:var(--led);color:var(--led-bright)}
.btn.stop{background:rgba(var(--crit-rgb),.13);border-color:rgba(var(--crit-rgb),.45);color:var(--crit-ink)}
.btn.stop:hover:not(:disabled){background:rgba(var(--crit-rgb),.22)}
.btn.ok{background:rgba(var(--ok-rgb),.13);border-color:rgba(var(--ok-rgb),.45);color:var(--ok)}
.btn.ok:hover:not(:disabled){background:rgba(var(--ok-rgb),.22)}
.btn.sm{font-size:0.75rem;padding:0.375rem 0.6875rem;border-radius:0.4375rem}
.btn.full{width:100%}

/* ------------------------------ contenedores ----------------------------- */
main{flex:1;min-height:0;display:flex;flex-direction:column}
.wrap{max-width:97.5rem;margin:0 auto;padding:1.375rem 1.25rem 2.5rem;width:100%}
.titulo{display:flex;align-items:center;gap:0.75rem;margin:0 0 0.25rem;flex-wrap:wrap}
.titulo h1{margin:0;font-size:1.375rem;font-weight:600;letter-spacing:-.02em}
.sub{color:var(--ink-3);font-size:0.7812rem;margin:0 0 1.25rem}

.panel{
  background:var(--surface);border:1px solid var(--line);border-radius:0.75rem;
  overflow:hidden;margin-bottom:1rem;
}
.panel-h{
  display:flex;align-items:center;gap:0.625rem;padding:0.75rem 1rem;
  background:var(--surface-2);border-bottom:1px solid var(--line);flex-wrap:wrap;
}
.panel-h h2{
  margin:0;font-size:0.6562rem;font-weight:600;letter-spacing:.15em;
  text-transform:uppercase;color:var(--ink-2);
}
.panel-h .nota{margin-left:auto;font-family:var(--mono);font-size:0.6562rem;color:var(--ink-3)}
.panel-b{padding:1rem}
.grid2{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:1rem}
@media (max-width:900px){.grid2{grid-template-columns:minmax(0,1fr)}}

/* --------------------------------- KPIs ---------------------------------- */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(10.5rem,1fr));gap:0.6875rem;margin-bottom:1.125rem}

/* ⚠️⚠️ ESTE BLOQUE VA ACÁ Y NO ARRIBA, Y ES LA RAZÓN POR LA QUE NO FUNCIONABA.
   Estaba junto al `html{font-size}` del principio, o sea ANTES de la regla de
   la línea de arriba. Con la misma especificidad gana la ÚLTIMA, así que la
   general lo pisaba y las tarjetas seguían apilándose de a una — sin ningún
   error, con la regla escrita y visible en el archivo.

   Qué hace: el `minmax` también escaló con el root (10,5rem = 210 px reales),
   y en un teléfono de 412 px dejaban de caber dos. Con doce tarjetas —Finanzas—
   eso es una pantalla de puro scroll. A 8,5rem (170 px) caben dos; en un
   teléfono angosto, el propio `auto-fit` vuelve a una sola sin decirle nada. */
@media (max-width:940px){
  .kpis{grid-template-columns:repeat(auto-fit,minmax(8.5rem,1fr))}
}
.kpi{
  background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--led);
  border-radius:0.625rem;padding:0.8125rem 0.9375rem;min-width:0;
}
.kpi.ok{border-left-color:var(--ok)}
.kpi.warn{border-left-color:var(--warn)}
.kpi.crit{border-left-color:var(--crit)}
.kpi .l{font-size:0.5938rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
.kpi .v{font-family:var(--mono);font-size:1.5625rem;font-weight:700;margin-top:3px;
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--ink)}

/* ⚠️⚠️ UNA CIFRA DE PLATA CORTADA NO ES UN DEFECTO DE FORMA: ES OTRO NÚMERO.
   En un teléfono, `.kpis` cae en dos columnas de ~170 px y a `.kpi` le quedan
   ~140 de contenido. Con la mono a 25 px fijos entran unos 9 caracteres, y
   `$270.872.873` son 12: en Finanzas se leía **«$270.872.87»** —diez veces
   menos— y `$353.016.324` igual. No lo agarra ningún assert: la consulta
   devuelve bien, el formateador imprime bien, y es la PANTALLA la que miente.
   Es el mismo género del «1 asientos» y del «$0 reservado».

   Se achica sola contra el ancho de SU tarjeta, no contra el del viewport: así
   sirve igual en dos columnas, en una o dentro del cajón de un cliente, y
   **no se vuelve a romper cuando las cifras crezcan** — que es lo que pasaría
   con un `font-size` fijo más chico, que solo mueve el problema un dígito más
   allá.

   El `@supports` no es adorno: donde no haya container queries, esto no aplica
   y queda el 25 px de arriba — o sea, degrada a lo que ya había, nunca a peor. */
@supports (container-type:inline-size){
  .kpi{container-type:inline-size}
  .kpi .v{font-size:clamp(0.875rem,12cqi,1.5625rem)}
}
.kpi.ok .v{color:var(--ok)} .kpi.warn .v{color:var(--warn)} .kpi.crit .v{color:var(--crit)}
.kpi .s{font-size:0.6562rem;color:var(--ink-3);margin-top:2px}
/* El medio de pago es un NOMBRE («Visa ···· 4242»), no una cifra: en la letra
   del texto y más chico, con el relleno que iguala el alto de sus vecinas. */
.kpi .v.kpi-medio{font-family:inherit;font-size:1.0625rem;font-weight:600;letter-spacing:-.01em;
  padding:0.25rem 0 3px;overflow-wrap:anywhere}

/* -------------------------------- tablas --------------------------------- */
.tabla-wrap{overflow-x:auto}

/* «Mis cargas» del panel en el teléfono: cada carga es una tarjeta (2026-10-21).
   Como tabla medía 812 px dentro de 277: se veía la fecha y una tira del punto,
   y el resto había que buscarlo arrastrando de lado. Arriba el punto, debajo
   los datos de a dos con su rótulo (`data-r`, lo pone `panel.js`). Es el mismo
   patrón de la consola (`kw-admin.css`) y de los textos legales. */
@media (max-width:640px){
  /* `table.t.hist-t` y no `.hist-t`: las reglas de `table.t td` (relleno,
     `nowrap`, alineado a la derecha) son más específicas y van después. */
  table.t.hist-t,table.t.hist-t tbody{display:block}
  table.t.hist-t thead{display:none}
  table.t.hist-t tbody tr{
    display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:0.5rem 0.875rem;
    padding:0.75rem 0;border-bottom:1px solid var(--line);
  }
  table.t.hist-t tbody tr:last-child{border-bottom:none}
  table.t.hist-t td,table.t.hist-t td.n{display:block;padding:0;border:none;text-align:left;white-space:normal;min-width:0}
  table.t.hist-t td.hist-p,table.t.hist-t td.hist-fin,table.t.hist-t td.vacio{grid-column:1 / -1}
  table.t.hist-t td[data-r]::before{
    content:attr(data-r);display:block;margin-bottom:2px;
    font-size:0.5625rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
    color:var(--ink-3);font-family:var(--sora);
  }
}
table.t{width:100%;border-collapse:collapse;font-size:0.7812rem}
table.t th{
  text-align:left;font-size:0.5625rem;letter-spacing:.12em;text-transform:uppercase;
  font-weight:600;color:var(--ink-3);padding:0 0.75rem 0.5rem 0;
  border-bottom:1px solid var(--line);white-space:nowrap;
}
table.t td{padding:0.5625rem 0.75rem 0.5625rem 0;border-bottom:1px solid var(--line-soft);color:var(--ink-2)}
table.t tr:last-child td{border-bottom:none}
table.t td.n{font-family:var(--mono);text-align:right;padding-right:1rem;
  font-variant-numeric:tabular-nums;white-space:nowrap}
table.t td.fuerte{color:var(--ink);font-weight:500}
/* El historial del panel (`panel.js`): fecha y conector enteros, en un renglón. */
table.t td.hist-f{white-space:nowrap}
table.t td.hist-k{font-family:var(--mono);font-size:0.6875rem;white-space:nowrap}
table.t .hist-c{font-size:0.6562rem;color:var(--ink-3)}
.vacio{padding:1.625rem 0;text-align:center;color:var(--ink-3);font-size:0.7812rem}
/* ⭐ ESQUELETOS QUE BRILLAN (2026-10-28) mientras llega un dato. «Cargando…»
   en gris se lee como «no hay nada»; un esqueleto con la forma de lo que viene
   se lee como «ya viene». Un brillo lento que cruza; quieto con «reducir
   movimiento». El que lo reemplaza es el JS de cada pantalla al pintar. */
.esq{display:block;border-radius:0.375rem;
  background:linear-gradient(90deg,var(--surface-2) 0%,var(--surface-3) 50%,var(--surface-2) 100%) 0 0/200% 100%;
  animation:esq 1.5s ease-in-out infinite}
@keyframes esq{from{background-position:100% 0}to{background-position:-100% 0}}
.esq-caja{display:flex;flex-direction:column;gap:0.625rem;padding:0.5rem 0;width:100%}
.esq-linea{height:0.75rem}
.esq-bloque{height:10rem;border-radius:0.625rem}
.esq-tarjeta{display:flex;flex-direction:column;gap:0.5rem;padding:1rem;border-radius:0.875rem;
  border:1px solid var(--line);background:var(--surface)}
.esq-fila td{padding:0.75rem 0.5rem}
.mc-donde>li:has(.esq-caja){display:block}
@media (prefers-reduced-motion:reduce){ .esq{animation:none} }
/* El dibujo de un vacío (`vc-*`, 2026-10-28): chico, centrado, sobre el texto.
   Un vacío sin dibujo se lee como «no cargó»; con dibujo, como «todavía no». */
.vacio .vc{display:block;width:min(10rem,60%);height:auto;margin:0 auto 0.625rem}
.err-il{display:block;width:min(20rem,86vw);height:auto;margin:0 auto 0.5rem}
.cod-vc{display:block;width:min(11rem,60%);height:auto;margin:0 auto}
/* La píldora de estado de una caja en columna se estiraba a todo el ancho. */
.cod-caja>.pill{align-self:flex-start}

/* -------------------------------- pastillas ------------------------------ */
.pill{
  display:inline-flex;align-items:center;gap:0.3125rem;font-size:0.625rem;font-weight:700;
  padding:3px 0.5625rem;border-radius:6.25rem;border:1px solid;letter-spacing:.03em;
  white-space:nowrap;font-family:var(--mono);
}
.pill.ok{color:var(--ok);border-color:rgba(var(--ok-rgb),.4);background:rgba(var(--ok-rgb),.09)}
.pill.warn{color:var(--warn);border-color:rgba(var(--warn-rgb),.4);background:rgba(var(--warn-rgb),.09)}
.pill.crit{color:var(--crit-ink);border-color:rgba(var(--crit-rgb),.45);background:rgba(var(--crit-rgb),.1)}
.pill.info{color:var(--led-bright);border-color:rgba(var(--led-bright-rgb),.4);background:rgba(var(--led-bright-rgb),.09)}
.pill.gris{color:var(--ink-3);border-color:var(--line);background:var(--surface-2)}
.dot{width:0.4375rem;height:0.4375rem;border-radius:50%;flex:none;background:currentColor}
.dot.late{animation:late 1.8s infinite}
@keyframes late{50%{opacity:.25}}

/* --------------------------------- avisos -------------------------------- */
.aviso{
  display:flex;gap:0.6875rem;align-items:flex-start;padding:0.75rem 0.875rem;border-radius:0.5625rem;
  font-size:0.7812rem;line-height:1.55;margin-bottom:0.875rem;
  /* ⚠️ `wrap` y no la falta de él: sin esto, un aviso con botón al lado no
     apila nunca y el texto se comprime hasta donde dé. En un teléfono de 390 px
     el de «falta confirmar su correo» quedaba en una columna de ~144 px y se
     partía en OCHO renglones — y es el aviso que ve TODO el que todavía no ha
     confirmado, o sea el primero que ve alguien nuevo.
     Los hijos ya traen lo que hace falta para que esto funcione solo: el texto
     es `flex:1 1 15rem` (pide 240 y cede), el ícono y el botón son `flex:none`.
     Así el botón baja cuando el texto no cabe en 240, y en escritorio —donde
     sobra ancho— no cambia nada. */
  flex-wrap:wrap;
}
.aviso.info{background:rgba(var(--led-rgb),.08);border:1px solid rgba(var(--led-rgb),.3);color:var(--led-pale)}
.aviso.warn{background:rgba(var(--warn-rgb),.08);border:1px solid rgba(var(--warn-rgb),.32);color:var(--warn-ink)}
.aviso.crit{background:rgba(var(--crit-rgb),.08);border:1px solid rgba(var(--crit-rgb),.35);color:var(--crit-ink-suave)}
.aviso.ok{background:rgba(var(--ok-rgb),.07);border:1px solid rgba(var(--ok-rgb),.3);color:var(--ok-ink)}
.aviso b{color:var(--ink-fuerte)}
/* El texto del aviso cede y pide 15rem (2-nov-2026). El comentario de arriba lo
   daba por hecho y ninguna regla lo decía: en las puertas de entrada el texto es un
   `<div>` pelado y, con un mensaje largo, a 390 px el ícono quedaba solo en un
   renglón y el texto bajaba debajo. */
.aviso>div{flex:1 1 15rem;min-width:0}

/* ================================ MAPA =================================== */
.mapa-stage{
  flex:1;min-height:0;display:grid;grid-template-columns:24.5rem minmax(0,1fr);
}
/* Con la ficha de un punto abierta el lateral se ensancha y el mapa se corre:
   quien ya eligió un punto NO está explorando, está gestionando —conectores,
   cupo, botón de cargar—, y a esa altura el mapa ya cumplió. Se deja una franja
   viva y no se cierra del todo: sirve de contexto y de salida.
   ⚠️ SIN ANIMACIÓN (2026-11-02). Se animaba `grid-template-columns` en 280 ms:
   rehacía el layout del mapa en cada cuadro, `mapa.js` esperaba 300 ms para
   medirlo y después volaba 600 ms al punto. Casi un segundo entre tocar y ver
   la ficha quieta, en algo que se toca decenas de veces. Ahora cambia de un
   salto y el mapa se centra en el acto.
   ⚠️ Solo desde 58.8125rem: por debajo, `@media (max-width:58.75rem)` ya puso el
   lateral y el mapa en FILAS, no en columnas — esta regla igual pesa más
   (dos clases) y sin acotarla ganaba también ahí, dejando un grid de dos
   columnas exprimido en una pantalla de teléfono. */
@media (min-width:941px){
  .mapa-stage.ficha-abierta{grid-template-columns:minmax(0,38.75rem) minmax(0,1fr)}

  /* ⚠️⚠️ ACÁ EL ALTO SE FIJA, Y SIN ESTO TODO EL DISEÑO DE ESTA PANTALLA ERA
     LETRA MUERTA. `.lista` pide `flex:1;overflow-y:auto` y `#mapa` pide
     `height:100%`, los dos contra el alto de su fila — pero la fila era `auto`,
     o sea del tamaño del contenido: con los 10 puntos de la demo, `.lado` medía
     1.847 px, la lista NO se desplazaba por dentro (su `overflow` nunca se
     activaba) y el mapa se estiraba a 1.847 px dentro de una ventana de 900,
     con la página entera desplazándose. Medido, no supuesto: documento=1.903 px
     a 1440x900. Y empeoraba con cada punto que entrara a la red.
     `flex:1` no alcanzaba: reparte el espacio LIBRE, y acá no había — el
     contenido ya pasaba de la ventana. Hace falta un alto definido para que la
     fila del grid deje de medir por contenido.
     ⚠️ En el teléfono NO se fija: ver el bloque de 940 px más abajo. */
  /* ⚠️ Y la FILA también hay que fijarla, no solo la caja: con
     `grid-template-rows` sin declarar la fila implícita es `auto`, o sea del
     tamaño del contenido, y una fila `auto` más alta que su contenedor se
     SALE de él —el alto quedaba puesto y la lista seguía midiendo 1.847 px,
     desbordando—. `minmax(0,1fr)` es lo que la obliga a caber, y recién ahí el
     `overflow-y:auto` de `.lista` tiene algo que desplazar. */
  /* ⚠️⚠️ Y `flex:none` NO SOBRA: es lo que hace que el `height` exista.
     `.mapa-stage` es un hijo flex de `main`, que es una COLUMNA, y el `flex:1`
     de la regla base significa `flex:1 1 0%` — en el eje principal de un flex,
     `flex-basis` PISA a `height`. Con el alto puesto y `flex:1`, el estilo
     calculado seguía diciendo 1.847 px: la declaración estaba ahí, aplicaba, y
     no hacía absolutamente nada. No falla, no avisa; se ve midiendo. */
  .mapa-stage{flex:none;height:calc(100vh - var(--nav));grid-template-rows:minmax(0,1fr) auto auto}
  /* El mapa toma la columna derecha ENTERA, las dos filas: a la izquierda van
     la barra lateral (fila 1) y el pie del vendedor (fila 2, pegado abajo).
     Sin este tramo el pie se colocaría solo en (2,1) —bien— pero el mapa se
     quedaría en la fila 1 y dejaría una franja muerta a su derecha. */
  .mapa-stage>#mapa{grid-area:1/2/4/3}
  /* La leyenda y el pie salieron de `<aside>`, así que la línea que marca el
     borde de la columna tiene que seguir en ellos o se corta a media altura. */
  .mapa-stage>.leyenda,
  .mapa-stage>.pie{border-right:1px solid var(--line)}
  /* ⚠️⚠️ Y SE DESPLAZA LA COLUMNA ENTERA, NO SOLO LA LISTA (2026-11-02, revisión de diseño,
     grave B9). Con la lista sola desplazándose, todo lo de arriba (escanear, escribir el código,
     «Así se carga», buscador, chips, selectores, cercanía) mide más que la ventana de un portátil
     y la lista se quedaba con lo que sobraba: 30 px a 1366x768 y 76 px a 1440x900, medido. No se
     veía ni un punto, y el botón de cercanía se pintaba encima de la leyenda.
     ⚠️ La especificidad no sobra: la regla base `.lista{flex:1;overflow-y:auto}` va MÁS ABAJO
     en este archivo y, con la misma especificidad, gana la última (la trampa de §4 de CLAUDE.md). */
  .mapa-stage .lado{overflow-y:auto;overscroll-behavior:contain}
  .mapa-stage .lado>.lista{flex:none;overflow:visible}
}

/* =========================================================================
   LA BARRA DE ARRIBA, DE VIDRIO, SOLO EN EL MAPA (2026-11-02, pedido del usuario)
   -------------------------------------------------------------------------
   El mapa sube hasta el borde de la pantalla y la barra flota encima, de vidrio:
   se sigue viendo la ciudad detrás, que es lo único de la app que vale la pena
   ver a través de algo (pantallas-como-ismael, `vidrio.md` §1). En las demás
   pantallas la barra sigue como estaba: esto vale solo donde `main` tiene el
   mapa, y sin `:has()` el navegador la deja como hoy.
   · La receta de la casa: `blur(10px) saturate(1.4)` y el fondo de
     `--nav-vidrio` (blanco al 72 % en claro, la tinta oscura al 66 % en oscuro).
   · El alto real de la barra lo mide `mapa.js` (`--nav-alto`): en el teléfono
     tiene dos renglones y no mide `--nav`. Con él bajan la columna, los
     controles de Leaflet y el encuadre de los puntos.
   · En el teléfono, solo en la vista MAPA: en la lista y en la ficha no hay nada
     detrás que ver, y la barra de pestañas sigue opaca.
   · Encima del vidrio la letra va un paso más gruesa y en la tinta fuerte: se
     midió contra el satelital, el fondo más cargado (≥ 4,5:1 en los dos temas).
   ========================================================================= */
@media (min-width:941px){
  body:has(> main > .mapa-stage) .nav{
    position:fixed;top:0;left:0;right:0;
    background:var(--nav-vidrio);
    -webkit-backdrop-filter:blur(10px) saturate(1.4);backdrop-filter:blur(10px) saturate(1.4);
  }
  body:has(> main > .mapa-stage) .nav-links a{font-weight:600;color:var(--ink)}
  body:has(> main > .mapa-stage) .mapa-stage{height:100vh;height:100dvh}
  body:has(> main > .mapa-stage) .mapa-stage>.lado{margin-top:var(--nav-alto,var(--nav))}
  body:has(> main > .mapa-stage) #mapa .leaflet-top{top:var(--nav-alto,var(--nav))}
}
@media (max-width:940px){
  body:has(> main > .mapa-stage.vista-mapa:not(.ficha-abierta)) .nav{
    position:fixed;top:0;left:0;right:0;
    background:var(--nav-vidrio);
    -webkit-backdrop-filter:blur(10px) saturate(1.4);backdrop-filter:blur(10px) saturate(1.4);
  }
  body:has(> main > .mapa-stage.vista-mapa:not(.ficha-abierta)) .nav-links a{font-weight:600;color:var(--ink)}
  body:has(> main > .mapa-stage.vista-mapa:not(.ficha-abierta)) #mapa .leaflet-top{top:var(--nav-alto,var(--nav))}
  /* Sin sesión no hay barra de pestañas, y `main` le reservaba igual su alto: una
     franja blanca de 54 px al pie del mapa y de la pantalla de pagar. La reserva
     queda solo donde la barra existe (pesa más que la regla general de `main`). */
  main:has(> .mapa-stage):not(:has(~ .tabbar)),
  main:has(> .cod-pag):not(:has(~ .tabbar)){padding-bottom:env(safe-area-inset-bottom,0px)}
  /* Y sin barra, lo que flota baja a su sitio: a la altura de la barra que no está,
     la píldora «Lista» quedaba encima de «Términos» en el pie del vendedor. */
  body:not(:has(.tabbar)) .conmuta{bottom:calc(0.875rem + env(safe-area-inset-bottom,0px))}
  body:not(:has(.tabbar)) .kw-espera,
  body:not(:has(.tabbar)) .kw-avisos{bottom:calc(4.5rem + env(safe-area-inset-bottom,0px))}
  /* Mientras se espera (hasta una hora), la banda no tapa el pie del vendedor:
     el pie le reserva su alto, igual que a la píldora. */
  body:has(#kw-espera) .mapa-stage>.pie{padding-bottom:10.5rem}
}
/* Respaldo: sólida, nunca letra ilegible. Sin `backdrop-filter`, con menos
   transparencia pedida o con más contraste (vidrio.md §7). ⚠️ Los dos selectores
   van en la lista para igualar el peso del de arriba: con uno solo, el del
   teléfono (más clases) le ganaba y el vidrio seguía transparente. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  body:has(> main > .mapa-stage.vista-mapa:not(.ficha-abierta)) .nav,
  body:has(> main > .mapa-stage) .nav{background:linear-gradient(var(--nav-bg),var(--nav-bg)),var(--ground)}
}
@media (prefers-reduced-transparency:reduce),(prefers-contrast:more){
  body:has(> main > .mapa-stage.vista-mapa:not(.ficha-abierta)) .nav,
  body:has(> main > .mapa-stage) .nav{
    -webkit-backdrop-filter:none;backdrop-filter:none;
    background:linear-gradient(var(--nav-bg),var(--nav-bg)),var(--ground);
  }
}
@media (prefers-contrast:more){
  body:has(> main > .mapa-stage.vista-mapa:not(.ficha-abierta)) .nav,
  body:has(> main > .mapa-stage) .nav{border-bottom-color:var(--ink-3)}
}

/* Y con la ficha abierta se apagan los controles de BÚSQUEDA. Quien entró a un
   punto no está buscando: está mirando si hay cupo y si puede cargar. El
   buscador, los filtros, «Ordenar por cercanía» y «Estoy frente a un cargador»
   son herramientas de la etapa anterior, y ocupaban media columna empujando la
   ficha hacia abajo — en un portátil, los cargadores quedaban bajo el pliegue.
   No se pierden: el botón «Volver a la lista» de la ficha los trae de vuelta. */
.ficha-abierta .buscador,
.ficha-abierta .filtros,
.ficha-abierta .cerca-btn,
.ficha-abierta .cod-btn,
.ficha-abierta .cargar-ya,
.ficha-abierta .lado-nota{display:none}
/* El conmutador lista/mapa existe SOLO en el teléfono, que es donde los dos no
   caben a la vez. En el escritorio están los dos en pantalla, y un botón para
   ir a algo que ya se está viendo no lleva a ninguna parte. Lo prende el
   bloque de 940 px. */
.conmuta{display:none}
/* El encabezado se encoge, no desaparece: es la salida a la app. */
.ficha-abierta .lado-h{padding:0.75rem 1.125rem 0.625rem}
.ficha-abierta .lado-h h1{font-size:0.9375rem}
.ficha-abierta .lado-h p{display:none}
#mapa{width:100%;height:100%;min-height:26.25rem;background:var(--mapa-bg)}

/* ⚠️⚠️ EN EL TELÉFONO LA LISTA Y EL MAPA NO CONVIVEN: SE CONMUTAN
   (decisión del usuario, 2026-10-07, después de probarlo apilado).

   Primero se apiló —la información arriba, el mapa debajo— porque el mapa
   «tenía mucho peso». Apilado no funcionó, y las dos razones valen escribirlas
   porque van a tentar de nuevo:

   1. **El mapa nunca se alcanzaba.** Entre encabezado, buscador, filtros y los
      dos botones hay ~400 px antes de la primera tarjeta, de ~740 útiles. Con
      la lista arriba, el mapa arrancaba en y=879 y la barra de pestañas tapa
      desde y=852: asomaba 0 px. Recortar más era quitarle sitio a la lista, que
      es justo lo que el cambio venía a proteger.
   2. ⚠️⚠️ **Y el gesto quedaba atrapado.** Para que la página no creciera sin
      fin se le había puesto techo a `.lista`, o sea un desplazamiento DENTRO de
      otro. El dedo arrastra sobre las tarjetas, la lista interna se mueve y la
      página no: hay que soltar y volver a arrastrar para que el mapa aparezca.
      Reportado tal cual: «tengo que bajar toda la lista para recién llegar al
      mapa, y cuando llego arrastro y se mueve el mapa; un usuario nuevo va a
      encontrar una interfaz tipo trampa».

   Conmutando, los dos problemas se van juntos: cada vista tiene la pantalla
   entera, no hay desplazamiento anidado —`.lista` vuelve a fluir en la página,
   sin techo— y el mapa está siempre a UN TOQUE, no a un recorrido.
   ~~Abre en la LISTA, que es la prioridad que se pidió.~~ Desde el 2026-10-28
   abre en el MAPA, por pedido del usuario: la clase `vista-mapa` viene puesta
   desde `mapa.html`. Escanear el QR sigue a un toque en «Cargar», arriba.

   ⚠️ En el escritorio no aplica nada de esto: ahí caben los dos a la vez y
   siguen lado a lado (bloque de 941 px). */
@media (max-width:940px){
  /* Tres filas en el orden del DOM —lateral, mapa, leyenda— más el pie: ya no
     hay `order:-1` sobre `#mapa`, que era lo que lo subía.
     ⚠️ La fila del mapa es `minmax(auto,1fr)` y no `auto`: cuando el contenido
     de arriba es corto, un grid con filas `auto` reparte el sobrante EN PARTES
     IGUALES y deja un hueco vacío al final de la página. Con `1fr` ese sobrante
     se lo come el mapa, que es lo único que puede estirarse sin mentir. */
  .mapa-stage{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(auto,1fr) auto auto}
  .lado{border-right:none;border-bottom:1px solid var(--line)}

  /* ---------- vista LISTA ---------- */
  /* El mapa y su leyenda no están: no se achican, no se esconden a medias, NO
     ESTÁN. Es lo que devuelve la pantalla entera a la lista y lo que permite
     que `.lista` fluya en la página sin techo ni desplazamiento propio.
     ⚠️ Con la ficha de un punto abierta SÍ vuelve el mapa, como tira de
     contexto arriba: ahí no compite con nada —la ficha no es una lista que se
     recorra— y responde «¿dónde queda esto?» sin que haya que ir a buscarlo. */
  .mapa-stage:not(.vista-mapa):not(.ficha-abierta)>#mapa,
  .mapa-stage:not(.vista-mapa):not(.ficha-abierta)>.leyenda{display:none}

  /* ---------- vista MAPA (la de entrada) ---------- */
  /* Se va la barra lateral entera y el mapa toma la fila elástica. No se le
     pone `height` en vh: la fila es `1fr` de un contenedor que ya mide la
     ventana menos la barra de arriba y la de pestañas, así que el mapa llena
     lo que queda — sin desplazamiento de página, que es lo que vuelve
     inequívoco que arrastrar mueve el MAPA. */
  .mapa-stage.vista-mapa{grid-template-rows:minmax(0,1fr) auto auto}
  .mapa-stage.vista-mapa>.lado{display:none}
  .mapa-stage.vista-mapa>#mapa{height:auto;min-height:0;order:0}

  /* ---------- la ficha de un punto ---------- */
  /* ⚠️⚠️ `order:-1` sobre un hijo de un grid no solo lo dibuja primero: cambia
     el orden de colocación automática, así que el mapa toma la fila 1 (`auto`,
     su tira de 5,5 rem) y la ficha la 2 (`1fr`, todo lo demás) sin tocar
     `grid-template-rows`. Si la ficha es corta, ese `1fr` la estira y no queda
     hueco al final. */
  /* 7 rem y sin los +/− (2026-11-02): en 5,5 rem el pin salía cortado por arriba y
     los botones y los créditos ocupaban media tira, con el «−» cortado. La tira es
     contexto, no un mapa para manejar: para eso está la vista mapa. */
  .mapa-stage.ficha-abierta #mapa{order:-1;height:7rem;min-height:7rem}
  .mapa-stage.ficha-abierta #mapa .leaflet-control-zoom,
  .mapa-stage.ficha-abierta #mapa .leaflet-control-layers,
  .mapa-stage.ficha-abierta #mapa .kw-yo{display:none}
  .mapa-stage.ficha-abierta .lado{border-bottom:none;border-top:1px solid var(--line)}

  /* ---------- el botón que conmuta ---------- */
  /* Flotante y SIEMPRE visible, que es el punto entero: un atajo que se
     desplaza junto con el encabezado no sirve justo cuando hace falta, que es
     cuando uno ya bajó.
     ⚠️ A la misma altura que `.kw-cinta` (la cinta de «estos N puntos» al tocar
     un globo agrupado) y que los `.toast`: la cinta va centrada y la píldora a
     la derecha, así que se reparten el renglón. Que de verdad no se pisen lo
     comprueba `probar_conmutador.py` midiendo las dos cajas, no mirándolo. */
  /* ⚠️ Y el final de la página le RESERVA SITIO. Un botón fijo tapa siempre lo
     último que haya debajo, y acá lo último es la identificación del vendedor:
     la píldora se sentaba justo encima de «NIT: Pendiente», que es un dato que
     el art. 50 de la Ley 1480 obliga a mostrar. Tapado por un botón no está
     mostrado. Se resuelve donde se causa —dejando aire al final— y no corriendo
     el botón a un sitio raro. */
  .mapa-stage>.pie{padding-bottom:4rem}
  .conmuta{
    display:flex;align-items:center;gap:0.4375rem;
    position:fixed;right:0.875rem;z-index:1200;
    bottom:calc(var(--tabbar) + 0.875rem + env(safe-area-inset-bottom,0px));
    padding:0.5625rem 0.9375rem;border:none;border-radius:6.25rem;
    /* 44 px de blanco: es el ÚNICO camino a la lista en el teléfono, y medía 31. */
    min-height:max(2.75rem,44px);
    font-family:var(--sora);font-size:0.75rem;font-weight:600;cursor:pointer;
    background:linear-gradient(180deg,var(--led),var(--led-hondo));
    /* ⚠️ Blanco fijo, no `var(--ink)`. El fondo es azul de marca en los DOS
       temas; una tinta que siga al tema deja el texto NEGRO sobre azul en
       claro — la trampa de `.vivo` y `.perf-av` (CLAUDE.md §4). Misma
       excepción y mismo motivo que `.btn.pri` y `.tab-qr-b`. */
    color:#fff;
    box-shadow:var(--sombra-flota);
    transition:transform .12s ease;
  }
  .conmuta:active{transform:translateY(1px) scale(.97)}
  .conmuta:hover{text-decoration:none}
  .conmuta svg{flex:none}
  /* Un botón, dos íconos: se muestra el de la vista A LA QUE LLEVA, nunca el
     de donde uno está. Un ícono que describe dónde se está, en un botón que
     cambia de sitio, es la forma clásica de que nadie sepa qué va a pasar. */
  .conmuta .i-lista{display:none}
  .vista-mapa .conmuta .i-lista{display:block}
  .vista-mapa .conmuta .i-mapa{display:none}
  /* ⚠️ En la LISTA la píldora va en la cabecera, no flotando (2026-11-02). Flotando
     quedaba encima del «Cargar» de la primera tarjeta (los dos en y=746) y, al
     desplazar, tapaba la mitad derecha de todo lo que pasaba por debajo: tocar el
     borde derecho de «Cargar» abría el mapa. Arriba a la derecha, junto a «Puntos
     de carga», no pisa nada. Con la ficha abierta no va: la ficha ya trae su
     «Volver al mapa» o «Volver a la lista», y flotando tapaba sus botones. */
  .mapa-stage{position:relative}
  .mapa-stage:not(.vista-mapa):not(.ficha-abierta) .lado-h{min-height:3.5rem;padding-right:8rem}
  .mapa-stage:not(.vista-mapa):not(.ficha-abierta)>.conmuta{
    position:absolute;top:0.5rem;bottom:auto;right:1.125rem;box-shadow:var(--sombra-flota);
  }
  .mapa-stage.ficha-abierta>.conmuta{display:none}

  /* Los recortes de la parte de arriba. Siguen valiendo aunque el mapa ya no
     esté debajo: son píxeles que separan el encabezado de la primera tarjeta.

     1. El subtítulo. «Dónde cargar su carro eléctrico ahora mismo» es la
        promesa de la portada, y en el escritorio vale; en el teléfono va
        pegado al párrafo de abajo —«Puede ver todo sin cuenta…»— que dice lo
        mismo con más utilidad. Dos frases para el mismo hueco: se queda la
        que informa.
        ⚠️ `:not(.lado-nota)` es lo que salva al párrafo útil: `.lado-h p` a
        secas se llevaba los dos por delante. */
  .lado-h>p:not(.lado-nota){display:none}

  /*  2. Los selectores de filtro, más apretados — pero el rótulo SIGUE ENCIMA
        del selector. Acá estuvo puesto al lado, para ganar el renglón de
        11 px: con un solo filtro se veía bien, y el día que la red tuvo dos
        tipos de conector apareció el segundo selector y los dos quedaron
        exprimidos en la misma fila, con «Cualquiera» pisado por su propia
        flecha. El ahorro era de 16 px; el defecto, permanente. */
  .filtros-sel{padding-top:0.4375rem;gap:0.4375rem}
  .filtros-sel .fsel>span{margin-bottom:0.125rem}

  /*  3. ⚠️ LOS CUATRO FILTROS SE ENVUELVEN, NO SE ARRASTRAN — y acá estuvo al
        revés unas horas. Se habían puesto en un renglón que se arrastra para
        ganar 60 px, cuando la lista y el mapa compartían la pantalla y cada
        píxel de arriba alejaba el mapa. Con los cuatro miden 455 px contra 411
        de ancho: el que se salía era **Favoritos**, que quedaba cortado por la
        mitad — y un filtro que no se ve no existe. Reportado como «un filtro
        de ver favoritos siempre».
        Desde que la lista y el mapa SE CONMUTAN, ese presupuesto de alto ya no
        existe: la lista tiene la pantalla entera y se desplaza sola. Los 45 px
        del segundo renglón ya no le quitan el mapa a nadie. */

  /*  4. La leyenda se aprieta. No se vuelve un renglón que se arrastra a
        propósito: escondería «Fuera de servicio», que es justo el estado que
        hay que poder reconocer en el mapa. */
  .leyenda{padding:0.5rem 1.125rem;gap:0.5rem 0.75rem}
}
.lado{
  background:var(--surface);border-right:1px solid var(--line);
  display:flex;flex-direction:column;min-height:0;
}
.lado-h{padding:1rem 1.125rem 0.75rem;border-bottom:1px solid var(--line)}
.lado-h h1{margin:0 0 3px;font-size:1.125rem;font-weight:600;letter-spacing:-.02em}
.lado-h p{margin:0;color:var(--ink-3);font-size:0.75rem}
.buscador{
  margin:0.75rem 1.125rem 0;display:flex;align-items:center;gap:0.5rem;background:var(--ground);
  border:1px solid var(--line);border-radius:0.5625rem;padding:0.5rem 0.6875rem;
}
.buscador input{
  flex:1;background:transparent;border:none;color:var(--ink);font-family:var(--sora);
  font-size:0.8125rem;outline:none;min-width:0;
}
.buscador input::placeholder{color:var(--ink-3)}
/* El campo lleva `outline:none` y la caja es la que se ve: el foco se marca en
   la caja, o con el teclado no había ninguna señal de dónde se estaba. */
.buscador{cursor:text}
.buscador:focus-within{border-color:var(--led);outline:2px solid var(--led);outline-offset:1px}
.lista{flex:1;overflow-y:auto;padding:0.75rem 1.125rem 1.125rem;display:flex;flex-direction:column;gap:0.5625rem}
.tarjeta{
  background:var(--surface-2);border:1px solid var(--line);border-radius:0.625rem;
  padding:0.75rem 0.8125rem;cursor:pointer;transition:border-color .15s,background .15s;
}
.tarjeta:hover{border-color:var(--led);background:var(--surface-3)}
.tarjeta.sel{border-color:var(--led-bright);background:rgba(var(--led-rgb),.1)}
.tarjeta .n{font-size:0.8438rem;font-weight:600;color:var(--ink);margin-bottom:2px}
.tarjeta .d{font-size:0.6875rem;color:var(--ink-3);line-height:1.4}
.tarjeta .barra{display:flex;gap:0.375rem;margin-top:0.5625rem;flex-wrap:wrap;align-items:center}

/* ⚠️⚠️ LAS ACCIONES DE LA TARJETA — y por qué están ESCRITAS y no insinuadas.
   La tarjeta siempre abrió la ficha al tocarla, y nadie lo sabía: reportado
   como «en esa lista ninguna estación es seleccionable, no dice ir ahí o ver
   en el mapa o mostrar foto, nada». No era un defecto de comportamiento —el
   clic funcionaba—: era que un bloque de texto con un borde se lee como un
   CARTEL, y en un teléfono no hay `:hover` que sugiera lo contrario. Una
   acción que no se anuncia no existe.
   Son tres y no una «Ver más», porque son las tres preguntas que uno le hace a
   un punto de carga: qué tiene, dónde queda, cómo llego. */
.t-acciones{
  display:flex;flex-wrap:wrap;gap:0.375rem;margin-top:0.625rem;padding-top:0.625rem;
  border-top:1px solid var(--line);
}
/* ⚠️ `flex:1 1 5.5rem` y NO `flex:1 1 0`. Con base 0 los tres se reparten el
   ancho por igual pase lo que pase, y en un teléfono angosto (360 px) eso deja
   91 px por botón para rótulos que piden 103: «Ver el punto» y «Cómo llegar»
   salían RECORTADOS, sin error y sin aviso. Con una base real el renglón se
   parte en dos cuando no alcanza —dos arriba y uno a lo ancho abajo— en vez de
   comerse las palabras. Medido a 320, 360, 390 y 412 px. */
.t-accion{
  flex:1 1 5.5rem;min-width:0;display:flex;align-items:center;justify-content:center;gap:0.25rem;
  padding:0.4375rem 0.375rem;border-radius:0.5rem;cursor:pointer;
  background:var(--ground);border:1px solid var(--line);
  font-family:var(--sora);font-size:0.6875rem;font-weight:500;color:var(--ink-2);
  white-space:nowrap;transition:border-color .15s,color .15s,background .15s;
}
.t-accion:hover{border-color:var(--led);color:var(--led-bright)}
.t-accion:focus-visible{outline:2px solid var(--led);outline-offset:1px}
.t-accion svg{
  width:0.875rem;height:0.875rem;flex:none;
  fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  opacity:.8;
}
/* La principal se distingue por PESO y color de marca, no por un relleno
   sólido: tres botones sólidos en una tarjeta compiten con el botón de verdad
   —«Cargar acá»— que vive en la ficha. */
.t-accion.pri{
  background:rgba(var(--led-rgb),.12);border-color:rgba(var(--led-rgb),.38);
  color:var(--led-bright);font-weight:600;
}
.t-accion.pri svg{opacity:1}

/* ⚠️ «CARGAR ACÁ» EN LA TARJETA (2026-10-19): lo que genera ingreso es lo más
   visible. El comentario de arriba decía que el botón de verdad «vive en la
   ficha», y por eso la lista no tenía ninguno: había que abrir la ficha y bajar.
   Ahora es un botón sólido, a lo ancho, ANTES de las tres acciones, que pasan a
   secundarias (`.sec`: sin borde superior propio, más quietas). */
.t-cargar{margin-top:0.75rem;min-height:2.75rem;font-size:0.875rem}
.t-acciones.sec{margin-top:0.5rem;padding-top:0;border-top:0}
.t-acciones.sec .t-accion{background:transparent;color:var(--ink-3)}
.t-acciones.sec .t-accion:hover{color:var(--led-bright)}

/* La cabecera: nombre + corazón. El corazón va ACÁ y no flotando sobre la
   tarjeta porque flotando se lleva por delante el clic de «ver el punto», que
   es la acción principal de la tarjeta. */
.t-cab{display:flex;align-items:flex-start;gap:0.5rem}
.t-cab .n{flex:1 1 auto;min-width:0}
.fav{
  flex:none;width:1.75rem;height:1.75rem;margin:-3px -4px 0 0;padding:0;
  display:grid;place-items:center;border:0;border-radius:0.5rem;cursor:pointer;
  background:transparent;color:var(--ink-3);
}
.fav svg{width:1.0625rem;height:1.0625rem;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linejoin:round}
.fav:hover{background:var(--surface-3);color:var(--crit)}
/* Marcado: relleno, no solo contorno. Un corazón «activo» que sigue siendo una
   línea se confunde con el que no lo está en una lista de diez. */
.fav.on{color:var(--crit)}
.fav.on svg{fill:currentColor}

/* Distancia y disponibles, en la misma caja que el nombre: es lo que decide si
   vale la pena ir, y tenerlo que abrir para verlo es un paso de más. */
.t-datos{
  display:flex;flex-wrap:wrap;gap:0.25rem 0.875rem;margin-top:0.4375rem;
  font-size:0.6875rem;color:var(--ink-2);
}
.t-dato{display:inline-flex;align-items:center;gap:0.3125rem;white-space:nowrap}
.t-dato svg{
  width:0.8125rem;height:0.8125rem;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;color:var(--ink-3);
}
.t-dato b{font-weight:700;color:var(--ink)}

/* La cinta del grupo tocado en el mapa. Dice cuántos son y CÓMO SALIR: sin la
   salida, el filtro sería una trampa. */
.grupo-cinta{
  display:flex;align-items:center;justify-content:space-between;gap:0.625rem;
  margin-bottom:0.5625rem;padding:0.5rem 0.6875rem;border-radius:0.625rem;
  background:rgba(var(--led-rgb),.10);border:1px solid rgba(var(--led-rgb),.35);
  font-size:0.7188rem;color:var(--ink-2);
}
.grupo-cinta b{color:var(--ink);font-weight:700;font-family:var(--mono)}
.grupo-salir{
  flex:none;border:0;background:transparent;cursor:pointer;padding:2px 0.25rem;
  font-family:var(--sora);font-size:0.7188rem;font-weight:600;color:var(--led-bright);
  text-decoration:underline;text-underline-offset:2px;
}

/* Ordenar por cercanía. Mismo lenguaje que `.cod-btn`, que vive dos filas más
   abajo en la misma columna: dos botones de utilidad que se ven distinto se
   leen como de dos apps. */
.cerca-btn{
  display:flex;align-items:center;justify-content:center;gap:0.5rem;
  margin:0.625rem 1.125rem 0;padding:0.5625rem;border-radius:0.625rem;cursor:pointer;
  font-family:var(--sora);font-size:0.75rem;font-weight:500;color:var(--ink-2);
  background:var(--surface-2);border:1px dashed var(--line);
}
.cerca-btn:hover{border-style:solid;border-color:var(--led);color:var(--led-bright)}
.cerca-btn svg{flex:none;opacity:.85}
.cerca-btn.on{
  border-style:solid;border-color:rgba(var(--led-rgb),.5);
  background:rgba(var(--led-rgb),.10);color:var(--led-bright);
}
.cerca-btn:disabled{opacity:.6;cursor:progress}
.leyenda{
  display:flex;gap:0.875rem;flex-wrap:wrap;padding:0.6875rem 1.125rem;border-top:1px solid var(--line);
  font-size:0.6875rem;color:var(--ink-3);background:var(--surface-2);
}
.leyenda i{width:0.5625rem;height:0.5625rem;border-radius:50%;display:inline-block;margin-right:0.3125rem}
/* «Sin cargadores todavía», con borde punteado como su pin: con el mismo punto liso
   de «Sin datos», la leyenda no los distinguía (2026-11-02). */
.leyenda i.ley-obra{box-sizing:border-box;background:transparent;border:1.5px dashed var(--pin-sindatos)}
/* «Cómo llegar» de un punto sin servicio: sin peso, no es a donde mandar a nadie. */
.t-accion.apagada{background:transparent;border-style:dashed;color:var(--ink-3);font-weight:500}
/* «¿Cómo se carga?»: abierto hasta la primera carga del teléfono, después plegado. */
.cargar-ya-como{flex:1 1 100%;min-width:0}
.cargar-ya-como>summary{
  list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:0.4375rem;
  min-height:2.25rem;padding:0.25rem 0.125rem;border-radius:0.5rem;
  font-size:0.8125rem;font-weight:600;color:var(--led-bright);
}
.cargar-ya-como>summary::-webkit-details-marker{display:none}
.cargar-ya-como>summary::after{
  content:"";width:0.4375rem;height:0.4375rem;margin-top:-0.1875rem;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg);
}
.cargar-ya-como[open]>summary::after{margin-top:0.25rem;transform:rotate(-135deg)}
.cargar-ya-como>summary:focus-visible{outline:2px solid var(--led);outline-offset:2px}
.cargar-ya-como .cargar-ya-pasos{margin-top:0.125rem}
/* Los puntos con cupo cerca, en la ficha de un punto donde no se puede cargar. */
.det-cerca{margin-top:0.25rem}
.det-pronto{margin-top:0.75rem}

/* ------------------------- detalle de un punto --------------------------- */
/* El corazón junto al nombre, como en «Información del cargador» de EVX: es
   donde ya se está mirando cuando se decide si el punto vale guardarse. */
.det-h-top{display:flex;align-items:flex-start;justify-content:space-between;gap:0.5rem}
.det-h-top h2{flex:1 1 auto;min-width:0}
.det-h-top .fav{margin:-3px -4px 0 0}
.det-h h2{margin:0 0 0.25rem;font-size:1.0625rem;font-weight:600;letter-spacing:-.01em}
.det-h p{margin:0;color:var(--ink-2);font-size:0.7812rem;line-height:1.55}
.det-pills{display:flex;gap:0.375rem;flex-wrap:wrap;margin-top:0.6875rem}

.det-sec{border-top:1px solid var(--line);padding-top:0.9375rem;margin-top:0.25rem}
.det-sec h3{
  margin:0 0 0.6875rem;font-size:0.5938rem;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;
}
.det-vacio{margin:0;color:var(--ink-3);font-size:0.7188rem;line-height:1.55}

/* Horario y acceso del punto (2026-10-28). Filas de dos columnas: el día a la
   izquierda, la hora a la derecha, como en la referencia de EVX. En el teléfono
   el texto largo del parqueadero se parte en renglones sin empujar el día. */
.det-horario{margin:0}
.det-horario>div{
  display:flex;justify-content:space-between;align-items:baseline;gap:0.75rem;
  padding:0.4375rem 0;border-bottom:1px solid var(--line);
}
.det-horario>div:last-child{border-bottom:0}
.det-horario dt{flex:0 0 auto;color:var(--ink-2);font-size:0.75rem}
.det-horario dd{margin:0;min-width:0;color:var(--ink);font-size:0.75rem;font-weight:500;text-align:right;line-height:1.45}
/* En el teléfono la nota del parqueadero ocupa dos o tres renglones, y alineada
   a la derecha se lee a saltos: ahí va debajo del rótulo, alineada a la izquierda. */
@media (max-width:940px){
  .det-horario>.det-parq{display:block}
  .det-horario>.det-parq dd{text-align:left;margin-top:0.1875rem}
}
.det-indic{
  margin-top:0.625rem;background:var(--surface-2);border:1px solid var(--line);
  border-radius:0.625rem;padding:0.625rem 0.75rem;
}
.det-indic b{display:block;font-size:0.6875rem;color:var(--ink-3);font-weight:600;margin-bottom:0.25rem}
.det-indic p{margin:0;font-size:0.75rem;line-height:1.55;color:var(--ink);overflow-wrap:anywhere}

.det-fotos{display:grid;grid-template-columns:repeat(auto-fill,minmax(6rem,1fr));gap:0.4375rem}
.det-fotos img{
  width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:0.5rem;display:block;
  border:1px solid var(--line);
}
.det-fotos a:hover img{border-color:var(--led)}

.det-eq{
  background:var(--surface-2);border:1px solid var(--line);border-radius:0.625rem;
  padding:0.6875rem 0.75rem;margin-bottom:0.5625rem;
}
.det-eq-h{display:flex;align-items:center;gap:0.5rem;flex-wrap:wrap}
.det-eq-h b{font-family:var(--mono);font-size:0.75rem;color:var(--ink)}
.det-eq-kw{
  margin-left:auto;font-family:var(--mono);font-size:0.6875rem;color:var(--led-bright);
  white-space:nowrap;
}
.det-eq-s{font-size:0.6562rem;color:var(--ink-3);margin-top:3px}

/* --- La tarjeta de un conector ---------------------------------------------
   Antes era una FILA (`.det-con`): correcta en escritorio, apretada en el
   teléfono, y el estado había que leerlo en una pastilla al final. La
   tarjeta lo pone en una cabecera de color — patrón EVX «Escoja su conector»
   (`referencias-kw/references/patrones-de-interfaz.md`) — que se ve antes de
   leer una sola palabra. `conectorCard()` en `mapa.js` es la ÚNICA función
   que arma esta tarjeta: la ficha del punto es hoy el único lugar donde se
   dibuja un conector, así que hay una sola plantilla que mantener. */
.con-tarjetas{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));
  gap:0.5rem;margin-top:0.5625rem;
}
.con-tarjeta{
  display:flex;flex-direction:column;border-radius:0.6875rem;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);
}
.con-tarjeta-h{
  display:flex;align-items:center;justify-content:space-between;gap:0.375rem;
  padding:0.3125rem 0.5625rem;font-family:var(--mono);font-size:0.5938rem;
}
.con-tarjeta-n{color:var(--ink-3);opacity:.85}
.con-tarjeta-estado{font-weight:700;letter-spacing:.02em}
.con-tarjeta.ok .con-tarjeta-h{background:rgba(var(--ok-rgb),.16)}
.con-tarjeta.ok .con-tarjeta-estado{color:var(--ok-ink)}
.con-tarjeta.warn .con-tarjeta-h{background:rgba(var(--warn-rgb),.16)}
.con-tarjeta.warn .con-tarjeta-estado{color:var(--warn-ink)}
.con-tarjeta.crit .con-tarjeta-h{background:rgba(var(--crit-rgb),.14)}
.con-tarjeta.crit .con-tarjeta-estado{color:var(--crit-ink)}
.con-tarjeta.info .con-tarjeta-h{background:rgba(var(--led-bright-rgb),.14)}
.con-tarjeta.info .con-tarjeta-estado{color:var(--led-bright)}
.con-tarjeta.gris .con-tarjeta-h{background:var(--surface-3)}
.con-tarjeta.gris .con-tarjeta-estado{color:var(--ink-3)}
/* Mío, además del verde de «Libre»: un poco más marcado, para distinguir el
   conector donde YO estoy cargando de uno que simplemente está libre. */
.con-tarjeta.es-mio{border-color:rgba(var(--ok-rgb),.4)}
.con-tarjeta.es-mio .con-tarjeta-h{background:rgba(var(--ok-rgb),.24)}

/* Es un `<button>` —abre la hoja de especificaciones—, así que primero hay
   que apagar todo lo que el navegador le pone por defecto a un botón antes de
   que se vea como el resto de la tarjeta. */
.con-tarjeta-b{
  flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;
  gap:2px;padding:0.625rem 0.5rem 0.5625rem;
  width:100%;background:none;border:none;font-family:inherit;color:inherit;
  cursor:pointer;
}
.con-tarjeta-b:hover{background:var(--surface-2)}
.con-tarjeta-b:focus-visible{outline:2px solid var(--led-bright);outline-offset:-2px}
.con-ico{
  width:1.75rem;height:1.75rem;border-radius:50%;display:grid;place-items:center;
  margin-bottom:3px;
}
.con-ico svg{width:0.9375rem;height:0.9375rem}
.con-ico.ok{background:rgba(var(--ok-rgb),.14);color:var(--ok)}
.con-ico.warn{background:rgba(var(--warn-rgb),.14);color:var(--warn)}
.con-ico.crit{background:rgba(var(--crit-rgb),.13);color:var(--crit)}
.con-ico.info{background:rgba(var(--led-bright-rgb),.14);color:var(--led-bright)}
.con-ico.gris{background:var(--surface-3);color:var(--ink-3)}

.con-tarjeta-b b{font-size:0.7188rem;color:var(--ink);font-weight:700}
.con-tarjeta-kw,
.con-tarjeta-qr,
.con-tarjeta-precio{font-family:var(--mono);font-size:0.5938rem;color:var(--ink-3)}
.con-tarjeta-precio{color:var(--led-bright)}

.con-tarjeta-f{border-top:1px solid var(--line-soft);padding:0.4375rem}
.con-tarjeta-f .btn{width:100%}

.det-uso{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0.5rem}
.det-uso div{
  background:var(--surface-2);border:1px solid var(--line);border-radius:0.5625rem;
  padding:0.625rem 0.5rem;text-align:center;min-width:0;
}
.det-uso b{
  display:block;font-family:var(--mono);font-size:1.0625rem;font-weight:700;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.det-uso span{display:block;font-size:0.5938rem;color:var(--ink-3);margin-top:2px;line-height:1.3}

/* marcadores Leaflet */
/* ⚠️ Los pines NO siguen el tema de la app. Van sobre TESELAS, y el fondo del
   mapa lo elige la persona en el selector de capas (`body.fondo-claro`), que es
   una decisión independiente: se puede tener la app en claro y el mapa oscuro.
   Un pin que siguiera al tema quedaría blanco sobre calles blancas. Por eso el
   blanco del halo, el blanco del número y las sombras negras de este bloque son
   literales: pertenecen al mapa, no a la interfaz. */
/* El pin ya no es un cuadrado girado 45° con `border-radius`: es el `#mk-pin`
   del sprite. La gota dibujada tiene punta afilada y bulbo redondo; el cuadrado
   girado dejaba las esquinas del bulbo a la vista y la punta roma, y se notaba
   al ampliar. Acá solo queda la caja, el número encima y la sombra. */
/* ⚠️⚠️ SE LLAMA `.mapa-pin` Y NO `.marca`, Y ESO NO ES COSMÉTICA.
   `.marca` estaba usada para DOS cosas distintas en esta misma hoja: el pin del
   mapa y —en `.caja .marca`, pantalla de Entrar— el bloque de marca con el
   logotipo y el tagline. Esa colisión ya había obligado a escribir allá un
   `width:auto;height:auto;transform:none;border:none;box-shadow:none` para
   deshacer, propiedad por propiedad, lo que el pin le imponía.

   Y ahí estaba la trampa: ese «deshacer» solo cubre las propiedades que el pin
   tenía CUANDO se escribió. Al cambiar el pin a SVG apareció `filter:
   drop-shadow(...)`, que nadie estaba cancelando — y el logotipo de la pantalla
   de entrar amaneció con una sombra. No da error, no rompe nada, y solo se ve
   mirando una pantalla que no tiene nada que ver con el mapa.

   Renombrar corta el problema de raíz en vez de agregar un `filter:none` a la
   lista de deshaceres, que habría dejado la próxima propiedad esperando. */
.mapa-pin{
  position:relative;width:2.125rem;height:2.875rem;
  display:flex;justify-content:center;align-items:flex-start;
  /* ⚠️ El segundo drop-shadow es el brillo «LED» que pidió el negocio: un halo
     del MISMO color que el estado, aclarado con `color-mix` porque `--pin-*`
     son tonos oscurecidos a propósito para que el número blanco se lea encima
     (ver el comentario de abajo) — ese mismo tono oscuro puesto de sombra da un
     halo apagado, no un brillo. Aclarado con blanco sí lee como encendido.
     Sigue siendo el color de ESTADO (cambia con libre/ocupado/fuera/sindatos):
     el anillo de proporción de más abajo es aparte y es azul fijo. */
  filter:drop-shadow(0 3px 0.4375rem rgba(0,0,0,.55))
         drop-shadow(0 0 0.375rem color-mix(in srgb, var(--mk-color, #227F4D) 65%, white));
}
/* ⚠️ Mismo choque con Leaflet que el globo agrupado: `leaflet.css` le pone
   `z-index:200` a los `<svg>` de adentro del mapa, así que sin estos dos
   z-index el pin taparía su propio número. */
.mapa-pin .mk{position:absolute;inset:0;width:100%;height:100%;z-index:0}
.mapa-pin b{
  position:relative;z-index:1;
  /* El bulbo del pin está centrado en y=17 de un viewBox de 46 de alto; con la
     línea de 11 px eso deja el texto arrancando en 11,5. */
  margin-top:0.6875rem;line-height:1;
  font-family:var(--mono);font-size:0.6875rem;font-weight:700;color:#fff;
  text-shadow:0 1px 2px rgba(0,0,0,.45);   /* el número, siempre legible */
}
/* Los tres tonos se oscurecieron para que el número BLANCO de adentro se lea:
   el verde daba 3,00:1 (justo en el límite) y el ámbar 2,80:1 (por debajo).
   ⚠️ Ahora van por `--mk-color` y no por `background`: el cuerpo del pin es un
   `<path>` y un `background` sobre el div no pinta la gota, pinta su caja. */
.mapa-pin.libre{--mk-color:var(--pin-libre)}
.mapa-pin.ocupado{--mk-color:var(--pin-ocupado)}
.mapa-pin.fuera{--mk-color:var(--pin-fuera)}
/* Sin latido reciente. El halo punteado dice "esto está incompleto" sin gastar
   la insignia de encima, que queda libre para «fuera de servicio». */
.mapa-pin.sindatos{--mk-color:var(--pin-sindatos);--mk-guion:3.5 3}
/* Y el punto donde todavía no hay cargadores instalados. Comparte el color y el
   halo punteado de «sin datos» —las dos cosas son incompletas y ninguna es una
   avería— pero tiene clase propia: el día que la marca quiera distinguirlo
   («Próximamente»), se cambia acá y en ningún otro lado. */
.mapa-pin.sinequipos{--mk-color:var(--pin-sindatos);--mk-guion:3.5 3}

/* Controles y créditos, en el mismo mundo oscuro.
   ⚠️ Acá hace falta `.leaflet-container` además del `html`: Leaflet declara el
   fondo de los créditos con ese selector delante, que pesa más que un `html`
   solo. El síntoma era una cajita BLANCA en la esquina de un mapa oscuro — y el
   `color` sí me lo tomaba, así que "parecía" que mi regla estaba aplicando. */
/* ⚠️ La tinta sale de `--mapa-chrome-*`, no de `--ink-*`: esta caja tiene fondo
   oscuro fijo porque va sobre teselas, así que su texto NO puede seguir al tema
   de la app. Con `--ink-3` daba 3,05:1 en claro, y el enlace «Leaflet» 2,13:1,
   en letra de 10 px. */
html .leaflet-container .leaflet-control-attribution{
  background:var(--mapa-chrome);color:var(--mapa-chrome-ink);
  font-size:0.625rem;padding:2px 0.4375rem;border-radius:0.375rem 0 0 0;
}
html .leaflet-container .leaflet-control-attribution a{color:var(--mapa-chrome-ink-2)}
html .leaflet-container .leaflet-control-attribution span{color:var(--mapa-chrome-ink)}
html .leaflet-container .leaflet-control-attribution svg{opacity:.55}
html .leaflet-bar{border:none;box-shadow:0 0.25rem 0.875rem rgba(0,0,0,.5)}
html .leaflet-bar a{
  background:var(--surface-2);color:var(--ink-2);border-bottom-color:var(--line);
  width:2rem;height:2rem;line-height:2rem;font-size:1.0625rem;
}
html .leaflet-bar a:first-child{border-radius:0.5625rem 0.5625rem 0 0}
html .leaflet-bar a:last-child{border-radius:0 0 0.5625rem 0.5625rem}
html .leaflet-bar a:hover{background:var(--surface-3);color:var(--ink)}
html .leaflet-bar a.leaflet-disabled{background:var(--surface);color:var(--ink-eco)}

/* --- El fondo del mapa, al tono de la app -------------------------------
   Medido, no estimado: el "Dark Gray Canvas" de Esri tiene el fondo en #474749
   (luminancia 71) contra el #090909 (11) del CARTO que se rompió — 6,5 veces
   más claro, o sea que "dark" es el nombre del producto, no una medición. Sin
   esto el mapa entra como un rectángulo gris claro en una app oscura.

   ⚠️ Y el `contrast` NO ayuda acá aunque suene a que sí: el contraste de CSS
   abre los valores alrededor del gris medio, así que sobre un mapa ya oscurecido
   empuja TODO hacia el negro y borra las calles — con `brightness(.30)
   contrast(1.22)` el mapa medía luminancia 1,0, o sea negro plano. Se baja el
   brillo y punto; la separación entre calle y fondo se conserva sola. */
body:not(.fondo-claro) .leaflet-tile-pane{
  filter:brightness(.46) saturate(.75);
}

/* ⚠️ ACÁ VIVÍA un filtro (`saturate(.30) sepia(.18) hue-rotate(52deg)
   brightness(1.04) contrast(.97)`) que teñía el fondo CLARO (OpenStreetMap) de
   un verde menta desaturado, tomado de la referencia de EVX, para bajarle el
   ruido de color a OSM crudo (vías naranjas, verdes fuertes, mucha etiqueta).
   Se quitó el 2026-08-31 a pedido del usuario: el tinte se veía peor que el
   ruido que corregía — el fondo CLARO es OpenStreetMap real, sin filtro. */

/* --- Globo de puntos agrupados ------------------------------------------ */
/* ⚠️ El cuerpo del globo YA NO lleva el color de disponibilidad: lo lleva el
   ANILLO, que además dice la PROPORCIÓN y no solo el veredicto. El relleno,
   el borde y la sombra los pinta ahora `#mk-grupo` (ver `kw-ilustracion.css`),
   así que acá solo queda la caja y el texto encima.

   Se conservan las tres clases de estado —`kwcl-ok`, `kwcl-lleno`,
   `kwcl-sindatos`— porque siguen diciendo algo que el anillo NO dice: la
   diferencia entre «cero libres» y «no sé». `iconoCluster()` las calcula con el
   orden de honestidad razonado allá y no se toca desde acá. */
.kwcl{
  position:relative;width:3.25rem;height:3.25rem;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  color:var(--mapa-pin-borde);line-height:1;
  filter:drop-shadow(0 3px 0.5rem rgba(0,0,0,.5));
}
/* El anillo va DETRÁS del texto y ocupa toda la caja.
   ⚠️⚠️ LOS `z-index` NO SON DECORATIVOS ACÁ. `leaflet.css` le pone
   `z-index:200` a los `<svg>` que viven dentro del mapa, así que el anillo
   —posicionado y con z-index 200— se pintaba ENCIMA del número y del pie, que
   estaban en `auto`. El globo salía como un círculo azul liso: sin el «2» y
   sin el «4 libres», con el texto presente en el DOM y `getBoundingClientRect`
   devolviendo 9×15 px. No da error, no aparece en consola y solo se ve
   mirando el mapa.
   Es el mismo choque con Leaflet que ya está documentado más abajo para el
   emergente: su hoja se carga desde el CDN DESPUÉS de esta. */
.kwcl .mk{position:absolute;inset:0;width:100%;height:100%;z-index:0}
.kwcl b,
.kwcl span{position:relative;z-index:1}
.kwcl b{font-size:0.9375rem;font-weight:700;font-family:var(--mono)}
/* ⚠️ El «14 libres» medía 6,8 px en el teléfono (0.4688rem al 90 %): ilegible al sol,
   y es lo que dice si vale la pena ir. A 0.625rem y en negrita (2026-11-02). */
.kwcl span{font-size:0.625rem;font-weight:700;opacity:.95;margin-top:1px;white-space:nowrap}

/* «0 libres» y «no sé» NO son lo mismo. Con cero verificados el anillo no pinta
   nada verde en ninguno de los dos casos, así que la distinción la carga el
   RIEL: entero cuando sabemos que están todos ocupados, punteado cuando el dato
   está incompleto. Es el mismo gesto que el borde punteado que ya tenía este
   globo y que sigue teniendo el pin `.marca.sindatos`. */
.kwcl-sindatos{--mk-riel-guion:3.5 3.5;--mk-riel-color:var(--pin-sindatos)}

/* Sobre fondo claro (calles o satélite) el halo blanco de los pines se pierde
   contra el mapa: se les pone borde oscuro para que sigan despegándose.
   ⚠️ El pin `.marca` sigue siendo un div con `border`; el globo ya es un SVG y
   su halo es un `stroke`. Por eso el globo va por variable —`--mk-borde`, que
   `kw-ilustracion.css` lee— y no por `border-color`, que sobre un SVG no
   pinta nada. Escribirlos juntos en el mismo selector hacía que el globo
   pareciera arreglado sin estarlo. */
body.fondo-claro .mapa-pin,
body.fondo-claro .kwcl{--mk-borde:var(--mapa-pin-borde-claro)}

/* El selector de fondo, con la piel de la app en vez de la caja blanca que
   trae Leaflet por defecto. */
html .leaflet-control-layers{
  background:var(--surface-2);border:1px solid var(--line);border-radius:0.625rem;
  color:var(--ink-2);box-shadow:0 0.375rem 1.25rem rgba(0,0,0,.45);
}
/* ⚠️ El ícono de capas de Leaflet es un PNG oscuro pensado para fondo claro.
   El primer intento fue `filter:invert(1)` sobre el botón — y el filtro se
   aplica al ELEMENTO ENTERO, así que también invertía el fondo oscuro que yo
   acababa de ponerle: quedaba un cuadrado BLANCO en una app negra. Se cambia el
   PNG por un SVG propio, del color del tema, y sin filtros. */
html .leaflet-control-layers-toggle{
  background-color:var(--surface-2);
  background-image:var(--leaflet-capas);
  background-size:1.25rem 1.25rem;background-position:center;background-repeat:no-repeat;
  border-radius:0.5625rem;
}
html .leaflet-control-layers-toggle:hover{background-color:var(--surface-3)}
html .leaflet-control-layers-expanded{padding:0.5625rem 0.6875rem;font-size:0.75rem}
html .leaflet-control-layers label{margin:2px 0;cursor:pointer}
html .leaflet-control-layers-selector{margin-right:0.375rem;accent-color:var(--led)}

.tarjeta.cargando-aqui{border-color:rgba(var(--ok-rgb),.42)}

.pasos{margin:0;padding-left:1.1875rem;color:var(--ink-2);font-size:0.8125rem;line-height:1.85}
.pasos>li{margin-bottom:0.625rem}
.pasos>li:last-child{margin-bottom:0}
/* `>b` y no `b`: sin el hijo directo, la regla también agarra los resaltados de
   adentro del texto y los vuelve bloque, partiendo la frase en tres renglones. */
.pasos>li>b{color:var(--ink-fuerte)}
.pasos-acc{display:flex;gap:0.375rem;flex-wrap:wrap;margin-top:0.4375rem}

/* Pie de la carga en vivo: el texto explica y el botón cierra. En pantalla
   angosta el botón baja solo en vez de estrujar la explicación. */
/* ⚠️ LOS TRES NÚMEROS DE LA CARGA. Van juntos y en una sola caja porque son
   UNA historia —pagó esto, lleva entregado esto, le volverá esto—; repartidos
   entre los otros datos habría que hacer la resta mentalmente. EVX preautoriza
   sin mostrarlo; KW lo muestra (ADR 0003).
   ⚠️ El rótulo del medio pasó de «Consumido» a «Entregado hasta ahora», que son
   tres palabras: por eso el `.l` lleva `line-height` propio y el corte de 420 px
   baja también la etiqueta. Sin eso, la columna del medio empujaba a las otras
   dos y los tres números dejaban de alinearse.
   ⚠️ El color se toma de los tokens ya redefinidos dentro de `.vivo` (que es
   azul de marca en los dos temas): escribir un color acá lo dejaría mudo en uno
   de los dos, que es la trampa documentada en CLAUDE.md §4. */
.vivo-tres{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0.625rem;
  margin-top:0.875rem;padding-top:0.8125rem;border-top:1px solid rgba(255,255,255,.14);
}
.vivo-tres .l{
  display:block;font-size:0.5625rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--led-pale);font-weight:600;line-height:1.35;
}
.vivo-tres b{
  display:block;font-family:var(--mono);font-size:0.9375rem;font-weight:700;margin-top:3px;
  font-variant-numeric:tabular-nums;color:var(--ink);
}
.vivo-tres b.vuelve{color:var(--ok)}
@media (max-width:420px){
  .vivo-tres{gap:0.4375rem}
  .vivo-tres .l{font-size:0.5312rem;letter-spacing:.08em}
  .vivo-tres b{font-size:0.8438rem}
}

.vivo-pie{
  display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap;margin-top:0.5625rem;
}
.vivo-pie>span{flex:1 1 15rem;min-width:0;font-size:0.6562rem;color:var(--led-pale)}

/* ==================== EMERGENTE DE LA CARGA EN CURSO ===================== */

/* ⚠️⚠️ UN EMERGENTE MÁS ALTO QUE LA PANTALLA SE CORTABA POR LOS DOS LADOS
   (2026-10-21). Reportado desde el teléfono en Cargadores: «se corta y no deja
   seleccionar esos botones». Era fijo y centrado, sin desplazamiento propio:
   medido a 390 × 560, «+ Punto de carga» empezaba en y=−48 y su «Guardar»
   quedaba en 539-580, fuera de la pantalla; con el teclado abierto (~420 de
   alto) les pasaba a los cuatro formularios de Cargadores. La página de atrás
   no se mueve, así que no había forma de llegar al botón.
   · `overflow-y:auto`: el velo se desplaza cuando el emergente no cabe.
   · `align-items:flex-start` + `margin:auto` en `.kwc`: centrado cuando cabe,
     arriba cuando no. Con `align-items:center` lo que sobra se reparte arriba
     y abajo, y la parte de ARRIBA queda en negativo, donde ningún
     desplazamiento llega (el título quedaba cortado).
   · `overscroll-behavior:contain`: al llegar al final, el gesto no se pasa a
     la página de atrás. */
.kwc-bg{
  position:fixed;inset:0;z-index:1200;                 /* encima del mapa de Leaflet */
  display:flex;align-items:flex-start;justify-content:center;padding:1.25rem;
  overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  background:var(--velo);backdrop-filter:blur(3px);
  animation:kwc-entra .16s ease-out;
}
.kwc-bg>.kwc{margin:auto}
@keyframes kwc-entra{from{opacity:0}to{opacity:1}}

.kwc{
  width:100%;max-width:28.75rem;
  background:linear-gradient(180deg,rgba(var(--led-rgb),.13),transparent 42%),var(--surface-2);
  border:1px solid rgba(var(--led-rgb),.34);border-radius:1.125rem;padding:1.5rem;
  box-shadow:var(--sombra-modal);
}
/* En una pantalla de escritorio ancha, 430 px de modal quedaba perdido en el
   medio de un mar de fondo desenfocado — se ve tacaño, no profesional. Un
   solo salto a los 640 px (donde ya sobra sitio de sobra) alcanza; no hace
   falta una escala continua para un cuadro de diálogo. */
@media (min-width:640px){
  .kwc{max-width:31.25rem;padding:1.875rem}
}
/* El carro cargando, arriba del todo: mismo criterio que `.cod-escena` en
   `codigo.html` — un techo para que en un modal ancho no crezca hasta
   comerse los números de abajo. */
.kwc-escena{padding:2px 0 0.625rem}
.kwc-escena .il{max-width:18.75rem;margin:0 auto}
@media (min-width:640px){
  .kwc-escena .il{max-width:21.25rem}
}
.kwc-h{display:flex;align-items:center;gap:0.5625rem;flex-wrap:wrap;margin-bottom:1rem}
/* `>h3` con hijo directo y sin margen: el h3 por defecto separa tanto que la
   pastilla de estado quedaba flotando lejos de su propio título. */
.kwc-h>h3{margin:0;font-size:1rem;color:var(--ink-fuerte);font-weight:600}

/* «Conector ocupado», «Conectá tu carro», «Conector no disponible» — es lo
   primero que se lee después de la escena, más grande que el título chico de
   arriba: es la respuesta a la única pregunta que importa acá, «¿puedo
   cargar YA?», sea la respuesta un sí o un no. Centrado, como en EVX. */
.kwc-mensaje{text-align:center;margin-bottom:1rem}
.kwc-mensaje h2{margin:0 0 0.4375rem;font-size:1.1875rem;font-weight:700;color:var(--ink-fuerte);letter-spacing:-.01em}
.kwc-mensaje p{margin:0;font-size:0.7812rem;color:var(--ink-2);line-height:1.55}

/* La hoja de especificaciones de un conector: fila a fila, etiqueta a la
   izquierda y dato a la derecha — lo mismo que EVX pone bajo «Información del
   cargador». */
.kwc-specs{border-top:1px solid var(--line);margin-bottom:0.375rem}
.kwc-spec{
  display:flex;align-items:baseline;justify-content:space-between;gap:0.875rem;
  padding:0.625rem 0;border-bottom:1px solid var(--line-soft);font-size:0.7812rem;
}
.kwc-spec span{color:var(--ink-3)}
.kwc-spec b{
  color:var(--ink);font-weight:600;text-align:right;font-family:var(--mono);
  font-variant-numeric:tabular-nums;
}

/* Los tres números en columnas iguales. `minmax(0,1fr)` para que un valor largo
   ($100.000) no le robe el ancho a los otros dos. */
.kwc-num{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0.625rem;margin-bottom:0.875rem}
.kwc-num>div{
  background:var(--surface);border:1px solid var(--line);border-radius:0.625rem;
  padding:0.6875rem 0.625rem;text-align:center;
}
.kwc-num span{
  display:block;font-family:var(--mono);font-size:1.1875rem;color:var(--led-bright);
  font-weight:700;line-height:1.15;
}
.kwc-num small{display:block;margin-top:0.25rem;font-size:0.5938rem;color:var(--ink-3);line-height:1.3}

.kwc-barra{height:0.4375rem;background:var(--surface);border-radius:6.25rem;overflow:hidden}
.kwc-barra>i{
  display:block;width:100%;height:100%;border-radius:6.25rem;transform-origin:left center;
  background:linear-gradient(90deg,var(--led),var(--led-bright));
}
/* La casilla de «recordar esta app» de la hoja de navegación. Es un `label`
   entero y no solo la casilla: en un teléfono, un cuadrito de 13 px es un
   blanco imposible, y tocar el texto tiene que funcionar igual. */
.nav-recordar{
  display:flex;align-items:center;gap:0.5rem;margin:0.625rem 0 0;cursor:pointer;
  font-size:0.75rem;color:var(--ink-2);
}
.nav-recordar input{width:1rem;height:1rem;flex:none;accent-color:var(--led);cursor:pointer}
/* 0.75rem y no 0.6562 (2026-11-02): la nota de una ventana que confirma algo medía 10,5 px, y es
   justo lo que hay que leer antes de aceptar. */
.kwc-nota{margin:0.5625rem 0 0;font-size:0.75rem;color:var(--ink-3);line-height:1.55}
.kwc-nota b{color:var(--ink-2)}

.kwc-pie{display:flex;gap:0.5rem;margin-top:1rem}
/* En el teléfono, «Guardar» y «Cancelar» se quedan abajo a la vista mientras
   se desplaza el emergente (con el teclado abierto es lo que se busca). Se
   pegan al borde del velo, que es el que se desplaza; el fondo es el mismo
   del final de `.kwc`, para que no se lea un campo debajo de los botones. */
@media (max-width:940px){
  .kwc-pie{
    /* −1,25 rem = el relleno de `.kwc-bg`: el pegado se mide sin él, y con
       `bottom:0` asomaba el campo siguiente por ese hueco. El relleno de
       abajo (y el margen que se come el de `.kwc`) llevan el fondo hasta el
       borde de la tarjeta. */
    position:sticky;bottom:-1.25rem;z-index:1;
    margin-bottom:-1.5rem;padding:0.75rem 0 1.5rem;background:var(--surface-2);
  }
}
.kwc-pie>*{flex:1 1 0;min-width:0;white-space:nowrap}

/* ---------------------------------------------------------------------------
   LAS HOJAS DEL MAPA (`montarHoja` en mapa.js, 2026-11-02). Solo las del mapa
   llevan `.hoja`: las ventanas de Cargadores usan `.kwc` y no cambian.
   · Entran con el rebote corto de la casa (200 ms) y salen desvaneciéndose en
     130 ms: entraban solo con el fundido del velo y se iban de golpe.
   · En el teléfono, la del conector y la de «Cómo llegar» suben desde abajo y
     se cierran arrastrando el tirador, el dibujo o la cabecera hacia abajo.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){
  .kwc-bg.hoja>.kwc{animation:hoja-entra .2s cubic-bezier(.34,1.56,.64,1) both}
  .kwc-bg.hoja.sale{animation:hoja-sale .13s ease-in both}
}
@keyframes hoja-entra{from{opacity:0;transform:translateY(0.75rem) scale(.98)}}
@keyframes hoja-sale{to{opacity:0}}
.kwc-tirador{display:none}
/* El título recibe el foco al abrir (para el lector de pantalla); no es un control,
   así que no lleva el anillo de foco de los botones. */
.kwc-bg.hoja .kwc h2[tabindex="-1"]:focus,.kwc-bg.hoja .kwc h3[tabindex="-1"]:focus{outline:none}
/* El botón de «mi ubicación» del mapa, centrado como los +/− de Leaflet. */
html .leaflet-bar.kw-yo a{display:grid;place-items:center}
@media (max-width:940px){
  .kwc-bg.hoja.abajo{align-items:flex-end;padding:2.5rem 0 0}
  .kwc-bg.hoja.abajo>.kwc{
    margin:auto 0 0;max-width:none;border-radius:1.25rem 1.25rem 0 0;border-bottom:0;
    padding-bottom:calc(1.5rem + env(safe-area-inset-bottom,0px));
  }
  .kwc-bg.hoja.abajo .kwc-pie{bottom:0;margin-bottom:calc(-1.5rem - env(safe-area-inset-bottom,0px));
    padding-bottom:calc(1.5rem + env(safe-area-inset-bottom,0px))}
  .kwc-bg.hoja.abajo .kwc-tirador{
    display:block;width:2.75rem;height:0.3125rem;margin:-0.625rem auto 0.875rem;
    border-radius:62rem;background:var(--ink-3);opacity:.45;
  }
  /* De donde se agarra para arrastrar, el navegador no desplaza: el gesto es de la hoja. */
  .kwc-bg.hoja.abajo .kwc-tirador,.kwc-bg.hoja.abajo .kwc-escena,
  .kwc-bg.hoja.abajo .kwc-h,.kwc-bg.hoja.abajo .nav-destino{touch-action:none}
  @media (prefers-reduced-motion:no-preference){
    .kwc-bg.hoja.abajo>.kwc{animation:hoja-sube .22s cubic-bezier(.16,1,.3,1) both}
  }
}
@keyframes hoja-sube{from{transform:translateY(40%);opacity:.4}}
/* La confirmación de «Terminar» dentro del emergente de la carga. */
.kwc-confirma{margin-top:1rem}

/* ---------------------------------------------------------------------------
   CÓMO LLEGAR — el botón de la ficha y la hoja para elegir la app
   El botón principal se lleva el ancho que sobre y «Cambiar» solo lo suyo: la
   acción es llegar, cambiar de app es el ajuste. `min-width:0` para que un
   nombre largo («Cómo llegar con Mapas de Apple») encoja en vez de empujar al
   botón de al lado fuera de la tarjeta.
   --------------------------------------------------------------------------- */
/* El botón principal de la ficha es CARGAR (2026-10-19): va antes que «Cómo
   llegar», que pasó a secundario. Vacío —sin conectores libres— no ocupa sitio. */
.det-cargar{margin-top:0.875rem}
.det-cargar:empty{display:none}
.det-cargar-n{margin:0.4375rem 0 0;font-size:0.75rem;line-height:1.5;color:var(--ink-3)}
.det-ir{display:flex;gap:0.5rem;margin-top:0.75rem}
.det-ir>a{flex:1 1 auto;min-width:0}
.det-ir>button{flex:0 0 auto}

/* ⚠️ SIN caja. Con el mismo fondo y el mismo borde que las filas de abajo, el
   destino se leía como una opción más de la lista —una que además no hace nada
   al tocarla—. En esta hoja la caja significa «esto se toca». */
.nav-destino{padding:0 2px 0.75rem;margin-bottom:0.75rem;border-bottom:1px solid var(--line)}
.nav-destino b{display:block;font-size:0.8125rem;font-weight:600;color:var(--ink)}
.nav-destino small{display:block;margin-top:3px;font-size:0.6875rem;color:var(--ink-3);line-height:1.5}
/* El piso NO es un dato más de la dirección: es lo único de esta hoja que la
   app de mapas no va a poder decir. Va con la tinta de aviso, no con la gris. */
.nav-piso{color:var(--ink-2)}

/* Una fila por app, alta para el pulgar: esto se toca de pie, al lado del carro. */
.nav-apps{display:grid;gap:0.5rem;margin-bottom:0.75rem}
.nav-app{
  display:flex;align-items:center;gap:0.75rem;text-decoration:none;
  background:var(--surface);border:1px solid var(--line);border-radius:0.75rem;
  padding:0.8125rem 0.875rem;transition:border-color .15s ease,background .15s ease;
}
/* ⚠️ `text-decoration:none` OTRA VEZ en el hover: la regla global `a:hover`
   (línea 30) gana por especificidad y subrayaba la fila entera —nombre y
   descripción, dos renglones— como si fuera un enlace de texto. Es el mismo
   apagado que ya hacen `.cod-otro:hover` y `.diag-doc:hover`. */
.nav-app:hover{border-color:rgba(var(--led-rgb),.5);text-decoration:none;background:var(--surface-2)}
/* La que ya está elegida se marca con el borde de marca, no con un check: el
   check se lee como «listo, hecho» y acá todas siguen siendo elegibles. */
.nav-app.on{border-color:rgba(var(--led-rgb),.55);background:rgba(var(--led-rgb),.07)}
.nav-app-t{flex:1 1 auto;min-width:0}
.nav-app-t b{display:block;font-size:0.8438rem;font-weight:600;color:var(--ink)}
.nav-app-t small{display:block;margin-top:2px;font-size:0.6562rem;color:var(--ink-3);line-height:1.45}
.nav-app-x{flex:0 0 auto;font-size:0.9375rem;line-height:1;color:var(--led-bright)}

/* Cinta para volver al emergente cuando se cerró con la carga andando. */
.kw-cinta{
  position:fixed;left:50%;transform:translateX(-50%);bottom:1.125rem;z-index:1150;
  display:flex;align-items:center;gap:0.5625rem;
  background:var(--surface-2);border:1px solid rgba(var(--ok-rgb),.42);
  border-radius:6.25rem;padding:0.5625rem 1.0625rem;cursor:pointer;
  color:var(--ink);font-family:inherit;font-size:0.75rem;
  box-shadow:var(--sombra-flota);
  transition:border-color .15s,transform .06s;
}
.kw-cinta:hover{border-color:var(--ok)}
.kw-cinta:active{transform:translateX(-50%) translateY(1px)}
.kw-cinta b{font-weight:600}
.kw-cinta span{color:var(--led-bright);font-size:0.6875rem}

/* ================================ ENTRAR ================================= */
.entrada{
  flex:1;display:grid;place-items:center;padding:2.125rem 1.25rem;
  background:
    radial-gradient(70% 55% at 50% 0%,rgba(var(--led-rgb),.16),transparent 62%),
    var(--ground);
}
.caja{width:100%;max-width:25.875rem}
.caja .marca{
  width:auto;height:auto;transform:none;border:none;box-shadow:none;display:block;
  text-align:center;margin-bottom:1.5rem;
}
/* En la pantalla de entrar el monograma es la marca a tamaño grande. 46 px de
   alto es lo que ocupaban los 66 px de tipografía: el trazo del dibujo llena
   mucho más su caja que unas letras, así que el mismo número se vería enorme. */
.caja .marca .logo{display:flex;justify-content:center}
.caja .marca .kw-mono{height:2.875rem}
/* El tagline del manual, textual: versales, tracking amplio, y ELÉCTRICO en
   azul (`docs/marca.md §7`). No es decoración — es la única línea de copy que
   el manual fija palabra por palabra. */
.caja .marca p{
  margin:0.875rem 0 0;color:var(--ink-3);font-size:0.7188rem;letter-spacing:.24em;
  text-transform:uppercase;
}
.caja .marca p b{color:var(--led-bright);font-weight:inherit}
.tarjeta-form{
  background:var(--surface);border:1px solid var(--line);border-radius:0.875rem;padding:1.5rem;
}
.tarjeta-form h1{margin:0 0 3px;font-size:1.1875rem;font-weight:600}
.tarjeta-form .p{margin:0 0 1.25rem;color:var(--ink-3);font-size:0.7812rem}
.campo{margin-bottom:0.875rem}
/* El nombre del campo, en oración y a 12 px (10,8 en el teléfono) desde el
   2-nov-2026. Era de 0,59 rem en versales: 9,5 px en el computador y 8,6 en el
   teléfono, el nombre del campo casi no se leía de pie, al lado del carro. */
.campo label{
  display:block;font-size:0.75rem;letter-spacing:.01em;
  color:var(--ink-2);font-weight:600;margin-bottom:0.375rem;
}
.campo input{
  width:100%;background:var(--ground);border:1px solid var(--line);border-radius:0.5625rem;
  padding:0.6875rem 0.8125rem;color:var(--ink);font-family:var(--sora);font-size:0.875rem;outline:none;
  transition:border-color .15s;
}
.campo input:focus{border-color:var(--led)}
.campo input::placeholder{color:var(--ink-3)}
.separa{display:flex;align-items:center;gap:0.75rem;margin:1.1875rem 0;color:var(--ink-3);font-size:0.6562rem;
  letter-spacing:.1em;text-transform:uppercase}
.separa::before,.separa::after{content:'';flex:1;height:1px;background:var(--line)}
/* ⚠️ Blanco y #1F1F1F literales: el botón de Google tiene sus colores fijados
   por Google en sus «Sign in with Google» branding guidelines. Tematizarlo lo
   volvería otra cosa — y en claro un botón blanco sobre fondo blanco necesita
   su borde, que ya se lo da la hoja de abajo. */
.btn-google{
  width:100%;background:#fff;color:#1F1F1F;border:1px solid var(--line);font-weight:600;font-size:0.875rem;
  border-radius:0.5625rem;padding:0.6875rem;display:flex;align-items:center;justify-content:center;gap:0.625rem;
  cursor:pointer;text-decoration:none;
}
.btn-google:hover{filter:brightness(.94);text-decoration:none;color:#1F1F1F}
.btn-google svg{width:1.125rem;height:1.125rem;flex:none}
.pie-caja{text-align:center;margin-top:1.125rem;font-size:0.7812rem;color:var(--ink-3)}
.demo{
  margin-top:1rem;background:var(--surface-2);border:1px dashed var(--line);border-radius:0.5625rem;
  padding:0.6875rem 0.8125rem;font-size:0.7188rem;color:var(--ink-3);line-height:1.6;
}
.demo b{color:var(--ink-2)}
.demo code{font-family:var(--mono);color:var(--led-bright);font-size:0.6875rem}

/* ============================== SESIÓN EN VIVO =========================== */
/* ⚠️⚠️ ESTA TARJETA ES OSCURA EN LOS DOS TEMAS, a propósito: el degradado azul
   profundo es el momento de marca de la app —lo mismo que los muros negros de
   las 6 láminas del manual— y es la pantalla que se mira de pie, al lado del
   carro, cargando. Aclararla la volvería una tarjeta más.

   Pero eso deja una trampa: el color del texto de adentro se HEREDA del <body>,
   que en claro es #0D0D0D. Sin lo de abajo, en modo claro esta tarjeta queda con
   texto negro sobre azul noche —ilegible— y no falla ningún assert: se ve como
   una tarjeta vacía.

   La solución no es escribir colores fijos regla por regla, sino REDEFINIR LOS
   TOKENS acá adentro. Las variables CSS cascadean, así que todo descendiente
   —incluidas las pastillas, que traen sus propias tintas— vuelve a la escala
   oscura sin que ninguna de sus reglas se entere. */
.vivo{
  --ink:#EEF2FA;      --ink-2:#A9B2C6;    --ink-3:#8A94A8;
  --ink-fuerte:#FFFFFF;
  --led-pale:#A8E0FF; --led-bright:#67C6FF;
  --ok:#3DD68C;       --ok-rgb:61,214,140;   --ok-ink:#9BE9C4;
  --warn:#FFB020;     --warn-rgb:255,176,32; --warn-ink:#FFD98A;
  --crit:#FF4D5E;     --crit-rgb:255,77,94;  --crit-ink:#FF8A94;
  --line:#1E2434;     --surface-2:#141824;
  color:var(--ink);

  background:
    radial-gradient(120% 140% at 100% 0%,rgba(var(--led-rgb),.24),transparent 60%),
    linear-gradient(160deg,var(--deep-2),#0A0D18);
  border:1px solid rgba(var(--led-rgb),.3);border-radius:0.875rem;padding:1.25rem;margin-bottom:1rem;
}
.vivo-h{display:flex;align-items:center;gap:0.625rem;margin-bottom:1rem;flex-wrap:wrap}
.vivo-h .t{font-size:0.9375rem;font-weight:600}
.vivo-g{display:grid;grid-template-columns:repeat(auto-fit,minmax(7.5rem,1fr));gap:0.875rem}
.vivo-g .l{font-size:0.5625rem;letter-spacing:.13em;text-transform:uppercase;color:var(--led-pale);font-weight:600}
.vivo-g .v{font-family:var(--mono);font-size:1.375rem;font-weight:700;margin-top:3px;
  font-variant-numeric:tabular-nums}
.barra-prog{height:0.375rem;border-radius:0.25rem;background:rgba(255,255,255,.09);overflow:hidden;margin-top:0.875rem}
.barra-prog i{display:block;height:100%;background:linear-gradient(90deg,var(--led),var(--led-bright));
  border-radius:0.25rem;transition:width .6s}

/* ------------------------------ mini gráfica ----------------------------- */
/* `justify-content:flex-start` + tope de ancho por barra: con `flex:1` a secas,
   una cuenta con UN solo mes pintaba una barra de 1.400 px de ancho que se leía
   como un bloque de color, no como un gráfico. Con muchos meses el tope no se
   alcanza y el reparto es el de siempre. */
.barras{display:flex;align-items:flex-end;justify-content:flex-start;
  gap:0.4375rem;height:8.25rem;padding-top:0.5rem}
.barras .b{flex:1 1 0;min-width:0;max-width:7.5rem;
  display:flex;flex-direction:column;justify-content:flex-end;
  align-items:center;gap:0.375rem;height:100%}
.barras .b i{
  display:block;width:100%;border-radius:0.3125rem 0.3125rem 2px 2px;
  background:linear-gradient(180deg,var(--led-bright),var(--led));min-height:3px;
  transition:height .4s;
}
.barras .b span{font-size:0.5938rem;color:var(--ink-3);font-family:var(--mono);white-space:nowrap}
.barras .b em{font-size:0.625rem;color:var(--ink-2);font-family:var(--mono);font-style:normal}

/* --------------------------------- varios -------------------------------- */
.filtros{display:flex;gap:0.5625rem;flex-wrap:wrap;align-items:center;margin-bottom:0.875rem}
/* Dentro de la barra lateral del mapa los filtros se alinean con el resto de la
   columna y no llevan separación abajo: los sigue el selector de potencia.
   ⚠️ Esto estaba escrito INLINE en `mapa.html` y en px (`padding:12px 18px 0`),
   que son las dos cosas que no se escriben acá (CLAUDE.md §4): inline no lo
   puede pisar ningún `@media`, y por eso el ajuste de teléfono de abajo no
   habría hecho nada. */
.lado .filtros{padding:0.75rem 1.125rem 0;margin-bottom:0}
.chip{
  background:var(--surface-2);border:1px solid var(--line);color:var(--ink-2);
  border-radius:6.25rem;padding:0.375rem 0.8125rem;font-size:0.75rem;cursor:pointer;
  transition:border-color .15s,color .15s,background .15s;
}
.chip:hover{border-color:var(--led);color:var(--ink)}
.chip.on{background:rgba(var(--led-rgb),.16);border-color:var(--led);color:var(--led-bright);font-weight:600}
/* 13 px (11,7 en el teléfono) desde el 2-nov-2026: era de 0,72 rem, 10,4 px en el
   teléfono, y es la identificación del vendedor que la Ley 1480 art. 50 pide a
   disposición y legible (`docs/estilo-de-avisos.md` §4). */
.pie{
  border-top:1px solid var(--line);padding:1rem 1.25rem;color:var(--ink-3);font-size:0.8125rem;
  display:flex;gap:0.875rem;flex-wrap:wrap;align-items:center;background:var(--surface);
}
/* El separador iba en --line (1,23:1): literalmente invisible, y su trabajo es
   separar. Va en el gris de texto, atenuado. */
.pie .sep{color:var(--ink-3);opacity:.5}
/* Cada renglón del pie de `base.html`. */
.pie-fila{flex:1 0 100%;display:flex;gap:0.375rem 0.75rem;flex-wrap:wrap;align-items:center}
/* ⚠️ EL «·» VA DELANTE DE CADA DATO, COMO PARTE DE ÉL (2-nov-2026). Era un `<span>`
   suelto entre dos datos: al partirse el renglón en el teléfono quedaba colgando al
   final («…vehículos eléctricos ·», «NIT 901.255.513-1 ·»). Pegado al dato que
   sigue, baja con él. `inline-block` para que no se subraye con el enlace, y el
   texto alternativo vacío (`/ ""`) para que el lector de pantalla no lo lea. */
.pie-sep>*+*::before{
  content:"·";content:"·" / "";display:inline-block;margin-right:0.75rem;
  color:var(--ink-3);opacity:.5;text-decoration:none;
}
.pie-sep{column-gap:0.75rem}
.err-pag{flex:1;display:grid;place-items:center;text-align:center;padding:3.75rem 1.25rem}
.err-pag .c{font-family:var(--mono);font-size:4rem;font-weight:700;color:var(--ink-eco);line-height:1}

.toast{
  position:fixed;right:1.125rem;bottom:1.125rem;z-index:2000;display:flex;flex-direction:column;gap:0.5625rem;
  max-width:min(26.875rem,calc(100vw - 2.25rem));
}
/* Dos regiones vivas en la misma pila (`base.html`): la cortés y la de errores. */
.toast{gap:0}
.toast>div{display:flex;flex-direction:column;gap:0.5625rem}
.toast>div:not(:empty)~div:not(:empty){margin-top:0.5625rem}
.toast-m{
  background:var(--surface-2);border:1px solid var(--line);border-left:3px solid var(--led);
  border-radius:0.5625rem;padding:0.6875rem 0.875rem;font-size:0.7812rem;color:var(--ink);
  box-shadow:var(--sombra-toast);animation:entra .22s ease-out;
  transition:opacity .13s ease;
}
.toast-m.sale{opacity:0}
.toast-m.ok{border-left-color:var(--ok)}
.toast-m.err{border-left-color:var(--crit)}
@keyframes entra{from{opacity:0;transform:translateY(0.5625rem)}}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important}
}

/* ⚠️ Los avisos flotantes del mapa se APILAN en un contenedor común.
   Cada uno tenía su `position:fixed; top:4rem` y al salir dos a la vez —«estás
   en este punto» y «el fondo está ampliado», que es exactamente lo que pasa al
   entrar por cercanía, porque ahí el mapa hace zoom— quedaban uno encima del
   otro, ilegibles. Con el contenedor no hace falta que ninguno sepa del otro. */
/* ⚠️ Centrado con `margin-inline:auto` y un ancho propio, NO con `left:50%` +
   `translateX(-50%)`: así el ancho disponible era la MITAD de la pantalla (195 px
   en un teléfono) y el aviso de cercanía se partía palabra por palabra
   («a / 610 / m / de / acá», 91 px de alto). Medido el 2026-11-02. */
.kw-avisos{
  position:fixed;left:0;right:0;margin-inline:auto;top:4rem;z-index:1145;
  width:max-content;max-width:min(35rem,92vw);
  display:flex;flex-direction:column;align-items:center;gap:0.5rem;
  pointer-events:none;
}
.kw-avisos>*{pointer-events:auto}

/* «Estás acá»: orientación al llegar, no un cartel permanente — se va sola. */
.kw-cerca{
  display:flex;align-items:center;gap:0.6875rem;max-width:100%;
  background:var(--surface-2);border:1px solid rgba(var(--ok-rgb),.42);
  border-radius:6.25rem;padding:0.5625rem 0.5625rem 0.5625rem 1.0625rem;
  box-shadow:var(--sombra-flota);animation:kwc-entra .2s ease-out;
}
.kw-cerca b{font-size:0.7812rem;color:var(--ink);font-weight:600;white-space:nowrap}
.kw-cerca span{font-size:0.6875rem;color:var(--ink-3);min-width:0}
.kw-cerca .kw-ir{
  flex:none;background:var(--led);border:none;border-radius:6.25rem;color:#fff;
  font-family:inherit;font-size:0.6875rem;font-weight:600;padding:0.375rem 0.9375rem;cursor:pointer;
}
.kw-cerca .kw-ir:hover{filter:brightness(1.12)}
@media (max-width:640px){
  /* En el teléfono el mapa arranca en ~101 px y es bajo: los avisos van abajo,
     donde no tapan lo que se está mirando. */
  .kw-cerca{padding:0.5rem 0.5rem 0.5rem 0.875rem}
  .kw-cerca b{font-size:0.75rem}
}

/* Aviso de fondo ampliado. Va arriba y centrado sobre el mapa, no abajo, para
   no chocar con la cinta de «estás cargando acá». */
.kw-ampliado{
  display:flex;align-items:center;gap:0.5625rem;max-width:100%;
  background:var(--surface-2);border:1px solid rgba(var(--warn-rgb),.42);
  border-radius:6.25rem;padding:0.5rem 0.9375rem;font-size:0.7188rem;color:var(--ink-2);
  box-shadow:var(--sombra-flota);
}
.kw-ampliado b{color:var(--ink)}
.kw-ampliado button{
  flex:none;background:var(--led);border:none;border-radius:6.25rem;
  color:#fff;font-family:inherit;font-size:0.6875rem;font-weight:600;
  padding:0.3125rem 0.75rem;cursor:pointer;
}
.kw-ampliado button:hover{filter:brightness(1.12)}
@media (max-width:640px){
  .kw-ampliado{font-size:0.6875rem;padding:0.4375rem 0.75rem}
}

/* ============================ LA RED ===================================== */

.red-tar{display:flex;align-items:center;gap:1.375rem;flex-wrap:wrap}
.red-tar>div{min-width:0}
.red-tar .l{display:block;font-size:0.6875rem;color:var(--ink-3);text-transform:uppercase;
  letter-spacing:.09em;margin-bottom:3px}
.red-tar b{font-family:var(--mono);font-size:1.375rem;color:var(--led-bright)}
.red-tar b.chico{font-size:0.8125rem;color:var(--ink-2);font-family:var(--sora)}
.red-tar small{color:var(--ink-3);font-size:0.6875rem}
.red-tar .btn{margin-left:auto}
/* El aviso de un reporte de precio pendiente y su botón (antes, px en línea). */
.red-rep{margin:0.75rem 0 0}
.red-rep-b{margin-left:0.375rem}
/* Una sección que no cargó: el motivo y la salida, juntos. */
.red-falla{display:flex;align-items:center;justify-content:center;gap:0.75rem;flex-wrap:wrap}

.red-punto{
  background:var(--surface);border:1px solid var(--line);border-radius:0.6875rem;
  padding:0.8125rem 0.9375rem;margin-bottom:0.6875rem;
}
.red-punto-h{display:flex;align-items:center;gap:0.625rem;flex-wrap:wrap}
/* `min-width:0` o una dirección larga empuja los botones fuera de la tarjeta. */
.red-punto-t{flex:1 1 15rem;min-width:0}
.red-punto-t b{display:block;font-size:0.875rem;color:var(--ink-fuerte);font-weight:600}
.red-punto-t span{display:block;font-size:0.7188rem;color:var(--ink-3);margin-top:2px}

/* Los equipos, indentados con un filete: se ve de un vistazo qué cuelga de qué.
   `box-shadow:inset` en vez de `border-left` para no restarle padding. */
.red-eq{
  margin:0.625rem 0 0 0.875rem;padding:0.625rem 0.75rem;
  background:var(--surface-2);border-radius:0.5625rem;
  box-shadow:inset 3px 0 0 var(--line);
}
.red-eq-h{display:flex;align-items:center;gap:0.5625rem;flex-wrap:wrap}
.red-eq-h>b{font-family:var(--mono);font-size:0.7812rem;color:var(--ink)}
.red-kw{font-family:var(--mono);font-size:0.6875rem;color:var(--led-bright)}
.red-marca{font-size:0.6875rem;color:var(--ink-3);flex:1 1 auto;min-width:0}

/* ⚠️⚠️ EL TABLERO VIVO (2026-10-16). Pedido así: «esto es operación eléctrica,
   el online resaltando un verde vibrante, deberíamos ver tableros energéticos
   vivos».
   La vibración va en el INDICADOR, nunca en el texto. El rótulo «En servicio»
   se queda en `--ok` porque tiene que pasar contraste DENTRO de su propia
   pastilla (5,26:1 medido); un verde que se vea encendido no pasaría. El punto
   y la barra lateral usan `--vivo`, que no lleva texto encima y por lo tanto
   solo tiene que despegarse del fondo. Dos trabajos, dos tokens.
   ⚠️ La clase es `en-linea`, NO `vivo`: `.vivo` ya es la cinta de la carga en
   curso, y reusar el nombre es el choque que en este proyecto ya costó caro
   con `--desliz` y con `.n`. */
.red-eq.en-linea{box-shadow:inset 3px 0 0 var(--vivo)}
/* ⚠️ En línea = halo FIJO; late solo el equipo que está cargando (`.cargando`,
   lo pone red.js si algún conector está en CHARGING). Hasta el 2026-11-02
   latían todos los equipos en línea a la vez (12 en la demo), sin fin, y el
   latido animaba un `box-shadow`, que se repinta en cada cuadro. Ahora la onda
   es un `::after` que solo cambia `transform` y `opacity` (se compone, no se
   repinta), y hay una por cada equipo que de verdad está entregando energía. */
.red-eq.en-linea .pill.ok .dot,
.red-f-estado.en-linea .pill.ok .dot{
  background:var(--vivo);box-shadow:0 0 0 2px rgba(var(--vivo-rgb),.35);
}
.red-eq.cargando .pill.ok .dot,
.red-f-estado.cargando .pill.ok .dot{position:relative;box-shadow:none}
.red-eq.cargando .pill.ok .dot::after,
.red-f-estado.cargando .pill.ok .dot::after{
  content:'';position:absolute;inset:0;border-radius:50%;background:var(--vivo);
  animation:red-onda 2.4s ease-out infinite;
}
@keyframes red-onda{
  0%  {transform:scale(1);opacity:.55}
  70% {transform:scale(2.7);opacity:0}
  100%{transform:scale(2.7);opacity:0}
}
/* Con menos movimiento pedido, el que carga también queda con el halo fijo. */
@media (prefers-reduced-motion:reduce){
  .red-eq.cargando .pill.ok .dot::after,
  .red-f-estado.cargando .pill.ok .dot::after{animation:none;opacity:0}
  .red-eq.cargando .pill.ok .dot,
  .red-f-estado.cargando .pill.ok .dot{box-shadow:0 0 0 2px rgba(var(--vivo-rgb),.35)}
}

/* El código del equipo ABRE su ficha, así que es un botón de verdad: teclado,
   lector de pantalla y foco visible.
   ⚠️ Un bloque que se puede tocar y no lo promete se lee como un cartel — es
   la lección de las tarjetas del mapa, que siempre abrieron la ficha y nadie
   lo sabía. Por eso subraya al pasar y no se queda mudo. */
.red-eq-cod{
  font-family:var(--mono);font-size:0.7812rem;color:var(--ink);
  background:none;border:0;padding:0;cursor:pointer;
  /* ⚠️ `--ink-3` y no `--line`: con el gris de las separaciones el subrayado
     casi no se veía en la captura, y un afford que no se ve no es un afford.
     Es la lección de «la tarjeta no prometía nada», otra vez. */
  border-bottom:1px dashed var(--ink-3);
}
/* Todo el renglón abre la ficha, así que todo el renglón se siente tocable. */
.red-eq-h{cursor:pointer}
.red-eq-h .red-acc,.red-eq-h button{cursor:auto}
.red-eq-h .btn{cursor:pointer}
.red-eq-h:hover .red-eq-cod{color:var(--led-bright);border-bottom-color:var(--led)}
.red-eq-cod:hover{color:var(--led-bright);border-bottom-color:var(--led)}
.red-eq-cod:focus-visible{outline:2px solid var(--led);outline-offset:3px;border-radius:3px}

/* La ficha del equipo, dentro del emergente. */
.red-ficha{padding:0.875rem 1.125rem 0}
.red-f-estado{
  display:flex;align-items:center;gap:0.625rem;
  padding:0.625rem 0.75rem;border-radius:0.5625rem;
  background:var(--surface-2);box-shadow:inset 3px 0 0 var(--line);
}
.red-f-estado.en-linea{box-shadow:inset 3px 0 0 var(--vivo)}
/* El punto de la ficha sigue la misma regla de la lista (arriba). */
.red-ficha-v{max-width:35rem}
.red-f-kw{font-family:var(--mono);font-size:0.9375rem;color:var(--led-bright);margin-left:auto}
.red-f-datos{display:grid;gap:0.0625rem;margin:0.875rem 0 0}
.red-f-d{
  display:grid;grid-template-columns:9rem 1fr;gap:0.75rem;align-items:baseline;
  padding:0.4375rem 0;border-bottom:1px solid var(--line);
}
.red-f-d:last-child{border-bottom:0}
.red-f-d>span{font-size:0.75rem;color:var(--ink-3)}
.red-f-d>b{font-size:0.8125rem;color:var(--ink);font-weight:500}
/* ⚠️ La nota va en su propio renglón y ocupa las dos columnas: metida al lado
   del valor, en un teléfono parte la rejilla y el dato queda en una tira de
   dos caracteres de ancho. */
/* ⚠️ Dos ranuras, dos colores. `--warn` es para lo que requiere atención; una
   dirección o un piso son datos neutros y en ámbar se leen como un problema.
   Se vio mirando la captura, con los asserts en verde. */
.red-f-d>i{grid-column:1 / -1;font-style:normal;font-size:0.6875rem}
.red-f-d>i.red-f-n{color:var(--ink-3)}
.red-f-d>i.red-f-a{color:var(--warn)}
.red-f-nd{color:var(--ink-3)}
.red-f-t{margin:1.125rem 0 0.5rem;font-size:0.75rem;color:var(--ink-3);font-weight:600}
.red-f-cons{display:grid;gap:0.375rem}
.red-f-con{
  display:flex;align-items:center;gap:0.5rem;flex-wrap:wrap;
  padding:0.4375rem 0.625rem;border-radius:0.4375rem;background:var(--surface-2);
}
.red-f-con>b{font-size:0.8125rem;color:var(--ink)}
.red-f-cargas{font-size:0.6875rem;color:var(--ink-3);margin-left:auto}

.red-con{
  display:flex;align-items:center;gap:0.5625rem;flex-wrap:wrap;
  margin-top:0.4375rem;padding:0.375rem 0.5625rem;background:var(--surface);
  border:1px solid var(--line);border-radius:0.5rem;
}
.red-con-n{
  flex:none;width:1.1875rem;height:1.1875rem;border-radius:0.375rem;background:var(--surface-3);
  color:var(--ink-3);font-family:var(--mono);font-size:0.6875rem;
  display:flex;align-items:center;justify-content:center;
}
.red-con>b{font-size:0.7188rem;color:var(--ink)}
.red-con code{
  font-family:var(--mono);font-size:0.6875rem;color:var(--ink-3);
  background:var(--ground);padding:2px 0.4375rem;border-radius:0.3125rem;
}
/* Las acciones se van a la derecha en las tres filas por igual. */
.red-acc{display:flex;gap:0.375rem;margin-left:auto;flex:none}
.red-acc .btn{padding:0.3125rem 0.6875rem;font-size:0.6875rem}
.red-vacio{margin:0.5625rem 0 0 0.875rem;font-size:0.7188rem;color:var(--ink-3)}

.red-campos{display:flex;flex-direction:column;gap:0.625rem;margin-bottom:0.25rem}
.red-campo{display:block}
.red-campo>span{
  display:block;font-size:0.6875rem;color:var(--ink-3);margin-bottom:0.25rem;
  text-transform:uppercase;letter-spacing:.07em;
}
.red-campo input,.red-campo select{
  width:100%;background:var(--ground);border:1px solid var(--line);border-radius:0.5rem;
  padding:0.5625rem 0.6875rem;color:var(--ink);font-family:var(--sora);font-size:0.8125rem;
}
.red-campo input:focus,.red-campo select:focus{
  outline:none;border-color:var(--led);
}
/* El texto largo (indicaciones) usa la misma caja que un campo de una línea. */
.red-campo textarea{
  width:100%;background:var(--ground);border:1px solid var(--line);border-radius:0.5rem;
  padding:0.5625rem 0.6875rem;color:var(--ink);font-family:var(--sora);font-size:0.8125rem;
  line-height:1.5;resize:vertical;min-height:4.5rem;
}
.red-campo textarea:focus{outline:none;border-color:var(--led)}
/* Un texto de ayuda («Ej.: KW Sabaneta») no puede leerse como un dato ya
   puesto: más tenue que el valor, que va en `--ink`. */
.red-campo input::placeholder,.red-campo textarea::placeholder{color:var(--ink-3);opacity:.8}
/* La ayuda de un campo, pegada debajo de él. */
.red-ayuda{margin:-0.25rem 0 0;font-size:0.75rem}
/* La pregunta de una confirmación se lee entera: no es letra de nota al pie. */
.red-v .kwc-nota{font-size:0.8125rem;color:var(--ink-2)}
.red-v{max-width:28rem}
/* El horario: un renglón por día, el nombre a la izquierda y la franja a la
   derecha. Con «Abierto 24 horas» marcado los días se esconden (`hidden`). */
.red-24h{display:flex;align-items:center;gap:0.5rem;font-size:0.8125rem;color:var(--ink);cursor:pointer}
.red-24h input{width:auto;accent-color:var(--led)}
.red-dias{display:flex;flex-direction:column;gap:0.375rem;margin-top:0.625rem}
.red-dias[hidden]{display:none}
.red-dia{display:grid;grid-template-columns:6rem minmax(0,1fr);align-items:center;gap:0.625rem}
.red-dia b{font-size:0.75rem;font-weight:500;color:var(--ink-2)}
.red-copiar{align-self:flex-start;margin-top:0.25rem}
/* El error del formulario va DENTRO del diálogo, junto a los botones: en un
   toast se pierde de vista justo cuando hay que corregir el campo que lo causó. */
.red-error{
  margin:0.75rem 0 0;padding:0.5625rem 0.6875rem;border-radius:0.5rem;font-size:0.7188rem;
  background:rgba(var(--crit-rgb),.12);border:1px solid rgba(var(--crit-rgb),.34);
  color:var(--crit-ink-suave);
}

/* El nombre y el avatar del navbar pasaron a ser UN enlace a la cuenta. Sin
   esto el `<a>` es inline y rompe la fila que antes armaba `.nav-yo` con sus
   dos hijos: se reproduce el mismo flex acá adentro. */
.yo-link{display:flex;align-items:center;gap:0.625rem;color:inherit}
.yo-link:hover{text-decoration:none}
.yo-link:hover .quien b{color:var(--led-bright)}
.yo-link:hover .avatar{border-color:var(--led)}

/* ======================== LA CUENTA DE LA PERSONA ========================
   Recuperar la clave y el perfil. Reusa `.campo` de las pantallas de entrar:
   lo de acá es lo que le faltaba a ese componente (un select, un par de campos
   en fila, un enlace dentro del rótulo) más la pantalla del perfil.
   ========================================================================= */

/* `float` y no flex a propósito: el rótulo es `display:block` y el enlace, al
   ser inline, ya cae en la misma línea — solo hay que empujarlo. Cambiar el
   rótulo a flex tocaría TODOS los campos de la app por un caso. */
/* «¿La olvidó?» medía 9,9 px en el teléfono y su zona de toque era la altura de
   la letra: el relleno vertical la lleva a ~24 px sin mover el rótulo (el margen
   negativo lo compensa). Revisión de diseño del 2026-11-02. */
.lbl-link{
  float:right;text-transform:none;letter-spacing:normal;font-size:0.75rem;
  font-weight:500;color:var(--led-bright);padding:0.375rem 0;margin:-0.375rem 0;
}
.lbl-link:hover{text-decoration:underline}
/* LA CLAVE CON «MOSTRAR» (entrar, registrarse, recuperar; `kw-entrada.js`). El
   botón va dentro del campo, a la derecha; el relleno del campo le deja sitio
   para que la clave no corra por debajo. */
.clave-caja{position:relative}
.clave-caja input{padding-right:5.25rem}
.clave-ver{
  position:absolute;right:0.3125rem;top:50%;transform:translateY(-50%);
  min-height:2rem;padding:0.375rem 0.625rem;border:0;border-radius:0.4375rem;
  background:transparent;color:var(--led-bright);cursor:pointer;
  font-family:var(--sora);font-size:0.75rem;font-weight:600;
  transition:background .15s;
}
.clave-ver:hover{background:var(--surface-2)}
.clave-ver:focus-visible{outline:2px solid var(--led);outline-offset:1px}
.clave-ver[hidden]{display:none}
/* El requisito de un campo, fijo debajo de él: el `placeholder` se borra con la
   primera letra. */
.ent-nota{margin:0.375rem 0 0;font-size:0.75rem;line-height:1.5;color:var(--ink-3)}
/* El rótulo grita en mayúsculas; el «(opcional)» no es parte del nombre del
   campo y gritado se lee como si fuera otro campo. */
.opc{text-transform:none;letter-spacing:normal;font-weight:400;color:var(--ink-3)}

.campo select{
  width:100%;background:var(--ground);border:1px solid var(--line);border-radius:0.5625rem;
  padding:0.6875rem 0.8125rem;color:var(--ink);font-family:var(--sora);font-size:0.875rem;outline:none;
}
.campo select:focus{border-color:var(--led)}
/* minmax(0,…) o el `select` con la opción más larga estira su columna y la
   fila se desborda. */
/* `end` (2-nov-2026): si un rótulo ocupa dos renglones y el otro uno, los dos
   campos quedan a la misma altura (a 390 px el selector de «Documento» quedaba
   14 px más abajo que «Número»). */
.campo-par{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:0 0.75rem;align-items:end}
/* El rótulo de un dato que no se edita (el correo en Cuenta): se ve como el de un
   campo, pero no es un `<label>` porque no hay campo que nombrar. */
.campo .campo-l{
  display:block;font-size:0.75rem;letter-spacing:.01em;
  color:var(--ink-2);font-weight:600;margin-bottom:0.375rem;
}

.ok-grande{
  display:grid;place-items:center;width:4rem;height:4rem;margin:0 auto 1rem;
  border-radius:50%;background:rgba(var(--ok-rgb),.1);border:1px solid rgba(var(--ok-rgb),.32);
  color:var(--ok);
}

/* ⚠️ `width:100%` NO sobra: `main` es `display:flex; flex-direction:column`, y
   ahí un `margin:0 auto` CANCELA el `align-items:stretch` — el hijo deja de
   ocupar el ancho disponible y colapsa al de su contenido. Medido: con 1440 px
   de ventana esto quedaba en 549 y las tres cajas se apilaban en una sola
   columna, con la mitad de la pantalla en negro. No falla nada: se ve como una
   decisión de diseño. */
.perf{width:100%;max-width:73.75rem;margin:0 auto;padding:1.625rem 1.25rem 2.875rem}
.perf-h{display:flex;align-items:center;gap:0.9375rem;flex-wrap:wrap;margin-bottom:1.25rem}
/* ⚠️ `color:#fff` y NO `var(--ink)`. El fondo es `--deep` (#092676), que es azul
   profundo de marca en LOS DOS temas — no se aclara. En claro `--ink` es
   #0D0D0D, así que las iniciales quedaban NEGRAS SOBRE AZUL NOCHE: 1,43:1,
   ilegibles. Lo encontró `capturar_temas.py`; a ojo el círculo se ve azul y
   lleno, y hay que fijarse para notar que las dos letras no están.
   Es el mismo caso de `.vivo`: fondo fijo, tinta fija. */
.perf-av{
  flex:none;width:3.5rem;height:3.5rem;border-radius:50%;display:grid;place-items:center;
  background:var(--deep);border:1px solid var(--line);color:#fff;
  font-size:1.1875rem;font-weight:600;overflow:hidden;
}
.perf-av img{width:100%;height:100%;object-fit:cover}
.perf-quien{min-width:0;flex:1 1 auto}
.perf-quien h1{margin:0;font-size:1.4375rem;font-weight:600;letter-spacing:-.01em}
.perf-quien p{margin:3px 0 0;font-size:0.7812rem;color:var(--ink-3);word-break:break-all}
.perf-chips{display:flex;gap:0.4375rem;flex-wrap:wrap}
.perf-ancho{margin-bottom:1.125rem}

/* 330 px de mínimo y no 300: con 300, una pantalla de 1024 daba TRES columnas
   de 317 px y los campos del formulario quedaban de 277 — caben, pero se
   escriben incómodos. Con 330 esa medida cae a dos columnas anchas y 1440
   sigue dando tres. `min(100%,…)` para que en el teléfono no fuerce un ancho
   mayor que la pantalla. */
/* `align-items:start` para que cada caja mida LO SUYO: por defecto el grid las
   estira todas a la altura de la más larga y las cortas quedan con un hueco
   negro abajo que se lee como contenido que falta. */
.perf-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,20.625rem),1fr));gap:1rem;
  align-items:start;
}
.perf-caja{
  min-width:0;padding:1.25rem;border-radius:0.875rem;
  background:var(--surface);border:1px solid var(--line);
}
.perf-caja h2{margin:0 0 0.25rem;font-size:0.9375rem;font-weight:600;color:var(--ink)}
.perf-sub{margin:0 0 1rem;font-size:0.7188rem;color:var(--ink-3);line-height:1.55}
.perf-caja .btn{margin-top:0.25rem}

/* Un dato que NO se edita se ve distinto de uno que sí: si tuviera la misma
   caja que un campo, la persona lo intentaría escribir. */
.perf-fijo{
  padding:0.6875rem 0.8125rem;border-radius:0.5625rem;font-size:0.8438rem;color:var(--ink-2);
  background:var(--surface-2);border:1px dashed var(--line);word-break:break-all;
}
.perf-fijo small{display:block;margin-top:0.25rem;font-size:0.6562rem;color:var(--ink-3)}

.perf-datos{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0.5625rem}
.perf-datos>div{
  padding:0.6875rem 0.5625rem;border-radius:0.625rem;text-align:center;
  background:var(--surface-2);border:1px solid var(--line);
}

/* Las tres opciones del tema. `auto-fit` y no tres columnas iguales: «El del
   dispositivo» tiene 18 caracteres y las otras dos tienen 5 — repartir el ancho
   en partes iguales lo parte en dos renglones y deja las otras dos medio vacías. */
.tema-op{display:flex;gap:0.4375rem;flex-wrap:wrap}
.tema-op button{
  flex:1 1 auto;min-width:0;padding:0.5625rem 0.75rem;border-radius:0.5625rem;cursor:pointer;
  font-family:var(--sora);font-size:0.7812rem;font-weight:500;
  background:var(--surface-2);border:1px solid var(--line);color:var(--ink-2);
  transition:border-color .15s,color .15s,background .15s;
}
.tema-op button:hover{border-color:var(--led);color:var(--ink)}
.tema-op button:focus-visible{outline:2px solid var(--led-bright);outline-offset:2px}
/* La opción activa se marca con fondo Y borde Y peso, no solo con color: en
   claro el azul de acento sobre gris claro no se despega lo suficiente como
   para que se lea «esta es la que está puesta» de un vistazo. */
.tema-op button.on{
  background:rgba(var(--led-rgb),.16);border-color:var(--led);
  color:var(--led-bright);font-weight:600;
}
.perf-datos span{display:block;font-size:0.5938rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin-bottom:0.25rem}
.perf-datos b{font-family:var(--mono);font-size:0.9375rem;color:var(--ink);white-space:nowrap}

/* ====================== EL CÓDIGO DE LA CALCOMANÍA =======================
   Pantalla de una sola columna angosta. No es una decisión estética: se abre
   con el teléfono en una mano y el cable en la otra, así que lo que se ve tiene
   que caber sin desplazar y el botón tiene que estar donde cae el pulgar.
   ========================================================================= */
/* `width:100%`: dentro de `main` (un flex en columna) el `margin:0 auto` encogía la
   columna a su contenido: 244 px en Viva Envigado, con 70 px de aire a cada lado
   en un teléfono (2026-11-02). */
.cod-pag{width:100%;max-width:32.5rem;margin:0 auto;padding:1.375rem 1rem 2.5rem}
.cod-caja{display:flex;flex-direction:column;gap:0.875rem}

/* La escena de la ilustración, arriba de todo. Es lo primero que se ve con el
   teléfono en una mano y el cable en la otra: dice el estado ANTES de que haya
   que leer nada.
   ⚠️ Sin fondo ni borde propios. Con tarjeta quedaban dos recuadros pegados
   —este y `.cod-conector`— y la pantalla arrancaba con una escalera de cajas.
   El aire de arriba y abajo es todo el encuadre que necesita. */
.cod-escena{padding:2px 0 0.375rem}
/* El SVG ya es `width:100%`; acá solo se le pone techo para que en escritorio
   no crezca hasta comerse la mitad de la pantalla. */
.cod-escena .il{max-width:21.25rem;margin:0 auto}
.cod-donde h1{margin:0;font-size:1.375rem;font-weight:600;line-height:1.2;text-wrap:balance}
.cod-sub{margin:0.375rem 0 0;font-size:0.7812rem;color:var(--ink-3);line-height:1.5}
.cod-nota{margin:0;font-size:0.6875rem;color:var(--ink-3);line-height:1.55}
/* Lo que se va a autorizar, dicho ANTES del botón: más grande que la nota de
   abajo porque es lo que la persona va a ver después en su banco. */
.cod-nota.cod-antes{font-size:0.7812rem;color:var(--ink-2)}
.cod-nota.cod-antes b{color:var(--ink);font-weight:600}

/* El enchufe. El borde izquierdo lleva el estado: es lo único que hace falta
   mirar para saber si se puede enchufar o no. */
.cod-conector{
  display:flex;align-items:center;gap:0.8125rem;padding:0.875rem;border-radius:0.75rem;
  background:var(--surface);border:1px solid var(--line);border-left-width:4px;
}
.cod-conector.ok{border-left-color:var(--ok)}
.cod-conector.mio{border-left-color:var(--led-bright)}
.cod-conector.no{border-left-color:var(--crit)}
.cod-conector.uso{border-left-color:var(--warn)}     /* ocupado: se libera, no está dañado */
.cod-num{
  flex:none;width:2.875rem;height:2.875rem;border-radius:0.6875rem;display:grid;place-items:center;
  font-family:var(--mono);font-size:1.25rem;font-weight:700;color:var(--ink);
  background:var(--surface-2);border:1px solid var(--line);
}
.cod-datos{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:3px}
.cod-datos b{font-size:0.875rem;font-weight:600;color:var(--ink)}
.cod-datos span{font-size:0.6875rem;color:var(--ink-3)}
.cod-conector .pill{flex:none}
/* Un número y su unidad son UNA cosa: partidos en dos renglones el número
   aparece suelto, sin decir qué mide. */
.nw{white-space:nowrap}

/* En el teléfono, al pagar, el enchufe en UN renglón y el dibujo a ~60 px de alto
   (2026-11-02): los dos juntos medían 250 px y dejaban los montos y los medios
   bajo el pliegue. En un impedimento (ocupado, fuera) el dibujo sigue grande:
   ahí es la pantalla. */
@media (max-width:940px){
  .cod-pagando .cod-escena .il{max-width:11rem}
  .cod-pagando .cod-escena{padding:0}
  div.cod-conector{padding:0.625rem 0.75rem;gap:0.625rem}
  div.cod-conector .cod-num{width:2.25rem;height:2.25rem;font-size:1.0625rem;border-radius:0.5625rem}
  div.cod-conector .cod-datos{flex-direction:row;flex-wrap:wrap;align-items:baseline;gap:0 0.5rem}
  /* La letra chica del dinero, legible a simple vista (Ley 1480 art. 37): con la
     letra del teléfono al 90 % los kWh de cada monto y la descripción del medio
     quedaban en 9,4 px y «Recomendado» en 8,1. A 0.75rem (2026-11-02). */
  .pagar .mon small,.pagar .med small,.pagar .mon-tag{font-size:0.75rem}
}
/* El botón que cobra, APAGADO cuando lo elegido no se puede pagar (bonos que no
   alcanzan, un monto fuera del rango): se ve que no va, y la causa está encima. */
#pg-ir[aria-disabled="true"],#an-ir[aria-disabled="true"]{opacity:.55;cursor:not-allowed;filter:saturate(.5)}
/* «Usar todo su bono», la salida que se ofrece cuando los bonos no alcanzan. */
.pagar-err .btn{margin-top:0.5rem}
/* El campo que falta, marcado donde está (2026-11-02): el error del correo se
   pintaba debajo del botón pegado, tapado, y el campo no se marcaba. */
.cod-pag .campo input[aria-invalid="true"]{border-color:var(--crit);box-shadow:0 0 0 1px var(--crit)}
.campo-err{margin:0.375rem 0 0;font-size:0.75rem;line-height:1.45;color:var(--crit-ink-suave)}
.campo-err[hidden]{display:none}
.campo-ayuda{margin:0.375rem 0 0;font-size:0.75rem;color:var(--ink-3)}
.cod-precio{
  margin:0;font-family:var(--mono);font-size:1.1875rem;font-weight:700;color:var(--ink);
  display:flex;align-items:baseline;gap:0.5rem;flex-wrap:wrap;
}
.cod-precio small{font-family:var(--sora);font-size:0.6875rem;font-weight:400;color:var(--ink-3)}

/* El botón es grande a propósito: se aprieta de pie, con una mano y a veces con
   guantes. 52 px es el alto donde deja de fallarse el toque. */
.btn.grande{min-height:3.25rem;font-size:0.9375rem;border-radius:0.75rem}

/* ⚠️ `auto-fit` y no `repeat(2,...)`: la tarjeta de la carga en curso pasó de
   dos números a TRES —entregado, cobrado y lo que vuelve— y con las dos columnas
   fijas el tercero caía solo a una fila entera, con el doble de ancho que sus
   hermanos y leyéndose como si fuera otra cosa. */
.cod-vivo{display:grid;grid-template-columns:repeat(auto-fit,minmax(6rem,1fr));gap:0.625rem}
/* Con el tope, son TRES números de una misma cuenta (autorizado = cobrados +
   no se cobrarían) y van en una sola fila también en el teléfono: con
   `auto-fit` el tercero caía solo a otra fila y la suma dejaba de leerse. */
.cod-vivo.tres{grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:440px){
  .cod-vivo.tres{gap:0.4375rem}
  .cod-vivo.tres>div{padding:0.6875rem 0.3125rem}
  .cod-vivo.tres b{font-size:1.0625rem}
}
/* Lo que no se cobraría del tope va en verde: es la cifra a favor de la persona. */
.cod-vivo b.vuelve{color:var(--ok)}
.cod-vivo>div{
  padding:0.8125rem;border-radius:0.6875rem;background:var(--surface);border:1px solid var(--line);
  text-align:center;
}
.cod-vivo b{
  display:block;font-family:var(--mono);font-size:1.4375rem;font-weight:700;
  color:var(--led-bright);font-variant-numeric:tabular-nums;
}
.cod-vivo small{display:block;margin-top:3px;font-size:0.625rem;color:var(--ink-3)}

/* ================== PAGAR ANTES DE CARGAR (ADR 0003) =====================
   La pantalla donde se decide cuánto pagar, con qué y a dónde vuelve lo que
   sobre. No es un formulario más: es el lugar donde se acepta un cobro, y la
   ley pide que todo eso esté A LA VISTA en la misma pantalla en que se acepta
   (Ley 1480 art. 26 y 47). Por eso nada va detrás de un desplegable salvo la
   explicación larga, que ya tiene su `<details>`.

   ⚠️ Ni un color escrito a mano: todo sale de `kw-tokens.css`. Una tarjeta
   elegida se marca con el azul de marca (`--led`), que en claro es #085CF0 y en
   oscuro #0D6BFF — dos valores, una sola regla.
   ========================================================================= */
.pagar{display:flex;flex-direction:column;gap:0.75rem}
.pagar-h{
  margin:0.375rem 0 0;font-size:0.6875rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.07em;color:var(--ink-3);
}
.pagar-ayuda{margin:0;font-size:0.75rem;line-height:1.6;color:var(--ink-3)}
.pagar-ayuda b{color:var(--ink-2);font-weight:600}

/* Los montos. `auto-fit` con un mínimo generoso: en teléfono caben dos por
   fila y en escritorio tres, y ninguno queda solo con el doble de ancho. */
.pagar-montos{display:grid;grid-template-columns:repeat(auto-fit,minmax(8.25rem,1fr));gap:0.5625rem}
.mon,.med{
  position:relative;display:block;cursor:pointer;
  padding:0.75rem 0.8125rem;border-radius:0.6875rem;
  background:var(--surface);border:1px solid var(--line);
  transition:border-color .14s,background .14s;
  /* ⚠️⚠️ EN EL TELÉFONO, DESPLAZARSE «SELECCIONABA» LA TARJETA (2026-10-21).
     Reportado: «cuesta desplazarse, es como si la selección quedara mala».
     Las tarjetas ocupan casi todo el ancho, así que el dedo SIEMPRE empieza el
     arrastre sobre una. Y ahí se juntaban: el resaltado de toque de Android
     (un velo sobre toda la etiqueta), la selección de texto si el dedo se
     demora, el `:hover` que en un táctil se queda pegado (borde azul, igual
     que el de la elegida) y el anillo de foco tras tocar. Cuatro señales de
     «elegida» sobre una tarjeta que no lo estaba. La única señal es ahora la
     de verdad: `:has(input:checked)`. */
  -webkit-tap-highlight-color:transparent;
  -webkit-user-select:none;user-select:none;-webkit-touch-callout:none;
  touch-action:manipulation;
}
/* El radio se esconde de la vista pero NO del teclado ni del lector: sigue
   siendo el control, y la tarjeta entera es su etiqueta. */
.mon input,.med input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.mon b{
  display:block;font-family:var(--mono);font-size:1.0625rem;font-weight:700;
  color:var(--ink);font-variant-numeric:tabular-nums;
}
/* La mono es para CIFRAS. «Otro monto» en JetBrains Mono se lee como un código,
   no como una opción. */
.mon b.texto{font-family:var(--sora);font-size:0.9062rem;font-weight:600}
.mon small,.med small{display:block;margin-top:3px;font-size:0.6562rem;line-height:1.45;color:var(--ink-3)}
/* Solo donde hay puntero de verdad: en un táctil el `:hover` se queda en la
   última tarjeta tocada y se confunde con la elegida. */
@media (hover:hover){ .mon:hover,.med:hover{border-color:var(--led)} }
/* Elegido. El borde de 2 px se compensa con el padding para que la tarjeta no
   salte un pixel al marcarse — el salto se lee como un defecto de pintado. */
.mon:has(input:checked),.med:has(input:checked){
  border-color:var(--led);border-width:2px;padding:0.6875rem 0.75rem;
  background:rgba(var(--led-rgb),.07);
}
.mon:has(input:checked) b,.med:has(input:checked) .med-n{color:var(--led-bright)}
/* El anillo, solo con teclado: con `:focus-within` cada toque dibujaba un
   segundo borde alrededor del borde de «elegida». */
.mon:has(input:focus-visible),.med:has(input:focus-visible){outline:2px solid var(--led);outline-offset:2px}
.mon-tag{
  display:inline-block;margin-top:0.375rem;padding:2px 0.4375rem;border-radius:62.4375rem;
  font-size:0.5625rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--led-bright);background:rgba(var(--led-rgb),.12);
}

.pagar-medios{display:flex;flex-direction:column;gap:0.5625rem}
.med-n{display:block;font-size:0.8438rem;font-weight:600;color:var(--ink)}
/* Un medio vencido se ve, pero no se puede elegir: esconderlo deja a la persona
   buscando una tarjeta que ella sabe que registró. */
.med.venc{opacity:.6;cursor:not-allowed}
@media (hover:hover){ .med.venc:hover{border-color:var(--line)} }
.med.venc small{color:var(--crit-ink-suave)}

.pagar-llave{
  padding:0.8125rem 0.875rem 2px;border-radius:0.6875rem;
  background:var(--surface-2);border:1px dashed var(--line);
}
.pagar-llave .pagar-ayuda{margin-bottom:0.75rem}

/* El resumen de lo que se va a aceptar, pegado al botón que lo acepta. */
.pagar-res{
  display:flex;flex-direction:column;gap:0.5625rem;padding:0.8125rem 0.875rem;border-radius:0.6875rem;
  background:rgba(var(--led-rgb),.06);border:1px solid rgba(var(--led-rgb),.28);
}
/* ⚠️ `hidden` pesa MENOS que cualquier regla de clase: `.pagar-res{display:flex}`
   le gana y el resumen escondido se ve igual. Pasó con el pago sin cuenta
   (ADR 0008): el de la tarjeta y el del datáfono salían los dos a la vez. */
.pagar-res[hidden]{display:none}
.pagar-res>div{display:flex;align-items:baseline;justify-content:space-between;gap:0.75rem}
.pagar-res span{font-size:0.7188rem;color:var(--ink-2)}
.pagar-res b{
  font-family:var(--mono);font-size:0.9375rem;font-weight:700;color:var(--ink);
  text-align:right;font-variant-numeric:tabular-nums;
}
/* El destino de la devolución es un nombre, no una cifra: en mono y en 15 px se
   parte en dos renglones y se lee como un código. */
.pagar-res>div:last-child b{font-family:var(--sora);font-size:0.7812rem;font-weight:600}

.pagar-err{
  margin:0;padding:0.6875rem 0.8125rem;border-radius:0.625rem;font-size:0.7812rem;line-height:1.55;
  color:var(--crit-ink-suave);background:rgba(var(--crit-rgb),.08);
  border:1px solid rgba(var(--crit-rgb),.35);
}

/* ---------- Las dos casillas de la pasarela (hoja «Agregar un medio») -------
   ⚠️ Van DESMARCADAS por omisión y son DOS, no una: el reglamento de uso de la
   pasarela y la autorización de tratamiento de datos son dos consentimientos
   distintos, y `pagos.agregar` exige los dos por separado. Una sola casilla que
   dijera «acepto todo» no sería consentimiento de ninguno de los dos.

   ⚠️ Se llama `.psp-acepto` y no `.acepto` a propósito: `.acepto` ya existe en
   `kw-legal.css` para las pantallas legales, y repetir el nombre haría que la
   apariencia de estas casillas dependiera de cuál hoja cargó la pantalla.
   Ni un color a mano: todo sale de `kw-tokens.css`. */
.psp-acepto{display:flex;flex-direction:column;gap:0.5rem;margin-top:0.875rem}
.psp-acepto label{
  display:flex;gap:0.625rem;align-items:flex-start;padding:0.6875rem 0.75rem;border-radius:0.625rem;
  background:var(--surface-2);border:1px solid var(--line);
  font-size:0.75rem;line-height:1.55;color:var(--ink-2);cursor:pointer;
  transition:border-color .15s;
}
.psp-acepto label:hover{border-color:var(--led)}
.psp-acepto label:focus-within{border-color:var(--led);outline:2px solid var(--led);outline-offset:2px}
.psp-acepto input{
  flex:none;width:1.0625rem;height:1.0625rem;margin:1px 0 0;accent-color:var(--led);cursor:pointer;
}
.psp-acepto b{color:var(--ink);font-weight:600}
.psp-acepto a{color:var(--led-bright);text-decoration:underline;text-underline-offset:2px}
.psp-acepto .psp-pend{color:var(--ink-3)}

.cod-aviso{
  padding:0.75rem 0.875rem;border-radius:0.6875rem;font-size:0.7812rem;line-height:1.55;color:var(--ink-2);
  background:var(--surface-2);border:1px solid var(--line);
}
.cod-aviso b{color:var(--ink)}

.cod-otros{display:flex;flex-direction:column;gap:0.5rem;margin-top:0.25rem}
.cod-otros h2{
  margin:0;font-size:0.6875rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.07em;color:var(--ink-3);
}
.cod-otro{
  display:flex;align-items:center;gap:0.75rem;padding:0.6875rem 0.8125rem;border-radius:0.6875rem;
  background:var(--surface);border:1px solid var(--line);color:var(--ink);
  font-size:0.8125rem;text-decoration:none;
}
.cod-otro:hover{border-color:var(--led);background:var(--surface-2);text-decoration:none}
.cod-otro>span:nth-child(2){flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.cod-otro small{display:block;font-size:0.6562rem;color:var(--ink-3)}
.cod-otro-n{
  flex:none;width:2.375rem;height:2.375rem;border-radius:0.5625rem;display:grid;place-items:center;
  font-family:var(--mono);font-size:1rem;font-weight:700;
  background:var(--surface-2);border:1px solid var(--line);
}
/* La distancia lleva más dígitos que un número de conector: se achica para no
   desbordar la misma caja. */
/* ⚠️ Con su ancho, no en la caja cuadrada (2026-11-02): «10400» con la «m» debajo
   no cabía en 40 px. Ahora dice «10,4 km» en un renglón. */
.cod-otro-n.dist{width:auto;min-width:2.375rem;padding:0 0.5rem;white-space:nowrap;
  font-size:0.75rem;letter-spacing:-.02em}
.cod-otro-n.dist small{font-size:0.5312rem;color:var(--ink-3);margin-left:1px}
.cod-otro-ir{flex:none;font-size:0.6875rem;font-weight:600;color:var(--led-bright)}

.cod-form{display:flex;gap:0.5rem}
.cod-form input{
  flex:1 1 auto;min-width:0;padding:0.75rem 0.8125rem;border-radius:0.625rem;font-size:0.9375rem;
  font-family:var(--mono);letter-spacing:.04em;
  background:var(--surface);border:1px solid var(--line);color:var(--ink);
}
.cod-form input:focus{outline:2px solid var(--led);outline-offset:1px;border-color:var(--led)}

.cod-mapa{
  margin-top:2px;text-align:center;font-size:0.75rem;color:var(--ink-3);
  padding:0.625rem;border-radius:0.625rem;border:1px dashed var(--line);
}
.cod-mapa:hover{color:var(--led-bright);border-color:var(--led);text-decoration:none}

/* El acceso desde el mapa. Discreto: no compite con la lista, que es a lo que
   se viene cuando NO se está frente a un cargador. */
.cod-btn{
  display:flex;align-items:center;justify-content:center;gap:0.5rem;
  margin:0.625rem 1.125rem 0;padding:0.625rem;border-radius:0.625rem;cursor:pointer;
  font-family:var(--sora);font-size:0.75rem;font-weight:500;color:var(--ink-2);
  background:var(--surface-2);border:1px dashed var(--line);
}
.cod-btn:hover{border-style:solid;border-color:var(--led);color:var(--led-bright)}
.cod-btn svg{flex:none;opacity:.85}
/* «Estoy frente a un cargador» es la puerta MÁS CORTA a una carga pagada:
   botón principal, no una línea punteada gris (2026-10-19). */
.cod-btn.pri{
  background:linear-gradient(180deg,var(--led),var(--led-hondo));color:#fff;
  border:1px solid transparent;font-size:0.8125rem;font-weight:600;padding:0.75rem;
}
.cod-btn.pri:hover{filter:brightness(1.12);color:#fff;border-color:transparent}
.cod-btn.pri svg{opacity:1}

/* CARGAR AHORA (`_cargar_ahora.html`, 2026-10-21): lo primero de la portada y
   de «Mi consumo». Escanear ocupa el ancho y lleva el peso; escribir el código
   va al lado, secundario. Los colores salen de los mismos tokens que `.btn.pri`
   y `.btn.gh`: ni un #hex, o queda mudo en el otro tema. */
.cargar-ya{
  display:flex;gap:0.5rem;align-items:stretch;flex-wrap:wrap;
  margin:0.75rem 1.125rem 0;
}
.cargar-ya-qr{
  flex:1 1 13rem;display:flex;align-items:center;gap:0.75rem;
  padding:0.875rem 1rem;border-radius:0.875rem;cursor:pointer;text-align:left;
  font-family:var(--sora);color:#fff;text-decoration:none;
  background:linear-gradient(180deg,var(--led),var(--led-hondo));
  border:1px solid transparent;box-shadow:0 0.375rem 1.125rem rgba(var(--led-rgb),.28);
}
.cargar-ya-qr:hover{filter:brightness(1.12);color:#fff;text-decoration:none}
.cargar-ya-qr svg{flex:none;width:1.75rem;height:1.75rem}
.cargar-ya-qr b{display:block;font-size:1rem;font-weight:600;line-height:1.25}
.cargar-ya-qr small{display:block;margin-top:0.125rem;font-size:0.75rem;opacity:.85;font-weight:400}
.cargar-ya-cod{
  flex:0 1 auto;display:flex;align-items:center;justify-content:center;
  padding:0.625rem 0.875rem;border-radius:0.875rem;cursor:pointer;
  font-family:var(--sora);font-size:0.8125rem;font-weight:500;color:var(--ink-2);
  text-decoration:none;background:var(--surface-2);border:1px solid var(--line);
}
.cargar-ya-cod:hover{border-color:var(--led);color:var(--led-bright);text-decoration:none}
/* «Así se carga»: los cuatro pasos, debajo de los botones y a lo ancho.
   Números en círculo con el color de marca; texto chico, que no le gane al
   botón de escanear. */
.cargar-ya-pasos{
  flex:1 1 100%;margin:0.25rem 0 0;padding:0.75rem 0.875rem;list-style:none;
  counter-reset:paso;display:grid;gap:0.375rem;
  background:var(--surface-2);border:1px solid var(--line);border-radius:0.875rem;
  font-size:0.8125rem;line-height:1.4;color:var(--ink-2);
}
.cargar-ya-pasos li{counter-increment:paso;display:flex;gap:0.5rem;align-items:baseline}
.cargar-ya-pasos li::before{
  content:counter(paso);flex:none;width:1.25rem;height:1.25rem;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;transform:translateY(0.15rem);
  font-family:var(--sora);font-size:0.6875rem;font-weight:600;
  color:var(--led-bright);background:rgba(var(--led-rgb),.14);
}
.cargar-ya-pasos b{color:var(--ink);font-weight:600}
/* El dibujo de cada paso, en su baldosa, con el número en la esquina
   (2026-10-27: «que se vean más los colores… o sea todo»). */
.cargar-ya-pasos li:has(.paso-ic){position:relative;align-items:center;gap:0.75rem}
.paso-ic{
  flex:none;width:2.5rem;height:2.5rem;padding:0.3125rem;border-radius:0.75rem;
  background:rgba(var(--led-rgb),.1);border:1px solid rgba(var(--led-rgb),.22);
}
.cargar-ya-pasos li:has(.paso-ic)::before{
  position:absolute;left:1.875rem;top:-0.25rem;transform:none;z-index:1;
  width:1.125rem;height:1.125rem;font-size:0.625rem;
  color:var(--ground);background:var(--led);box-shadow:0 0 0 2px var(--surface-2);
}
/* Fuera del mapa vive dentro de `.wrap`, que ya tiene sus márgenes. */
.wrap>.cargar-ya{margin:1rem 0}

.cod-bg{
  position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:1rem;
  background:var(--velo);backdrop-filter:blur(3px);
}
.cod-modal{
  width:min(26.25rem,100%);padding:1.25rem;border-radius:0.9375rem;
  background:var(--surface);border:1px solid var(--line);
  box-shadow:var(--sombra-modal);
}
.cod-modal-h{display:flex;align-items:center;gap:0.625rem;margin-bottom:0.375rem}
.cod-modal-h h3{margin:0;flex:1 1 auto;font-size:0.9688rem;font-weight:600;color:var(--ink)}
.cod-x{
  flex:none;width:1.875rem;height:1.875rem;border-radius:0.5rem;cursor:pointer;font-size:1.1875rem;
  line-height:1;background:var(--surface-2);border:1px solid var(--line);color:var(--ink-2);
}
.cod-x:hover{border-color:var(--crit);color:var(--crit-ink)}
.cod-modal-p{margin:0 0 0.875rem;font-size:0.7188rem;color:var(--ink-3);line-height:1.55}
.cod-msj{margin:0.5625rem 0 0;font-size:0.7188rem;color:var(--ink-2);min-height:1rem;line-height:1.5}
#cod-cam{margin-top:0.75rem}
/* `#000` literal: es el hueco de la CÁMARA mientras arranca. Negro en los dos
   temas — un rectángulo blanco parpadeando antes de la imagen se lee como que
   algo falló. */
#cod-cam video{
  width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:0.6875rem;
  background:#000;border:1px solid var(--line);
}

/* ========================= EL DIAGNÓSTICO ===============================
   Lista de documentos internos. Reusa `.wrap`, `.titulo`, `.sub` y `.aviso`;
   lo de acá es solo la fila de documento, que no existía como componente.
   ========================================================================= */

.diag-lista{display:flex;flex-direction:column;gap:0.625rem;margin:1.125rem 0 1.375rem}

/* La fila entera es el enlace: en el teléfono se abre con el pulgar y un
   objetivo de una línea de texto se falla. */
.diag-doc{
  display:flex;align-items:center;gap:0.875rem;padding:0.9375rem 1rem;
  background:var(--surface);border:1px solid var(--line);border-radius:0.75rem;
  color:var(--ink);text-decoration:none;transition:border-color .15s,background .15s;
}
.diag-doc:hover{border-color:var(--led);background:var(--surface-2);text-decoration:none}
.diag-doc:focus-visible{outline:2px solid var(--led-bright);outline-offset:2px}

.diag-tipo{
  flex:none;width:2.75rem;height:2.75rem;border-radius:0.625rem;display:grid;place-items:center;
  background:var(--surface-2);border:1px solid var(--line);
  font-family:var(--mono);font-size:0.625rem;font-weight:700;color:var(--ink-3);
  letter-spacing:.04em;
}
/* `min-width:0` o un título largo empuja la flecha fuera de la caja. */
.diag-txt{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:3px}
.diag-txt b{font-size:0.9375rem;font-weight:600;color:var(--ink)}
.diag-txt small{font-size:0.75rem;color:var(--ink-3);line-height:1.5}
.diag-ir{flex:none;font-size:1.0625rem;color:var(--ink-3)}
.diag-doc:hover .diag-ir{color:var(--led-bright)}

.diag-nota{
  margin:0;font-size:0.75rem;color:var(--ink-3);line-height:1.6;max-width:80ch;
}
.diag-nota code{
  font-family:var(--mono);font-size:0.6875rem;color:var(--ink-2);
  background:var(--surface-2);padding:2px 0.375rem;border-radius:0.3125rem;
}

@media (max-width:640px){
  .diag-doc{padding:0.8125rem 0.8125rem;gap:0.6875rem}
  .diag-tipo{width:2.375rem;height:2.375rem;font-size:0.5625rem}
  .diag-txt b{font-size:0.875rem}
}

/* ==================== EL CIUDADANO SIN CUENTA ============================
   La invitación a entrar. Aparece en el emergente del mapa y en la ficha del
   punto, UNA vez por ficha y solo cuando hay conectores libres de verdad.

   ⚠️ NO reemplaza la pastilla de estado: el ciudadano sigue viendo «Libre»,
   «Ocupado» o «Sin datos» igual que cualquiera. Esto se SUMA, no tapa —
   esconder el estado detrás de un muro es lo contrario de dejar ver.
   ========================================================================= */

.kw-invita{
  display:flex;align-items:center;gap:0.6875rem;margin:0.625rem 0 2px;
  padding:0.6875rem 0.8125rem;border-radius:0.6875rem;text-decoration:none;
  background:rgba(var(--led-rgb),.09);
  border:1px solid rgba(var(--led-rgb),.32);
  transition:background .15s,border-color .15s;
}
.kw-invita:hover{
  text-decoration:none;
  background:rgba(var(--led-rgb),.16);
  border-color:var(--led);
}
.kw-invita:focus-visible{outline:2px solid var(--led-bright);outline-offset:2px}

/* `min-width:0` o un nombre largo empuja la flecha fuera de la caja. */
.kw-invita-t{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.kw-invita-t b{font-size:0.7812rem;font-weight:600;color:var(--led)}
.kw-invita-t small{font-size:0.6875rem;color:var(--ink-2);line-height:1.4}
.kw-invita-ir{flex:none;font-size:0.9375rem;color:var(--led)}

/* La nota de la barra lateral. Dice de entrada que se puede mirar todo sin
   cuenta: es lo que evita la sorpresa al llegar al botón. */
.lado-nota{
  margin:0.5rem 1.125rem 0;font-size:0.75rem;line-height:1.5;color:var(--ink-3);
}
.lado-nota b{color:var(--ink-2);font-weight:600}


/* =========================================================================
   TELÉFONO
   ⚠️ Este bloque va AL FINAL del archivo a propósito. Estaba escrito arriba,
   junto al navbar, y no aplicaba: a igual especificidad gana la última regla
   del archivo, así que la definición normal de `.chip` (que está más abajo)
   le ganaba a la del media query. El síntoma era que los toques seguían
   midiendo 28 px con la regla puesta y visible en el archivo.
   ========================================================================= */
@media (max-width:640px){
  /* El navbar se PISABA: alto fijo + enlaces con `flex-wrap`, así que al no
     caber pasaban a una segunda línea que se salía de la caja y «Entrar»
     quedaba encima de «Puntos de carga». Se deja que la barra crezca y los
     enlaces van a su propia fila, con desliz si aun así no entran. */
  /* ⚠️ Las reglas de `.nav` y `.nav-links` que vivían acá SE SUBIERON al bloque
     de 940 px —el de la barra de abajo—, porque entre 641 y 900 la barra se
     desbordaba. El porqué está escrito allá. Acá queda solo lo que de verdad
     empieza a los 640. */
  /* El nombre y el cargo no caben al lado de los botones; el avatar ya
     identifica la sesión y el nombre está en el panel. */
  .nav-yo .quien{display:none}

  /* Toques cómodos: medían 28 px de alto y con el dedo el mínimo razonable son
     ~40. No se tocan en escritorio, donde 28 está bien con el mouse. */
  .chip{padding:0.625rem 0.9375rem;font-size:0.8125rem}
  .btn.sm{padding:0.625rem 0.875rem;font-size:0.7812rem}
  .nav-links a{padding:0.5625rem 0.75rem}

  /* Las tres cifras del perfil quedan en cajas de ~97 px: medido, la fecha
     completa (`27/04/2026`) mide 90 y con el padding se salía 2 px por el
     borde. No se parte la fecha ni se quitan las cajas — se achica la cifra,
     que a este tamaño sigue siendo la más grande de su caja. */
  .perf-datos>div{padding:0.625rem 0.375rem}
  .perf-datos b{font-size:0.7812rem}
  .perf-datos span{font-size:0.5625rem;letter-spacing:.06em}

  /* Los +/- de Leaflet vienen de 30 px: con el dedo, y sobre un mapa que se
     arrastra, es fácil errarles y terminar moviendo el mapa.

     ⚠️⚠️ TERCERA vez con el mismo patrón en este archivo: en táctil Leaflet
     agrega `.leaflet-touch` al contenedor y su regla pesa (0,2,1), más que un
     `html .leaflet-bar a` (0,1,2) — se comparan primero las CLASES, así que dos
     clases le ganan a dos elementos. Hay que igualar el `.leaflet-touch`.
     El síntoma es siempre el mismo: la regla está escrita, se ve en el archivo,
     el media query aplica (los chips sí cambiaron) y ESA propiedad no cambia. */
  html .leaflet-touch .leaflet-bar a,
  html .leaflet-bar a{width:2.5rem;height:2.5rem;line-height:2.5rem;font-size:1.25rem}
}
/* Con el dedo, 44 px de verdad (2026-11-02): con la letra del teléfono al 90 %,
   2,5 rem son 36 px. El mismo `.leaflet-touch` de arriba, por el mismo motivo. */
@media (pointer:coarse){
  html .leaflet-touch .leaflet-bar a,
  html .leaflet-bar a{width:max(2.75rem,44px);height:max(2.75rem,44px);line-height:max(2.75rem,44px)}

  /* Los blancos de la lista de puntos, medidos a 390 (2026-11-02): corazón 25×25,
     «Ver el punto / En el mapa / Cómo llegar» de 28 de alto, chips 33, «Ordenar por
     cercanía» 33, selectores 32. Con el dedo, 44. El corazón crece en su blanco, no
     en su dibujo: el margen negativo le devuelve a la fila el alto que tenía. */
  .lado .chip,.cerca-btn,.fsel select,.t-accion,.t-cargar{min-height:max(2.75rem,44px)}
  .fav{width:44px;height:44px;margin:-0.625rem -0.75rem -0.625rem -0.25rem}
  .grupo-salir{min-height:44px}

  /* Campos de 16 px o más: por debajo, el iPhone amplía la pantalla al enfocar y
     la deja ampliada (medido: buscador 11,7 px; «Otro monto», correo y placa
     12,6 px). Solo en las pantallas del mapa, de pagar y de los bonos. */
  .buscador input,.fsel select,.cod-pag select,.cod-modal input,
  .cod-pag input:not([type=radio]):not([type=checkbox]),
  .bonos-pag input:not([type=radio]):not([type=checkbox]){font-size:max(1rem,16px)}
}

/* =========================================================================
   PAGOS — los cobros de cada carga y las devoluciones (`pagos.html`)
   -------------------------------------------------------------------------
   Los nombres `.sal-*` vienen de la pantalla «Saldo», que esta reemplazó el
   2026-09-19 (ADR 0002). El formato de fila es el de «Mis Movimientos» de
   Flypass: hora arriba, monto a la derecha con signo y color, concepto y
   detalle debajo.
   ========================================================================= */
.sal-cab{display:grid;grid-template-columns:minmax(0,1fr);gap:0.75rem;margin-bottom:1.125rem}
@media (min-width:760px){.sal-cab{grid-template-columns:minmax(0,320px) minmax(0,1fr)}}
/* La nota del tope retenido ocupa la fila entera, debajo de las dos cajas. */
.sal-cab .sal-res{grid-column:1 / -1}

.sal-tot{
  background:var(--surface);border:1px solid var(--line);border-radius:0.875rem;
  padding:1rem 1.125rem;border-left:3px solid var(--led);min-width:0;
}
.sal-tot .l{
  display:block;font-size:0.5938rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;
}
/* `>b` y no `b`: la línea del tope lleva sus propios resaltados adentro, y con
   el descendiente a secas se volvían bloques de 30 px partiendo la frase. */
.sal-tot>b{display:block;font-size:1.875rem;font-weight:600;letter-spacing:-.02em;margin:0.375rem 0 2px;
  font-family:var(--mono)}
/* El medio de pago es un NOMBRE («Visa ···· 4242»), no una cifra: más chico y
   en la letra del texto, o no cabe en la columna angosta. */
.sal-tot>b.sal-medio{font-family:inherit;font-size:1.25rem;overflow-wrap:anywhere}
.sal-tot .s{display:block;font-size:0.7188rem;color:var(--ink-3)}
.sal-tot .s b{color:var(--ink-2);font-weight:600}
.sal-tot .sal-tope{margin-top:0.75rem;padding-top:0.6875rem;border-top:1px solid var(--line-soft);
  line-height:1.55}
.sal-acc{display:flex;gap:0.5rem;margin-top:0.875rem;flex-wrap:wrap}

/* El tope retenido en una carga en curso, explicado. Sin esto, la retención
   que muestra el banco se lee como un cobro por el tope entero. */
.sal-res{
  background:rgba(var(--led-rgb),.07);border:1px solid rgba(var(--led-rgb),.28);
  border-radius:0.875rem;padding:0.875rem 1rem;
}
.sal-res-h{display:flex;align-items:center;gap:0.5rem;color:var(--led-bright)}
.sal-res-h b{font-size:0.9375rem;font-weight:600}
.sal-res p{margin:0.375rem 0 0;font-size:0.7812rem;color:var(--ink-2);line-height:1.6}

.mov{list-style:none;margin:0;padding:0}
.mov li{
  display:flex;align-items:flex-start;justify-content:space-between;gap:0.875rem;
  padding:0.8125rem 0;border-bottom:1px solid var(--line-soft);
}
.mov li:last-child{border-bottom:0}
.mov-l{min-width:0}
.mov-f{
  display:block;font-size:0.6562rem;color:var(--ink-3);font-family:var(--mono);
  margin-bottom:3px;
}
/* `>b` y no `b`: la explicación de cada línea (`.mov-x`) lleva sus propios
   resaltados —«Autorizado hasta <b>$60.000</b>»— y con el descendiente a secas
   se volvían bloques del tamaño del título, partiendo la frase en tres. */
.mov-l>b{display:block;font-size:0.8438rem;font-weight:600;color:var(--ink)}
.mov-x b{font-weight:600;color:var(--ink)}
.mov-d{display:block;font-size:0.7188rem;color:var(--ink-3);margin-top:2px}
.mov-x{display:block;font-size:0.7188rem;color:var(--ink-2);margin-top:0.3125rem;line-height:1.55;
  max-width:62ch}
.mov-v{display:inline-block;font-size:0.7188rem;margin-top:0.375rem}
/* ⚠️ El monto NO se parte en dos renglones: el número y su signo son una sola
   cosa, y partido queda un «−» suelto al final de una línea. */
.mov-m{
  font-family:var(--mono);font-size:0.9062rem;font-weight:600;white-space:nowrap;
  flex:none;padding-top:0.875rem;
}
.mov .entra .mov-m{color:var(--ok)}
.mov .sale .mov-m{color:var(--ink)}
/* Lo que no movió plata (en curso, sin costo, negado, fallido): apagado, para
   que no compita con los montos que sí salieron o entraron. */
.mov .nada .mov-m{color:var(--ink-3);font-weight:500}

/* El día, como encabezado de grupo. ⚠️ Va DESPUÉS de `.mov li` y con dos clases
   (0,2,1 contra 0,1,1): escrito antes o con una sola clase, la regla general le
   ganaba por orden y el encabezado salía como una fila flex más, con su borde y
   su alto — la lista quedaba igual de pareja y el agrupado no se veía. */
.mov li.mov-dia{
  display:block;position:sticky;top:0;z-index:2;
  padding:0.75rem 0 0.375rem;font-size:0.625rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;background:var(--surface);border-bottom:0;
}
.mov li.mov-dia:first-child{padding-top:0}
/* La devolución, debajo del pago de su carga y sangrada (2-nov-2026): se lee cuál
   va con cuál sin perder que son dos movimientos. El pago de arriba pierde su
   línea divisoria para que los dos se lean como un grupo. */
.mov li:has(+ li.mov-sub){border-bottom-color:transparent;padding-bottom:0.375rem}
.mov li.mov-sub{margin-left:1rem;padding:0.375rem 0 0.8125rem 0.75rem;
  border-left:2px solid var(--line)}
.mov li.mov-sub .mov-l>b{font-size:0.7812rem}
.mov li.mov-sub .mov-m{padding-top:0.5rem}
.mov-x.mov-motivo{color:var(--ink-3);margin-top:0.1875rem}

/* =========================================================================
   «¿CÓMO SE COBRA?» — la explicación donde se decide
   -------------------------------------------------------------------------
   Cerrado por omisión: quien ya lo entendió no lo vuelve a leer cada vez, y
   quien no, lo tiene sin cambiar de pantalla. `<details>` nativo por lo mismo
   que en Ayuda — se abre sin JavaScript y el lector de pantalla ya lo sabe leer.
   ========================================================================= */
.cobro{
  border:1px solid var(--line);border-radius:0.75rem;background:var(--surface);
  margin:0 0 0.875rem;
}
.cobro summary{
  cursor:pointer;padding:0.75rem 0.875rem;font-size:0.7812rem;font-weight:600;color:var(--ink-2);
  list-style:none;position:relative;padding-right:1.875rem;
}
.cobro summary::-webkit-details-marker{display:none}
.cobro summary::after{
  content:'';position:absolute;right:0.875rem;top:50%;width:0.375rem;height:0.375rem;
  border-right:2px solid var(--ink-3);border-bottom:2px solid var(--ink-3);
  transform:translateY(-70%) rotate(45deg);transition:transform .18s ease;
}
.cobro[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.cobro summary:hover{color:var(--led-bright)}
.cobro summary:focus-visible{outline:2px solid var(--led);outline-offset:-2px;border-radius:0.75rem}
.cobro>div{padding:0 0.875rem 0.75rem}
.cobro ol{margin:0;padding-left:1.125rem}
.cobro li{font-size:0.7812rem;line-height:1.65;color:var(--ink-2);margin-bottom:0.5rem}
.cobro li b{color:var(--ink)}
.cobro p{margin:0.625rem 0 0;font-size:0.75rem;line-height:1.6;color:var(--ink-3)}
.cobro p b{color:var(--ink-2)}
.cobro-mas{margin-top:0.625rem !important;font-size:0.75rem}

/* La misma explicación, en la hoja del mapa. */
.cobro-modal ol{margin:0;padding-left:1.125rem}
.cobro-modal li{font-size:0.8125rem;line-height:1.65;color:var(--ink-2);margin-bottom:0.625rem}
.cobro-modal li b{color:var(--ink)}
.cobro-modal p{margin:0.75rem 0 0;font-size:0.7812rem;line-height:1.6;color:var(--ink-3)}
.cobro-modal p b{color:var(--ink-2)}

/* Una pastilla que además es un botón. Mismo tamaño y forma que sus vecinas
   —vive en la misma fila— pero se toca, así que lo dice con el cursor, el
   subrayado al pasar y un foco visible. */
.pill.enlace{
  border-style:dashed;cursor:pointer;font-family:inherit;
  background:none;color:var(--ink-3);
}
.pill.enlace:hover{color:var(--led-bright);border-color:var(--led)}
.pill.enlace:focus-visible{outline:2px solid var(--led);outline-offset:2px}

/* =========================================================================
   LOS AVISOS — la campana y su panel
   ========================================================================= */
.avi-btn{
  position:relative;flex:none;width:2.25rem;height:2.25rem;display:grid;place-items:center;
  border-radius:0.625rem;border:1px solid var(--line);background:var(--surface);
  color:var(--ink-2);cursor:pointer;transition:border-color .15s,color .15s;
}
.avi-btn:hover{border-color:var(--led);color:var(--led-bright)}
.avi-btn:focus-visible{outline:2px solid var(--led);outline-offset:2px}
.avi-btn svg{width:1.125rem;height:1.125rem}
/* El número, no un punto: «hay algo» sin decir cuánto obliga a abrir para saber
   si valía la pena. Y en rojo de marca, que es el único color que en esta app
   significa «esto le toca a usted».
   ⚠️ La tinta sale de `--crit-sobre`, NO de un `#fff` escrito acá: en el tema
   oscuro `--crit` es un rojo CLARO (#FF4D5E) y el blanco daba 3,24:1 en 10 px.
   Es exactamente la trampa de CLAUDE.md §4 —relleno fijo con tinta que no sigue
   al tema— y la cazó `capturar_temas.py` apenas hubo un aviso sin leer. */
.avi-n{
  position:absolute;top:-5px;right:-5px;min-width:1.0625rem;height:1.0625rem;padding:0 0.25rem;
  border-radius:0.5625rem;background:var(--crit);color:var(--crit-sobre);
  font-size:0.625rem;font-weight:700;line-height:1.0625rem;text-align:center;
  font-family:var(--mono);
}
/* ⚠️ El `[hidden]` PRIMERO y con la clase. `display:flex` en `.avi-panel` le
   gana al `display:none` que el navegador le da a `[hidden]` —la hoja de estilo
   del user-agent tiene menos peso que cualquier clase—, así que el panel nacía
   VISIBLE y el `hidden` del HTML no hacía nada: se veía flotando sobre la
   pantalla desde el primer momento y Escape parecía no funcionar. Es el mismo
   defecto que muerde con `[hidden]` sobre cualquier `display` explícito. */
.avi-panel[hidden]{display:none}
.avi-panel{
  position:fixed;top:calc(var(--nav) + 0.375rem);right:0.875rem;z-index:1100;
  width:min(22.5rem,calc(100vw - 1.75rem));max-height:min(70vh,32.5rem);
  display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--line);border-radius:0.875rem;
  box-shadow:var(--sombra-modal);overflow:hidden;
}
.avi-h{
  display:flex;align-items:center;justify-content:space-between;gap:0.625rem;
  padding:0.75rem 0.875rem;border-bottom:1px solid var(--line);
}
.avi-h b{font-size:0.875rem;font-weight:600}
.avi-lista{overflow-y:auto;padding:0.25rem 0}
.avi-lista a{display:block;color:inherit;text-decoration:none}
.avi-lista a:hover{background:var(--surface-2);text-decoration:none}
.avi-i{padding:0.6875rem 0.875rem;border-bottom:1px solid var(--line-soft)}
.avi-i b{display:block;font-size:0.8125rem;font-weight:600;color:var(--ink)}
.avi-i p{margin:3px 0 0;font-size:0.7188rem;line-height:1.55;color:var(--ink-2)}
.avi-i span{display:block;margin-top:0.3125rem;font-size:0.625rem;color:var(--ink-3)}
/* El no leído se marca con un filete a la izquierda ADEMÁS del fondo: el fondo
   solo es demasiado sutil en el tema claro. */
.avi-i.nuevo{border-left:3px solid var(--led);background:rgba(var(--led-rgb),.05);
  padding-left:0.6875rem}
.avi-pie{padding:0.625rem 0.875rem;border-top:1px solid var(--line);font-size:0.7188rem}
/* Las dos pestañas (2026-10-30): «Suyos» y «De la red». Subrayado LED en la
   activa, como las secciones de Cuenta; cada una con su número. */
.avi-tabs{display:flex;border-bottom:1px solid var(--line);padding:0 0.5rem}
.avi-tabs button{
  flex:1;display:flex;align-items:center;justify-content:center;gap:0.375rem;
  padding:0.5625rem 0.5rem;background:none;border:0;border-bottom:2px solid transparent;
  margin-bottom:-1px;color:var(--ink-3);font:inherit;font-size:0.75rem;font-weight:600;
  cursor:pointer;transition:color .15s,border-color .15s;
}
.avi-tabs button:hover{color:var(--ink)}
.avi-tabs button.on{color:var(--ink);border-bottom-color:var(--led)}
.avi-tabs button:focus-visible{outline:2px solid var(--led);outline-offset:-2px;border-radius:0.375rem}
.avi-tn{font-style:normal;min-width:1rem;height:1rem;padding:0 0.25rem;border-radius:0.5rem;
  background:var(--crit);color:var(--crit-sobre);font-size:0.5625rem;line-height:1rem;
  text-align:center;font-family:var(--mono)}
/* El tipo del anuncio, encima del título: dice de qué va antes de leerlo. */
.avi-tipo{display:block;font-style:normal;font-size:0.5938rem;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:var(--led-bright);margin-bottom:0.1875rem}
.avi-tipo.t-MANTENIMIENTO{color:var(--warn)}

/* Los dos selectores de filtro del mapa. Van en una fila que se parte sola:
   en el teléfono quedan uno encima del otro sin robarle alto al mapa. */
.filtros-sel{display:flex;gap:0.5625rem;flex-wrap:wrap;padding:0.625rem 1.125rem 0}
.fsel{flex:1 1 8.75rem;min-width:0}
.fsel>span{
  display:block;font-size:0.5625rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;margin-bottom:0.25rem;
}
.fsel select{
  width:100%;background:var(--surface);border:1px solid var(--line);border-radius:0.5625rem;
  padding:0.5rem 0.625rem;color:var(--ink);font-family:var(--sora);font-size:0.7812rem;outline:none;
}
.fsel select:focus{border-color:var(--led)}
.ficha-abierta .filtros-sel{display:none}

/* MI CARRO (2026-10-28). El conector que no le sirve a su carro se apaga un
   poco y lo dice; no se esconde, porque el dato del carro lo puso la persona y
   puede estar mal. */
.con-tarjeta.no-sirve{opacity:.62}
.con-tarjeta.no-sirve:hover{opacity:.85}
.con-tarjeta-nocarro{
  display:block;margin-top:0.25rem;font-size:0.625rem;font-weight:600;color:var(--warn-ink);
}
.det-nocarro{
  margin:0 0 0.625rem;padding:0.5625rem 0.6875rem;border-radius:0.5rem;font-size:0.7188rem;line-height:1.5;
  background:rgba(var(--warn-rgb),.12);border:1px solid rgba(var(--warn-rgb),.34);color:var(--ink);
}

/* AVÍSEME CUANDO SE LIBERE (2026-10-28). Una banda fija abajo, de vidrio, que
   se queda mientras se espera y cambia de color cuando el conector se libera.
   Abajo y no arriba: arriba están la búsqueda y el título, y en el teléfono es
   donde está el pulgar. Por encima de la barra de pestañas del teléfono. */
.kw-espera{
  position:fixed;left:50%;transform:translateX(-50%);bottom:1rem;z-index:1200;
  display:flex;align-items:center;gap:0.625rem;flex-wrap:wrap;justify-content:center;
  width:max-content;max-width:calc(100vw - 2rem);padding:0.625rem 0.75rem 0.625rem 0.875rem;
  border-radius:0.875rem;border:1px solid rgba(var(--led-rgb),.34);
  background:color-mix(in srgb, var(--surface-2) 86%, transparent);
  -webkit-backdrop-filter:blur(10px) saturate(1.4);backdrop-filter:blur(10px) saturate(1.4);
  box-shadow:var(--sombra-modal);color:var(--ink);font-size:0.75rem;line-height:1.45;
  animation:kw-espera-entra .2s cubic-bezier(.34,1.56,.64,1);
}
.kw-espera-t{display:flex;align-items:center;gap:0.5rem;min-width:0}
.kw-espera .dot{color:var(--led)}
.kw-espera.listo{border-color:rgba(var(--ok-rgb),.5);box-shadow:var(--sombra-modal),0 0 0 3px rgba(var(--ok-rgb),.18)}
@keyframes kw-espera-entra{from{opacity:0;transform:translate(-50%,0.5rem)}to{opacity:1;transform:translate(-50%,0)}}
/* ⚠️ En el teléfono, POR ENCIMA del conmutador lista/mapa (2026-11-02). Estaba a
   4,75 rem y tapaba la píldora «Lista/Mapa» (los dos con z-index 1200, la banda
   después en el DOM): mientras se esperaba, hasta una hora, no se podía cambiar
   de vista. Ahora a la altura de `.kw-cinta`, que ya se había subido por lo mismo.
   Los avisos de cercanía y de fondo ampliado van abajo también (arriba están la
   barra y los controles del mapa), y más arriba todavía si hay banda o cinta. */
@media (max-width:940px){
  .kw-espera{bottom:calc(var(--tabbar) + 4.5rem + env(safe-area-inset-bottom, 0px))}
  .kw-avisos{top:auto;bottom:calc(var(--tabbar) + 4.5rem + env(safe-area-inset-bottom, 0px))}
  body:has(#kw-espera,#kw-cinta) .kw-avisos{bottom:calc(var(--tabbar) + 9rem + env(safe-area-inset-bottom, 0px))}
}
@media (prefers-reduced-motion:reduce){ .kw-espera{animation:none} }
/* Respaldo del vidrio de la banda: sólida sin `backdrop-filter`, con menos
   transparencia pedida o con más contraste (vidrio.md §7). */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .kw-espera{background:var(--surface-2)}
}
@media (prefers-reduced-transparency:reduce),(prefers-contrast:more){
  .kw-espera{background:var(--surface-2);-webkit-backdrop-filter:none;backdrop-filter:none}
}
@media (prefers-contrast:more){ .kw-espera{border-color:var(--ink-3)} }

/* =========================================================================
   LA CONFIABILIDAD EN LA FICHA DEL PUNTO
   -------------------------------------------------------------------------
   El dato que ninguna app en Colombia publica. Va con el mismo vocabulario de
   color de las pastillas —ok / warn / crit / gris— porque significa lo mismo
   que ellas: si se puede contar con este punto.
   ========================================================================= */
.det-conf{
  margin:0.75rem 0 0;padding:0.6875rem 0.8125rem;border-radius:0.6875rem;font-size:0.7812rem;
  line-height:1.55;border:1px solid var(--line);background:var(--surface-2);
  color:var(--ink-2);
}
.det-conf b{font-family:var(--mono);font-size:0.875rem;color:var(--ink)}
.det-conf span{display:block;margin-top:0.3125rem;font-size:0.6875rem;color:var(--ink-3)}
/* Cada tono redefine `--ink-3` adentro: si la letra chica se queda con el token
   del tema, la caja de color la deja ilegible (la trampa de `.vivo`). */
.det-conf.ok{background:rgba(var(--ok-rgb),.09);border-color:rgba(var(--ok-rgb),.3);
  color:var(--ok-ink);--ink:var(--ok-ink);--ink-3:var(--ok-ink)}
.det-conf.warn{background:rgba(var(--warn-rgb),.09);border-color:rgba(var(--warn-rgb),.32);
  color:var(--warn-ink);--ink:var(--warn-ink);--ink-3:var(--warn-ink)}
.det-conf.crit{background:rgba(var(--crit-rgb),.09);border-color:rgba(var(--crit-rgb),.34);
  color:var(--crit-ink-suave);--ink:var(--crit-ink-suave);--ink-3:var(--crit-ink-suave)}
.det-conf.vacia{font-size:0.7188rem;color:var(--ink-3);background:none;border:0;padding:0.5625rem 0 0}

.det-pot{margin:0.625rem 0 0;font-size:0.75rem;line-height:1.6;color:var(--ink-2)}
.det-pot b{font-family:var(--mono);color:var(--ink)}
.det-pot small{display:block;margin-top:0.25rem;font-size:0.6562rem;color:var(--ink-3)}

/* =========================================================================
   AYUDA — preguntas plegables y el formulario
   -------------------------------------------------------------------------
   `<details>` nativo y no un acordeón en JavaScript: se abre sin scripts, el
   buscador del navegador (Ctrl+F) encuentra el texto adentro desde Chrome 120,
   y el lector de pantalla ya sabe leerlo. Un acordeón propio serían 40 líneas
   para conseguir menos.
   ========================================================================= */
.ayu{border-bottom:1px solid var(--line-soft)}
.ayu:last-child{border-bottom:0}
.ayu summary{
  cursor:pointer;padding:0.8125rem 1.625rem 0.8125rem 0;font-size:0.8438rem;font-weight:600;
  color:var(--ink);position:relative;list-style:none;
}
.ayu summary::-webkit-details-marker{display:none}
/* La flecha, dibujada con bordes: un carácter de texto cambia de tamaño con la
   fuente del sistema y se desalinea. */
.ayu summary::after{
  content:'';position:absolute;right:0.375rem;top:50%;width:0.4375rem;height:0.4375rem;
  border-right:2px solid var(--ink-3);border-bottom:2px solid var(--ink-3);
  transform:translateY(-70%) rotate(45deg);transition:transform .18s ease;
}
.ayu[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.ayu summary:hover{color:var(--led-bright)}
.ayu summary:focus-visible{outline:2px solid var(--led);outline-offset:2px;border-radius:0.375rem}
.ayu>div{padding:0 0 0.875rem}
.ayu p{margin:0 0 0.625rem;font-size:0.8125rem;line-height:1.7;color:var(--ink-2);max-width:70ch}
/* Las respuestas con pasos o motivos (2026-10-21): misma letra que el párrafo. */
.ayu ol,.ayu ul{margin:0 0 0.625rem;padding-left:1.25rem;font-size:0.8125rem;line-height:1.6;
  color:var(--ink-2);max-width:70ch;display:grid;gap:0.375rem}
.ayu li::marker{color:var(--led-bright);font-weight:600}
.ayu p:last-child{margin-bottom:0}
.ayu .num{font-family:var(--mono);font-size:0.75rem}

.ayu-form{max-width:35rem}
.ayu-form textarea{
  width:100%;background:var(--ground);border:1px solid var(--line);border-radius:0.5625rem;
  padding:0.6875rem 0.8125rem;color:var(--ink);font-family:var(--sora);font-size:0.875rem;
  outline:none;resize:vertical;line-height:1.6;
}
.ayu-form textarea:focus{border-color:var(--led)}
.ayu-form textarea::placeholder{color:var(--ink-3)}

/* =========================================================================
   MIS CARGAS — la lista de sesiones
   ========================================================================= */
.cgs{list-style:none;margin:0;padding:0}
.cgs li{border-bottom:1px solid var(--line-soft)}
.cgs li:last-child{border-bottom:0}
.cg-d .cg-placa{color:var(--ink-2);font-weight:600;letter-spacing:.04em}

/* FILTRAR EL HISTORIAL (2026-10-30, `filtro-historial.js`): periodo en
   segmentos, y punto o placa en selectores que salen solo si hay de dónde
   elegir. Vale para «Mis cargas» y para «Pagos».
   ⚠️ `[hidden]` con `!important`: `.mov li` y `.cgs li` no fijan `display`
   hoy, pero el día que una lo haga (un `display:flex` para alinear), la fila
   filtrada volvería a verse sin que nada fallara. */
[data-fecha][hidden],.mov li.mov-dia[hidden]{display:none !important}
.filtro-h[hidden]{display:none}
.filtro-h{display:flex;flex-wrap:wrap;align-items:flex-end;gap:0.625rem 0.875rem;
  padding:0.75rem 1.125rem 0}
.filtro-per{display:inline-flex;padding:0.1875rem;border-radius:0.625rem;
  background:var(--surface-2);border:1px solid var(--line);flex:none;max-width:100%;overflow-x:auto}
.filtro-per button{
  border:0;background:none;color:var(--ink-2);font:inherit;font-size:0.75rem;white-space:nowrap;
  padding:0.375rem 0.75rem;border-radius:0.4375rem;cursor:pointer;
  transition:background .15s,color .15s,box-shadow .15s;
}
.filtro-per button:hover{color:var(--ink)}
.filtro-per button.on{background:var(--surface);color:var(--ink);font-weight:600;
  box-shadow:var(--sombra-flota)}
.filtro-per button:focus-visible{outline:2px solid var(--led);outline-offset:1px}
.filtro-sel{display:flex;flex-direction:column;gap:0.25rem;flex:1 1 9rem;min-width:0;max-width:15rem}
.filtro-sel span{font-size:0.5625rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.filtro-sel select{font:inherit;font-size:0.75rem;padding:0.375rem 0.5rem;border-radius:0.5rem;
  border:1px solid var(--line);background:var(--surface);color:var(--ink);min-width:0}
.filtro-vacio[hidden]{display:none}
.cg{
  display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:0.875rem;
  align-items:center;padding:0.8125rem 0;color:var(--ink);text-decoration:none;
}
.cg:hover{text-decoration:none}
.cg:hover .cg-l b{color:var(--led-bright)}
.cg:focus-visible{outline:2px solid var(--led);outline-offset:2px;border-radius:0.5rem}
.cg-f{display:block;font-size:0.6562rem;color:var(--ink-3);font-family:var(--mono);margin-bottom:3px}
.cg-l b{display:block;font-size:0.8438rem;font-weight:600}
.cg-d{display:block;font-size:0.7188rem;color:var(--ink-3);margin-top:2px}
.cg-d .num{font-family:var(--mono);font-size:0.6562rem;white-space:nowrap}
/* La fecha y el código del conector no se parten: «19/10/2026 · / 05:06» y
   «KW-POB- / 02-C2» en dos renglones se leían como dos datos distintos. */
.cg-f{white-space:nowrap}
.cg-n{text-align:right;white-space:nowrap}
.cg-n b{display:block;font-family:var(--mono);font-size:0.875rem;font-weight:600}
.cg-n b small{font-size:0.6562rem;color:var(--ink-3);font-weight:400}
.cg-n span{display:block;font-size:0.6562rem;color:var(--ink-3);margin-top:2px}
.cg-p{text-align:right;white-space:nowrap;min-width:6rem}
.cg-p b{display:block;font-family:var(--mono);font-size:0.875rem;font-weight:600}
.cg-m{display:block;font-size:0.6562rem;color:var(--ink-3);margin-top:3px}
/* La explicación de PENDING_CDR: cuelga de la fila, no la reemplaza. */
.cg-x{
  margin:0 0 0.8125rem;padding:0.625rem 0.75rem;border-radius:0.625rem;font-size:0.75rem;
  line-height:1.6;color:var(--warn-ink);
  background:rgba(var(--warn-rgb),.08);border:1px solid rgba(var(--warn-rgb),.3);
}
/* La flecha que dice que la fila abre el recibo (2-nov-2026). */
.cgs a.cg{grid-template-columns:minmax(0,1fr) auto auto auto}
/* ⚠️ `a.cg svg.cg-ir` y no `.cg-ir`: el tablero del equipo usa también la clase
   `.cg` y su `.cg svg{width:100%}` (más abajo) le ganaba: la flecha salía gigante. */
a.cg svg.cg-ir{width:1rem;height:1rem;color:var(--ink-3);flex:none;display:block}
a.cg:hover svg.cg-ir{color:var(--led-bright)}
/* Bandas en el escritorio (2-nov-2026): a 1440 el nombre del punto quedaba a la
   izquierda y la energía y el costo a ~1.000 px, sin nada que guiara el ojo. */
@media (min-width:641px){
  .cgs li{padding:0 0.75rem;border-radius:0.5rem}
  .cgs li:nth-child(even of :not([hidden])){background:var(--surface-2)}
}
@media (max-width:640px){
  .cgs a.cg{grid-template-columns:minmax(0,1fr) auto auto;gap:0.5rem 0.75rem}
  .cg-n{grid-column:2;grid-row:1}
  .cg-p{grid-column:2;grid-row:2;text-align:right}
  .cg-l{grid-row:1 / span 2}
  .cg-ir{grid-column:3;grid-row:1 / span 2}
}

/* Los dos atajos del encabezado de Ayuda: la acción, antes de las preguntas. */
.ayu-atajos{display:flex;gap:0.5rem;flex-wrap:wrap;margin:0.875rem 0 0.5rem}
.btn-wa{display:inline-flex;align-items:center;gap:0.4375rem}
/* La placa al pagar: se escribe como se lee en el carro. */
.pagar-placa input{text-transform:uppercase;letter-spacing:.1em;font-family:var(--mono);
  max-width:11rem}
.ayu-atajos-nota{margin:0 0 1.125rem;color:var(--ink-3);font-size:0.75rem}

/* ⚠️ `scroll-margin-top` o el salto de ancla mete el título de la sección
   DEBAJO de la barra de arriba, que es `position:sticky`: el navegador alinea
   el borde del destino con el borde de la ventana y no descuenta nada. Sin
   esto, «Radicar una PQR» dejaba el formulario empezado a media altura y su
   encabezado tapado. Es la misma trampa que ya mordió con el mapa. */
#escribir,#mis-solicitudes,#comprar,#cookies{scroll-margin-top:4.5rem}
@media (max-width:940px){
  #escribir,#mis-solicitudes,#comprar,#cookies{scroll-margin-top:6.5rem}
}

/* =========================================================================
   EL RECIBO
   ========================================================================= */
.rec-volver{display:inline-block;font-size:0.7812rem;margin-bottom:0.75rem}
.rec-arriba{display:flex;justify-content:space-between;align-items:center;gap:0.75rem;flex-wrap:wrap}
.rec-arriba .rec-volver{margin-bottom:0}
.rec-arriba{margin-bottom:0.75rem}
.rec-imprimir{display:inline-flex;align-items:center;gap:0.4375rem}
/* LA HOJA DE IMPRESIÓN (2026-10-28): fuera lo que no es el documento (la
   navegación, los botones); el pie
   con el vendedor se queda. Los colores los pone `kw-tokens.css` en claro. */
@media print{
  .nav,.tabbar,.rec-arriba,.avisos,.conmuta,#avi-panel{display:none !important}
  /* Y lo que en papel no se toca (2-nov-2026): copiar, reportar, ver el punto y
     «toque la curva». */
  .rec-acc,#rec-copiar,.rec-solo-pantalla,.toast{display:none !important}
  body{background:var(--ground)}
  .wrap,.rec-pag{max-width:none;padding:0}
  .panel,.rec-tres{break-inside:avoid;box-shadow:none}
  .kg-zona,.kg-tip,.kg-cruz,.kg-ojo{display:none}
  *{animation:none !important;transition:none !important}
  .rec-pag .rec-imp-t{display:block}   /* con el padre: la regla base va después */
}
/* El título de la hoja impresa: solo se ve en papel. */
.rec-imp-t{display:none;margin:0 0 0.75rem;padding-bottom:0.5rem;border-bottom:1px solid var(--line);
  font-size:0.8125rem;font-weight:600;color:var(--ink-2)}
/* Cuándo fue la carga, debajo de la dirección. */
.rec-cuando{margin:0.25rem 0 0;font-size:0.8125rem;color:var(--ink-2);font-family:var(--mono)}
.rec-motivo{color:var(--ink-3)}
.rec-falla{display:flex;gap:0.5rem;align-items:flex-start;color:var(--crit-ink-suave)}
.rec-falla svg{flex:none;margin-top:0.125rem}
.rec-cab{display:flex;align-items:flex-start;justify-content:space-between;gap:0.875rem;
  flex-wrap:wrap;margin-bottom:1.125rem}
.rec-cab h1{margin:0;font-size:1.375rem;font-weight:600;letter-spacing:-.02em}

/* LOS TRES NÚMEROS. Van juntos y en una fila porque son UNA historia —se
   autorizó un tope, se cobró lo consumido, el resto no se cobró—; separados en tres tarjetas sueltas
   habría que reconstruir mentalmente la resta. */
.rec-tres{
  display:grid;grid-template-columns:1fr auto 1fr auto 1fr;gap:0.625rem;align-items:center;
  background:var(--surface);border:1px solid var(--line);border-radius:0.875rem;
  padding:1.125rem 1.25rem;margin-bottom:1.125rem;
}
.rec-tres>div{min-width:0}
.rec-tres .l{
  display:block;font-size:0.5938rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;
}
.rec-tres b{display:block;font-family:var(--mono);font-size:1.3125rem;font-weight:600;
  margin:0.3125rem 0 3px;letter-spacing:-.02em}
.rec-tres b.fuerte{color:var(--ink)}
.rec-tres b.vuelve{color:var(--ok)}
.rec-tres .s{display:block;font-size:0.6562rem;color:var(--ink-3)}
.rec-flecha{color:var(--ink-3);font-size:1rem;text-align:center}
@media (max-width:640px){
  /* ⭐ EN UNA FILA TAMBIÉN EN EL TELÉFONO (2-nov-2026): apiladas medían 290 px de
     alto, con el «−» y el «=» solos en su renglón (el «−» suelto se confundía con
     una viñeta). Con la cifra a 1 rem caben las tres a 390 px. */
  .rec-tres{grid-template-columns:minmax(0,1fr) auto minmax(0,1fr) auto minmax(0,1fr);
    gap:0.375rem;padding:0.875rem;align-items:start}
  .rec-tres b{font-size:1rem}
  .rec-tres .rec-flecha{padding-top:1.375rem;text-align:center;width:auto}
  /* ⚠️⚠️ ACÁ NO SE GIRA NADA, Y EL COMENTARIO QUE HABÍA EXPLICA POR QUÉ SE
     GIRABA: «la flecha horizontal apilada queda apuntando al vacío». Era
     cierto **cuando el contenido era una flecha** (`→`), que girada 90° apunta
     hacia abajo y sigue significando lo mismo.

     Pero el contenido dejó de ser una flecha: hoy son los signos de la cuenta,
     `−` y `=`, que son los que dejan ver que **pagado − entregado = devuelto**.
     Girados 90° se convierten en `|` y `||` — dos palitos que no significan
     nada, y la aritmética del recibo deja de leerse. Medido en un teléfono: el
     recibo mostraba «PAGADO | ENTREGADO || DEVUELTO».

     La regla seguía aplicando, con el valor equivocado: no falla, no avisa, y
     solo se ve mirando. Un `−` y un `=` se leen igual apilados que en fila, así
     que no hay nada que girar. */
  .rec-flecha{text-align:left;width:1rem}
}

.rec-t{width:100%;border-collapse:collapse}
.rec-t td{padding:0.5625rem 0;border-bottom:1px solid var(--line-soft);font-size:0.8125rem}
.rec-t td.n{text-align:right;font-family:var(--mono);white-space:nowrap}
/* ⚠️⚠️ «Cómo terminó» es una FRASE, no una cifra, y con el `nowrap` de `td.n`
   no se podía partir: «Se entregó todo lo que pagó» ensanchaba la tabla más
   que su panel (que recorta), y a 360 px el recibo decía «$48.8» donde la carga
   costó $48.803 —y «Visa ···· 18», «32,53 k»—. Una cifra de plata cortada es
   otro número. La frase se parte; las cifras siguen enteras. */
.rec-t td.rec-frase{text-align:right;overflow-wrap:anywhere}
.rec-t td:first-child{padding-right:0.75rem}
.rec-t .rec-total td{border-bottom:0;padding-top:0.75rem;font-weight:600;font-size:0.9375rem}
.rec-nota{font-size:0.7188rem;color:var(--ink-3);line-height:1.65;margin:0.75rem 0 0}

.rec-cont{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0.625rem}
.rec-cont>div{background:var(--surface-2);border:1px solid var(--line);border-radius:0.625rem;
  padding:0.625rem 0.6875rem;min-width:0}
.rec-cont .l{display:block;font-size:0.5625rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;margin-bottom:0.3125rem}
.rec-cont b{font-family:var(--mono);font-size:0.8125rem;font-weight:600;overflow-wrap:anywhere}
.rec-cont b.fuerte{color:var(--led-bright)}

.rec-datos{display:grid;grid-template-columns:repeat(auto-fit,minmax(9.375rem,1fr));gap:0.625rem;
  margin-top:0.875rem}
.rec-datos>div{min-width:0}
.rec-datos span{display:block;font-size:0.5625rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;margin-bottom:3px}
.rec-datos b{font-size:0.7812rem;font-weight:600;overflow-wrap:anywhere}
.rec-datos b.num{font-family:var(--mono);font-size:0.7188rem}

.rec-id{display:flex;align-items:center;gap:0.625rem;flex-wrap:wrap;
  background:var(--surface-2);border:1px solid var(--line);border-radius:0.625rem;padding:0.6875rem 0.8125rem}
.rec-id code{font-family:var(--mono);font-size:0.75rem;color:var(--ink);overflow-wrap:anywhere;
  flex:1;min-width:0}
.rec-acc{display:flex;gap:0.5rem;margin-top:0.875rem;flex-wrap:wrap}

/* =========================================================================
   LA BARRA DE ABAJO — cinco pestañas con el QR al centro
   -------------------------------------------------------------------------
   Solo en teléfono. En escritorio la barra de arriba ya tiene todo a la vista y
   una franja fija abajo sería pantalla robada sin ganar nada.

   ⚠️ Es `position:fixed`, así que TAPA el final del contenido: por eso `main`
   reserva su alto abajo. Sin esa reserva, el último movimiento de la lista de
   pagos queda debajo de la barra y se lee como que la lista se cortó.
   ========================================================================= */
.tabbar{display:none}

@media (max-width:940px){
  :root{--tabbar:3.75rem}

  /* ⚠️⚠️ LA BARRA DE ARRIBA SE DESBORDABA ENTRE 641 Y ~900 px, Y ES EL MISMO
     DEFECTO QUE YA SE HABÍA ARREGLADO UNA VEZ.
     El arreglo de «el navbar se pisaba» (ver el bloque de 640 px más abajo)
     quedó encerrado en `max-width:40rem`. Pero `.nav` tiene alto FIJO y sus
     enlaces hacen `flex-wrap`, así que entre 641 y ~900 —tablet, teléfono
     acostado, ventana angosta— los que no caben se salen de la caja: con una
     cuenta de operador, que tiene siete enlaces, «Insumos» aparecía DEBAJO de
     la línea divisoria, encima del saludo, y la primera fila se iba por arriba.
     Un operador lo veía siempre; nadie lo vio porque `capturar_temas.py` miraba
     a 1440 px.

     Va en ESTE bloque y no en el de 640 a propósito: 940 es el umbral de la
     barra de abajo, o sea donde esta app deja de ser de escritorio. Los dos
     describen el mismo hecho, así que tienen que ser el mismo número — tenerlos
     distintos es lo que abrió la franja rota. */
  .nav{height:auto;flex-wrap:wrap;padding:0.5625rem 0.875rem;gap:0.5rem}
  .nav-links{
    order:3;flex-basis:100%;flex-wrap:nowrap;gap:0.375rem;
    overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;
  }
  .nav-links::-webkit-scrollbar{display:none}
  /* Los enlaces que ya están en la barra de pestañas de abajo no se repiten
     acá: ese renglón es angosto y lo que sobra echa fuera a lo que no está
     abajo — que fue lo que pasó con «Ayuda y PQR». Ver el comentario de
     `nav-dup` en `base.html`. */
  .nav-links a.nav-dup{display:none}
  .nav-yo .nav-cargar-tab{display:none}
  .nav-yo{margin-left:auto}

  .tabbar{
    display:grid;grid-template-columns:repeat(5,1fr);align-items:end;
    position:fixed;left:0;right:0;bottom:0;z-index:950;
    height:calc(var(--tabbar) + env(safe-area-inset-bottom,0px));
    padding-bottom:env(safe-area-inset-bottom,0px);
    background:var(--nav-bg);backdrop-filter:blur(0.75rem);
    border-top:1px solid var(--line);
  }
  .tabbar a{
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:3px;height:var(--tabbar);color:var(--ink-3);text-decoration:none;
    font-size:0.625rem;font-weight:500;
    -webkit-tap-highlight-color:transparent;
  }
  .tabbar a:hover{text-decoration:none}
  .tabbar a svg{width:1.3125rem;height:1.3125rem}
  /* Dónde estoy parado. El color solo no alcanza —hay quien no lo distingue—
     así que además engorda el trazo: dos señales, no una. */
  .tabbar a.on{color:var(--led-bright);font-weight:600}
  .tabbar a.on svg{stroke-width:2.2}
  .tabbar a:focus-visible{outline:2px solid var(--led);outline-offset:-3px;border-radius:0.625rem}

  /* El QR no es una pestaña más: es LA acción, y se levanta por encima de la
     barra como en EVX. */
  .tabbar .tab-qr{gap:2px;justify-content:flex-end;padding-bottom:0.375rem}
  .tab-qr-b{
    display:grid;place-items:center;width:3.25rem;height:3.25rem;border-radius:1.0625rem;
    background:linear-gradient(180deg,var(--led),var(--led-hondo));
    /* ⚠️ Blanco fijo, no `var(--ink)`. El fondo es azul de marca en los DOS
       temas; una tinta que sigue al tema deja el ícono NEGRO sobre azul en
       claro — la trampa de `.vivo` y `.perf-av` (CLAUDE.md §4). Misma excepción
       y mismo motivo que `.btn.pri`. */
    color:#fff;
    box-shadow:var(--sombra-flota);
    margin-top:-22px;transition:transform .12s ease;
  }
  .tab-qr:active .tab-qr-b{transform:translateY(1px) scale(.97)}
  .tab-qr-b svg{width:1.5625rem;height:1.5625rem}
  .tab-qr-t{font-size:0.625rem;color:var(--ink-3);font-weight:600}

  /* El sitio que la barra ocupa.
     ⚠️⚠️ Acá decía «en `main` porque el pie va adentro», y el pie NO va
     adentro: en `base.html` el orden es `main` → `.tabbar` → `footer.pie`. La
     reserva quedaba como un hueco en blanco ANTES del pie, y el final del pie
     —Política de datos, «Ayuda y PQR» y el enlace a la SIC que exige el art. 50
     de la Ley 1480— quedaba debajo de la barra, sin forma de destaparlo. En las
     18 pantallas con sesión, medido el 2026-10-19 a 360, 390, 412, 768 y 820.
     Ahora: `main` reserva solo cuando NO lo sigue un pie (el QR y el mapa
     anulan el bloque `pie`); cuando hay pie, la reserva la lleva el pie, que
     es lo último de la página. Sin `:has()` queda el hueco de antes, pero el
     pie igual se lee entero. */
  main{padding-bottom:calc(var(--tabbar) + env(safe-area-inset-bottom,0px))}
  main:has(~ .pie){padding-bottom:0}
  .tabbar ~ .pie{padding-bottom:calc(1rem + var(--tabbar) + env(safe-area-inset-bottom,0px))}

  /* ⚠️⚠️ Y LO QUE FLOTA NO ESTÁ EN `main`, ASÍ QUE ESA RESERVA NO LO ALCANZA.
     `.toast` y `.kw-cinta` son `position:fixed` con `bottom:1.125rem`: en un
     teléfono eso cae DENTRO de los 60 px de la barra. Y como las dos tienen
     más `z-index` que ella (2000 y 1150 contra 950), no quedan tapadas —
     quedan ENCIMA, cubriendo las pestañas.

     Con un toast se nota poco porque se va solo. Con `.kw-cinta` no: es la
     cinta de «volver a la carga» y se queda **todo lo que dure la carga**, que
     es justo cuando la persona más necesita la barra para moverse por la app.

     No se vio antes porque `capturar_temas.py` miraba la app a 1440 px, donde
     la barra no existe. Hoy tiene `--movil`. */
  .toast{bottom:calc(var(--tabbar) + 1.125rem + env(safe-area-inset-bottom,0px))}
  /* ⚠️ La cinta de «tiene una carga andando» se sube POR ENCIMA del conmutador
     lista/mapa, que en el teléfono flota a `var(--tabbar) + 0.875rem` a la
     derecha. La cinta va centrada y es ancha —«Está cargando en KW Oviedo ·
     Ver»—, así que a la misma altura se pisan sí o sí. Apiladas se leen las
     dos, y la de la carga queda arriba, que es la que no se puede perder.
     ⚠️ Y el ajuste va ACÁ y no en el bloque del mapa, aunque sea de esa
     pantalla: este `@media` está más abajo en el archivo y, con la misma
     especificidad, manda el último (CLAUDE.md §4). Escrito allá no habría
     hecho nada. */
  .kw-cinta{bottom:calc(var(--tabbar) + 4.25rem + env(safe-area-inset-bottom,0px))}
}


/* ===========================================================================
   EL TABLERO DE UN EQUIPO  (/equipo/<id>, 2026-10-16)

   Pedido: «un dashboard completo del estado del equipo, muy interactivo, y en
   el centro una imagen de un cargador vibrante y dinámica».

   ⚠️ Ni un color escrito acá. El dibujo entero se pinta con tokens, y el
   estado solo cambia DOS variables locales —`--cg-vivo` y `--cg-glow`—; todo
   lo demás las hereda. Es el mismo patrón de `.vivo` y `.perf-av`: redefinir
   el token dentro del bloque en vez de repintar regla por regla.
   =========================================================================== */

.eq-cab{margin:0 0 1rem}
.eq-volver{font-size:0.75rem;color:var(--ink-3)}
.eq-volver:hover{color:var(--led-bright)}
.eq-cab h1{margin:0.25rem 0 0;font-family:var(--mono)}

.eq-tablero{display:grid;grid-template-columns:15rem 1fr;gap:1.5rem;align-items:start}
/* ⚠️ En teléfono el dibujo va ARRIBA y en una tira baja: a 15 rem de ancho
   fijo empujaba los datos a una columna de dos palabras. */
@media (max-width:760px){
  .eq-tablero{grid-template-columns:1fr}
  /* ⚠️⚠️ ACOTADO A `.eq-tablero`: `.cg` es TAMBIÉN la fila del historial de
     «Mis cargas» (`cargas.html`). Suelta, esta regla encogía cada fila a 11 rem
     centrados en todo teléfono: el punto en tres renglones («KW / El /
     Poblado») y el conector en cuatro. Mismo choque que `--desliz` y `.n`. */
  .eq-tablero .cg{max-width:11rem;margin:0 auto}
}
/* En el teléfono el dibujo ocupaba ~300 px antes de la pastilla y los datos
   («Desde», «Causa vigente») quedaban bajo el pliegue (revisión del
   2026-11-02). Ahora va chico, a la derecha de la pastilla, y los datos debajo
   a todo el ancho: el estado se sigue leyendo de un vistazo. `display:contents`
   deja que la pastilla y los datos se acomoden en la grilla del tablero. */
@media (max-width:640px){
  .eq-tablero{grid-template-columns:minmax(0,1fr) 5.5rem;grid-template-areas:"est dib" "datos datos";
    gap:0.5rem 1rem;align-items:center}
  .eq-tablero .eq-estado{display:contents}
  .eq-tablero .eq-estado>div:first-child{grid-area:est;margin:0}
  .eq-tablero .eq-estado>.red-f-datos{grid-area:datos;margin:0}
  .eq-tablero .cg{grid-area:dib;max-width:none;width:5.5rem;margin:0}
}
/* Cuando el equipo no existe o no cargó, el cuerpo dice qué pasó y la salida. */
.eq-cab h1.eq-h1-texto{font-family:inherit}
.eq-nohay p{margin:0 0 0.875rem;font-size:0.875rem;color:var(--ink-2)}
.eq-nohay-acc{display:flex;gap:0.5rem;flex-wrap:wrap}

/* --- el cargador ------------------------------------------------------- */
.cg{
  --cg-vivo:var(--ink-3);          /* el color del estado: gris mientras no se sabe */
  --cg-glow:0;                     /* 0 apagado, 1 encendido */
}
.cg.en-linea{--cg-vivo:var(--vivo);--cg-glow:1}
.cg.cargando{--cg-vivo:var(--led);--cg-glow:1}
.cg.falla{--cg-vivo:var(--crit);--cg-glow:1}
.cg svg{width:100%;height:auto;display:block}

.cg-halo{
  fill:var(--cg-vivo);opacity:calc(0.07 * var(--cg-glow));
  transition:opacity .4s;
}
.cg.en-linea .cg-halo,.cg.cargando .cg-halo{animation:cg-respira 3.2s ease-in-out infinite}
.cg-sombra{fill:var(--ink);opacity:.07}

/* El cuerpo: cabeza ancha + columna angosta + zócalo. Un cargador rápido se
   reconoce por esa silueta, no por una caja con un rectángulo adentro. */
.cg-base{fill:var(--deep);opacity:.75}
.cg-columna{fill:var(--surface-2);stroke:var(--line);stroke-width:2}
.cg-cabeza{fill:var(--surface);stroke:var(--line);stroke-width:2}
/* ⚠️ La visera lleva el AZUL DE MARCA siempre, en los dos temas y en todos
   los estados. Es lo que saca al dibujo del gris sin mentir sobre si el
   equipo está vivo: lo que informa es el LED, no la carcasa. */
.cg-visera{fill:var(--led)}
.cg.falla .cg-visera{fill:var(--crit)}

/* La pantalla: fondo azul profundo —como una pantalla apagada de verdad— y
   encima el medidor, que es lo que se enciende. */
.cg-pant{fill:var(--deep);opacity:calc(0.55 + 0.45 * var(--cg-glow))}
.cg-aguja{fill:none;stroke:var(--led-pale);stroke-width:4;stroke-linecap:round;opacity:.35}
.cg-aguja2{
  fill:none;stroke:var(--cg-vivo);stroke-width:4;stroke-linecap:round;
  opacity:calc(0.25 + 0.75 * var(--cg-glow));
}
.cg-barra{fill:var(--led-pale);opacity:.3}
.cg-barra2{fill:var(--cg-vivo);opacity:calc(0.25 + 0.75 * var(--cg-glow))}
.cg.en-linea .cg-aguja2,.cg.cargando .cg-aguja2{animation:cg-led 2.6s ease-in-out infinite}
/* El rayo de la pantalla: la señal inequívoca de que está cargando. */
.cg-rayo{fill:var(--cg-vivo);opacity:0}
.cg.cargando .cg-rayo{opacity:1;animation:cg-led 1.1s ease-in-out infinite}
.cg.cargando .cg-aguja,.cg.cargando .cg-aguja2,.cg.cargando .cg-barra,
.cg.cargando .cg-barra2{opacity:0}
.cg-lector{fill:var(--line)}
.cg-piloto{fill:var(--cg-vivo);opacity:calc(0.3 + 0.7 * var(--cg-glow))}

/* La línea LED quebrada del manual (§6): lo único que de verdad dice el
   estado, y por eso es lo único que late. */
.cg-led{
  fill:none;stroke:var(--cg-vivo);stroke-width:6;
  stroke-linecap:round;stroke-linejoin:round;
  opacity:calc(0.3 + 0.7 * var(--cg-glow));
}
.cg.en-linea .cg-led,.cg.cargando .cg-led{animation:cg-led 2.6s ease-in-out infinite}

/* ⚠️ El cable va en `--ink-3`, no en `--line`. Con el gris de las
   separaciones quedaba tan claro que no se leía como un cable: parecía el
   contorno del dibujo, y los conectores al final flotaban sueltos. Un cable
   tiene que verse como un cable o sobra. */
.cg-cable{fill:none;stroke:var(--ink-3);opacity:.55;stroke-width:9;stroke-linecap:round}
.cg-plug{fill:var(--ink-3)}
.cg.en-linea .cg-plug,.cg.cargando .cg-plug{fill:var(--cg-vivo)}

/* ⚠️⚠️ LA ENERGÍA CORRIENDO POR EL CABLE, EN TRES NIVELES.
   Reportado así: «muy sencillo, solo agregaste color; me gustaría algo modo
   GIF, que tenga cables y se vea el cambio de color simulando que pasa
   energía». Y había una razón de fondo para que no se viera NUNCA: el flujo
   solo se animaba con una carga viva, y en la demo hay cero.
   Entonces son tres estados, y cada uno dice algo distinto de verdad:
     · apagado   — quieto y gris: no hay energía
     · EN LÍNEA  — pulsos lentos y tenues: hay energía disponible, esperando
     · CARGANDO  — pulsos rápidos y brillantes, con resplandor: está saliendo
   El «cambio de color» es literal: los pulsos se dibujan ENCIMA del cable
   gris, así que uno ve el color recorriéndolo.

   ⚠️ El salto del `dashoffset` tiene que ser EXACTAMENTE un periodo del
   patrón (12 + 28 = 40). Con cualquier otro número el bucle da un tirón al
   reiniciarse, y ese tirón se lee como que la pantalla se trabó. */
.cg-flujo{
  fill:none;stroke:var(--cg-vivo);stroke-width:7;stroke-linecap:round;
  stroke-dasharray:12 28;opacity:0;
}
/* ⚠️ EN LÍNEA TAMBIÉN SE MUEVE, Y SE VE. Reportado dos veces: «sigo sin
   verlo bien», «yo quiero aquí un GIF». Y era cierto: a opacidad 0,55 y 2,8 s
   el pulso existía pero nadie lo notaba, y el estado «cargando» —el único
   bien visible— NO SE VE NUNCA porque la demo no tiene cargas vivas.
   Un equipo en servicio está energizado de verdad: que se le vea la corriente
   esperando no es mentir, es lo que pasa. Lo que no puede pasar es que se
   confunda con estar cargando — por eso abajo el de carga cambia de COLOR,
   de velocidad y enciende el rayo de la pantalla, no solo va más rápido. */
.cg.en-linea .cg-flujo{opacity:.9;stroke-width:8;animation:cg-flujo 1.9s linear infinite}
.cg.cargando .cg-flujo{
  opacity:1;stroke-width:9;animation:cg-flujo 1s linear infinite;
  filter:drop-shadow(0 0 3px var(--cg-vivo));
}
/* El segundo cable va medio ciclo corrido: dos chorros sincronizados al
   milímetro se leen como una animación, no como dos cables. */
.cg.en-linea .cg-flujo:nth-of-type(2){animation-delay:.95s}
.cg.cargando .cg-flujo:nth-of-type(2){animation-delay:.5s}

/* Y la energía también entra por la columna: el LED quebrado recorre el mismo
   pulso hacia abajo cuando está cargando. */
.cg-led-flujo{
  fill:none;stroke:var(--cg-vivo);stroke-width:6;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:14 36;opacity:0;
}
.cg.en-linea .cg-led-flujo{opacity:.75;animation:cg-led-flujo 2.2s linear infinite}
.cg.cargando .cg-led-flujo{
  opacity:1;animation:cg-led-flujo 1.2s linear infinite;
  filter:drop-shadow(0 0 3px var(--cg-vivo));
}

/* El medidor de la pantalla barre mientras hay carga. */
.cg.cargando .cg-aguja2{animation:cg-barrido 2.6s ease-in-out infinite}

@keyframes cg-respira{0%,100%{opacity:calc(0.05 * var(--cg-glow))}
                      50%    {opacity:calc(0.13 * var(--cg-glow))}}
@keyframes cg-led    {0%,100%{opacity:1} 50%{opacity:.55}}
@keyframes cg-flujo  {from{stroke-dashoffset:40} to{stroke-dashoffset:0}}
@keyframes cg-led-flujo{from{stroke-dashoffset:50} to{stroke-dashoffset:0}}
/* El barrido del medidor: de un cuarto de arco a casi entero y vuelta. */
@keyframes cg-barrido{0%,100%{stroke-dasharray:14 100} 50%{stroke-dasharray:52 100}}

/* ⚠️ Quien pidió menos movimiento no quiere un cargador latiendo toda la
   jornada. Se apaga el movimiento y se deja el COLOR, que es lo que informa. */
@media (prefers-reduced-motion:reduce){
  /* ⚠️ TODAS, no tres. Faltaban `cg-led-flujo`, `cg-rayo` y `cg-aguja2`, así
     que con las animaciones apagadas el dibujo quedaba a medio camino: unas
     cosas quietas y otras moviéndose. */
  .cg-halo,.cg-led,.cg-flujo,.cg-led-flujo,.cg-rayo,.cg-aguja2{animation:none!important}
  /* Sin movimiento, lo que informa es el color y el contraste: el pulso se
     deja fijo y bien visible en vez de desaparecer. */
  .cg.en-linea .cg-flujo,.cg.cargando .cg-flujo{opacity:1}
  .cg.cargando .cg-rayo{opacity:1}
}

/* --- el resto del tablero ---------------------------------------------- */
/* «sin reportar» es un dato que pide atención, no uno más. */
.eq-mudo{color:var(--warn)}
.eq-estado>div:first-child{margin-bottom:0.75rem}
.eq-con{
  display:flex;align-items:center;gap:0.625rem;flex-wrap:wrap;
  padding:0.625rem 0.75rem;border-radius:0.5625rem;background:var(--surface-2);
  margin-bottom:0.375rem;box-shadow:inset 3px 0 0 var(--line);
}
.eq-con.cargando{box-shadow:inset 3px 0 0 var(--led)}
.eq-con.libre{box-shadow:inset 3px 0 0 var(--vivo)}
.eq-con>b{font-size:0.8125rem;color:var(--ink)}
.eq-con .nota{margin-left:auto}
.eq-hist{display:grid;gap:0.25rem}
.eq-hev{
  display:grid;grid-template-columns:9.5rem 1fr;gap:0.75rem;
  padding:0.4375rem 0;border-bottom:1px solid var(--line);font-size:0.75rem;
}
.eq-hev:last-child{border-bottom:0}
.eq-hev>span{color:var(--ink-3);font-family:var(--mono);font-size:0.6875rem}

/* La unidad al lado de la cifra en una tarjeta: más chica y en gris, para
   que el número mande y la unidad acompañe. */
.kpi-u{font-size:0.875rem;color:var(--ink-3);font-weight:400}

/* ===========================================================================
   EL PANEL DE MUESTRA  (2026-10-16)

   ⚠️⚠️ Esto existe para que una pantalla que todavía no tiene datos reales
   NO parezca que los tiene. Pedido: «vamos construyendo el HTML, una muestra
   de lo que va ahí, al menos con datos no reales» — y una muestra sin marcar
   es, lisa y llanamente, una pantalla que miente sobre un equipo de verdad.
   El borde punteado y la tinta a media asta son la marca; se quitan el día
   que entren los datos del equipo.
   =========================================================================== */
.eq-muestra{border-style:dashed}

.eq-sub{margin:1.125rem 0 0.5rem;font-size:0.75rem;color:var(--ink-3);font-weight:600}

.eq-fallas{display:grid;gap:0.375rem}
.eq-falla{
  display:grid;grid-template-columns:6.5rem minmax(0,1.2fr) minmax(0,1fr);gap:0.75rem;
  align-items:baseline;
  padding:0.5rem 0.75rem;border-radius:0.5625rem;background:var(--surface-2);
  box-shadow:inset 3px 0 0 var(--line);
}
.eq-falla.crit{box-shadow:inset 3px 0 0 var(--crit)}
.eq-falla.warn{box-shadow:inset 3px 0 0 var(--warn)}
.eq-falla>code{font-size:0.75rem;color:var(--led-bright)}
.eq-falla>b{font-size:0.8125rem;color:var(--ink)}
.eq-falla>span{font-size:0.6875rem;color:var(--ink-3)}
/* ⚠️ En teléfono las tres columnas dejan el código en una tira de cuatro
   letras. Se apilan antes que recortar una palabra. */
@media (max-width:760px){.eq-falla{grid-template-columns:1fr}}

/* Lo que el Titan sabe avisar (2026-10-28): los 51 códigos del manual,
   agrupados. Plegado por omisión: es referencia, no lo que se mira primero. */
.eq-cat{margin-top:1.125rem}
.eq-cat>p.nota{font-size:0.75rem;margin:0.5rem 0 0}
.eq-cat>summary{cursor:pointer;font-size:0.8125rem;font-weight:600;color:var(--ink)}
.eq-grupo{margin-top:0.75rem}
.eq-grupo>h5{margin:0 0 0.375rem;font-size:0.75rem;font-weight:600;color:var(--ink-3)}
.eq-falla.vigente{background:var(--surface-3);outline:1px solid var(--crit)}
.eq-nomanda{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:0.5rem}
.eq-nomanda>div{
  padding:0.5rem 0.75rem;border-radius:0.5625rem;background:var(--surface-2);
  box-shadow:inset 3px 0 0 var(--line);
}
.eq-nomanda b{display:block;font-size:0.8125rem;color:var(--ink)}
.eq-nomanda span{display:block;font-size:0.6875rem;color:var(--ink-3)}

.eq-acciones{display:flex;gap:0.5rem;flex-wrap:wrap}
/* Apagados los cuatro por igual: borde punteado como el panel, tinta tenue y
   sin la opacidad que los volvía casi invisibles (`.eq-falso` × `:disabled`). */
.eq-acciones .btn[disabled]{opacity:1;cursor:not-allowed;background:transparent;
  border-style:dashed;border-color:var(--line);color:var(--ink-3)}

/* ===================== EL CÓDIGO, REHECHO (2026-10-16) =====================
   Reportado: «en la versión web no usa toda la pantalla… y en versión móvil
   revisar». Era una sola columna de 520 px en el centro de un monitor.

   · COMPUTADOR: a la izquierda DÓNDE está (punto, enchufe, precio); a la
     derecha QUÉ HACER. Al pagar, lo que se elige y el resumen con el botón van
     lado a lado, y el resumen se queda quieto mientras se elige: lo que se
     acepta está a la vista al tocar el botón.
   · TELÉFONO: una columna como siempre, pero el botón de pagar se PEGA ABAJO
     (encima de la barra de pestañas). Antes quedaba tres pantallas más abajo,
     después de los montos, los medios y dos párrafos.
   ⚠️ Ni un color a mano: todo sale de `kw-tokens.css`.
   ========================================================================= */
.cod-lado,.cod-accion{display:flex;flex-direction:column;gap:0.875rem;min-width:0}
.pagar-elegir,.pagar-cierre,.pagar-pie{display:flex;flex-direction:column;gap:0.75rem;min-width:0}

@media (min-width:960px){
  .cod-pag{max-width:78rem;padding:2rem 2rem 3rem}
  .cod-dos{display:grid;grid-template-columns:minmax(17rem,23rem) minmax(0,1fr);
    gap:2.5rem;align-items:start}
  .cod-escena .il{max-width:100%}
  .cod-donde h1{font-size:1.625rem}
  .cod-accion{padding-top:0.5rem}
  /* Los medios de pago en rejilla: en una sola columna de 50 rem cada
     tarjeta era una franja casi vacía. */
  .pagar-medios{display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr))}
}
@media (min-width:1180px){
  .cod-pagando .pagar{display:grid;grid-template-columns:minmax(0,1fr) minmax(17rem,21rem);
    gap:1.75rem;align-items:start}
  .pagar-cierre{
    position:sticky;top:calc(var(--nav) + 1rem);
    padding:1rem;border-radius:0.875rem;background:var(--surface);border:1px solid var(--line);
    box-shadow:var(--sombra-flota);
  }
  .pagar-cierre .pagar-h{margin-top:0}
}
/* ⚠️⚠️ Este tramo llegaba hasta 959 px y el resumen fijo de arriba empieza en
   1180: entre los dos —el iPad acostado, 1024— ni el botón ni el resumen se
   pegaban, y «Pagar» quedaba bajo el pliegue. Tienen que ser el mismo número
   menos uno, o se abre la franja rota otra vez.
   ⚠️⚠️ Y el selector nombraba `#pg-ir` y `#dat-ir`, pero el botón del pago SIN
   CUENTA es `#an-ir`: en el camino que abre el QR para quien no tiene cuenta
   —el que exige la Res. 40123 art. 6— «Pagar» quedaba a 1.940 px en un
   teléfono de 844, dos pantallas abajo. Medido el 2026-10-19. */
@media (max-width:1179px){
  /* ⚠️ `display:contents` y no un sticky sobre el resumen: un `position:sticky`
     solo se pega DENTRO de su padre. Con el botón dentro de `.pagar-cierre`
     —que está al final— se habría pegado solo cuando ya se veía. Disuelto el
     contenedor, el padre del botón es el formulario entero. */
  .pagar-cierre{display:contents}
  .pagar-cierre>.pagar-h{display:none}
  /* ⚠️⚠️ SE PEGA EL PIE ENTERO, NO EL BOTÓN SOLO (2026-11-02, revisión de diseño, graves B7 y
     B8). Antes se pegaba `#pg-ir`/`#an-ir` y el resumen («Paga ahora · Alcanza para · Lo que
     sobre vuelve a») y la frase «Al tocarlo acepta…» quedaban dos pantallas más abajo: se podía
     aceptar un cobro sin ver qué se aceptaba. Sin cuenta, esa frase es la única aceptación de los
     Términos y la Política. `.pagar-pie` (codigo.html) junta resumen, error, botón y aceptación,
     y su padre es el formulario porque `.pagar-cierre` está disuelto.
     ⚠️ Sigue el halo del color del fondo (2026-10-21): flotando a secas, las tarjetas de monto
     asomaban por los lados y no se sabía si el dedo caía en el botón, que COBRA, o detrás. */
  .pagar-pie{
    position:sticky;z-index:5;gap:0.5rem;
    bottom:calc(var(--tabbar,0px) + env(safe-area-inset-bottom,0px));
    padding:0.5rem 0 0.625rem;background:var(--ground);border-top:1px solid var(--line);
    box-shadow:0 0 0 0.625rem var(--ground);
  }
  /* El resumen, en dos renglones y no en una caja de tres filas: tiene que caber con el botón y
     la aceptación sin taparle media pantalla a lo que se está eligiendo. */
  .pagar-pie .pagar-res{flex-direction:row;flex-wrap:wrap;gap:0.1875rem 1rem;padding:0.4375rem 0.75rem}
  .pagar-pie .pagar-res>div{justify-content:flex-start;gap:0.375rem}
  .pagar-pie .pagar-res b{font-size:0.8125rem}
  .pagar-pie .cod-nota{line-height:1.45}
  /* ⚠️ El botón «Cargar» de la barra de pestañas sube 22 px por encima de ella y se
     montaba sobre el borde de abajo del botón que cobra: tocar ahí abría el lector
     de QR (2026-11-02). El pie lleva ese alto de relleno abajo. Y sin sesión no hay
     barra: el pie va al borde, no a 60 px de él. */
  body:has(.tabbar) .pagar-pie{padding-bottom:calc(0.625rem + 22px)}
  body:not(:has(.tabbar)) .pagar-pie{bottom:env(safe-area-inset-bottom,0px)}
}

/* Con el formulario de Wompi abierto (`pasarela.js`, `volverAlCerrar`): lo de
   KW se recorta al alto que Wompi le puso al `body`, para que la página se
   desplace hasta el final del formulario y no siga de largo detrás del velo.
   ⚠️ El `overflow` del `html` NO es para bloquear: es lo que impide que el
   `clip` del `body` suba a la ventana (si el `html` es `visible`, el
   `overflow` del `body` se le pasa a la ventana y la página entera dejaría de
   desplazarse, que es justo lo que dejó el pago «más pegado»). */
html.kw-wompi-abierto{overflow-y:auto}
html.kw-wompi-abierto body{overflow:clip}

/* En el teléfono, la barra de arriba y la de abajo sin desenfoque: el
   `backdrop-filter` de dos capas fijas se recalcula en CADA cuadro del
   desplazamiento, y en un Android de gama media es lo que lo vuelve pesado.
   ⚠️ Sin desenfoque, el 8 % transparente de `--nav-bg` dejaba leer el título
   de la página detrás del logo: se compone el mismo token sobre `--ground`,
   que da el mismo tono pero opaco, sin escribir un color nuevo. */
@media (max-width:940px){
  .nav,.tabbar{
    backdrop-filter:none;-webkit-backdrop-filter:none;
    background:linear-gradient(var(--nav-bg),var(--nav-bg)),var(--ground);
  }
}

/* ------------------------- LA CARGA EN CURSO ------------------------------ */
.cod-carga{
  display:flex;flex-direction:column;gap:1rem;padding:1.125rem;border-radius:1rem;
  background:var(--surface);border:1px solid var(--line);
}
.cod-carga-h{display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem 1.125rem}
.cod-estado{font-size:0.75rem;padding:0.3125rem 0.75rem}
.cod-dato{font-size:0.75rem;color:var(--ink-3);white-space:nowrap}
.cod-dato b{font-family:var(--mono);font-size:0.9375rem;font-weight:700;color:var(--ink)}

/* ⚠️ Acá vivía `.cod-barra`, la barra de la carga en curso, que reemplazó el anillo
   (`.cx-anillo`, 2026-10-27). Se quitó el 2026-11-02: no la usaba nadie y animaba `width`. */
.cod-medidor-pie{display:flex;justify-content:space-between;gap:0.75rem;margin-top:0.5rem;
  font-size:0.75rem;color:var(--ink-3)}
.cod-medidor-pie b{font-family:var(--mono);font-size:0.9375rem;color:var(--ink)}
.cod-medidor-pie span:last-child{font-family:var(--mono);font-weight:700;color:var(--led-bright)}

.cod-carga .cod-vivo>div{background:var(--surface-2)}
.cod-carga .cod-vivo small{font-size:0.6875rem}
.cod-estima{margin:0;font-size:0.75rem;line-height:1.55;color:var(--ink-2)}
.cod-estima:empty{display:none}

/* Terminar: borde rojo y fondo limpio. Lleno de rojo, en grande, era lo que
   más gritaba de la pantalla — y es lo que menos se quiere tocar sin querer. */
.btn.gh-stop{background:transparent;border-color:rgba(var(--crit-rgb),.45);color:var(--crit-ink)}
.btn.gh-stop:hover:not(:disabled){background:rgba(var(--crit-rgb),.08)}
.cod-terminar[hidden],.cod-confirma[hidden]{display:none}
.cod-confirma{
  display:flex;flex-direction:column;gap:0.75rem;padding:0.9375rem;border-radius:0.875rem;
  background:rgba(var(--crit-rgb),.06);border:1px solid rgba(var(--crit-rgb),.4);
}
.cod-confirma p{margin:0;font-size:0.8125rem;line-height:1.55;color:var(--ink-2)}
.cod-confirma p b{font-family:var(--mono);color:var(--ink)}
.cod-confirma-b{display:grid;grid-template-columns:1fr 1fr;gap:0.5rem}
.cod-confirma-b .btn{min-height:2.875rem}

@media (min-width:960px){
  .cod-carga{padding:1.5rem;gap:1.25rem}
  .cod-carga .cod-vivo b{font-size:1.75rem}
  .cod-barra{height:1.25rem}
}
/* En el teléfono la ilustración ocupaba media pantalla y empujaba abajo lo que
   se vino a mirar: los montos al pagar, y la barra con la carga andando. El
   estado lo sigue diciendo igual de grande la píldora del enchufe. */
@media (max-width:959px){
  .cod-escena .il{max-width:14rem}
  .cod-dos:has(.cod-carga) .cod-escena .il{max-width:10rem}
}

/* ===== UN IMPEDIMENTO ES UNA PANTALLA (2026-10-28): el pago que no pasó ====
   Ventana con su dibujo (`impedimento()` en codigo.html), sobre el velo
   difuminado de `.cod-bg`. Entra con un rebote corto. */
.imp-bg{z-index:1300}
.cod-modal.imp{
  width:min(28rem,100%);padding:1.25rem 1.25rem 1.125rem;text-align:center;
  animation:imp-entra .24s cubic-bezier(.34,1.56,.64,1) both;
}
.imp-escena{display:block;width:100%;height:auto;margin:0 0 0.75rem}
.imp h2{margin:0 0 0.375rem;font-size:1.25rem;font-weight:600;line-height:1.25;color:var(--ink);text-wrap:balance}
.imp p{margin:0 0 1.125rem;font-size:0.8125rem;line-height:1.55;color:var(--ink-2);text-wrap:pretty}
.imp-b{display:grid;gap:0.5rem}
@keyframes imp-entra{from{opacity:0;transform:translateY(0.5rem) scale(.97)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){ .cod-modal.imp{animation:none} }

/* ================= LA CARGA COMO RECORRIDO (2026-10-27) ===================
   «Eso es pura experiencia»: una pantalla por fase (pagado → iniciando →
   cargando → terminando → terminada), con el anillo de lo pagado, la curva de
   potencia y un dibujo en cada fase sin anillo. Lo mueve `carga-en-vivo.js`
   cambiando `data-fase` en `.cx`; acá solo se dice qué se ve en cada una.
   ⚠️ Ni un color a mano: todo sale de `kw-tokens.css`. La única excepción es
   el visto blanco sobre el círculo azul de un paso hecho (texto sobre relleno
   de marca, `marca-kw` §1).
   ⚠️⚠️ `hidden` PIERDE contra un `display` propio (ya pasó con los dos
   resúmenes de pago): todo lo que se esconde con `hidden` y tiene `display`
   lleva su `[hidden]{display:none}`.                                        */
.cod-medio{
  display:flex;justify-content:space-between;align-items:baseline;gap:0.75rem;
  padding:0.75rem 0.875rem;border-radius:0.75rem;background:var(--surface);
  border:1px solid var(--line);font-size:0.75rem;color:var(--ink-3);
}
.cod-medio b{font-family:var(--mono);font-size:0.875rem;color:var(--ink);text-align:right}
/* En el teléfono, primero la carga y después el lugar: es lo que se vino a mirar. */
@media (max-width:959px){ .cod-mia .cod-accion{order:-1} }

/* ⭐ EL COLOR DE CADA FASE. Pedido: «que se ponga colores cuando se carga,
   cuando se enchufa… eso es pura experiencia». Toda la pantalla de la carga
   —el resplandor, el borde, el anillo, los pasos, la curva, el dibujo y la
   tarjeta del conector— toma el color de la fase y PASA de uno a otro:
     pagado · iniciando · terminando   azul de la marca (esperando)
     cargando                          verde que se funde en azul (fluye energía)
     en pausa                          ámbar (⚠️ no rojo: casi siempre es el carro lleno)
     terminada                         verde
   ⚠️ Son los tokens de estado de `kw-tokens.css`, nunca un color nuevo: así
   valen en los dos temas. Y `@property` es lo que deja TRANSICIONAR una
   variable de color (sin él cambia de golpe, que también sirve). El valor
   inicial `transparent` no se ve nunca: la fase sale pintada del servidor. */
@property --cx-c{syntax:'<color>';inherits:true;initial-value:transparent}
@property --cx-c2{syntax:'<color>';inherits:true;initial-value:transparent}
.cod-mia{--cx-c:var(--led);--cx-c2:var(--led-bright);
  transition:--cx-c .3s ease,--cx-c2 .3s ease}   /* el color de la fase, en 300 ms (era 900) */
.cod-mia:has(.cx[data-fase="carga"]){--cx-c:var(--ok);--cx-c2:var(--led)}
.cod-mia:has(.cx[data-fase="pausa"]){--cx-c:var(--warn);--cx-c2:var(--warn)}
.cod-mia:has(.cx[data-fase="fin"]){--cx-c:var(--ok);--cx-c2:var(--ok)}
.cod-mia .cod-conector.mio{border-left-color:var(--cx-c)}

/* `scroll-margin-top`: al terminar, la vista sube al título; sin el margen
   quedaba debajo de la barra de arriba, con el recorrido y el dibujo tapados. */
.cx{
  position:relative;gap:1.25rem;scroll-margin-top:calc(var(--nav) + 0.75rem);
  border-color:color-mix(in srgb,var(--cx-c) 32%,var(--line));
  box-shadow:0 1.25rem 2.75rem -1.75rem color-mix(in srgb,var(--cx-c) 60%,transparent);
}
/* Al cambiar de fase, la tarjeta da un destello del color nuevo: se nota que
   PASÓ algo aunque se esté mirando otra cosa de la pantalla. */
.cx.cx-cambio{animation:cx-destello 1.3s ease-out}
@keyframes cx-destello{
  from{box-shadow:0 0 0 0.5rem color-mix(in srgb,var(--cx-c) 30%,transparent),
                  0 1.25rem 2.75rem -1.75rem color-mix(in srgb,var(--cx-c) 60%,transparent)}
  to{box-shadow:0 0 0 0 transparent,
                0 1.25rem 2.75rem -1.75rem color-mix(in srgb,var(--cx-c) 60%,transparent)}
}
/* Terminada ya ofrece «Volver al mapa»: el enlace de abajo lo repetía. */
.cod-mia:has(.cx[data-fase="fin"]) .cod-mapa{display:none}
/* El resplandor desde arriba, del color de la fase. ⚠️ QUIETO (2026-11-02): respiraba
   sin parar mientras cargaba, en la pantalla que se mira con la plata en juego. Lo
   que está vivo ya lo dicen el cometa del anillo y el paso actual. */
.cx::before{
  content:"";position:absolute;inset:0 0 auto;height:18rem;border-radius:inherit;
  pointer-events:none;
  background:radial-gradient(75% 100% at 50% 0%,color-mix(in srgb,var(--cx-c) 18%,transparent),transparent 72%);
}
.cx>*{position:relative}
.cx-fin[hidden],.cod-vivo[hidden]{display:none}

/* --- los pasos ------------------------------------------------------------ */
.cx-pasos{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}
.cx-pasos li{
  position:relative;display:flex;flex-direction:column;align-items:center;gap:0.375rem;
  font-size:0.6875rem;color:var(--ink-3);text-align:center;
}
/* la línea que une con el paso anterior, de centro a centro */
/* La línea que une los pasos se LLENA de izquierda a derecha al avanzar
   (2026-10-28): es el progreso, y se ve pasar en vez de cambiar de golpe. En
   250 ms (2026-11-02; eran 700): una respuesta, no un número que se espera. */
.cx-pasos li::before{
  content:"";position:absolute;top:0.6875rem;right:50%;width:100%;height:2px;
  background:linear-gradient(var(--cx-c),var(--cx-c)) 0 0/0% 100% no-repeat,var(--line);
  transition:background-size .25s cubic-bezier(.22,1,.36,1);
}
.cx-pasos li:first-child::before{display:none}
.cx-pasos li.hecho::before,.cx-pasos li.actual::before{background-size:100% 100%,auto}
.cx-pasos i{
  position:relative;z-index:1;width:1.375rem;height:1.375rem;border-radius:50%;
  display:grid;place-items:center;background:var(--surface);border:2px solid var(--line);
  transition:background .3s ease,border-color .3s ease,box-shadow .3s ease;
}
.cx-pasos li.hecho,.cx-pasos li.actual{color:var(--ink)}
.cx-pasos li.actual span{font-weight:600}
.cx-pasos li.hecho i{background:var(--cx-c);border-color:var(--cx-c)}
/* El visto va en `--ground` y no en blanco: sobre el verde claro del tema
   oscuro un visto blanco casi no se ve; el del fondo sí, en los dos temas. */
.cx-pasos li.hecho i::after{
  content:"";width:0.3125rem;height:0.5625rem;margin-top:-0.125rem;
  border:solid var(--ground);border-width:0 2px 2px 0;transform:rotate(45deg);
  animation:cx-visto .38s cubic-bezier(.34,1.56,.64,1) both;
}
@keyframes cx-visto{from{transform:rotate(45deg) scale(.4);opacity:0}}   /* no nace de la nada */
.cx-pasos li.actual i{border-color:var(--cx-c);
  box-shadow:0 0 0 0.25rem color-mix(in srgb,var(--cx-c) 20%,transparent)}
.cx-pasos li.actual i::after{
  content:"";width:0.5rem;height:0.5rem;border-radius:50%;background:var(--cx-c);
  animation:late 1.8s infinite;
}

/* --- el centro: anillo o dibujo --------------------------------------------- */
.cx-hero{display:flex;flex-direction:column;align-items:center;text-align:center;gap:0.875rem}
.cx-visual{position:relative;display:grid;place-items:center;width:min(14rem,72vw);aspect-ratio:1}
.cx-anillo,.cx-escena{grid-area:1/1}
.cx-anillo{position:relative;width:100%;height:100%}
.cx-anillo svg{width:100%;height:100%;transform:rotate(-90deg);overflow:visible}
.cx-riel{fill:none;stroke:color-mix(in srgb,var(--cx-c) 13%,transparent);stroke-width:12}
.cx-arco{
  fill:none;stroke:url(#cx-grad);stroke-width:12;stroke-linecap:round;
  transition:stroke-dasharray .25s cubic-bezier(.22,1,.36,1);   /* era 1,1 s (2026-11-02) */
  filter:drop-shadow(0 0 0.4375rem color-mix(in srgb,var(--cx-c) 55%,transparent));
}
.cx-g1{stop-color:var(--cx-c2)}
.cx-g2{stop-color:var(--cx-c)}
/* LA ENERGÍA ENTRANDO: un destello que recorre el anillo desde el arranque
   hasta la punta de lo entregado, solo mientras carga. Llega hasta
   `--cx-pct`, que pone `carga-en-vivo.js`. El blanco es brillo SOBRE el
   relleno de la marca (la excepción de `marca-kw` §1), no un color de la app. */
.cx-cometa{
  display:none;fill:none;stroke:#fff;stroke-width:8;stroke-linecap:round;
  stroke-dasharray:3 97;opacity:0;
  animation:cx-cometa 2.4s cubic-bezier(.45,0,.55,1) infinite;
}
.cx[data-fase="carga"] .cx-cometa{display:block}
@keyframes cx-cometa{
  0%{stroke-dashoffset:3;opacity:0}
  15%,80%{opacity:.6}
  100%{stroke-dashoffset:calc(3 - var(--cx-pct,0));opacity:0}
}
.cx-centro{
  position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:0.125rem;
}
.cx-centro b{
  font-family:var(--mono);font-size:2.375rem;font-weight:700;letter-spacing:-.02em;
  color:var(--ink);font-variant-numeric:tabular-nums;line-height:1.05;
}
.cx-centro small{font-size:0.75rem;color:var(--ink-3)}
.cx-pct{
  margin-top:0.375rem;padding:0.125rem 0.5rem;border-radius:62rem;
  font-family:var(--mono);font-size:0.75rem;font-weight:700;color:var(--ink);
  background:color-mix(in srgb,var(--cx-c) 16%,transparent);
}
/* ⚠️ `.cx .cx-escena` y no `.cx-escena`: `.il` (kw-ilustracion.css, que se
   carga DESPUÉS) le gana a una sola clase y el dibujo salía encima del anillo. */
.cx .cx-escena{display:none;width:100%;height:auto}
/* Las fases SIN anillo: el dibujo, ancho como su escena. */
.cx[data-fase="pago"] .cx-anillo,.cx[data-fase="inicio"] .cx-anillo,.cx[data-fase="pausa"] .cx-anillo,
.cx[data-fase="cierre"] .cx-anillo,.cx[data-fase="fin"] .cx-anillo{display:none}
.cx[data-fase="pago"] .cx-escena,.cx[data-fase="inicio"] .cx-escena,.cx[data-fase="pausa"] .cx-escena,
.cx[data-fase="cierre"] .cx-escena,.cx[data-fase="fin"] .cx-escena{display:block}
.cx[data-fase="pago"] .cx-visual,.cx[data-fase="inicio"] .cx-visual,.cx[data-fase="pausa"] .cx-visual,
.cx[data-fase="cierre"] .cx-visual,.cx[data-fase="fin"] .cx-visual{aspect-ratio:auto;width:min(22rem,100%)}

.cx-texto{display:flex;flex-direction:column;align-items:center;gap:0.5rem;max-width:32rem}
.cx-texto h2{margin:0;font-size:1.375rem;font-weight:600;line-height:1.2;color:var(--ink);text-wrap:balance}
.cx-texto h2:focus{outline:none}
.cx-texto p{margin:0;font-size:0.8125rem;line-height:1.55;color:var(--ink-2);text-wrap:pretty}
.cx-texto p:empty{display:none}
/* La batería del carro, en una pastilla bajo el título. */
.cx-soc{display:inline-flex;align-items:center;gap:0.5rem;padding:0.3125rem 0.75rem;
  border-radius:62rem;font-size:0.8125rem;color:var(--ink-2);
  background:color-mix(in srgb,var(--cx-c) 12%,transparent)}
.cx-soc[hidden]{display:none}
.cx-soc b{font-family:var(--mono);color:var(--ink)}
.cx-soc svg{width:1.75rem;height:auto;flex:none}
.cx-soc-marco{fill:none;stroke:var(--ink-3);stroke-width:1.6}
.cx-soc-polo{fill:var(--ink-3)}
.cx-soc-nivel{fill:var(--cx-c);transform-box:fill-box;transform-origin:left center;
  transition:transform .25s cubic-bezier(.22,1,.36,1)}   /* `scaleX`, no `width` (2026-11-02) */

/* Al cambiar de fase, el centro entra con un rebote corto (una vez). */
.cx-cambio .cx-visual,.cx-cambio .cx-texto{animation:cx-entra .34s cubic-bezier(.34,1.56,.64,1) both}
.cx-cambio .cx-texto{animation-delay:.06s}
@keyframes cx-entra{from{opacity:0;transform:translateY(0.375rem) scale(.97)}to{opacity:1;transform:none}}

/* --- potencia, cuánto lleva, cuánto falta ------------------------------------ */
.cx-datos{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr);gap:0.625rem}
.cx-dato{
  display:flex;flex-direction:column;gap:0.25rem;min-width:0;padding:0.75rem 0.875rem;
  border-radius:0.75rem;background:var(--surface-2);
}
.cx-dato .l{font-size:0.6875rem;color:var(--ink-3)}
/* En Sora y no en mono: «menos de 1 min» en mono no cabe en media columna
   de teléfono y se partía en dos renglones. El número de la potencia sí va en
   mono (`.cx-pot b`), que es una cifra que cambia. */
.cx-dato b{font-size:1.0625rem;font-weight:600;color:var(--ink);line-height:1.25}
.cx-pot b{font-family:var(--mono);font-size:1.25rem;font-weight:700}
.cx-dato b small{font-family:var(--sora);font-size:0.75rem;font-weight:400;color:var(--ink-3)}
.cx-dato>small{font-size:0.625rem;color:var(--ink-3)}
.cx-chispa{margin-top:0.25rem}
.cx[data-fase="pago"] .cx-datos,.cx[data-fase="inicio"] .cx-datos,
.cx[data-fase="fin"] .cx-datos{display:none}
@media (max-width:600px){
  .cx-datos{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .cx-pot{grid-column:1/-1}
}

/* --- terminada ------------------------------------------------------------- */
.cx-fin{display:flex;flex-direction:column;gap:0.875rem}
.cx-fin-dev{
  margin:0;padding:0.875rem 1rem;border-radius:0.75rem;font-size:0.8125rem;line-height:1.55;
  color:var(--ink);background:rgba(var(--ok-rgb),.08);border:1px solid rgba(var(--ok-rgb),.3);
}
.cx-fin-b{display:grid;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));gap:0.625rem}

@media (min-width:960px){
  .cx-hero{flex-direction:row;align-items:center;text-align:left;gap:2.25rem}
  .cx-visual{width:15.5rem;flex:none}
  .cx[data-fase="pago"] .cx-visual,.cx[data-fase="inicio"] .cx-visual,.cx[data-fase="pausa"] .cx-visual,
  .cx[data-fase="cierre"] .cx-visual,.cx[data-fase="fin"] .cx-visual{width:20rem}
  .cx-texto{align-items:flex-start}
  .cx-texto h2{font-size:1.75rem}
  .cx-centro b{font-size:2.75rem}
}
/* La curva de «Potencia ahora» y los dibujos de cada fase, del mismo color. */
.cx .kg-linea{stroke:var(--cx-c)}
.cx .kg-s1,.cx .kg-s2{stop-color:var(--cx-c)}
.cx .kg-punta{fill:var(--cx-c)}
.cx .kg-halo{fill:color-mix(in srgb,var(--cx-c) 35%,transparent)}
.cx .kg-ojo{stroke:var(--cx-c)}
.cx .il{--il-marca:var(--cx-c);--il-estado:var(--cx-c)}

@media (prefers-reduced-motion:reduce){
  .cx-arco{transition:none}
  .cx-cambio .cx-visual,.cx-cambio .cx-texto,.cx.cx-cambio{animation:none}
  .cx::before,.cx-cometa{animation:none}
  .cx-cometa{display:none}
}

/* ===================== MI CONSUMO, EN GRÁFICAS (2026-10-27) ================= */
.mc-grid{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:1rem;margin-bottom:1rem}
.mc-grid .panel{margin-bottom:0}
@media (max-width:900px){ .mc-grid{grid-template-columns:minmax(0,1fr)} }
.mc-seg{display:flex;gap:0.375rem;margin-left:auto}
.mc-seg .chip{padding:0.25rem 0.75rem}
.mc-tend{margin:0 0 0.75rem;font-size:0.8125rem;line-height:1.5;color:var(--ink-2)}
.mc-tend:empty{display:none}
.mc-tend b{font-family:var(--mono);color:var(--ink)}
.mc-graf{min-height:11.875rem}
.mc-donde{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:0.875rem}
.mc-fila{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;gap:0.375rem 0.75rem}
.mc-fila .n1{min-width:0;font-size:0.8125rem;font-weight:600;color:var(--ink);overflow-wrap:anywhere}
.mc-fila .n1 a{color:inherit}
.mc-fila .n1 a:hover{color:var(--led-bright)}
.mc-fila .n1 small{margin-left:0.375rem;font-weight:400;color:var(--ink-3)}
.mc-fila .n2{font-family:var(--mono);font-size:0.75rem;color:var(--ink-2);white-space:nowrap}
.mc-riel{grid-column:1/-1;height:0.5rem;border-radius:62rem;background:var(--surface-2);overflow:hidden}
/* ⚠️ Sin crecer desde cero (2-nov-2026): son datos que se leen y se comparan, y
   cada vez que se abría Mi consumo las barras tardaban 0,9 s en decir cuánto. El
   ancho lo pone `panel.js` en el HTML. */
.mc-riel i{
  display:block;width:0;height:100%;border-radius:inherit;
  background:linear-gradient(90deg,var(--led),var(--led-bright));
}
.mc-consejo{
  margin:1rem 0 0;padding:0.625rem 0.75rem;border-radius:0.625rem;font-size:0.75rem;
  line-height:1.5;color:var(--ink-2);background:rgba(var(--led-rgb),.08);
}
@media (prefers-reduced-motion:reduce){ .mc-riel i{transition:none} }

/* ---------- Mi consumo: lo que cambió en la revisión de diseño (2-nov-2026) ---------- */
/* Su última carga, en una línea, con el recibo a un toque. */
.mc-ultima{
  display:flex;align-items:center;flex-wrap:wrap;gap:0.375rem 0.875rem;
  padding:0.75rem 1rem;margin:0 0 1rem;border-radius:0.75rem;
  background:var(--surface);border:1px solid var(--line);color:var(--ink-2);
  font-size:0.8125rem;text-decoration:none;transition:border-color .15s;
}
.mc-ultima:hover{border-color:var(--led);text-decoration:none}
.mc-ultima-l{font-size:0.6875rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-3)}
.mc-ultima-d{flex:1 1 16rem;min-width:0}
.mc-ultima-d b{color:var(--ink);font-weight:600}
.mc-ultima-d .num{font-family:var(--mono)}
.mc-sep{color:var(--ink-3);opacity:.6}
.mc-ultima-ir{color:var(--led-bright);font-weight:600;white-space:nowrap}
/* El único vacío de una cuenta nueva. */
.mc-vacio b{color:var(--ink-2)}
.mc-vacio-acc{margin-top:0.75rem}
/* No se pudo actualizar: lo pintado se queda y esto lo dice. */
.mc-aviso{align-items:center}
.mc-falla .btn{margin-left:0.375rem}
/* «Ver todas sus cargas», debajo de las cinco últimas. */
.mc-pie{margin:0.75rem 0 0}
.mc-pie[hidden]{display:none}
/* La columna del recibo: el enlace a la derecha. */
table.t td.hist-r{text-align:right;white-space:nowrap}
table.t th .hist-th-r{display:block;text-align:right}
/* Filas con bandas en la tabla de escritorio (el gusto de la casa: «así sea un gris
   clarito»). En el teléfono cada fila ya es una tarjeta. */
@media (min-width:641px){
  table.t.hist-t tbody tr:nth-child(even) td{background:var(--surface-2)}
}
/* «Cómo terminan sus cargas» no se estira al alto de la gráfica de al lado: dejaba
   ~150 px en blanco a 1440. */
.mc-grid .mc-fin-p{align-self:start}
/* La unidad de la carga en vivo, sin px escritos a mano en `panel.js`. */
.vivo-u{font-size:0.75rem;color:var(--led-pale)}
.vivo-g .v.vivo-hora{font-size:1rem}
@media (max-width:640px){
  /* La quinta tarjeta (la tarifa) quedaba sola en media fila. */
  .kpis .kpi-tarifa{grid-column:1/-1}
  /* El enlace al recibo, abajo de la tarjeta y a lo ancho. */
  table.t.hist-t td.hist-r{grid-column:1/-1;text-align:left}
}

/* ====================== LAS GRÁFICAS (`kw-graficas.js`) ===================== */
.kg{position:relative}
.kg-svg{display:block;overflow:visible}
.kg-s1{stop-color:var(--led);stop-opacity:.3}
.kg-s2{stop-color:var(--led);stop-opacity:0}
.kg-linea{fill:none;stroke:var(--led);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.kg-grilla{stroke:var(--line-soft);stroke-width:1}
.kg-camino{stroke:var(--line);stroke-width:2;stroke-dasharray:2 4;stroke-linecap:round}
.kg-eje{font-family:var(--mono);font-size:0.6562rem;fill:var(--ink-3)}
.kg-punta{fill:var(--led);stroke:var(--surface);stroke-width:2}
.kg-halo{
  fill:rgba(var(--led-rgb),.3);transform-box:fill-box;transform-origin:center;
  animation:kg-late 1.8s ease-out infinite;
}
@keyframes kg-late{from{transform:scale(.5);opacity:.9}to{transform:scale(2);opacity:0}}
.kg-cruz{stroke:var(--ink-3);stroke-width:1;stroke-dasharray:3 3;opacity:0}
.kg-ojo{fill:var(--surface);stroke:var(--led);stroke-width:2.5;opacity:0}
.kg-activa .kg-cruz,.kg-activa .kg-ojo{opacity:1}
/* ⚠️ `pan-y`: el dedo que arrastra de lado recorre la curva; el que sube o
   baja sigue desplazando la página. Sin esto la gráfica atrapa el gesto. */
.kg-zona{fill:transparent;cursor:crosshair;touch-action:pan-y}
.kg-tip{
  position:absolute;left:0;top:0;z-index:3;pointer-events:none;
  padding:0.375rem 0.625rem;border-radius:0.5rem;white-space:nowrap;
  font-size:0.6875rem;line-height:1.45;color:var(--ink-2);
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--sombra-flota);
  opacity:0;transform:translateY(0.25rem);
  transition:opacity .13s ease,transform .2s cubic-bezier(.34,1.56,.64,1);
}
.kg-tip.ver{opacity:1;transform:none}
.kg-tip b{font-family:var(--mono);color:var(--ink)}
.kg-tip .t{display:block;font-weight:600;color:var(--ink)}
/* ⚠️ Las columnas NO crecen desde cero (quitado el 2-nov-2026): crecían 550 ms al
   abrir, y otra vez al cambiar de medida o de ancho de ventana. Una gráfica de
   trabajo se lee y se compara; redibujarla desde cero en cada cambio solo la
   demora (animar.md §3-bis, rama 3). */
.kg-col{
  fill:var(--led);opacity:.82;transition:opacity .15s ease;
}
.kg-col.marcada{fill:var(--led-bright)}
.kg-col.foco{opacity:1;filter:drop-shadow(0 0 0.375rem rgba(var(--led-rgb),.5))}
/* La columna con el foco del teclado (2-nov-2026): cada una se puede recorrer con
   Tab y dice su valor. */
.kg-zona:focus{outline:none}
.kg-zona:focus-visible{stroke:var(--led-bright);stroke-width:2;stroke-dasharray:3 3}
@media (prefers-reduced-motion:reduce){ .kg-halo{animation:none} .kg-tip{transition:none} }

/* ===================== LOS TRES MEDIOS (2026-10-16) ======================= */
/* Los bonos, como medio: un borde de marca para que se distingan de una
   tarjeta — no son un instrumento del banco, son plata que ya está en KW. */
.med.med-bono{border-style:dashed}
.med.med-bono:has(input:checked){border-style:solid}

/* «Comprar un bono», donde se paga. Es un enlace, no un botón: no compite con
   el que acepta el cobro. */
.pagar-bono{
  display:flex;flex-direction:column;gap:2px;padding:0.75rem 0.875rem;border-radius:0.6875rem;
  border:1px dashed var(--line);background:var(--surface-2);text-decoration:none;color:var(--ink-2);
}
.pagar-bono b{font-size:0.8125rem;color:var(--led-bright)}
.pagar-bono span{font-size:0.6875rem;line-height:1.5;color:var(--ink-3)}
.pagar-bono:hover{border-color:var(--led);text-decoration:none}
.campo .opcional{font-weight:400;color:var(--ink-3)}

/* La espera de la tarjeta en el datáfono. Pantalla propia: mientras dura, el
   conector está apartado y no hay otra cosa que hacer. */
.cod-espera{
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:0.625rem;
  padding:1.75rem 1.25rem;border-radius:1rem;background:var(--surface);border:1px solid var(--line);
}
.cod-espera[hidden]{display:none}
.cod-espera h2{margin:0;font-size:1.25rem;font-weight:600;color:var(--ink-fuerte)}
.cod-espera p{margin:0;font-size:0.8438rem;line-height:1.55;color:var(--ink-2);max-width:26rem}
.cod-espera-ic{
  width:3rem;height:3rem;border-radius:50%;
  border:3px solid rgba(var(--led-rgb),.2);border-top-color:var(--led);
  animation:cod-gira 1s linear infinite;
}
@keyframes cod-gira{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .cod-espera-ic{animation:none;border-top-color:rgba(var(--led-rgb),.2)} }
.cod-espera.mal{border-color:rgba(var(--crit-rgb),.45);background:rgba(var(--crit-rgb),.05)}
.cod-espera.mal .cod-espera-ic{animation:none;border-color:rgba(var(--crit-rgb),.4)}
.cod-espera.mal #dat-msj{color:var(--crit-ink)}

/* --------------------------- LA PANTALLA DE BONOS -------------------------- */
.bonos-pag{max-width:72rem}
.bonos-dos{display:grid;gap:1rem;margin:1rem 0}
@media (min-width:960px){ .bonos-dos{grid-template-columns:minmax(16rem,22rem) minmax(0,1fr);align-items:start} }
.bonos-saldo .l{font-size:0.6562rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
.bonos-cifra{display:block;font-family:var(--mono);font-size:2.25rem;font-weight:700;
  color:var(--led-bright);margin:0.25rem 0 0.5rem;font-variant-numeric:tabular-nums}
.bonos-saldo p{margin:0;font-size:0.8125rem;line-height:1.55;color:var(--ink-2)}
/* ⭐ EL SALDO COMO TARJETA DE REGALO DE KW (2026-10-28). Fondo de marca FIJO en
   los dos temas, y las tintas redefinidas ADENTRO con los mismos valores de
   `.vivo` (la trampa de `marca-kw` §1: fondo fijo + tinta que sigue al tema =
   texto invisible). Proporción de tarjeta de verdad (1,586). */
.bono-tarjeta{
  --ink:#EEF2FA; --ink-2:#A9B2C6; --ink-3:#8A94A8; --led-bright:#67C6FF;
  position:relative;overflow:hidden;display:flex;flex-direction:column;gap:0.125rem;
  aspect-ratio:1.586;width:100%;max-width:22rem;padding:1.125rem 1.25rem;margin:0 0 0.875rem;
  border-radius:1rem;color:var(--ink);
  background:
    radial-gradient(120% 120% at 100% 0%,rgba(var(--led-rgb),.36),transparent 55%),
    linear-gradient(150deg,var(--deep),var(--deep-2));
  border:1px solid rgba(var(--led-rgb),.35);
  box-shadow:0 1rem 2rem -1.25rem rgba(var(--led-rgb),.6);
}
.bono-tarjeta>*:not(.bono-tarjeta-rayo){position:relative;z-index:1}
.bono-tarjeta-h{display:flex;align-items:center;gap:0.625rem;margin-bottom:auto;
  font-size:0.75rem;font-weight:600;color:var(--ink-2)}
.bono-tarjeta-logo{display:inline-flex;width:2.75rem}
.bono-tarjeta-logo .kw-mono{display:block;width:100%;height:auto}
.bono-tarjeta .bono-tarjeta-logo .kw-mono path{stroke:var(--ink)}
.bono-tarjeta .l{color:var(--ink-2)}
.bono-tarjeta .bonos-cifra{color:var(--ink);margin:0.125rem 0 0.375rem}
.bono-tarjeta-pie{display:flex;justify-content:space-between;flex-wrap:wrap;gap:0.25rem 0.75rem;
  font-size:0.6875rem;color:var(--ink-2)}
.bono-tarjeta-rayo{position:absolute;right:-1.75rem;bottom:-2rem;width:9.5rem;height:9.5rem;
  fill:rgba(var(--led-rgb),.2);transform:rotate(-10deg);pointer-events:none}
/* ⚠️ El reflejo que cruzaba la tarjeta 1,8 s al abrir Bonos SE QUITÓ (2026-11-02,
   animar.md §3-bis): adorno puro en una pantalla de dinero, en cada visita. */
/* Los dibujos de los vacíos y las confirmaciones ya NO entran subiendo (quitado el
   2-nov-2026): subían 550 ms cada vez que se abría la pantalla, y un vacío no es un
   momento que celebrar. Quietos desde el primer cuadro. */
@media (prefers-reduced-motion:reduce){
  .bono-tarjeta::after{animation:none;display:none}
  .vacio .vc,.vc-spot,.err-il,.cod-vc{animation:none}
  .cx-pasos li::before{transition:none}
  .cx-pasos li.hecho i::after{animation:none}
}

/* El dibujo de una confirmación (`vc-*`, 2026-10-28): correo, enlace, cuenta.
   ⚠️ `.il.vc-spot` y no `.vc-spot` a secas (2026-11-02): `.il{width:100%}` vive en
   `kw-ilustracion.css`, que se carga DESPUÉS, y con la misma especificidad ganaba la última. El
   ancho de aquí nunca aplicó: lo disimulaba la columna de 230 px de `/eliminar-cuenta`, y al darle
   a esa página una sola columna (`.legal.solo`) el dibujo pasó a ocupar media pantalla. */
.il.vc-spot{display:block;width:min(11rem,62%);height:auto;margin:0 auto 0.75rem}
.il.vc-spot.vc-izq{margin-left:0}
.bonos-den{grid-template-columns:repeat(auto-fit,minmax(9rem,1fr))}

/* La ficha del bono: las cinco cosas que la norma pide ANTES del pago. */
.bono-ficha{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:0.5rem;margin:0.25rem 0 0}
.bono-ficha>div{padding:0.625rem 0.75rem;border-radius:0.625rem;background:var(--surface-2);border:1px solid var(--line)}
.bono-ficha dt{font-size:0.625rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
.bono-ficha dd{margin:0.1875rem 0 0;font-size:0.8438rem;font-weight:600;color:var(--ink)}
.bono-reglas{margin:0.25rem 0 0;padding-left:1.125rem;display:grid;gap:0.375rem;
  font-size:0.7812rem;line-height:1.55;color:var(--ink-2)}

.bono-lista{list-style:none;margin:0;padding:0;display:grid;gap:0.5rem}
.bono{display:flex;align-items:center;justify-content:space-between;gap:0.75rem;
  padding:0.75rem 0.875rem;border-radius:0.6875rem;border:1px solid var(--line);background:var(--surface)}
.bono-n b{display:block;font-size:0.875rem;color:var(--ink)}
.bono-n small,.bono-s small{display:block;font-size:0.6875rem;color:var(--ink-3);margin-top:2px}
.bono-s{text-align:right}
.bono-s b{font-family:var(--mono);font-size:1rem;color:var(--led-bright)}
.bono.vencido,.bono.agotado{opacity:.6}
.bono.vencido .bono-s b{color:var(--ink-3)}

/* ===================== CARGADORES EN EL TELÉFONO (2026-10-16) ===============
   Reportado con el APK. `.red-acc` es `flex:none` —en el escritorio los botones
   no se encogen—, así que en 390 px «+ Conector» y «Borrar» quedaban FUERA de la
   tarjeta, recortados: se veían a medias y no se podían tocar. Y tres cajas
   anidadas (punto → equipo → conector), cada una con su margen, se comían un
   tercio del ancho antes de llegar al contenido. */
@media (max-width:640px){
  .red-punto{padding:0.75rem 0.625rem}
  .red-eq{margin:0.625rem 0 0 0.25rem;padding:0.5625rem 0.5rem}
  .red-con{padding:0.375rem 0.5rem}
  .red-vacio{margin-left:0.25rem}
  /* Los botones bajan a su propia fila y se reparten el ancho: todos se ven,
     todos se pueden tocar con el pulgar. */
  .red-acc{flex:1 1 100%;margin-left:0;flex-wrap:wrap}
  .red-acc .btn{flex:1 1 auto;justify-content:center;text-align:center;min-height:2.25rem}
  /* «Borrar» no se estira: si baja de fila, queda de su tamaño y no se vuelve
     el botón más grande de la tarjeta (revisión del 2026-11-02). */
  .red-acc .btn.red-borrar{flex:0 0 auto}
}

/* ===========================================================================
   ⚠️⚠️ EL iPHONE ACERCA LA PANTALLA AL TOCAR UN CAMPO CON LETRA < 16 px
   (medido el 2026-10-19). Con el root a 112,5 %, los campos quedaban en
   15,75 px (0,875 rem): Safari hacía zoom al tocar el correo, la clave, el
   formulario de PQR, Cuenta y —el que más importa— el correo del pago SIN
   CUENTA que abre el QR, y la página quedaba acercada y corrida de lado.
   En el APK (Chrome en Android) no pasa; en un iPhone que escanea el QR, sí.

   Solo en iOS: `-webkit-touch-callout` existe únicamente en el WebKit de
   Apple, así que en Android y en el escritorio no cambia ni un píxel.
   ⚠️ El `16px` es la EXCEPCIÓN a «todo en rem», igual que los breakpoints: no
   mide contenido, mide el umbral que Safari aplica. `max()` respeta a quien
   tenga la letra más grande. El `!important` es porque las reglas de cada
   campo (`.campo input`, `.chip`…) pesan más que un selector de elemento. */
@supports (-webkit-touch-callout:none){
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=submit]):not([type=button]),
  select,textarea{font-size:max(16px,0.875rem)!important}
}

/* ===========================================================================
   ⭐ EL TELÉFONO: PISO DE LETRA, BLANCOS DE TOQUE Y LA BARRA QUE SE VA (2-nov-2026)
   ---------------------------------------------------------------------------
   Revisión de diseño del 2-nov (módulo C). Va aquí, DESPUÉS de las reglas que
   pisa: con la misma especificidad manda la última (CLAUDE.md §4).

   1. LA LETRA. La raíz al 90 % (decisión del usuario, 28-oct) se queda para el
      cuerpo, pero lo funcional tiene un piso: nada por debajo de 0,75 rem
      (10,8 px). Medido antes: rótulos de las tarjetas 8,6 px, «PUNTO» del filtro
      8,1, pestañas de abajo 9, la fecha de cada carga 9,45. Solo lo de la
      app de la persona y los componentes comunes; la consola, el mapa y la
      pantalla de pagar tienen sus dueños. Y los campos, a 16 px también fuera del
      iPhone: a 12,6 px se escribía a ciegas.
   2. LOS TOQUES. Campana 32×32, tema 31×31, avatar 29×29, «kWh · Pesos ·
      Cargas» 48×22, periodos del filtro 50×27: con el pulgar el mínimo son 44.
      Donde el dibujo no debe crecer, un `::after` invisible agranda el área.
   3. LA BARRA DE ARRIBA se va al bajar y vuelve al subir (`base.html`).
   =========================================================================== */
@media (max-width:940px){
  .panel-h h2,.panel-h .nota,.kpi .l,.kpi .s,table.t th,table.t.hist-t td[data-r]::before,
  table.t td.hist-k,table.t .hist-c,.pill,.avi-i p,.avi-i span,.avi-pie,.avi-tipo,
  .tabbar a,.tab-qr-t,.vivo-tres .l,.vivo-g .l,.vivo-pie>span,.sal-tot .l,.sal-tot .s,
  .mov-f,.mov-d,.mov-x,.mov-v,.mov li.mov-dia,.filtro-sel span,.cg-f,.cg-d,.cg-d .num,
  .cg-n b small,.cg-n span,.cg-m,.rec-tres .l,.rec-tres .s,.rec-nota,.rec-cont .l,
  .rec-datos span,.rec-datos b.num,.kg-eje,.kg-tip,.mc-fila .n1 small,.bonos-sub,
  .avi-n,.avi-tn{font-size:0.75rem}
  .avi-n{min-width:1.25rem;height:1.25rem;line-height:1.25rem}
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=submit]):not([type=button]),
  select,textarea{font-size:max(16px,1rem)!important}

  .avi-btn,.tema-btn,.yo-link,.nav-yo>.btn,.mc-seg .chip{position:relative}
  .avi-btn::after,.tema-btn::after{content:"";position:absolute;inset:-0.4375rem}
  .yo-link::after{content:"";position:absolute;inset:-0.5rem}
  .nav-yo>.btn::after{content:"";position:absolute;inset:-0.375rem 0}
  .mc-seg .chip{padding:0.5rem 0.875rem}
  .mc-seg .chip::after{content:"";position:absolute;inset:-0.4375rem 0}
  .filtro-per button{min-height:3rem;padding:0.5rem 0.875rem}
  .filtro-sel select{min-height:3rem}

  .nav{transition:transform .2s cubic-bezier(.16,1,.3,1)}
  .nav.nav-oculta{transform:translateY(-100%)}
  html.kw-nav-oculta main .cta-nav{top:0}
  /* El desvanecido del borde de la fila de enlaces vive con `.nav-links`, arriba. */
}

/* ⚠️ EL TELÉFONO ACOSTADO (medido el 2026-10-19 a 844×390): la barra de arriba
   en dos renglones (108 px) más la de pestañas (67 px) dejaban 214 px útiles
   de 390 —un 55 %— y la de arriba no se iba nunca. Con poca altura deja de ser
   fija: se va al desplazar, y la de pestañas, que es la que lleva a todas
   partes, se queda. El panel de avisos es `position:fixed` por su cuenta, así
   que no depende de esto. */
@media (max-width:940px) and (max-height:500px){
  .nav{position:relative}
  /* Lo que se pegaba DEBAJO de la barra se pega ahora arriba del todo. */
  main .cta-nav{top:0}   /* `main` le da el peso para ganarle a kw-cuenta.css, que carga después */
}

/* El «Cargar acá · N libres» de la ficha desplaza hasta acá: sin margen, el
   título quedaba DEBAJO de la barra fija de arriba (126 px en un teléfono). */
#det-cargadores{scroll-margin-top:8.5rem}

/* La espera del formulario de Wompi (`static/js/pasarela.js`). */
.pas-espera{display:flex;flex-direction:column;gap:0.5rem;margin-top:0.75rem}
.pas-espera[hidden]{display:none}
.pas-msj{margin:0;font-size:0.8125rem;line-height:1.5;color:var(--ink-2)}

/* «Guardar esta tarjeta» en la pantalla de pago (2026-10-19). Misma caja que las
   casillas de la pasarela (`.psp-acepto`), para que se lea como una decisión
   más del pago y no como letra chica. */
.pagar-guardar{
  display:flex;gap:0.625rem;align-items:center;margin-top:0.75rem;padding:0.6875rem 0.75rem;
  border-radius:0.625rem;background:var(--surface-2);border:1px solid var(--line);
  font-size:0.8125rem;color:var(--ink);cursor:pointer;
}
.pagar-guardar input{flex:none;width:1.0625rem;height:1.0625rem;margin:0;accent-color:var(--led)}

/* El botón de escanear va ARRIBA de la caja de texto (2026-10-19). */
#cod-cam:not(:empty){margin-bottom:0.75rem}

/* ¿En qué conector enchufó? (2026-10-19): el código de la calcomanía, grande,
   para compararlo de un vistazo con el que tiene enfrente. */
/* ⚠️ Con `section`: la sección reusa la clase de la tarjeta del enchufe, y escrita a
   secas ponía ESA tarjeta en columna (el «1» en su caja arriba, los datos debajo y
   la píldora al final: 150 px antes de los montos en el teléfono). Hallado el
   2026-11-02. */
section.cod-conector{display:flex;flex-direction:column;gap:0.75rem}
section.cod-conector[hidden]{display:none}
.cod-conector-cod{
  padding:0.875rem;border-radius:0.75rem;text-align:center;
  font-family:var(--mono);font-size:1.5rem;font-weight:600;letter-spacing:.04em;
  color:var(--led-bright);background:rgba(var(--led-rgb),.08);
  border:1px dashed rgba(var(--led-rgb),.45);
}
/* ⚠️ `hidden` pierde contra `display` de clase: sin esto, el pago se veía antes
   de confirmar el conector. */
.pagar[hidden]{display:none}

/* ⚠️ La ventana del código se SALÍA de la pantalla a 390 px (la X y «Buscar»
   cortados): `.cod-bg` es un grid con la columna en `auto`, que CRECE con su
   contenido, y entonces el `100%` del ancho de `.cod-modal` medía la columna ya
   estirada. Es la trampa de `.mapa-stage` en CLAUDE.md: hace falta `minmax(0,1fr)`.
   Saltó al poner el botón de escanear arriba (2026-10-19). */
.cod-bg{grid-template-columns:minmax(0,1fr)}
.cod-modal{justify-self:center;max-width:100%;box-sizing:border-box}
.cod-form input{min-width:0;flex:1 1 auto}

/* SUS BONOS (_bonos_caja.html): el saldo y la compra, en Cuenta y en Pagos.
   Mismas medidas que `.cta-caja`, pero aquí porque Pagos no carga
   kw-cuenta.css. */
.bonos-caja{
  background:var(--surface);border:1px solid var(--line);border-radius:0.875rem;
  padding:1.25rem;margin:1rem 0;min-width:0;
  grid-column:1/-1;               /* en la cuadrícula de Pagos, a lo ancho */
}
.bonos-caja>h2{margin:0 0 0.25rem;font-size:0.9375rem;font-weight:600;color:var(--ink);
  letter-spacing:-.01em}
.bonos-caja-cifra{display:block;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:1.875rem;font-weight:600;letter-spacing:-.02em;color:var(--ink);margin:0.25rem 0 0}
.bonos-sub{margin:0;font-size:0.75rem;color:var(--ink-3);line-height:1.6}
.bonos-sub b{color:var(--ink-2)}
.bonos-aviso{margin:0.75rem 0 0;padding:0.625rem 0.75rem;border-radius:0.5rem;
  background:var(--surface-2);border:1px solid var(--line);
  font-size:0.75rem;color:var(--ink-2);line-height:1.55}
.bonos-aviso b{color:var(--ink)}
.bonos-acc{display:flex;gap:0.5rem;margin-top:1rem;flex-wrap:wrap}
.bonos-acc .btn{flex:0 1 auto;min-width:0}
/* La versión en una línea (Pagos y Cuenta › Medios de pago, 2-nov-2026). */
.bonos-caja.bonos-linea{display:flex;align-items:center;justify-content:space-between;
  flex-wrap:wrap;gap:0.5rem 1rem;padding:0.75rem 1rem}
.bonos-linea-t{font-size:0.8125rem;line-height:1.5;color:var(--ink-2);min-width:0}
.bonos-linea-t b{color:var(--ink)}
.bonos-linea-t b.num{font-family:var(--mono);font-size:0.9375rem;font-weight:600}
.bonos-linea .btn{flex:none}

/* El resumen del bono ANTES del botón (bonos.html), por capas: a la vista lo que
   exige la SIC y lo que más sorprende; el detalle, en un desplegable. */
.bono-resumen{margin:1.25rem 0 1rem;padding:1rem;border-radius:0.75rem;
  background:var(--surface-2);border:1px solid var(--line)}
.bono-resumen-v{display:flex;align-items:baseline;justify-content:space-between;gap:0.75rem;
  margin-bottom:0.625rem}
.bono-resumen-v span{font-size:0.6562rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600}
.bono-resumen-v b{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:1.625rem;font-weight:700;color:var(--ink)}
/* Dentro del resumen, la ficha va en FILAS etiqueta / dato, como `.kwc-spec`, sin
   cajas propias (2026-11-02): eran tres cajas dentro de una caja dentro del panel,
   y el emisor con su NIT quedaba en cuatro renglones. */
.bono-resumen .bono-ficha{display:block;margin:0;border-top:1px solid var(--line)}
.bono-resumen .bono-ficha>div{
  display:flex;align-items:baseline;justify-content:space-between;gap:0.875rem;
  padding:0.5625rem 0;border:0;border-bottom:1px solid var(--line-soft);border-radius:0;background:none;
}
.bono-resumen .bono-ficha dt{flex:none;font-size:0.75rem;letter-spacing:0;text-transform:none;font-weight:400}
.bono-resumen .bono-ficha dd{margin:0;text-align:right;font-size:0.8125rem}
.bono-clave{margin:0.75rem 0 0;font-size:0.7812rem;line-height:1.55;color:var(--ink-2)}
.bono-clave b{color:var(--ink)}
.bono-mas{margin-top:0.5rem;border-bottom:0}
.bono-mas summary{padding-top:0.5rem;padding-bottom:0.5rem;font-size:0.7812rem}

/* En el teléfono, los valores del bono en DOS columnas: en una, los nueve
   ocupaban casi dos pantallas antes de llegar a «¿Con qué paga?». */
@media (max-width:640px){
  .bonos-den{grid-template-columns:repeat(2,minmax(0,1fr))}
}
.bono-mas-lista{margin-top:0.5rem;border-bottom:0}
.bono-mas-lista summary{font-size:0.7812rem}
.bono-mas-lista .bono-lista{margin-top:0.25rem}
.bonos-hist{margin-top:0.875rem}
@media (min-width:960px){ .bonos-ir{display:none} }
.nav-cargar{display:inline-flex;align-items:center;gap:0.375rem}
.nav-cargar svg{width:1rem;height:1rem;flex:none}
