/* ============================================================================
   Mapa de operación — estilos.

   TOKENS: el panel de /cuenta define --surface/--text/--muted/--line en oscuro,
   pero NO define --card/--border/--t1/--t2/--t3; esos solo existen en el tema
   claro. Usarlos crudos deja fondos TRANSPARENTES y bordes en blanco puro (la
   ficha se encimaba con el hilo detrás). Por eso todo pasa por --zm-*, que
   resuelve primero el token del módulo y cae al del portal.
   ========================================================================== */
/* La ficha vive en <body> (capa superior del <dialog>), fuera del root del
   módulo, así que necesita su propia copia de los tokens. */
#zk-mapa-root, .zm-sheet{
  --zm-card:  var(--card,   var(--surface-2, #1d1a1f));
  --zm-sunk:  var(--surface,#151316);
  --zm-line:  var(--border, var(--line, rgba(255,255,255,.10)));
  --zm-t1:    var(--t1,     var(--text,    #faf9f7));
  --zm-t2:    var(--t2,     var(--muted,   #a39fa6));
  --zm-t3:    var(--t3,     var(--muted-2, #8a8591));
  --zm-a:     var(--violet, #7c3aed);
  --zm-ok:    #10b981;
  --zm-warn:  #f59e0b;
  --zm-on-a:  var(--on-violet, #fff);
  /* Escala de 4pt. El ritmo del mapa sale de alternar estos valores, no de
     repetir el mismo hueco en todos lados. */
  --zm-1:4px; --zm-2:8px; --zm-3:12px; --zm-4:16px; --zm-6:24px; --zm-8:32px; --zm-12:48px;
  /* Tinta que va ENCIMA del disco de tono. En oscuro los tonos son los pasos
     claros de cada rampa y quieren tinta; en claro son los oscuros y quieren
     blanco. Es la misma regla de pareja del violeta de Zook, aplicada a los
     siete tonos. */
  --ton:#0a0a0b;
  /* Filtro que aplana el dibujo a UN solo color. El sprite trae un acento
     violeta horneado en cada icono; sobre el relleno translúcido de antes se
     leía, pero encima de un disco sólido o desaparece o pelea con el fondo.
     brightness(0) lo lleva todo a negro conservando el alfa, y el invert lo
     pasa a blanco cuando el disco es oscuro. Reescribir fill/stroke por CSS NO
     sirve: Chrome respeta el atributo de presentación del sprite y Safari deja
     ganar a la herencia, así que el mismo icono saldría distinto en cada
     teléfono. El filtro se comporta igual en los dos. */
  --tflt:brightness(0);
  /* Disco de los agentes: neutro y OPACO. Su personaje ya trae sus colores y
     encima de un disco de su mismo tono se ensucia; el tono va en el aro. */
  --zm-face:#26232b;
  color:var(--zm-t1);
}
/* Contraste medido, no estimado. Sobre blanco, el --t3 del portal (#847d90) da
   3.95:1 y el verde #10b981 da 2.54:1; los dos reprueban AA para texto chico.
   Solo se corrigen DENTRO del mapa: los tokens del portal no se tocan. */
html[data-theme="light"] #zk-mapa-root,
html[data-theme="light"] .zm-sheet{ --zm-t3:#6b6577; --zm-ok:#047857;
  /* El ámbar de la alerta: #f59e0b sobre su propia píldora clara da 1.9:1 y no
     se lee. #92400e da 6.4:1 sobre esa misma píldora. */
  --zm-warn:#92400e;
  --ton:#ffffff; --zm-face:#f2f0f6; --tflt:brightness(0) invert(1); }

/* El módulo es compartido y no puede depender del reset del host: sin
   border-box, la tarjeta del equipo (width:100% + padding) se sale 26 px de su
   columna. En un host que ya lo tiene, esta regla no cambia nada. */
.zm-wrap,.zm-wrap *,.zm-sheet,.zm-sheet *{box-sizing:border-box}

/* ── Tonos ───────────────────────────────────────────────────────────────── */
/* El color de cada burbuja vive aquí y NO en el JS, porque el mismo violeta no
   sirve en los dos temas: sobre la tarjeta oscura, #7c3aed da 2.3:1 contra el
   fondo y el icono se pierde. En oscuro van los tonos claros de cada rampa
   (≥3:1 sobre la tarjeta) y en claro los oscuros. */
#zk-mapa-root [data-tone],.zm-sheet [data-tone]{--tc:#94a3b8; --tbg:rgba(148,163,184,.16)}
#zk-mapa-root [data-tone="violet"],.zm-sheet [data-tone="violet"] {--tc:#a78bfa; --tbg:rgba(167,139,250,.16)}
#zk-mapa-root [data-tone="emerald"],.zm-sheet [data-tone="emerald"]{--tc:#34d399; --tbg:rgba(52,211,153,.15)}
#zk-mapa-root [data-tone="blue"],.zm-sheet [data-tone="blue"]   {--tc:#60a5fa; --tbg:rgba(96,165,250,.15)}
#zk-mapa-root [data-tone="sky"],.zm-sheet [data-tone="sky"]    {--tc:#38bdf8; --tbg:rgba(56,189,248,.15)}
#zk-mapa-root [data-tone="amber"],.zm-sheet [data-tone="amber"]  {--tc:#fbbf24; --tbg:rgba(251,191,36,.15)}
#zk-mapa-root [data-tone="rose"],.zm-sheet [data-tone="rose"]   {--tc:#fb7185; --tbg:rgba(251,113,133,.16)}
html[data-theme="light"] #zk-mapa-root [data-tone],
html[data-theme="light"] .zm-sheet [data-tone]{--tc:#64748b; --tbg:rgba(100,116,139,.12)}
html[data-theme="light"] #zk-mapa-root [data-tone="violet"],html[data-theme="light"] .zm-sheet [data-tone="violet"] {--tc:#6d28d9; --tbg:rgba(109,40,217,.10)}
html[data-theme="light"] #zk-mapa-root [data-tone="emerald"],html[data-theme="light"] .zm-sheet [data-tone="emerald"]{--tc:#047857; --tbg:rgba(4,120,87,.10)}
html[data-theme="light"] #zk-mapa-root [data-tone="blue"],html[data-theme="light"] .zm-sheet [data-tone="blue"]   {--tc:#1d4ed8; --tbg:rgba(29,78,216,.10)}
html[data-theme="light"] #zk-mapa-root [data-tone="sky"],html[data-theme="light"] .zm-sheet [data-tone="sky"]    {--tc:#0369a1; --tbg:rgba(3,105,161,.10)}
html[data-theme="light"] #zk-mapa-root [data-tone="amber"],html[data-theme="light"] .zm-sheet [data-tone="amber"]  {--tc:#b45309; --tbg:rgba(180,83,9,.10)}
html[data-theme="light"] #zk-mapa-root [data-tone="rose"],html[data-theme="light"] .zm-sheet [data-tone="rose"]   {--tc:#be123c; --tbg:rgba(190,18,60,.10)}

/* Banda propia dentro del Resumen: el mapa ocupa el lugar donde estaba la fila
   de KPIs, entre la cifra de ventas y el embudo, y necesita separarse de las dos. */
.zk-mapa-slot{margin:var(--zm-8,32px) 0;padding:var(--zm-6,24px) 0;
  border-top:1px solid var(--zm-line,rgba(255,255,255,.10));
  border-bottom:1px solid var(--zm-line,rgba(255,255,255,.10))}

.zm-load{padding:var(--zm-6) 0;font-size:.88rem;color:var(--zm-t3)}
.zm-head{display:flex;align-items:flex-start;gap:var(--zm-3);justify-content:space-between;
  flex-wrap:wrap;margin-bottom:var(--zm-1)}
.zm-title{font-size:1.35rem;font-weight:800;letter-spacing:-.01em;color:var(--zm-t1);margin:0;
  text-wrap:balance}
.zm-sub{font-size:.88rem;line-height:1.5;color:var(--zm-t2);margin:var(--zm-1) 0 0;max-width:62ch;
  text-wrap:pretty}
.zm-note{font-size:.82rem;line-height:1.5;color:var(--zm-t2);background:rgba(124,58,237,.10);
  border:1px solid rgba(124,58,237,.26);border-radius:12px;padding:10px 13px;margin:var(--zm-3) 0 0}

.zm-btn{min-height:40px;padding:9px 14px;border-radius:10px;border:1px solid var(--zm-line);
  background:#1d1a1f; background:var(--zm-card);color:var(--zm-t1);font:inherit;font-size:.84rem;font-weight:600;
  cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;
  transition:border-color .16s ease,background .16s ease}
.zm-btn:hover{border-color:var(--zm-a)}
.zm-btn:active{transform:translateY(1px)}
/* Regla de pareja del violeta de Zook: sobre superficie oscura el acento es
   #7c5cff y quiere TINTA encima (4.55:1); blanco sobre ese violeta da 4.35:1 y
   reprueba. En claro el acento es #6d3bff y quiere blanco (5.65:1). El portal
   ya resuelve cuál toca en --on-violet. */
.zm-btn-p{background:var(--zm-a);border-color:var(--zm-a);color:var(--zm-on-a)}
.zm-btn-d{color:#fb7185;border-color:rgba(251,113,133,.4)}
html[data-theme="light"] .zm-btn{background:#fff;background:var(--zm-card)}
/* Va DESPUÉS del fondo claro de .zm-btn y con la misma especificidad: si no,
   el botón primario en tema claro sale blanco sobre blanco. */
html[data-theme="light"] .zm-btn-p{background:var(--zm-a);border-color:var(--zm-a)}
html[data-theme="light"] .zm-btn-d{color:#be123c}
.zm-reset{margin-top:var(--zm-4);background:none;border:0;color:var(--zm-t3);font:inherit;
  font-size:.8rem;text-decoration:underline;cursor:pointer;padding:var(--zm-2)}

/* ── Tablero ─────────────────────────────────────────────────────────────── */
/* Las tres etapas arrancan arriba para que sus encabezados queden a la misma
   altura; solo el centro se centra contra el alto total del tablero. */
.zm-board{position:relative;display:grid;grid-template-columns:1fr 1.15fr 1fr;
  gap:clamp(20px,4vw,52px);align-items:start;margin-top:var(--zm-6)}
.zm-links{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;z-index:0}
.zm-ln{fill:none;stroke:var(--zm-line);stroke-width:2}
.zm-ln-off{stroke-dasharray:3 5;opacity:.5}
.zm-fl{fill:none;stroke:var(--zm-a);stroke-width:2.5;opacity:.8;stroke-linecap:round;
  stroke-dasharray:4 14;animation:zm-flow 1.5s linear infinite}
.zm-fl-out{animation-name:zm-flow-out;stroke:var(--zm-ok)}
@keyframes zm-flow{from{stroke-dashoffset:36}to{stroke-dashoffset:0}}
@keyframes zm-flow-out{from{stroke-dashoffset:0}to{stroke-dashoffset:36}}

.zm-col{position:relative;z-index:1;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(84px,1fr));
  gap:var(--zm-4) var(--zm-2);align-content:start;justify-items:center}
.zm-colh{grid-column:1/-1;justify-self:stretch;margin-bottom:var(--zm-1)}
.zm-colh h3{font-size:.95rem;font-weight:800;letter-spacing:-.01em;color:var(--zm-t1);margin:0}
/* Resumen de la etapa. Reemplaza al "Sin movimiento aún" repetido pieza por
   pieza: una cuenta honesta arriba pesa menos que cinco avisos de vacío. */
.zm-colsum{font-size:.78rem;color:var(--zm-t2);margin:2px 0 0;line-height:1.4;font-weight:600}
.zm-colempty,.zm-add{grid-column:1/-1;justify-self:stretch}
.zm-colempty{font-size:.8rem;color:var(--zm-t3);margin:0;padding:var(--zm-3);
  border:1px dashed var(--zm-line);border-radius:12px}

/* ── Burbuja ─────────────────────────────────────────────────────────────── */
/* La pieza dejó de ser un recuadro: es un círculo con su icono y el texto
   debajo. El número va abajo y no como insignia encima porque "$48,500" no cabe
   en una insignia sin cortarse, y cortar una cifra es peor que no ponerla. */
.zm-node{display:flex;flex-direction:column;align-items:center;gap:6px;width:100%;
  padding:2px 0;background:none;border:0;color:inherit;font:inherit;text-align:center;
  cursor:pointer;border-radius:16px}
.zm-node:focus-visible,.zm-ag:focus-visible{outline:3px solid var(--zm-a);outline-offset:3px}
.zm-bub{position:relative;display:block;width:60px;height:60px;flex:0 0 auto;
  animation:zm-float 5.4s ease-in-out infinite;
  animation-delay:calc(var(--i,0) * -.63s);
  animation-duration:calc(5.1s + var(--i,0) * .33s)}
/* Disco LLENO y opaco, sin excepción. Antes los logos de plataforma iban en
   color sólido y el resto en un relleno translúcido del 10-16 %: dos lenguajes
   de dibujo en la misma fila, y el translúcido además cambiaba de tono según lo
   que tuviera detrás. Ahora el fondo es el color del tono y el dibujo va en la
   tinta que le hace pareja (--ton): claro sobre disco oscuro en tema claro,
   oscuro sobre disco claro en tema oscuro. Los pasos de --tc ya están medidos
   contra la tarjeta de cada tema, así que el disco siempre se separa del fondo. */
.zm-ic{display:flex;align-items:center;justify-content:center;width:100%;height:100%;
  border-radius:50%;background:var(--tc);color:var(--ton);border:0;
  transition:transform .22s cubic-bezier(.22,1,.36,1),box-shadow .22s ease}
/* El dibujo ocupa la mayor parte del disco y se mide en PORCENTAJE, no en px:
   así el mismo número sirve en el tablero, en la ficha y en móvil, y cambiar el
   tamaño de la burbuja no deja el icono nadando dentro. Cada familia lleva su
   proporción porque no dibujan igual dentro del lienzo de 24: los de línea
   traen margen propio y los logos de marca llegan al borde.

   OJO CON EL PREFIJO. zk-modules.css trae `[id^="zk-"][id$="-root"] .zk-ic`
   con `width:15px`, y esa regla —dos atributos y una clase— le gana a un
   `.zm-ic .zk-ic` pelado. Sin el `[id$="-root"]` de aquí el icono sale de 15 px
   dentro de un disco de 60 y la burbuja se ve VACÍA en el teléfono. Cada regla
   de tamaño de icono del tablero necesita el prefijo; la ficha no, porque vive
   en <body> y ahí la regla de zk-modules ni siquiera aplica. */
[id$="-root"] .zm-ic .zk-ic,.zm-sheet .zm-ic .zk-ic{width:58%;height:58%;
  color:var(--ton);filter:var(--tflt)}

/* ── Discos llenos: plataformas y agentes ────────────────────────────────── */
/* Un canal de un tercero se reconoce por su marca antes que por su etiqueta.
   El logo va en blanco sobre el color de la plataforma, que es como su dueño
   lo publica; el degradado de Instagram se pinta en CSS porque una
   <linearGradient> dentro de un sprite EXTERNO no resuelve al clonarse.
   El borde queda transparente: un aro encima del disco de marca lo ensucia. */
.zm-ic[data-brand]{color:#fff;background:#334155}
/* `fill` EXPLÍCITO, no heredado del atributo del sprite. zk-modules.css pone
   `fill:none` en el <svg> y los logos de marca son figuras rellenas, no de
   trazo. Chrome respeta el `fill="currentColor"` que traen sus paths y los
   pinta; Safari de iOS deja ganar al `fill:none` heredado dentro del árbol
   sombra del <use> y los tres logos salían como discos de color VACÍOS en el
   teléfono. Declararlo aquí lo resuelve en los dos motores. Va acotado a
   [data-brand]: en un icono de línea, `fill:currentColor` lo convertiría en
   una mancha. */
[id$="-root"] .zm-ic[data-brand] .zk-ic,.zm-sheet .zm-ic[data-brand] .zk-ic{
  width:52%;height:52%;color:#fff;fill:currentColor;stroke:none;filter:none}
.zm-ic[data-brand="whatsapp"]{background:#25d366}
.zm-ic[data-brand="facebook"]{background:#1877f2}
.zm-ic[data-brand="instagram"]{background:#d6249f;
  background:radial-gradient(circle at 28% 106%,#fdf497 0%,#fdf497 5%,#fd5949 45%,#d6249f 62%,#285aeb 92%)}
/* En tema claro el verde de WhatsApp contra la tarjeta blanca da 1.98:1: el
   disco se ve, pero su borde casi no. Un aro tenue lo recorta sin tocar el
   color de la marca. Quién es cada canal no depende del color: el nombre va
   debajo en texto de contraste pleno. */
html[data-theme="light"] .zm-ic[data-brand]{border-color:rgba(15,17,21,.10)}

/* Los agentes llevan SU personaje, el mismo robot que el cliente ya vio en la
   landing y en "Cómo funciona". Un dibujo genérico obligaba a aprenderse dos
   caras para el mismo Alex. Van sobre el relleno tenue de su propio color, no
   sobre un disco lleno: el personaje ya trae los suyos y encima de un disco
   saturado se ensucia. El anillo que late y el disco más grande siguen
   marcando que estos tres son los que actúan. */
.zm-ic-ag{background:var(--zm-face);border:2px solid var(--tc)}
.zm-face{width:92%;height:92%;object-fit:contain;display:block;pointer-events:none}
/* El dibujo del sprite es el respaldo: solo sale si la imagen no cargó. */
.zm-noface .zm-face{display:none}
[id$="-root"] .zm-ic-ag .zk-ic,.zm-sheet .zm-ic-ag .zk-ic{display:none;filter:none}
[id$="-root"] .zm-ic-ag.zm-noface .zk-ic,.zm-sheet .zm-ic-ag.zm-noface .zk-ic{
  display:block;width:56%;height:56%;color:var(--tc)}
.zm-node:hover .zm-ic,.zm-ag:hover .zm-ic{transform:scale(1.07);
  box-shadow:0 0 0 6px rgba(148,163,184,.12);
  box-shadow:0 0 0 6px color-mix(in srgb, var(--tc) 14%, transparent)}
.zm-node:active .zm-ic,.zm-ag:active .zm-ic{transform:scale(.94)}
.zm-node.zm-sel .zm-ic,.zm-ag.zm-sel .zm-ic{box-shadow:0 0 0 3px var(--zm-card),0 0 0 6px var(--tc)}
.zm-txt{display:flex;flex-direction:column;align-items:center;gap:4px;max-width:100%}
.zm-lb{font-size:.82rem;font-weight:700;line-height:1.25;color:var(--zm-t1);text-wrap:balance}
.zm-v{font-size:.78rem;font-weight:700;color:var(--zm-t2);font-variant-numeric:tabular-nums;
  background:rgba(148,163,184,.14);border-radius:999px;padding:1px 9px}
.zm-tag{font-size:.72rem;color:var(--zm-t3)}
/* Cuándo pasó lo último aquí. Solo la hora: la frase completa no cabe bajo una
   burbuja de 90 px sin convertirla en una columna de texto, y va en la ficha. */
.zm-ago{font-size:.7rem;color:var(--zm-t3);line-height:1.2;font-variant-numeric:tabular-nums}
/* Punto vivo: "aquí pasó algo hace poco, ábrelo". Al borde del círculo y con
   halo del color de la tarjeta para que se despegue del icono. */
.zm-live::before{content:"";position:absolute;top:2px;right:2px;width:9px;height:9px;
  border-radius:50%;background:var(--zm-ok);box-shadow:0 0 0 2.5px var(--zm-card);z-index:2}
/* Pieza del agente que se está viendo. Se MARCA, no se aísla: atenuar el resto
   dejaría a Vega con dos burbujas encendidas y el tablero parecería roto. */
.zm-mine .zm-ic{box-shadow:0 0 0 4px rgba(148,163,184,.22);
  box-shadow:0 0 0 4px color-mix(in srgb, var(--tc) 26%, transparent)}
.zm-off{opacity:.55}
.zm-off .zm-ic{filter:grayscale(1)}
.zm-edic{position:absolute;right:-2px;bottom:-2px;display:flex;width:22px;height:22px;
  border-radius:50%;align-items:center;justify-content:center;color:var(--zm-t1);
  background:var(--zm-card);border:1px solid var(--zm-line)}
[id$="-root"] .zm-edic .zk-ic{width:12px;height:12px;color:currentColor}
.zm-add{min-height:44px;border:1px dashed var(--zm-line);border-radius:14px;background:none;
  color:var(--zm-t3);font:inherit;font-size:.84rem;font-weight:600;cursor:pointer}
.zm-add:hover{border-color:var(--zm-a);color:var(--zm-t1)}

/* ── Animaciones ─────────────────────────────────────────────────────────── */
/* Flotación: cada burbuja con su propio ritmo (`--i` corre el retardo y estira
   la duración). Sincronizadas se leerían como un parpadeo de la página entera,
   no como piezas vivas. El recorrido es de 5 px: se nota y no marea. */
@keyframes zm-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
/* Entrada escalonada, solo en el primer pintado. */
.zm-enter .zm-node,.zm-enter .zm-ag{animation:zm-pop .46s cubic-bezier(.22,1,.36,1) both;
  animation-delay:calc(var(--i,0) * 55ms)}
@keyframes zm-pop{from{opacity:0;transform:translateY(8px) scale(.86)}to{opacity:1;transform:none}}
/* Toque: la onda sale del pseudo-elemento y no del contenedor, así no pelea con
   la flotación por la propiedad `animation`. */
.zm-bub::after{content:"";position:absolute;inset:0;border-radius:50%;
  border:2px solid var(--tc,#94a3b8);opacity:0;pointer-events:none}
.zm-bub.zm-tap::after{animation:zm-ripple .55s cubic-bezier(.22,1,.36,1)}
@keyframes zm-ripple{from{opacity:.85;transform:scale(1)}to{opacity:0;transform:scale(1.75)}}
/* La animación no es opcional: sin esta rama el mapa marea a quien pidió menos
   movimiento. Se conserva el estado final de cada una, nunca el arranque. */
@media(prefers-reduced-motion:reduce){
  .zm-fl{animation:none;opacity:.4}
  .zm-bub{animation:none}
  .zm-enter .zm-node,.zm-enter .zm-ag{animation:none}
  .zm-bub.zm-tap::after{animation:none}
  .zm-ic{transition:none}
}

/* ── El equipo ───────────────────────────────────────────────────────────── */
/* Tres agentes, no uno. Alex atiende, Nova da seguimiento y Vega atrae y mide;
   cada burbuja abre qué módulos del panel trabaja. */
.zm-colc{align-self:center;grid-template-columns:1fr}
.zm-hub{position:relative;border-radius:22px;border:1px solid rgba(124,58,237,.34);
  background:#1d1a1f; background:var(--zm-card);width:100%;
  padding:var(--zm-4) var(--zm-4) var(--zm-3);
  box-shadow:0 0 0 6px rgba(124,58,237,.06),0 18px 44px -34px rgba(124,58,237,.55)}
html[data-theme="light"] .zm-hub{background:#fff;background:var(--zm-card)}
.zm-hub-k{font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;
  color:var(--zm-t3);margin:0 0 var(--zm-3);text-align:center}
/* Las tres caras son PESTAÑAS y se quedan siempre a la vista. La elegida crece
   y late; las otras dos se quedan chicas y atenuadas, apoyadas en la misma
   línea de abajo para que crecer no empuje la fila. */
.zm-team{--tgap:var(--zm-2);display:grid;grid-template-columns:repeat(3,1fr);
  gap:var(--tgap);align-items:end}
/* El elegido se corre al CENTRO y los otros dos a los lados. El desplazamiento
   viaja en `--shift` (en columnas) y se aplica con transform, no reordenando el
   DOM: un cambio de `order` en la rejilla salta sin interpolar, y además el
   orden de lectura para teclado y lector de pantalla se queda como está.
   El ancho de columna es el 100% del propio botón más el hueco de la rejilla. */
.zm-ag{display:flex;flex-direction:column;align-items:center;gap:6px;padding:2px 0;
  background:none;border:0;color:inherit;font:inherit;text-align:center;cursor:pointer;
  border-radius:16px;min-width:0;opacity:.62;
  transform:translateX(calc(var(--shift,0) * 100% + var(--shift,0) * var(--tgap)));
  transition:transform .36s cubic-bezier(.22,1,.36,1),opacity .2s ease}
.zm-ag:hover{opacity:.85}
.zm-ag-on{opacity:1}
.zm-bub-ag{width:52px;height:52px;transition:width .2s cubic-bezier(.22,1,.36,1),height .2s cubic-bezier(.22,1,.36,1)}
.zm-ag-on .zm-bub-ag{width:66px;height:66px}
/* Anillo vivo solo en el agente que se está viendo: es el que está trabajando
   en pantalla. En los tres a la vez era decoración y ya no señalaba nada. */
.zm-ag-on .zm-bub-ag::before{content:"";position:absolute;inset:-5px;border-radius:50%;
  border:2px solid rgba(167,139,250,.42);
  border-color:color-mix(in srgb, var(--tc,#a78bfa) 42%, transparent);
  animation:zm-pulse 2.8s ease-out infinite}
@keyframes zm-pulse{0%{transform:scale(.94);opacity:.85}70%{transform:scale(1.16);opacity:0}100%{opacity:0}}
@media(prefers-reduced-motion:reduce){
  .zm-ag-on .zm-bub-ag::before{animation:none;opacity:.45}
  /* El acomodo se conserva (es la posición final, no un adorno); lo que se
     quita es el recorrido. */
  .zm-bub-ag,.zm-ag{transition:none}
}
.zm-ag-nm{font-size:.86rem;font-weight:700;color:var(--zm-t2);letter-spacing:-.01em;line-height:1.2}
.zm-ag-on .zm-ag-nm{font-size:.98rem;font-weight:800;color:var(--zm-t1)}

/* ── Detalle del agente elegido ──────────────────────────────────────────── */
.zm-agd{margin-top:var(--zm-4);text-align:center}
.zm-agd-h{margin:0;font-size:.8rem;color:var(--zm-t3);display:flex;gap:7px;
  align-items:baseline;justify-content:center;flex-wrap:wrap}
.zm-agd-h b{font-size:1rem;color:var(--zm-t1);letter-spacing:-.01em}
.zm-agd-v{margin:3px 0 0;font-size:.84rem;font-weight:600;color:var(--zm-t2);line-height:1.35}
/* El camino al módulo va SIEMPRE, con o sin movimiento. Cuando la cuenta está
   vacía es cuando más falta hace: es lo que la saca de vacía. */
.zm-agd-go{display:flex;align-items:center;justify-content:center;gap:var(--zm-2);
  flex-wrap:wrap;margin-top:var(--zm-3)}
.zm-agd-mas{background:none;border:0;color:var(--zm-t3);font:inherit;
  font-size:.79rem;text-decoration:underline;cursor:pointer;padding:var(--zm-2);min-height:40px}
.zm-agd-mas:hover{color:var(--zm-t1)}

/* ── Lo último que hizo ──────────────────────────────────────────────────── */
/* La pieza que convierte al mapa en prueba: un hecho con hora, con nombre y con
   el tiempo que tardó. Va alineado a la izquierda aunque el resto de la tarjeta
   esté centrado: es una frase, no un rótulo, y centrada se lee peor. */
.zm-last{display:flex;align-items:center;gap:10px;text-align:left;
  margin:var(--zm-3) 0 0;padding:10px 11px;border-radius:14px;
  background:rgba(148,163,184,.10);border:1px solid var(--zm-line)}
.zm-last-t{flex:1;min-width:0;font-size:.82rem;line-height:1.4;color:var(--zm-t2);text-wrap:pretty}
.zm-last-t b{color:var(--zm-t1);font-weight:700}
.zm-ic-s{width:34px;height:34px;flex:0 0 auto}
.zm-last-b{flex:0 0 auto;min-height:36px;padding:7px 12px;border-radius:9px;
  border:1px solid var(--zm-line);background:var(--zm-card);color:var(--zm-t1);
  font:inherit;font-size:.79rem;font-weight:600;cursor:pointer}
.zm-last-b:hover{border-color:var(--zm-a)}
/* Sin hechos todavía: se dice qué falta y se ofrece el camino. Es el estado del
   primer día y es cuando el botón más falta hace. */
.zm-last-0{display:block;font-size:.82rem;line-height:1.45;color:var(--zm-t2);text-wrap:pretty}
.zm-last-0 .zm-last-b{display:block;margin-top:var(--zm-2)}

.zm-hub-pct{font-size:.76rem;color:var(--zm-ok);font-weight:600;text-wrap:pretty;
  margin:var(--zm-2) 0 0;text-align:center;line-height:1.4}
/* La segunda línea del agente: lo que está esperando (Nova) o el ritmo con el
   que sale (Vega). No es una cifra de logro como el porcentaje de Alex, así que
   va en tinta secundaria: informa, no presume. */
.zm-agd-n{font-size:.78rem;color:var(--zm-t3);text-wrap:pretty;
  margin:var(--zm-2) 0 0;text-align:center;line-height:1.45}
/* Lo que ya se pasó de fecha. Va aparte y con su color porque en la línea gris
   de arriba desaparecía justo cuando era lo único que había que ver. */
.zm-agd-w{display:inline-flex;align-items:center;gap:6px;font-size:.76rem;font-weight:600;
  color:var(--zm-warn);background:rgba(245,158,11,.14);
  border-radius:999px;padding:4px 11px;margin:var(--zm-2) auto 0;line-height:1.3}
.zm-agd-w .zk-ic{width:13px;height:13px;color:currentColor}
.zm-acts{list-style:none;margin:var(--zm-3) 0 0;padding:var(--zm-3) 0 0;
  border-top:1px solid var(--zm-line);display:flex;flex-wrap:wrap;gap:var(--zm-2);justify-content:center}
.zm-act{display:flex;align-items:center;gap:6px;font-size:.78rem;color:var(--zm-t2);line-height:1.2;
  background:rgba(148,163,184,.12);border-radius:999px;padding:6px 11px 6px 9px}
[id$="-root"] .zm-act .zk-ic{width:13px;height:13px;color:var(--zm-ok);flex:0 0 auto}
.zm-act.zm-off{opacity:.5}
.zm-editing .zm-act{cursor:pointer;min-height:36px}
.zm-editing .zm-act:hover{background:rgba(124,58,237,.16)}
.zm-act-t{font-style:normal;font-size:.72rem;color:var(--zm-t3)}

/* ── Ficha ───────────────────────────────────────────────────────────────── */
/* <dialog> nativo: capa superior (nunca la tapa ni la atraviesa el tablero),
   trampa de foco, Escape y cierre al tocar fuera, sin pelear con z-index. */
/* El fondo va en DOS declaraciones a propósito. La primera es un color literal;
   la segunda usa el token. Si el token no resuelve (host que no define --card,
   o que lo define translúcido tipo cristal como /admin), la declaración
   inválida se descarta y queda la literal. Una ficha transparente deja las
   letras encima del hilo, que es exactamente lo que no puede volver a pasar. */
.zm-sheet{position:fixed;margin:0;inset:auto 20px 20px auto;width:min(340px,calc(100vw - 40px));
  max-height:76vh;overflow:auto;border:1px solid var(--zm-line);
  border-radius:18px;padding:var(--zm-4);color:var(--zm-t1);color-scheme:dark;
  background:#1d1a1f;
  background:var(--zm-card);
  box-shadow:0 30px 70px -40px rgba(0,0,0,.75),0 0 0 1px rgba(0,0,0,.25)}
html[data-theme="light"] .zm-sheet{--zm-card:#ffffff;color-scheme:light;
  box-shadow:0 30px 70px -42px rgba(15,17,21,.4),0 0 0 1px rgba(15,17,21,.06)}
/* En escritorio la ficha es un panel de esquina: atenuar todo el tablero para
   leer una explicación es desproporcionado. En móvil sí cubre media pantalla,
   y ahí el fondo oscuro es lo que separa la hoja del contenido. */
.zm-sheet::backdrop{background:rgba(10,10,11,.24)}
/* Respaldo del ::backdrop para navegadores sin <dialog> modal. El z-index es
   uno menos que el de la ficha, que ahí tampoco está en la capa superior. */
.zm-scrim{position:fixed;inset:0;z-index:59;background:rgba(10,10,11,.55)}
.zm-sheet[open]{z-index:60}
.zm-sh-h b:focus-visible{outline:none}
.zm-sheet[open]{animation:zm-in .22s cubic-bezier(.22,1,.36,1)}
@keyframes zm-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.zm-sheet[open]{animation:none}}
.zm-sh-h{display:flex;align-items:center;gap:10px;margin-bottom:var(--zm-3);flex-wrap:wrap}
.zm-sh-h .zm-ic{width:42px;height:42px;flex:0 0 auto}
.zm-sh-h b{font-size:1rem;color:var(--zm-t1);min-width:0}
.zm-sh-s{font-size:.76rem;color:var(--zm-t3);flex:1;min-width:0}
.zm-x{width:40px;height:40px;border:0;border-radius:10px;background:none;color:var(--zm-t3);
  cursor:pointer;display:flex;align-items:center;justify-content:center;flex:0 0 auto;margin-left:auto}
.zm-x:hover{background:rgba(148,163,184,.16);color:var(--zm-t1)}
.zm-x .zk-ic{width:16px;height:16px;color:currentColor}
/* Los hechos, arriba de todo en la ficha: es a lo que el dueño abrió esta pieza.
   Pueden ser de agentes distintos (Alex agendó la cita, Nova la recordó), así
   que cada uno lleva el nombre de quién fue. Sin ese nombre serían un montón de
   frases sueltas y el mapa dejaría de explicar quién hace qué. */
.zm-ev{font-size:.87rem;line-height:1.5;color:var(--zm-t2);margin:0 0 var(--zm-2);
  padding:10px 12px;border-radius:12px;text-wrap:pretty;
  background:rgba(148,163,184,.10);border:1px solid var(--zm-line)}
.zm-ev:last-of-type{margin-bottom:var(--zm-3)}
.zm-ev b{color:var(--zm-t1);font-weight:700}
.zm-ev i{display:block;font-style:normal;font-size:.72rem;font-weight:700;
  letter-spacing:.02em;color:var(--zm-t3);margin-bottom:2px}
.zm-why{font-size:.87rem;line-height:1.55;color:var(--zm-t2);margin:0 0 var(--zm-3);text-wrap:pretty}
.zm-big{font-size:1.7rem;font-weight:800;color:var(--zm-t1);font-variant-numeric:tabular-nums;
  margin-bottom:var(--zm-3);line-height:1.1}
.zm-big small{display:block;font-size:.75rem;font-weight:500;color:var(--zm-t3);margin-top:2px}
/* Módulos del agente. Los que no están contratados NO se esconden: enseñar de
   qué se encarga el agente que todavía no tienes explica el producto mejor que
   un anuncio, y se distinguen por el candado, no solo por el color. */
.zm-modh{font-size:.72rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--zm-t3);margin:0 0 var(--zm-2)}
.zm-mods{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:var(--zm-3)}
.zm-mod{display:inline-flex;align-items:center;gap:6px;min-height:34px;padding:6px 11px;
  border-radius:999px;border:1px solid var(--zm-line);background:var(--zm-sunk);
  color:var(--zm-t1);font:inherit;font-size:.8rem;font-weight:600;cursor:pointer}
.zm-mod .zk-ic{width:13px;height:13px;color:var(--zm-ok);flex:0 0 auto}
.zm-mod:hover{border-color:var(--zm-a)}
.zm-mod-off{color:var(--zm-t3);cursor:default;opacity:.85}
.zm-mod-off .zk-ic{color:var(--zm-t3)}
.zm-mod-off:hover{border-color:var(--zm-line)}
.zm-warn{font-size:.82rem;line-height:1.5;color:var(--zm-t2);background:rgba(217,119,6,.12);
  border:1px solid rgba(217,119,6,.32);border-radius:10px;padding:9px 11px;margin:0 0 var(--zm-3)}
.zm-f{display:block;margin-bottom:var(--zm-3)}
.zm-f span{display:block;font-size:.76rem;font-weight:600;color:var(--zm-t3);margin-bottom:5px}
.zm-f input{width:100%;min-height:44px;padding:10px 12px;border-radius:10px;
  border:1px solid var(--zm-line);background:var(--zm-sunk);color:var(--zm-t1);font:inherit;font-size:.9rem}
.zm-f input:focus-visible{outline:3px solid var(--zm-a);outline-offset:2px}
.zm-sw{display:flex;align-items:center;gap:10px;min-height:44px;cursor:pointer;margin-bottom:var(--zm-3)}
.zm-sw input{position:absolute;opacity:0;width:0;height:0}
.zm-sw .sl{width:44px;height:26px;border-radius:999px;background:var(--zm-sunk);
  border:1px solid var(--zm-line);position:relative;flex:0 0 auto;transition:background .18s ease}
.zm-sw .sl::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;
  background:var(--zm-t3);transition:transform .18s cubic-bezier(.22,1,.36,1),background .18s ease}
.zm-sw input:checked + .sl{background:var(--zm-a);border-color:var(--zm-a)}
.zm-sw input:checked + .sl::after{transform:translateX(18px);background:#fff}
.zm-sw input:focus-visible + .sl{outline:3px solid var(--zm-a);outline-offset:2px}
.zm-sw-l{font-size:.86rem;color:var(--zm-t1)}
.zm-icons{display:grid;grid-template-columns:repeat(6,1fr);gap:6px;margin-bottom:var(--zm-3)}
.zm-icp{aspect-ratio:1;min-height:40px;border-radius:9px;border:1px solid var(--zm-line);
  background:var(--zm-sunk);cursor:pointer;display:flex;align-items:center;justify-content:center}
.zm-icp .zk-ic{width:17px;height:17px;color:var(--zm-t2)}
.zm-icp.on{border-color:var(--zm-a);background:rgba(124,58,237,.14)}
.zm-move{display:flex;gap:var(--zm-2);flex-wrap:wrap}

/* ── Móvil ───────────────────────────────────────────────────────────────── */
/* A una columna, las burbujas caben de tres en tres: la etapa entera se ve de
   un golpe y la secuencia (llegan → equipo → resultados) se lee sin scrollear
   entre una y otra. */
@media(max-width:900px){
  .zm-board{grid-template-columns:1fr;gap:var(--zm-6)}
  .zm-col{grid-template-columns:repeat(3,1fr);gap:var(--zm-4) var(--zm-2)}
  /* El equipo NO se parte en tres: es un bloque a todo el ancho. */
  .zm-colc{grid-template-columns:1fr;align-self:stretch}
  .zm-bub{width:56px;height:56px}
  .zm-lb{font-size:.78rem}
  .zm-hub{padding:var(--zm-4) var(--zm-3) var(--zm-3)}
  .zm-team{--tgap:var(--zm-1)}
  .zm-bub-ag{width:48px;height:48px}
  .zm-ag-on .zm-bub-ag{width:62px;height:62px}
  .zm-ag-nm{font-size:.8rem}
  .zm-ag-on .zm-ag-nm{font-size:.92rem}
  /* El "Ver" cae a su propio renglón cuando ya no cabe: al lado de la frase, en
     390 px le dejaba dos palabras por línea. El icono se queda pegado al texto
     (mismo renglón) porque solo, arriba, no se entiende de qué es. */
  .zm-last{flex-wrap:wrap}
  .zm-last-t{flex:1 1 130px}
  .zm-last-b{margin-left:auto}
  /* Ficha como hoja inferior, pegada al borde y con área segura del teléfono.
     El ancho sale de los `inset`, no de width:100%: con la barra de scroll el
     100% se mide contra el viewport completo y el botón de cerrar se cortaba. */
  .zm-sheet{inset:auto 0 0 0;width:auto;max-width:none;max-height:80vh;
    border-radius:20px 20px 0 0;border-bottom:0;
    padding:var(--zm-4) var(--zm-4) calc(var(--zm-4) + env(safe-area-inset-bottom))}
  .zm-sheet::backdrop{background:rgba(10,10,11,.55)}
  .zm-sheet[open]{animation:zm-up .24s cubic-bezier(.22,1,.36,1)}
  @keyframes zm-up{from{transform:translateY(100%)}to{transform:none}}
  /* Asa de arrastre: dice "esto se cierra hacia abajo" sin texto. */
  .zm-sh-h::before{content:"";position:absolute;top:8px;left:50%;transform:translateX(-50%);
    width:38px;height:4px;border-radius:2px;background:var(--zm-line)}
  .zm-sh-h{padding-top:var(--zm-3)}
}
/* A dos columnas solo en los teléfonos de verdad angostos. El corte estaba en
   360 px, que es un ancho comunísimo (iPhone SE, Androids chicos), y les partía
   cada etapa en una fila más sin necesidad: a 360 las tres columnas caben. */
@media(max-width:344px){
  .zm-col{grid-template-columns:repeat(2,1fr)}
}
