/* ═══════════════════════════════════════════════════════════════════════════
   INBOX v2 — la hoja del rediseño aprobado
   ═══════════════════════════════════════════════════════════════════════════

   🔴 ESTE ARCHIVO NO SE EDITA A MANO PARA CAMBIAR UN VALOR.

   Es una COPIA VERBATIM del CSS de `docs/design/mockup-inbox-lienzo.html`
   (líneas 78-222, 232-1291 y 2261-2315 de ese archivo), extraída con un corte
   por índices y verificada: 326 llaves que abren, 326 que cierran, medido
   DESPUÉS de quitar los comentarios — porque el mockup tiene una llave suelta
   dentro de un comentario (`.orden{` citado en prosa) y un contador ingenuo la
   cuenta como código. Es la trampa nº4 del handoff, y mordió acá mismo.

   Se copió en vez de reescribirse por una razón: **retipear un valor es cómo se
   inventa uno.** Cada color de este archivo está medido y su medición vive en
   `docs/design/PALETA.html`. Un `#12314F` que alguien escriba de memoria como
   `#12314E` no lo caza ningún test y no lo ve ningún ojo.

   Lo que NO se copió, y por qué:
     · `.demo-barra` y `.demo-pista`  — la barra de controles del mockup, que
       existe para enseñar estados que en el producto dependen del dato.
     · `.notas` y `#med`             — la memoria de diseño y el medidor de
       contraste embebido. Viven en el mockup, no en el producto.

   Cómo se cambia algo acá: se cambia en el mockup, se mide con su medidor, y
   se vuelve a extraer. El mockup es la fuente; esto es el destino.

   ⚠️ Mientras v2 y v1 convivan, esta hoja NO puede tocar a `base.html`: se sirve
   solo desde `inbox_v2.html`. Un selector de acá que llegue a producción cambia
   el inbox que la gente usa todo el día.
   ═══════════════════════════════════════════════════════════════════════════ */

  /* ════════════════════════════════════════════════════════════════════
     🔴 LA TIPOGRAFIA DE DISPLAY (JC, 29-ago: «el tipo de letra me dijeron que
     esta medio antiguo»). Tenian razon, y literalmente: era `Iowan Old Style`
     con Georgia de respaldo — un serif de ESTILO ANTIGUO. En una herramienta
     densa lee editorial, no instrumento.
     LO QUE SE HACE, y es lo de Linear y Stripe: el contraste ya no lo da la
     FAMILIA, lo dan el PESO y el TRACKING. Display y cuerpo comparten familia;
     lo que separa un nombre de una vista previa es que uno va en 650-700 con
     tracking negativo y el otro en 400. La tercera voz sigue siendo la
     monoespaciada, que es la que da el aire de instrumento.
     POR QUE ESTA PILA Y NO UNA FUENTE WEB:
       · `Segoe UI Variable Display` es la optica de TITULOS de Windows 11 — no
         es «la generica de siempre», es la variante para tamaños grandes.
       · Cero descarga: nada que bloquee el primer pintado en una recepcion con
         internet flojo, ni que dependa de un CDN externo.
       · Degrada en orden: Segoe UI en Windows 10, la del sistema en Mac/Linux.
     ⚠️ Con el sans BAJA el peso compensatorio del tema oscuro: el serif se
     adelgazaba por irradiacion y pedia 800; un sans a 800 se ve pesado.
     ════════════════════════════════════════════════════════════════════ */
  /* ───────── Escala de Atlassian, literal ───────── */
  :root{
    --s2:2px;--s4:4px;--s6:6px;--s8:8px;--s12:12px;--s16:16px;--s20:20px;
    --s24:24px;--s32:32px;--s40:40px;--s48:48px;--s64:64px;
    --fd:'Segoe UI Variable Display','Segoe UI',-apple-system,BlinkMacSystemFont,Inter,system-ui,sans-serif;
    --fb:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
    --fm:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
    --rp:18px;   /* radio de panel */
    --rc:12px;   /* radio de componente */
  }

  /* ════════════════════════════════════════════════════════════════════
     LA PALETA — AZUL. Decision de JC, 29-ago: «el terracota se va a ir para
     darle paso al azul».
     🔴 El terracota NO desaparece del producto: sobrevive en la TAZA del
     carril y en la marca de agua. Deja de ser el acento de INTERFAZ, que era
     la pregunta sin resolver del BACKLOG §0-D3 («reemplaza o convive»).
     Convivir daba cinco familias de color, que fue la objecion literal de JC.

     DOS ROLES DEL AZUL, y la razon es de accesibilidad, no de gusto:
       --primary       tinta y iconos SOBRE el fondo del tema
       --primary-fill  relleno solido de un boton, con --primary-ink encima
     En oscuro no pueden ser el mismo: un azul claro sirve para leer sobre
     negro, pero como relleno obliga a poner tinta casi negra encima — que es
     exactamente lo que JC reporto dos veces («las letras negras siguen siendo
     muy dark»). Con dos roles, el relleno es un azul profundo y la tinta va
     blanca. Medido: blanco sobre #31689F = 5.81:1, y ese relleno contra el
     panel = 3.00:1 (el minimo de WCAG 1.4.11 para componentes).
     ════════════════════════════════════════════════════════════════════ */
  :root{
    /* 🔴 LA TINTA NO ES NEGRA (JC, 29-ago): «las letras esas negras no me
       gustan; puede que esten correctas en papel pero no lo veo agradable ni
       comodo... azul oscuro pero que no parezca negro, y tampoco un carnaval».
       Tenia razon y hay precedente: los sistemas azules serios no usan negro
       para la tinta — Stripe usa #0A2540, Linear un pizarra azulado. Un negro
       puro sobre papel golpea, y ademas no pertenece a la paleta: es el unico
       color de la pantalla que no sale de ningun lado.
       LO QUE EVITA EL CARNAVAL: las TRES tintas son el MISMO azul a distintas
       claridades, la misma familia que --primary (tono ~209 grados). Una tinta,
       tres pesos — no tres colores.
         --text  #12314F   peor par 10.52  (antes 13.79, un casi-negro)
         --muted #3E5A76   peor par  5.67
         --tenue #4A6280   peor par  4.96
       Los tres siguen muy por encima de 4.5: se gano comodidad sin gastar
       contraste. */
    --hondo:#E4E5E2;        /* EL LIENZO — el fondo hundido */
    --panel:#FCFCFA;        /* lo que flota encima */
    --panel-2:#EFEFEC;      /* superficie dentro de un panel */
    --text:#12314F;         /* 16.98 sobre panel */
    --muted:#3E5A76;        /* 6.53 */
    --tenue:#4A6280;        /* 5.69 */
    /* 🔴 CORREGIDO (JC, 29-ago): «en oscuro el enfasis quedo perfecto, en claro
       no se nota». Era el espejo exacto del defecto de ayer: `--realce` era
       #FFFFFF y `--panel` es #FCFCFA — 1.027:1 entre ellos, o sea invisible.
       En oscuro la elevacion puede subir en luminosidad porque hay recorrido;
       en claro el panel ya esta casi en blanco y no queda hacia donde subir.
       Entonces en tema claro la elevacion se dice al reves: la fila activa
       BAJA hacia un tinte de acento, con el mismo salto que la escalera oscura
       (1.16 contra el panel, igual que los 1.14-1.20 de alla).
       Medido sobre este realce: text 14.64 · muted 5.63 · tenue 4.91 ·
       primary 7.55. Los cuatro pasan. */
    --realce:#E4ECF6;       /* la fila ACTIVA en claro: tinte de acento */
    --primary:#1B4B7A;      /* MEDIDO 25-ago · 8.76 sobre panel */
    --primary-fill:#1B4B7A; /* en claro el relleno es el mismo azul */
    --primary-ink:#FFFFFF;  /* 9.00 sobre el relleno */
    --soft:rgba(27,75,122,.12);
    --foco-halo:rgba(27,75,122,.15);
    --out:#DEE8F2;          /* burbuja saliente */
    --warn:#87590A;         /* SOLO donde haga falta 4.5 (casi nunca) */
    /* 🔴 LA TINTA QUE VA SOBRE UN RELLENO --warn, Y ES UN TOKEN PORQUE CAMBIA DE LADO
       ENTRE LOS TEMAS. En claro --warn es un marron oscuro y la tinta va blanca; en
       oscuro es un ambar CLARO y la blanca se desaparece (MEDIDO 2.09; umbral de
       icono 3.0). Se estaba usando --primary-ink —blanco en los dos temas— en los
       DOS sitios que rellenan con --warn, asi que el defecto era doble.
       ⚠️ Y la hoja lo tenia anotado como «la tinta blanca encima mide 5.77», sin decir
       de que tema. Un numero medido en UN tema y escrito sin su tema se lee como si
       valiera para los dos, y apaga la pregunta (E32). */
    --warn-ink:#FFFFFF;     /* 6.06 sobre --warn */
    /* 🔴 EL ACENTO DE LA MARCA, Y VA POR TEMA. Lo cazo la medicion al escribir esto,
       reintroduciendo un defecto YA PAGADO: `#C2410C` fijo da 5.04 en claro pero **3.30
       en oscuro**, y ese 3.30 exacto ya esta escrito en la bitacora del rediseño —«se
       dejo fijo razonando *la marca es identidad, no cambia con el tema*»—.
       `base_v2.html` lo resolvio con dos valores; aca se repiten porque el inbox NO carga
       esa hoja. ⚠️ Hay prueba que exige que los dos archivos digan lo MISMO: dos copias de
       un color se separan en silencio. */
    --marca-co:#C2410C;     /* 5.04 sobre --panel */
    --warn-vivo:#B87D00;    /* borde, icono y relleno: umbral 3.0 -> 3.42 */
    --ok:#2C6642; --danger:#A32020;
    --sombra-1:0 1px 2px rgba(20,26,34,.06);
    --sombra-2:0 2px 4px rgba(20,26,34,.06), 0 12px 28px rgba(20,26,34,.09);
    --sombra-3:0 4px 8px rgba(20,26,34,.08), 0 24px 56px rgba(20,26,34,.14);
  }
  :root[data-theme="dark"]{
    /* 🔴 EL SERIF EN OSCURO SE VE MAS DELGADO que en claro — es optico, no de
       contraste (los 35 pares miden bien). Texto claro sobre fondo oscuro
       "adelgaza" por irradiacion. Se compensa con peso, no con color. */
    /* 🔴 ESCALERA DE ELEVACION (JC, 29-ago): «cuando esta enfatizado se ve bien,
       pero cuando no, lo negro hace perder accesibilidad». Medi los 28 pares y
       NO fallaba ninguno — el problema no era contraste, era elevacion:
       la fila activa subia a `--panel`, que ES el color de la lista donde vive.
       En claro eso funciona porque la sombra se ve; en oscuro una sombra sobre
       casi-negro no existe, asi que la lista entera se leia como una masa plana.
       En tema oscuro la elevacion sube en LUMINOSIDAD, no en sombra. Cuatro
       peldanos, cada uno un salto medido de ~1.15-1.20 respecto del anterior. */
    --hondo:#0B0D11;        /* el lienzo, el mas hundido */
    --panel:#181C23;        /* lista, hilo, ficha            salto 1.14 */
    --panel-2:#242A34;      /* campos, chips, segmentos      salto 1.18 */
    --realce:#2F3743;       /* la fila ACTIVA                salto 1.20 */
    --text:#E8EBF0;         /* 14.30 sobre panel */
    --muted:#A6AEBC;        /* 5.38 hasta en la superficie mas clara */
    --tenue:#9BA4B3;        /* 4.78 en la mas clara — subido por la escalera */
    --primary:#9CC0EA;      /* MEDIDO 25-ago · 9.25 sobre panel */
    --primary-fill:#31689F; /* relleno profundo: la tinta va BLANCA encima */
    --primary-ink:#FFFFFF;  /* 5.81 sobre el relleno */
    --soft:rgba(156,192,234,.16);
    --foco-halo:rgba(156,192,234,.24);
    --out:#20303F;
    --warn:#DFAB4C; --warn-vivo:#E7B85E;
    --warn-ink:#0B0D11;     /* 9.32 sobre --warn — en oscuro la tinta SE DA VUELTA */
    --marca-co:#E8743C;     /* 5.69 sobre --panel — el fijo daba 3.30 */
    --ok:#84BC99; --danger:#EA8D84;
    --sombra-1:0 1px 2px rgba(0,0,0,.5);
    --sombra-2:0 2px 4px rgba(0,0,0,.4), 0 12px 28px rgba(0,0,0,.5);
    --sombra-3:0 4px 8px rgba(0,0,0,.5), 0 24px 56px rgba(0,0,0,.6);
  }

  /* 🔴 EL INTERRUPTOR DE DENSIDAD SE QUITO (JC, 29-ago): «a veces menos cosas
     es mejor; me quedo con el modelo mas compacto». Un control que solo se toca
     una vez en la vida no gana su sitio en una barra que se mira todo el dia.
     Los valores compactos pasan a ser LOS valores. */
  :root{ --fila-y:var(--s6); --msg-y:2px }

  *{box-sizing:border-box}
  html,body{height:100%}
  body{margin:0; background:var(--hondo); color:var(--text);
       font:15px/1.5 var(--fb); overflow-x:clip;
       -webkit-font-smoothing:antialiased}
  .num{font-family:var(--fm); font-variant-numeric:tabular-nums lining-nums;
       letter-spacing:-.02em}
  :focus-visible{outline:2px solid var(--primary); outline-offset:3px; border-radius:6px}
  button{font:inherit}

  /* ══════════════════════════════════════════════════════════════════
     EL LIENZO — rejilla de aire. Ni una línea divisoria en toda la app.
     ══════════════════════════════════════════════════════════════════ */
  .lienzo{
    display:grid;
    grid-template-columns:62px 340px 1fr 308px;
    gap:var(--s12);
    padding:var(--s12);
    /* 🔴 `dvh` Y NO `vh`. JC, 5-sep, en el iPhone: «si hago scroll no se mueve el cuerpo
       del chat sino toda la pantalla, pero si recargo ya se mueve el texto del chat».
       Es el clasico de iOS Safari: `100vh` mide la ventana SIN la barra del navegador,
       asi que la pagina sale mas alta que lo visible y scrollea entera en vez de scrollear
       el hilo por dentro. Al recargar, la barra se contrae y coincide — por eso «si
       recargo ya se mueve», que es exactamente el sintoma que describio.
       `dvh` sigue a la barra mientras aparece y desaparece. El `vh` queda de respaldo
       para navegadores sin `dvh`; el orden importa, gana la ultima que se entienda. */
    height:100vh;
    height:100dvh;
  }
  .panel{
    background:var(--panel); border-radius:var(--rp);
    box-shadow:var(--sombra-2);
    display:flex; flex-direction:column; overflow:hidden; min-width:0;
  }

  /* ══════════ 1 · EL CARRIL ══════════ */
  .carril{
    background:var(--panel); border-radius:var(--rp); box-shadow:var(--sombra-2);
    display:flex; flex-direction:column; align-items:center;
    padding:var(--s12) 0; gap:var(--s4); position:relative;
    /* 🔴 `hidden auto`, no `hidden`. Al mudar Campanas y Agente aca (4-sep) el carril paso
       de cuatro entradas etiquetadas a SEIS: ~446px -> ~560px estimados. Con `height:100vh`
       en el lienzo y `overflow:hidden`, en una ventana CORTA —no estrecha: corta— lo que se
       recortaba era lo de abajo, y lo de abajo es SALIR. Y sin barra de scroll: no es que
       costara alcanzarlo, es que no estaba.
       En X sigue `hidden`. En Y, `auto` no pinta nada mientras quepa —el caso normal— y
       aparece solo cuando de verdad no cabe. Es una red, no un cambio de aspecto. */
    overflow:hidden auto;
  }
  /* La taza, con su humito vivo. Portada de base.html. */
  .marca-taza{position:relative; width:26px; height:30px; margin-bottom:var(--s12);
    background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 46'%3E%3Cpath d='M7,14C7.5,27,14,40,24,40C34,40,40.5,27,41,14Z' fill='%239A3412' stroke='%23E8743C' stroke-width='2.4' stroke-linejoin='round'/%3E%3Cpath d='M40,16c6.5,0.5,6.5,12,-0.5,13' fill='none' stroke='%23E8743C' stroke-width='2.4' stroke-linecap='round'/%3E%3Cellipse cx='24' cy='14' rx='17' ry='7' fill='%23C2410C' stroke='%23E8743C' stroke-width='2.4'/%3E%3Cpath d='M24,12.2C23,10.6,20.6,10.2,19.2,11.7C17.6,13.4,18.8,15.2,24,17.8C29.2,15.2,30.4,13.4,28.8,11.7C27.4,10.2,25,10.6,24,12.2Z' fill='%23FBF5EA'/%3E%3C/svg%3E") no-repeat center bottom; background-size:24px auto}
  .marca-taza::before,.marca-taza::after{content:""; position:absolute; bottom:16px;
    width:3px; height:9px; border-radius:4px;
    background:linear-gradient(to top,var(--text),transparent); opacity:0; filter:blur(.7px)}
  .marca-taza::before{left:9px; animation:humito 2.6s ease-out infinite}
  .marca-taza::after{left:15px; animation:humito 2.6s ease-out infinite 1.3s}
  @keyframes humito{0%{opacity:0;transform:translateY(4px) scaleY(.6)}
    30%{opacity:.5} 100%{opacity:0;transform:translateY(-9px) scaleY(1.1)}}
  @media (prefers-reduced-motion:reduce){
    .marca-taza::before,.marca-taza::after{animation:none;opacity:.35}}

  /* 🔴 ESCONDIDA EN ESCRITORIO, y por eso este cambio no toca lo que JC aprobo: el
     carril de escritorio mide 62px y ahi la taza sola es correcta —es una columna de
     iconos—. La palabra aparece SOLO en movil, donde el carril es una fila ancha y una
     taza sola no dice de que app se trata. */
  .marca-nombre{display:none}

  .rail-b{
    position:relative; width:44px; height:44px; border:0; background:transparent;
    border-radius:var(--rc); display:grid; place-items:center; cursor:pointer;
    color:var(--muted); transition:background .14s ease, color .14s ease;
  }
  .rail-b:hover{background:var(--panel-2); color:var(--text)}
  .rail-b.act{background:var(--soft); color:var(--primary)}
  /* 🔴 El estado activo NO depende solo del color: lleva un filo sólido a la
     izquierda. Si alguien no distingue el tinte, la barra sigue ahí. */
  /* 🔴 -9px, NO -12px, y la cuenta es exacta: el carril mide 62px (columna del grid, y
     `padding:var(--s12) 0` no deja relleno lateral) con un boton de 44px centrado, o sea
     9px a cada lado. Con -12px el filo caia en -3px..0px —ENTERO fuera del carril— y
     `.carril{overflow:hidden}` lo recortaba. Con -9px ocupa 0..3px: pegado al borde, que
     es donde se disenio.
     ⚠️ NO ES COSMETICO, y el comentario de abajo dice por que: es la senial que hace que
     el activo NO dependa solo del color. Recortado, el activo se decia solo con
     `background:--soft` + `color:--primary` — justo lo que esta linea vino a evitar.
     Y fallaba EN SILENCIO: la regla estaba en la hoja, asi que cualquier prueba que la
     buscara la encontraba. */
  .rail-b.act::before{content:""; position:absolute; left:-9px; top:9px; bottom:9px;
    width:3px; border-radius:0 3px 3px 0; background:var(--primary)}
  .rail-b .glifo{font-size:17px; line-height:1}
  .rail-b .n{position:absolute; top:4px; right:4px; min-width:16px; height:16px;
    border-radius:999px; background:var(--primary-fill); color:var(--primary-ink);
    font:700 9px/16px var(--fm); text-align:center; padding:0 3px}
  /* Salir vive al PIE del carril, lejos de las acciones de la conversación.
     Lo pidió JC: arriba, junto a «Cerrar», se puede leer como cerrar el hilo. */
  .rail-b.salir{color:var(--tenue); margin-top:var(--s4)}
  .rail-b.salir:hover{color:var(--danger); background:color-mix(in srgb,var(--danger) 12%,transparent)}
  .rail-et{font:600 9px/1 var(--fb); letter-spacing:.04em; color:var(--tenue);
    margin-top:-2px; margin-bottom:var(--s6)}
  .rail-sep{flex:1}
  /* 🔴 INERTE EN ESCRITORIO, y a proposito: `display:contents` deja a los cuatro filtros
     como hijos directos del carril, o sea el MISMO layout de flex que antes de que este
     envoltorio existiera. En movil se convierte en la barra de abajo (ver el @media). */
  .rail-filtros{display:contents}

  /* 🔴 EL BANNER DEL PERMISO DE NOTIFICACIONES. Portado de V1 con los tokens de acá.
     `[hidden]` tiene que GANARLE al `display:flex` o el banner se ve siempre — es la
     misma trampa que ya esta anotada en V1, y por eso la regla va explicita. */
  .notif-aviso{display:flex; align-items:center; justify-content:space-between;
    gap:var(--s12); padding:var(--s8) var(--s16); background:var(--panel);
    border-radius:var(--rp); box-shadow:var(--sombra-1); margin-bottom:var(--s8);
    color:var(--text); font-size:.88rem; grid-column:1/-1}
  .notif-aviso[hidden]{display:none}
  /* 🔴 EL BANNER VA EN SU PROPIA FILA, Y ESTO NO ES ESTETICA: ES UN DEFECTO QUE CAUSO.
     JC, 5-sep: «ahorita no se ve el "Respondiendo desde"». No se habia perdido — el
     banner es hijo directo de `.lienzo`, que es un grid, y al meterlo **se comio la fila
     del panel**: el hilo caia en una fila implicita `auto`, crecia mas alla de `100dvh`
     y empujaba el composer entero fuera de pantalla.
     Un elemento nuevo dentro de un grid no «se agrega»: REACOMODA a todos los demas.
     Con la colocacion explicita, cuando el banner esta oculto su fila mide CERO y no
     mueve absolutamente nada — que es como tiene que comportarse algo que aparece dos
     veces al dia. */
  .lienzo{grid-template-rows:auto 1fr}
  .notif-aviso{grid-row:1}
  .carril,.col-lista,.col-hilo,.ficha{grid-row:2}
  .notif-aviso .acc{display:flex; align-items:center; gap:var(--s8); flex-shrink:0}

  /* ══════════ 2 · LA LISTA ══════════ */
  .lista-top{padding:var(--s16) var(--s16) var(--s8)}
  .lt-fila{display:flex; align-items:flex-start; gap:var(--s12)}
  .lt-txt{min-width:0; flex:1}
  .lt-acc{display:flex; align-items:center; gap:var(--s6); flex-shrink:0}
  /* 🔴 «+ Nueva» NO es un botón más: es la ÚNICA puerta para escribirle a un
     número que todavía no existe en la base. Se me había perdido al rehacer la
     barra superior y lo cazó JC. Va con etiqueta, no solo ícono: quien atiende
     no tiene por qué deducir un glifo. */
  /* `text-decoration:none` (19-sep): «+ Nueva» es un `<a>` con aspecto de boton, y salia
     SUBRAYADO en escritorio — «un ancla subraya y un boton no», como dice `.seg` mas arriba.
     Es el unico `<a class="btn">` del rediseño; se cazo mirando una captura de otra cosa. */
  .btn.nueva{padding:8px 13px; text-decoration:none}
  .nueva-pista{margin:var(--s8) 0 0; font-size:.68rem; color:var(--tenue); line-height:1.35}
  .nueva-pista b{color:var(--muted)}
  /* El sello del tenant: dice DÓNDE estás parado (PC compartida, TEST vs
     cliente). Vive pegado al conteo porque juntos forman la frase que se lee
     primero al llegar: «Acme, 8 abiertas, 2 esperando». */
  .sub .tenant{font-weight:700; letter-spacing:.04em; color:var(--text)}
  /* ════════════════════════════════════════════════════════════════════
     🔴 «VENCEN HOY» EN VEZ DE «SIN RESPONDER HACE MAS DE 24 H» — JC (29-ago):
       «si ya pasaron las 24 horas en WhatsApp no se puede responder libre;
        seria mejor un aviso de cuales estan PROXIMAS a vencer, no crees?».
     Tiene razon, y el proyecto ya habia llegado ahi una vez: el mockup por
     espera reemplazo la mediana por «Ventana cierra hoy» con este argumento
     textual — «el numero util es el que se puede tocar hoy».
     Mi cifra anterior era una AUTOPSIA: cuando la lees ya no hay nada gratis
     que hacer, solo mandar plantilla, y eso cuesta. La cifra nueva es dinero
     que todavia se puede no gastar, y se arregla contestando.
     ⚠️ Solo cuenta canales CON ventana (`canales.VENTANA_24H`): en webchat y
     en web no hay ventana, asi que sumarlos inflaria el numero con
     conversaciones que no vencen nunca. Es el mismo cuidado del `else`.
     Es un boton, no un rotulo: el numero lleva a las tres. ════════════════ */
  /* 🔴 NO ES AMBAR (JC): el ambar significa UNA cosa —lo interno no sale— y
     esto es un plazo, no una nota. Va en --danger, el mismo color que la
     pastilla de la fila: MISMO significado, MISMO color. Asi el filtro y las
     filas que trae se reconocen como lo mismo. */
  .vencen{border:0; cursor:pointer; background:color-mix(in srgb,var(--danger) 14%,transparent);
    color:var(--danger); border-radius:999px; padding:2px 9px;
    font:700 .72rem/1.45 var(--fb)}
  .vencen:hover{background:color-mix(in srgb,var(--danger) 24%,transparent)}
  /* 🔴 AQUI VIVIA `@media (max-width:1240px){ .btn.nueva span.et{display:none} }`, y se
     quita el 4-sep. No era una regla de anchura: era una compensacion por el APRETUJON
     de tres botones en esta barra. Con campanas y el agente mudados al carril queda uno
     solo, y esconder su etiqueta reintroducia —justo en la unica puerta para escribirle
     a un numero nuevo— el mismo problema que se estaba arreglando: un icono sin nombre.

     Y la columna NO se encoge con la ventana: mide 340px, y 320px por debajo de 1280px
     (ver el @media de la ficha). O sea que entre 1241 y 1280 la etiqueta YA se mostraba
     con la columna en 320px — la misma anchura que hay de 1240 para abajo, hasta los
     961px donde la lista desaparece del todo. Si cabia alli, cabe aqui.

     ⚠️ Lo que esto NO prueba es que quepa el TEXTO en cualquier idioma o zoom; se dejo
     anotado en BACKLOG §0-N para mirarlo en pantalla. */
  .lista-top h2{margin:0; font:700 1.26rem/1.1 var(--fd); letter-spacing:-.025em}
  .lista-top .sub{margin-top:3px; font-size:.76rem; color:var(--muted)}
  .buscador{
    margin:var(--s8) var(--s12) var(--s4); display:flex; align-items:center; gap:var(--s8);
    background:var(--panel-2); border-radius:999px; padding:var(--s8) var(--s12);
    color:var(--muted); font-size:.82rem; cursor:pointer; border:0; width:calc(100% - var(--s24));
  }
  .kbd{font:600 10px/1 var(--fm); background:var(--panel); color:var(--tenue);
    border-radius:5px; padding:3px 5px; box-shadow:var(--sombra-1); white-space:nowrap}
  .orden{display:flex; align-items:center; justify-content:space-between;
    padding:var(--s8) var(--s16) var(--s4); font-size:.7rem; color:var(--tenue)}
  .seg{display:inline-flex; gap:2px; background:var(--panel-2); border-radius:999px; padding:2px}
  /* 🔴 EL SELECTOR CUBRE <button> Y <a>, y no es por gusto: el mockup usa botones
     porque no navega, y la app usa anclas porque estos filtros SI navegan (son
     ?alcance= del servidor, no JS). Con `.seg button` a secas, a la app no le
     aplicaba NI UNA regla del segmento: sin pastilla, sin flex, sin estado activo,
     y con el azul de enlace por defecto. Lo cazo JC mirando la pantalla.
     El `text-decoration:none` existe por lo mismo: un ancla subraya y un boton no.
     Es la regla 11 del inventario -- el arreglo no es agregar mi caso, es que el
     selector deje de suponer una etiqueta. */
  .seg button, .seg a{border:0; text-decoration:none; background:transparent; color:var(--muted); cursor:pointer;
    font:600 .68rem/1 var(--fb); padding:5px 10px; border-radius:999px}
  .seg .act{background:var(--panel); color:var(--text); box-shadow:var(--sombra-1)}

  /* ════════════════════════════════════════════════════════════════════
     EL ALCANCE — rediseniado por dos pedidos de JC (28-ago):
       · «no repetir Yo Yo» — el disparador decia el nombre dos veces.
       · «si ponemos Todas al lado, que este enfatizado el Yo o Todas» — y
         tenia razon de fondo: «Todas» es una de las DOS vistas que se usan
         todo el dia, y no puede exigir que despliegues nada para verla.
     Entonces: segmento SIEMPRE visible para las dos vistas de siempre, y el
     desplegable guarda solo lo ocasional (filtrar por una persona concreta).
     🔴 Y este bloque se habia perdido entero: una limpieza mia cortaba desde
     un comentario hasta `.orden{` y se llevo el CSS por delante. Por eso JC
     veia botones nativos del navegador en vez de estos.
     ════════════════════════════════════════════════════════════════════ */
  .alcance-fila{position:relative; display:flex; align-items:center; gap:var(--s6);
    padding:0 var(--s16) var(--s4)}
  .seg.alc{flex:1}
  .seg.alc button, .seg.alc a{flex:1; justify-content:center; display:inline-flex; align-items:center;
    gap:5px; padding:7px 10px; font-size:.76rem}
  .seg.alc .num{font-size:.7rem; opacity:.75}
  /* JC: «esos botones quiza deberian verse como este» (la pastilla de Atiendes
     tu). Mismo lenguaje: pildora, tinte de acento, sin borde. */
  .equipo{display:inline-flex; align-items:center; gap:4px; border:0; cursor:pointer;
    background:var(--soft); color:var(--primary); border-radius:999px;
    padding:7px 10px; flex-shrink:0; font:600 .74rem/1 var(--fb)}
  .equipo:hover{box-shadow:var(--sombra-1)}
  .equipo .ph{width:15px; height:15px}
  /* 🔴 LA FLECHITA A LA ALTURA DEL TEXTO (JC). `vertical-align` NO funciona
     dentro de un contenedor flex: ahi manda `align-items`. Por eso se veia
     caida, y por eso el caret lleva clase propia y no la de icono. */
  .caret{width:11px; height:11px; flex-shrink:0; align-self:center; opacity:.75}
  .pk-pop{position:absolute; right:var(--s16); top:calc(100% + 4px); z-index:30;
    min-width:230px; background:var(--panel); border-radius:14px; padding:var(--s6);
    box-shadow:var(--sombra-3);
    /* 🔴 TECHO Y SCROLL. Lo cazo JC comparando con produccion: el equipo tiene 20+
       personas y la lista salia entera, tapando media pantalla y sin forma de llegar
       al final. Produccion siempre tuvo `max-height:330px; overflow:auto` — el mockup
       NO, asi que al copiarlo verbatim se copio el hueco.
       Es la regla 8 del inventario incumplida: «toda lista que puede crecer necesita
       un techo, y el techo sale de aritmetica escrita, no de un numero a ojo». Los
       330 px son los de produccion: ~7 filas visibles, que es donde deja de leerse
       como lista y empieza a leerse como muro. */
    max-height:330px; overflow:auto}
  .pk-pop[hidden]{display:none}
  .pk-av{width:22px; height:22px; border-radius:50%; display:grid; place-items:center;
    background:var(--soft); color:var(--primary); font:700 .58rem/1 var(--fb); flex-shrink:0}
  .pk-av .ph{width:13px; height:13px}
  .opt{display:flex; align-items:center; gap:var(--s8); width:100%; border:0;
    background:transparent; color:var(--text); font:inherit; font-size:.83rem;
    padding:7px var(--s8); border-radius:9px; cursor:pointer; text-align:left}
  .opt:hover{background:var(--soft)}
  .opt .nm{flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .opt .num{color:var(--muted); font-size:.74rem; font-weight:700}
  .pk-sep{font:700 .58rem/1 var(--fb); letter-spacing:.12em; text-transform:uppercase;
    color:var(--tenue); padding:var(--s6) var(--s8) var(--s4)}
  /* LA TUMBA de una union hecha (ADR 0039): registro + reversa. */
  /* `margin-bottom` porque se APILAN: con dos uniones las tarjetas se pintaban
     pegadas, sin una linea que las separe. Hoy casi nadie tiene dos — que es la
     definicion de defecto latente. */
  .union{background:color-mix(in srgb,var(--panel-2) 55%,transparent);
    border-radius:var(--rc); padding:var(--s12); margin-bottom:var(--s8);
    transition:box-shadow .16s ease, transform .16s ease, background .14s}
  .union:hover{transform:translateY(-1px); background:var(--panel-2);
    box-shadow:var(--sombra-1)}
  .union p{margin:0 0 var(--s8); font-size:.78rem; color:var(--muted); line-height:1.4}
  .union strong{color:var(--text)}
  .btn.chico{padding:6px 11px; font-size:.74rem; background:var(--panel);
    box-shadow:var(--sombra-1)}
  /* ════════════════════════════════════════════════════════════════════
     EL AVISO DE CEDULA DUPLICADA — JC (29-ago): «ese color chocolatoso se sale
     del concepto, por que no lo ponemos rojo?».
     🔴 ROJO NO, y la razon esta escrita en `_cedula_aviso.html`: «es
     ADVERTENCIA, no error: el duplicado es LEGAL hasta que alguien decida
     unir». Dos cedulas iguales pasan de verdad —la misma persona con dos
     fichas, o una madre y una hija compartiendo telefono—. Si eso sale en
     rojo, la ejecutiva aprende a ignorar los rojos, y el dia que salga un rojo
     de verdad (un envio fallido, algo destructivo) ya no significa nada.
     PERO el chocolate SI era un defecto, y de otra causa: ese ambar se afino
     para la identidad terracota y quedo como el unico marron de una pantalla
     azul.
     LA SALIDA no es cambiarle el tono al ambar: es QUITAR EL BANO. El aviso ya
     dice la advertencia con PALABRAS y con un ICONO — el bloque de color no
     agregaba significado, solo ruido. Superficie neutra, y el ambar se queda
     donde de verdad informa: el icono y el titulo.
     Medido asi: claro 5.26 el titulo / 15.14 el cuerpo · oscuro 6.92 / 12.07.
     Y el ambar sigue intacto donde JC lo quiere: el punteado de la nota. */
  /* 🔴 EL AVISO DE CEDULA DUPLICADA LLEGA CON LAS CLASES DE PRODUCCION.
     `_cedula_aviso.html` es COMPARTIDO —lo siembra el server y lo repinta el
     chequeo en vivo— y emite `cli-dup*`, no las clases `dup*` del mockup. Sin
     estos selectores el aviso salia SIN UNA SOLA REGLA, igual que le paso a
     `.men-pop`. No se cambia el parcial: es de produccion y ahi funciona. */
  .cli-dup{background:color-mix(in srgb,var(--panel-2) 55%,transparent);
    border-radius:var(--rc); padding:var(--s8) var(--s12); margin:0 0 var(--s8);
    transition:box-shadow .16s ease, transform .16s ease, background .14s}
  .cli-dup:hover{transform:translateY(-1px); background:var(--panel-2);
    box-shadow:var(--sombra-1)}
  /* 🔴 SEGUNDA PASADA (JC, 29-ago): «este color marron aca no se ha tocado».
     Cierto: quite el bano pero deje el TITULO en ambar, que era justo el marron
     que chocaba con el azul. Me quede a medio camino.
     El titulo pasa a tinta normal. La advertencia no la pierde: la cargan el
     ICONO (que si se queda ambar, es pequeno y es la senal) y las PALABRAS
     («ya esta en otro perfil»). Eso es exactamente la regla del proyecto —
     ningun estado se dice solo con color— aplicada al reves: si las palabras
     ya lo dicen, el color no tiene que gritarlo.
     Medido: titulo 15.14 en claro / 12.07 en oscuro. El icono ambar sobre la
     misma superficie: 5.26 y 6.92. */
  .cli-dup-tit{display:flex; align-items:center; gap:5px; font:700 .74rem/1.35 var(--fb);
    color:var(--text)}
  /* 🔴 TERCERA Y ULTIMA PASADA (JC, 29-ago): «ese icono ahi esta marron, ponlo
     azul». Hecho, y con esto el aviso queda SIN un solo marron.
     Lo que se cede: el icono deja de gritar «advertencia» por color. Lo que NO
     se pierde: lo dicen las PALABRAS («ya esta en otro perfil») y el propio
     icono de advertencia por su FORMA. Es la regla del proyecto llevada hasta
     el final — ningun estado se dice solo con color, y cuando las palabras y la
     forma ya lo dicen, el color no aporta.
     Consecuencia buena para la paleta: `--warn` deja de aparecer en la
     interfaz salvo en UN sitio, la nota interna. Un color con un solo uso es
     un color que significa algo. */
  .cli-dup .ic{width:14px; height:14px; flex-shrink:0; color:var(--primary)}
  .cli-dup-p{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s8);
    margin-top:var(--s6); font-size:.76rem}
  .cli-dup-n{font-weight:700}
  .cli-dup-sin{color:var(--tenue); font-size:.68rem}
  .cli-dup-p a{color:var(--primary); font-weight:600; text-decoration:none}
  .cli-dup-p a:hover{text-decoration:underline}
  .cli-dup-cuerpo{flex:1; min-width:0}
  .cli-dup-lista{list-style:none; margin:0; padding:0}
  .cli-dup-mas{margin:var(--s6) 0 0; font-size:.68rem; color:var(--tenue)}
  .cli-dup-unir{margin-left:var(--s6)}
  .cli-dup-pie{margin:var(--s6) 0 0; font-size:.68rem; color:var(--tenue); line-height:1.35}
  /* LA SENAL CRUZADA. Una linea, y el texto se recorta con puntos suspensivos en
     vez de envolver: un nombre largo no puede convertir una linea en dos. */
  .cruce{display:flex; align-items:center; gap:var(--s8); flex-shrink:0;
    padding:7px var(--s16); background:var(--panel-2); font-size:.74rem;
    color:var(--muted)}
  .cruce .ph{width:14px; height:14px; color:var(--primary); flex-shrink:0}
  .cruce span{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
    white-space:nowrap}
  .cruce strong{color:var(--text)}
  .cruce a{color:var(--primary); font-weight:700; text-decoration:none; flex-shrink:0}
  .convs{flex:1; overflow-y:auto; padding:var(--s4) var(--s8) var(--s12)}
  .conv{display:flex; gap:var(--s12); width:100%; max-width:100%; text-align:left; border:0;
    background:transparent; cursor:pointer; border-radius:var(--rc);
    padding:var(--fila-y) var(--s12); margin-bottom:var(--s2);
    transition:background .12s ease, box-shadow .16s ease, transform .16s ease}
  .conv:hover{background:var(--panel-2)}

  /* 🔴 DEFECTO CORREGIDO (JC, 28-ago): «el énfasis solo lo veo cuando aprieto
     las teclas». Tenía razón, y la causa era boba — `.conv.sel` se pintaba de
     `--panel`, que ES el fondo del panel donde vive. Se pintaba de su propio
     fondo. Lo que él veía era el anillo de FOCO del navegador, no mi estado.
     Ahora el seleccionado lleva TRES señales, y ninguna depende de las otras:
       · un filo sólido a la izquierda (FORMA — sobrevive en blanco y negro)
       · un tinte de acento (COLOR)
       · elevación (PROFUNDIDAD)
     Y el hover se conserva encima, que es lo que JC pidió expresamente. */
  .conv{position:relative}
  .conv.sel{background:var(--soft); box-shadow:var(--sombra-2); transform:translateY(-1px)}
  /* 🔴 EL FILO DE ACENTO SE QUITO (JC, 29-ago): «esa cosita del enfasis, la
     barrita azul, se ve como muy IA». Tiene razon: es el patron mas plantilla
     que existe — todo tablero generado lleva su barra de acento a la izquierda.
     La alternativa no es otro adorno, es usar la idea que ya manda aca. El
     seleccionado se distingue por TIPOGRAFIA y SUPERFICIE:
       · la ficha se ELEVA sobre el lienzo        (profundidad)
       · el nombre sube a peso y tinta plenos     (jerarquia)
       · la vista previa sube de --muted a --text (contraste interno)
     Tres senales, ninguna decorativa, y las tres sobreviven en blanco y negro.
     Es lo que hacen Superhuman y Linear: el activo no se decora, se lee mas
     fuerte que el resto. */
  .conv.sel{background:var(--realce); box-shadow:var(--sombra-2); transform:translateY(-1px)}
  /* 🔴 EL ENFASIS VUELVE (JC, 29-ago): «ahora todas son blancas y se pierde
     lo del enfasis; pon las no seleccionadas en el tono de las letritas de
     abajo, que es un azul clarito, y deja la seleccionada como esta».
     Tiene razon: al subir todas a blanco arregle la legibilidad y borre la
     jerarquia. Ahora hay DOS niveles de tinta y no uno:
       no seleccionada -> --muted  (el azul clarito, 7.65 sobre el panel)
       seleccionada    -> --text   (blanco, 10.05 sobre el realce)
     El peso 800 se queda en las dos: eso era lo que arreglaba lo optico. */
  .conv.sel .quien{font-weight:700; color:var(--text)}
  .conv.sel .prev{color:var(--text)}
  /* 🔴 EL AVATAR ILUMINADO (JC, 29-ago): «que se ilumine el circulo de las
     iniciales para dar a entender SIN DUDA donde estas parado».
     Es la señal que faltaba, y es la mejor de todas porque el avatar esta en el
     BORDE IZQUIERDO de la fila: se ve de reojo sin leer nada, y sigue el ojo
     mientras recorres la lista con las flechas.
     Relleno solido con tinta encima — no un tinte, que en oscuro casi no se ve
     (esa leccion ya la pago el foco del composer). Medido: 9.00 en claro y
     10.32 en oscuro. */
  .conv.sel .ava .cara{background:var(--primary-fill); color:var(--primary-ink);
    box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 35%,transparent)}
  .conv.sel:hover{box-shadow:var(--sombra-3); transform:translateY(-2px)}
  /* El foco de teclado NO se confunde con el seleccionado: es un anillo, no un
     relleno. Dos estados distintos, dos lenguajes distintos. */
  .conv:focus-visible{outline:2px solid var(--primary); outline-offset:-2px}

  /* Íconos Phosphor duotone — el estándar del proyecto. */
  .ph{width:1.05em; height:1.05em; display:inline-block; vertical-align:-.18em; flex-shrink:0}
  .rail-b .ph{width:20px; height:20px}
  .modo-seg .ph{width:15px; height:15px}
  .buscador .ph{width:15px; height:15px}

  /* 🔴 EL ANILLO SE QUITÓ (JC, 28-ago). Él preguntó «¿las barritas sobre las
     iniciales qué significan?» — y esa pregunta ES el veredicto: un instrumento
     que hay que explicar ya falló. Se probó rotularlo con una leyenda y JC dijo
     que igual podía confundir. Tenía razón: era una segunda escala, con su
     propio idioma, para un dato que YA estaba escrito al lado en letras.
     Lo que queda es más simple y se lee solo: las iniciales limpias, y el
     TIEMPO en una pastilla teñida. La pastilla dice «6 d» — no hay nada que
     deducir. La urgencia sigue teniendo dos señales (el peso de la letra y el
     tinte), así que sobrevive sin distinguir colores. */
  .ava{position:relative; width:38px; height:38px; flex-shrink:0}
  .ava .cara{position:absolute; inset:0; border-radius:50%; background:var(--panel-2);
    display:grid; place-items:center; font:600 .78rem/1 var(--fb); color:var(--muted)}
  /* 🔴 REGLA VIEJA ELIMINADA (JC, 29-ago: «en claro, al seleccionar desaparece
     la inicial»). Esta linea venia despues de la del avatar iluminado y la
     pisaba: dejaba el circulo en --panel (casi blanco en claro) con la tinta
     blanca encima = invisible. En oscuro se salvaba por casualidad, porque
     --panel ahi es oscuro.
     Leccion: al agregar una regla, buscar si ya existe otra para el MISMO
     selector mas abajo. El orden decide, no la intencion. */

  .conv .cuerpo{min-width:0; flex:1; overflow:hidden}
  .conv .f1{display:flex; align-items:baseline; justify-content:space-between; gap:var(--s8)}
  /* 🔴 El celeste de los nombres es SOLO del tema oscuro (JC, 29-ago: «el tema
     claro le moviste, ese no habia que moverlo, era al oscuro solamente»).
     Tenia razon: el pidio ese color mirando una captura en oscuro, donde el
     problema era el adelgazamiento optico del serif. En claro ese problema no
     existe y la tinta azul oscura ya funciona. Aplicarlo a los dos fue mio.
     En claro el nombre sigue en --text; la regla del celeste vive abajo, dentro
     del bloque de tema oscuro. */
  .conv .quien{font:600 .95rem/1.25 var(--fd); letter-spacing:-.015em; color:var(--text);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0}
  .conv .edad{font-size:.7rem; color:var(--muted); flex-shrink:0;
    background:var(--panel-2); border-radius:999px; padding:3px 7px}
  /* 🔴 FUERA EL ESTADO INTERMEDIO (29-ago). Eran tres tintes —neutro, ambar,
     rojo— y el ambar era el unico SIN una razon detras: yo mismo escribi que
     el umbral intermedio «todavia no tiene razon». Un tinte sin razon es ruido,
     y encima era el marron que JC venia senalando en tres sitios.
     Quedan DOS estados, y los dos con motivo:
       neutro     -> no hay nada urgente que hacer
       por vencer -> la ventana de 24 h se cierra hoy, y despues responder CUESTA
     Esa es la unica frontera que este negocio tiene de verdad. */
  /* ════════════════════════════════════════════════════════════════════
     🔴 QUE SIGNIFICA LA PASTILLA ROJA — rehecho porque MENTIA (JC, 29-ago):
       «si lo aprieto me trae las que llevan varios dias esperando y no las que
        estan por vencerse... creo que las de menos de 8 horas son las que
        tienen que contarse».
     Tenia razon y el error era conceptual, no de color: yo estaba marcando las
     que llevan MUCHO ESPERANDO. Pero esas YA VENCIERON — ahi ya no hay nada
     gratis que hacer, solo pagar una plantilla. Marcarlas en rojo es pedir
     urgencia por algo que ya no tiene arreglo urgente.
     LO ACCIONABLE ES AL REVES: las que TODAVIA tienen ventana abierta y les
     queda poco. Contestar ahi cuesta cero; dejarlas pasar cuesta una plantilla.
     LA DEFINICION, entonces:
       rojo  = ventana ABIERTA con menos de 8 h restantes  -> «quedan 3 h»
       plano = todo lo demas, incluidas las YA vencidas
     Y la pastilla dice el tiempo QUE QUEDA, no el que paso: «quedan 3 h» se
     lee solo; «22 h» obliga a restar de 24 mentalmente.
     ⚠️ El umbral de 8 h es de JC y tiene su razon —si el dia pasa sin mantener
     la conversacion viva, se pierde la respuesta libre—, pero conviene
     confirmarlo con el inventario real del tenant antes de construirlo.
     ════════════════════════════════════════════════════════════════════ */
  /* 🔴 EL RELOJITO VA DENTRO DE LA PASTILLA, no en vez de las iniciales
     (JC propuso reemplazarlas). Las iniciales dicen QUIEN ES; la pastilla dice
     CUANDO. Meter la urgencia en el avatar sobrecarga un solo sitio con dos
     significados, que es justo lo que veniamos evitando — y encima el nombre
     se lee menos. Asi la fila tiene dos senales independientes y ninguna pisa
     a la otra. */
  .conv .edad.u{display:inline-flex; align-items:center; gap:3px;
    color:var(--danger); font-weight:700;
    background:color-mix(in srgb,var(--danger) 15%,transparent)}
  .conv .edad.u .ph{width:11px; height:11px}
  /* El filtro activo: la lista solo muestra las que vencen hoy. */
  .vencen{display:inline-flex; align-items:center; gap:4px}
  .vencen .ph{width:12px; height:12px}
  .vencen[aria-pressed="true"]{background:var(--danger); color:#FFFFFF}
  .convs[data-filtro="vencen"] .conv:not(.u){display:none}
  .conv.sel .edad{background:var(--panel)}
  .conv .prev{font-size:.8rem; color:var(--muted); margin-top:2px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .conv .f3{display:flex; align-items:center; gap:5px; min-width:0; margin-top:var(--s6);
    font-size:.68rem; color:var(--tenue)}
  /* 🔴 REGLA REAL, sacada de `_lista.html` (F5/D3), no inventada: el chip de
     canal aparece en una fila de WhatsApp SOLO si el contacto vive en mas de un
     canal. Si todo es WhatsApp, el chip es ruido en cada fila. Y la etiqueta del
     NUMERO va siempre y POR SEPARADO — antes era el `elif` del chip, asi que
     estrenar chip en una fila de WhatsApp se la habria comido. Es la misma
     trampa del `else` que este proyecto ya pago cuatro veces. */
  /* 🔴 EL CHIP DE CANAL NO SE PIERDE, y en WhatsApp va con SU NUMERO: con
     multinumero, saber por cual linea entro la conversacion es lo que evita
     contestar desde el numero equivocado. JC: «los canales, incluyendo web en
     los tenant que este activo, no se pueden perder».
     El chip no se trunca nunca: se trunca el nombre, jamas el canal. */
  .via{display:inline-flex; align-items:center; gap:4px; flex-shrink:0; max-width:60%}
  .via .ph{width:12px; height:12px; opacity:.85}
  /* 🔴 EL NUMERO POR EL QUE ENTRO LA CONVERSACION, en su propia linea, como en
     produccion. JC: «no veo el numero desde donde te escribieron, eso tampoco se
     puede perder». Con multinumero es lo que evita contestar desde la linea
     equivocada, y por eso no comparte renglon con nada: si se truncara, se
     truncaria justo el dato que lo hace util. */
  /* 🔴 DESBORDE A LA DERECHA (JC, 29-ago). La causa es fina y vale anotarla:
     este renglon es `display:flex`, y en un contenedor flex el `text-overflow`
     NO recorta — recorta el hijo, y solo si el hijo puede encogerse. Un item
     flex no baja de su contenido sin `min-width:0`. Faltaban las dos cosas, y
     el numero largo empujaba la fila entera fuera del panel. */
  .conv .linea{display:flex; align-items:center; gap:4px; margin-top:3px; min-width:0;
    font-size:.66rem; color:var(--tenue)}
  .conv .linea .num{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .conv .linea .ph{width:11px; height:11px; flex-shrink:0; opacity:.8}
  .sinleer{width:9px;height:9px;border-radius:50%;background:var(--primary);flex-shrink:0}
  /* 🔴 UN PUNTO DE 7px NO DESTACA NADA. JC, 5-sep: «todavia no se logra destacar el chat
     que recibio un mensaje nuevo si estoy parado en otro chat».
     Produccion no usa UNA senial: usa TRES —fila tenida, nombre en negrita y punto— y por
     eso se ve de reojo, que es como de verdad se mira una lista mientras trabajas.
     Se portan las tres, con los tokens del rediseño:
       · el punto (ya estaba)
       · el nombre en 700
       · la fila con un tinte de acento
     ⚠️ EL TINTE NO PUEDE SER EL DE LA FILA SELECCIONADA (`--soft` / `--realce`): si los
     dos fueran iguales, «la que estoy viendo» y «la que tiene algo nuevo» se dirian con
     el mismo color y ninguna de las dos informaria. Se usa el ambar de las notas, que en
     esta pantalla no significa otra cosa, al 10% para que sea un fondo y no un aviso.
     🔴 Y la seleccionada GANA: si estas parada en ella, ya la estas leyendo. */
  /* 🔴 SIN TINTE DE FILA, Y LA RAZON ES UNA MEDICION, no un gusto.
     JC, 5-sep: «no me gusto el color… cambiemoslo a uno que ya este en la paleta y
     combine mejor». Se probaron los de la paleta azul y el resultado cierra la puerta:

       oscuro · «nuevo» con --primary 18%  ->  #303A47
       oscuro · fila SELECCIONADA (--realce) ->  #2F3743
       contraste entre las dos ............. 1.04   (1.00 = el MISMO color)

     O sea que en tema oscuro **cualquier tinte azul es indistinguible de la fila que
     estas mirando**, y entonces «la que miro» y «la que tiene algo nuevo» se dirian con
     el mismo color: dos estados, una sola senial, ninguno informa. El ambar no chocaba —
     por eso estaba— pero a JC le sonaba a terracota, que en esta paleta ya significa otra
     cosa (la marca).

     La salida no es buscar un tercer tinte: es **poner la fuerza donde SI se puede medir**.
     El estado queda dicho por dos seniales que superan su umbral con holgura:

       el punto  --primary sobre el panel .... claro 7.85 · oscuro 7.41   (umbral 3.0)
       el nombre en 700 ...................... peso, no color: funciona en los dos temas
                                               y tambien para quien no distingue matices

     ⚠️ Y el punto sube de 7 a 9px: a 7px es un detalle; a 9 se ve de reojo, que es como
     de verdad se mira una lista mientras trabajas. Sigue sin sumar alto a la fila. */
  .conv.nuevo .quien{font-weight:700; color:var(--text)}

  .atajo-nota{width:100%; margin-top:3px; color:var(--tenue); font-size:.62rem; opacity:.85}
  .atajos{display:flex; flex-wrap:wrap; gap:var(--s6); align-items:center;
    padding:var(--s10,10px) var(--s16); font-size:.66rem; color:var(--tenue)}

  /* ══════════ 3 · EL HILO ══════════ */
  /* 🔴 DEFECTO CORREGIDO (JC, 28-ago): al angostar la ventana los textos de la
     cabecera se montaban unos sobre otros. La causa: el título no tenía cómo
     ceder, así que empujaba. Ahora el NOMBRE cede con ellipsis y los botones
     nunca se encogen — mismo patrón que ya se usó para el botón Cerrar. */
  .hilo-top{display:flex; align-items:center; gap:var(--s12);
    padding:var(--s12) var(--s16); flex-wrap:nowrap}
  .hilo-top .quien{flex:1 1 auto; min-width:0; overflow:hidden}
  .hilo-top h1{margin:0; font:650 1.16rem/1.15 var(--fd); letter-spacing:-.02em;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .hilo-top .sub{margin-top:2px; font-size:.72rem; color:var(--muted);
    display:flex; align-items:center; gap:var(--s6);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .hilo-top .sub span{white-space:nowrap}
  .hilo-top .pastilla,.hilo-top .btn{flex-shrink:0}
  /* 🔴 EL NOMBRE SE CORTABA DEMASIADO PRONTO (JC, 29-ago). La prioridad estaba
     al reves: el nombre cedia primero y los botones no cedian nunca, asi que en
     una ventana angosta el titulo se quedaba sin sitio aunque los botones
     estuvieran holgados.
     LA REGLA: cede lo REPETIBLE, no lo que IDENTIFICA. El nombre del paciente
     es lo unico que dice a quien le escribes; el canal y el numero ya estan en
     la fila de la lista, y la ventana ya esta —y mas fresca— en el composer.
     Orden de cesion: 1) el dato de la ventana, 2) quien soy yo, 3) el subtitulo
     entero, 4) la etiqueta de «Cerrar» (queda el icono con su title).
     El nombre nunca es el primero en ceder. */
  .quien-soy{font-size:.8rem; color:var(--muted); flex-shrink:0; white-space:nowrap}
  .hilo-top .quien{flex:1 1 0; min-width:90px}
  /* 🔴 AQUI SE ESCONDIA EL CANAL Y EL NUMERO DEL CLIENTE bajo 1040px, y JC lo cazo el
     5-sep comparando con produccion: «no esta tampoco el numero de la persona».
     ES LA CUARTA VEZ ESTA SEMANA con la misma enfermedad —«+ Nueva», campañas/agente,
     «Tomar yo»/«Cerrar», y ahora esto—, asi que ya no es un descuido: es un patron.
     Y este es el peor de los cuatro: el numero es COMO SE VERIFICA CON QUIEN ESTAS
     HABLANDO. En un producto donde un mensaje puede irse a la paciente equivocada, la
     linea que dice a quien le escribes no es decoracion.

     ⚠️ LA VENTANA (`.vent`) SI SE SIGUE ESCONDIENDO, y la distincion es la que importa:
     ese dato NO se pierde — se dice completo y en palabras en el aviso de abajo («Fuera
     de la ventana de 24h…»). Es una COPIA de algo que vive fresco en otro sitio, y esa
     es exactamente la que se puede sacar cuando falta espacio. El canal y el numero no
     estan en ningun otro lado de esta pantalla. */
  @media (max-width:1180px){ .hilo-top .sub .vent{display:none} .quien-soy{display:none} }
  /* 🔴 AQUI SE ESCONDIAN LOS NOMBRES DE «Tomar yo» Y «Cerrar» BAJO 920px, y JC lo cazo
     el 5-sep en el iPhone: «el boton de tomar yo y el boton de cerrar se ven como manchas
     sin el nombre del boton».
     Es la MISMA regla que ya se quito de «+ Nueva» el 4-sep, y la misma enfermedad: se
     esconde el nombre para ganar sitio, y lo que queda es un glifo que hay que adivinar
     — justo donde MENOS se puede adivinar, porque «Cerrar» es irreversible de un toque.
     El atajo de teclado (`.kbd`) SI se esconde: en un telefono no hay teclado fisico, asi
     que ahi no informa, estorba. */
  @media (max-width:920px){ .hilo-top .btn .kbd{display:none} }
  .pastilla{display:inline-flex; align-items:center; gap:var(--s6); border:0;
    background:var(--soft); color:var(--primary); border-radius:999px;
    padding:7px 13px; font:600 .8rem/1 var(--fb); cursor:pointer}
  .btn{display:inline-flex; align-items:center; gap:var(--s6); border:0;
    background:var(--panel-2); color:var(--text); border-radius:var(--rc);
    padding:8px 13px; font:600 .8rem/1 var(--fb); cursor:pointer;
    box-shadow:var(--sombra-1); transition:transform .1s ease}
  .btn:hover{transform:translateY(-1px)}
  .btn.pri{background:var(--primary-fill); color:var(--primary-ink)}
  /* 🔴 CAZADO POR JC EN OSCURO: «cuidado con las letras negras». El «Guardar»
     deshabilitado llevaba `opacity:.45`, y la opacidad apaga el TEXTO igual que
     el fondo — fabrica un par ilegible sin cambiar un solo color, y es la forma
     más común de romper el contraste sin darse cuenta. Un estado apagado se
     dice con COLORES apagados: así se puede medir y sigue pasando el mínimo. */
  /* 🔴 Y EL MISMO ASPECTO PARA `:disabled`, cazado por JC el 1-sep: «guardar cuando no
     hay cambios no debe funcionar... ahora veo que funciona siempre». El «Guardar» de la
     ficha NACE con el atributo `disabled` y nunca llevo la clase `apagado`, asi que un
     boton apagado se veia EXACTAMENTE igual que uno vivo. No hacia falta que fallara
     nada: la pantalla estaba mintiendo sobre su propio estado.
     Va con el MISMO cuerpo de regla y no con una copia: dos definiciones del mismo
     estado se separan en silencio. Y va por el pseudo-selector `:disabled`, que lee la
     PROPIEDAD real del boton — una clase hay que acordarse de ponerla y quitarla, y
     olvidarse no da error. */
  .btn.apagado,.cli-form .btn:disabled{background:var(--panel-2); color:var(--muted);
    cursor:not-allowed; box-shadow:none}
  .btn.apagado:hover,.cli-form .btn:disabled:hover{transform:none}
  .btn.ic{padding:8px; width:34px; height:34px; justify-content:center}

  /* El canvas de mensajes va HUNDIDO dentro del panel: las burbujas flotan. */
  .mensajes{flex:1; overflow-y:auto; background:var(--hondo);
    padding:var(--s12) var(--s16);
    display:flex; flex-direction:column; gap:var(--msg-y)}
  /* 🔴 DENSIDAD — JC comparo con TEST: «estas burbujas tienen demasiado espacio
     arriba y abajo… en produccion estan mas tight, y en movil el espacio es
     todo». Los valores salen de sus capturas, no de mi gusto. */
  /* 🔴 DENSIDAD — SEGUNDA PASADA (JC, 29-ago): «todavia tienen mucho espacio
     arriba y abajo, mira como esta en test y prod, asi debe quedar».
     Los valores salen de comparar sus capturas lado a lado, no de mi gusto.
     La burbuja es CASI SOLO el texto: el relleno vertical apenas separa la
     tinta del borde, y el pie va pegado. En movil esto es lo unico que hay. */
  /* 🔴 LA CAUSA DE LA BURBUJA ALTA, LA DEFINITIVA (JC insistio tres veces y
     tenia razon las tres). NO era el padding — lo baje tres veces y no cedia.
     Era que `.msg` llevaba `white-space:pre-wrap` para respetar los parrafos
     del cliente, y con eso el navegador pinta TAMBIEN los saltos de linea y la
     sangria del CODIGO FUENTE. Entre `CAMBIAR` y su `<span class="meta">` hay
     un salto y ocho espacios en el HTML: eso era un renglon invisible en cada
     burbuja.
     Produccion no tiene el problema porque envuelve el texto en `.txt`
     (`_mensajes.html`) y la burbuja queda en `normal`. Se copia esa estructura.
     Leccion: en un contenedor `pre-wrap`, la sangria del codigo es contenido. */
  .msg{max-width:min(580px,74%); padding:5px 11px 6px; border-radius:13px;
    background:var(--panel); box-shadow:var(--sombra-1); font-size:.88rem;
    line-height:1.38; white-space:normal; overflow-wrap:anywhere}
  .msg .txt{white-space:pre-wrap}
  .msg .txt a{color:var(--primary); font-weight:600}
  .msg .txt.waze{display:block; margin-top:2px}
  .msg.entra{align-self:flex-start; border-bottom-left-radius:6px}
  .msg.sale{align-self:flex-end; background:var(--out); border-bottom-right-radius:6px}
  /* 🔴 LA BURBUJA ENORME (JC, 29-ago): «el wrapper sigue estando demasiado
     grande con respecto al contenido». La causa NO era el padding — era que
     `.msg` lleva `white-space:pre-wrap` para respetar los parrafos del cliente,
     y con eso el navegador tambien pinta los SALTOS DE LINEA Y LA SANGRIA DEL
     MARCADO. Cada `<div>` de la tarjeta, indentado en el HTML, aportaba un
     renglon invisible. Por eso se veia aire donde el CSS no ponia ninguno.
     El arreglo: `pre-wrap` vale para el TEXTO del mensaje; todo hijo con caja
     propia vuelve a `normal`. */
  .msg .meta{display:block; margin-top:1px; font-size:.65rem; color:var(--tenue);
    line-height:1.25}
  .msg .re{display:inline-block; margin-top:0; font-size:.65rem; font-weight:700;
    text-decoration:none;   /* el mockup pone un <span>; la app un <a>, que subraya */
    line-height:1.25; color:var(--primary); cursor:pointer}
  /* Vocabulario funcional — la MANERA de mostrarlo no cambia. */
  .chip-boton{display:flex; width:fit-content; align-items:center; gap:4px; margin-bottom:2px;
    padding:1px 7px 1px 5px; border-radius:999px; background:var(--soft);
    color:var(--primary); font:700 .6rem/1.5 var(--fb); letter-spacing:.05em;
    text-transform:uppercase}
  .chip-boton .ph{width:12px; height:12px}
  .chip-anuncio{display:flex; width:fit-content; max-width:100%; align-items:center; gap:4px;
    margin-bottom:2px; padding:2px 8px 2px 6px; border-radius:999px;
    background:var(--soft); color:var(--primary); font:600 .68rem/1.35 var(--fb)}
  .chip-anuncio .ph{width:13px; height:13px}
  /* El enlace no se parte a mitad de palabra al angostar (lo cazó JC en su
     captura: decía «ver anunci / o»). */
  .chip-anuncio a{color:var(--primary); font-weight:700; white-space:nowrap}
  .tarjeta{display:flex; align-items:center; gap:9px; max-width:100%;
    padding:7px 11px 7px 9px; border-radius:12px; background:var(--panel);
    box-shadow:var(--sombra-1); line-height:1.3}
  .tarjeta > div{min-width:0}
  .msg.sale .tarjeta{background:var(--panel)}
  .tarjeta + .tarjeta{margin-top:7px}
  .tarjeta svg{width:26px; height:26px; flex-shrink:0; color:var(--primary)}
  .tarjeta .rot{font:700 .58rem/1.2 var(--fb); letter-spacing:.06em;
    text-transform:uppercase; color:var(--tenue)}
  .tarjeta .nom{font:650 .9rem/1.25 var(--fd); letter-spacing:-.01em; margin-top:1px}
  .tarjeta .tel{font-size:.78rem; line-height:1.25; color:var(--muted)}
  /* La nota, como en produccion: el rotulo NO se lleva un renglon entero. */
  /* La nota, con la densidad de produccion: el rotulo es un ICONO en linea con
     el texto, nunca un renglon propio. Y la nota puede contener audio o imagen
     igual que cualquier mensaje — no es solo texto. */
  .msg.nota{align-self:center; max-width:min(520px,78%); background:transparent;
    box-shadow:none; border:1.5px dashed var(--warn-vivo);
    padding:4px 10px 5px; color:var(--text)}
  .msg.nota .txt{display:inline}
  .msg.nota .chip-boton{display:inline; margin:0 4px 0 0; padding:0;
    background:transparent; font-size:0; vertical-align:-2px}
  .msg.nota .chip-boton .ph{width:13px; height:13px; color:var(--warn-vivo)}
  .msg.nota .chip-boton{background:transparent; color:var(--warn-vivo); padding-left:0}
  .msg.evento{align-self:center; background:transparent; box-shadow:none;
    color:var(--tenue); font-size:.7rem; text-align:center; padding:2px 8px}

  /* ══════════ 5 · EL COMPOSER, FLOTANDO ══════════ */
  .composer{position:relative; display:flex; flex-direction:column; gap:var(--s8);
    padding:var(--s12) var(--s16) var(--s16); background:var(--panel)}
  .fila-modo{display:flex; align-items:center; gap:var(--s8)}
  .modo-seg{display:flex; gap:2px; width:max-content; background:var(--panel-2);
    border-radius:999px; padding:3px}
  .modo-seg button{display:flex; align-items:center; gap:6px; border:0; background:transparent;
    color:var(--muted); font:600 .75rem/1 var(--fb); padding:6px 13px;
    border-radius:999px; cursor:pointer; transition:background .14s ease, color .14s ease}
  .modo-seg button.act{background:var(--panel); color:var(--text); box-shadow:var(--sombra-1)}
  /* 🔴 El modo Nota activo NO lleva texto ambar: en tema claro un ambar
     legible es marron (medido: el unico que pasa 4.5 da 5.90 y ya se ve
     chocolate). La senal la llevan el ICONO ambar y el punteado de la caja,
     que solo necesitan 3.0 y ahi si cabe un ambar de verdad. */
  .modo-seg button.nota.act{color:var(--text)}
  .modo-seg button.nota.act .ph{color:var(--warn-vivo)}
  .compartir{margin-left:auto; width:32px; height:32px; display:grid; place-items:center;
    border:0; background:transparent; color:var(--tenue); border-radius:999px; cursor:pointer}
  .compartir:hover{color:var(--primary); background:var(--panel-2)}
  .desde{display:flex; align-items:center; gap:5px; color:var(--tenue); font-size:.72rem}
  .desde strong{color:var(--muted); font-weight:600}
  /* 🔴 `overscroll-behavior:contain` NO es un detalle: sin el, al llegar al final
     de los chips el gesto SIGUE en el hilo de atras, y en el celular eso se siente
     como que la pantalla se va sola. Produccion lo tiene. */
  .chips{display:flex; flex-wrap:wrap; gap:var(--s8); max-height:100px; overflow-y:auto;
    overscroll-behavior:contain}
  .chip-adj{display:inline-flex; align-items:center; gap:var(--s8); max-width:220px;
    background:var(--panel-2); border-radius:var(--rc); padding:6px 8px}
  .chip-adj .mini{width:32px;height:32px;border-radius:9px;display:grid;place-items:center;
    background:var(--panel); color:var(--tenue); font:700 .58rem/1 var(--fm); flex-shrink:0;
    box-shadow:var(--sombra-1)}
  .chip-adj .nom{font-size:.8rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .chip-adj .x{border:0;background:transparent;color:var(--tenue);cursor:pointer}
  .chip-adj .x:hover{color:var(--danger)}
  /* ══ «PLANTILLA», SUS DOS ESTADOS (19-sep) ══
     Era `.modo.difer{opacity:.45}` — una pestaña DIFERIDA. Ya no lo es: con la ventana
     vencida lleva a /nuevo con el contacto; con la ventana abierta se apaga porque ahi
     no aplica (se escribe libre).
     · APAGADA: con COLOR apagado, no con `opacity` — la misma regla que ya tiene el
       «WhatsApp» apagado mas abajo: `opacity` apaga tambien el texto y fabrica un par
       ilegible; un color se puede medir. El `disabled` bloquea; el cursor dice «no aqui».
     · VIVA: es un ENLACE, no un boton, y `.modo-seg button` no lo alcanza — sin esta
       regla saldria subrayado y en azul de enlace (la trampa del `<a>`, quinta vez).
       Tres señales, no solo el color: el acento, la superficie elevada y la flecha de
       «te lleva a otra pantalla». */
  .modo.inactivo{color:var(--tenue); cursor:not-allowed}
  .modo-seg a.modo{display:flex; align-items:center; gap:6px; text-decoration:none;
    font:600 .75rem/1 var(--fb); padding:6px 13px; border-radius:999px;
    background:var(--panel); color:var(--primary); box-shadow:var(--sombra-1);
    transition:background .14s ease}
  .modo-seg a.modo:hover{background:var(--soft)}
  .modo-seg a.modo .ph{width:13px; height:13px}
  /* Chip de nota de voz: el preview ES un reproductor, no una miniatura. Sin esto no
     hay forma de re-escuchar la nota antes de mandarsela a un paciente. */
  .chip-adj.chip-audio{max-width:260px}
  .chip-adj audio{height:32px; max-width:190px}
  .chip-adj img.mini{object-fit:cover}

  /* -- ARRASTRAR UN ARCHIVO ENCIMA -----------------------------------------
     🔴 La senial no es adorno: sin ella no se distingue "el composer lo acepta" de
     "no pasa nada", y el que suelta no sabe si tiene que volver a intentar. */
  .composer.drop-activo .caja{background:var(--soft);
    box-shadow:inset 0 0 0 2px var(--primary)}

  /* -- GRABADORA DE NOTAS DE VOZ -------------------------------------------
     Mientras se graba, la fila del composer SE CONVIERTE en la barra de grabacion
     (punto + timer + descartar + listo). Se ocultan solo los HIJOS DIRECTOS de la
     caja: los botones de la barra viven DENTRO de `.rec-ui` y no se tocan.
     🔴 La barra mide lo mismo que la fila normal, asi que no empuja el hilo hacia
     arriba al arrancar — un salto de layout al empezar a grabar se lee como un error. */
  .rec-ui{display:none; flex:1; align-items:center; gap:10px; min-height:38px;
    min-width:0; padding:0 4px}
  .caja.grabando .rec-ui{display:flex}
  .caja.grabando > .ic-b,
  .caja.grabando > textarea{display:none}
  .rec-punto{width:10px; height:10px; border-radius:50%; flex-shrink:0;
    background:var(--danger); animation:rec-pulso 1.2s ease-in-out infinite}
  @keyframes rec-pulso{0%,100%{opacity:1} 50%{opacity:.25}}
  /* Cifras de ancho fijo: sin esto el timer BAILA en cada segundo, y un numero que se
     mueve mientras grabas parece que algo va mal. */
  .rec-timer{font-variant-numeric:tabular-nums; font-weight:600; font-size:.9rem;
    color:var(--text); flex-shrink:0}
  .rec-pista{flex:1; min-width:0; color:var(--muted); font-size:.8rem;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .rec-ui .ic-b{flex-shrink:0}
  .caja.grabando .rec-ui .rec-listo{background:var(--primary-fill); color:var(--primary-ink)}
  /* En modo nota la caja es ambar: el boton de listo la acompania, o se lee como si
     la nota fuera a salir por WhatsApp. */
  /* `--warn-ink`, no `--primary-ink`: ver el token. Este boton tenia el MISMO defecto
     que el avion y no lo reporto nadie — se llega a el grabando una nota de voz EN
     MODO NOTA, que es un camino que casi no se prueba a mano. Arreglar solo el avion
     habria dejado la mitad del defecto vivo y con cara de resuelto. */
  .caja.nota-modo.grabando .rec-ui .rec-listo{background:var(--warn); color:var(--warn-ink)}
  @media (prefers-reduced-motion: reduce){
    .rec-punto{animation:none}
  }
  /* La caja: hundida en reposo, ELEVADA al enfocar. El resaltado que ya
     existía, dicho en el idioma nuevo. */
  .caja{display:flex; align-items:flex-end; gap:var(--s4); background:var(--panel-2);
    border-radius:22px; padding:4px 4px 4px 7px;
    box-shadow:inset 0 1px 2px rgba(0,0,0,.05);
    transition:box-shadow .16s ease, background .16s ease}
  /* 🔴 EL FOCO DEL COMPOSER (JC, 29-ago): «en oscuro no es suficiente el
     enfasis, pero debe estar de acuerdo con accesibilidad». La causa: el halo
     era `--soft`, un tinte translucido al 16%. Sobre papel contrasta; sobre
     casi-negro es casi nada. Un tinte translucido no puede ser el UNICO
     indicador de foco en un tema oscuro.
     Ahora son dos capas y la primera es la que cumple: ANILLO SOLIDO de 2px
     mas un resplandor exterior. WCAG 2.2 (Focus Appearance) pide >=3:1 contra
     lo de adentro Y lo de afuera, y >=2px. Medido: oscuro 7.65/9.06,
     claro 7.81/8.76. */
  .caja:focus-within{background:var(--panel-2);
    box-shadow:0 0 0 2px var(--primary), 0 0 0 6px var(--foco-halo), var(--sombra-2)}
  .caja.nota-modo{background:color-mix(in srgb,var(--warn-vivo) 9%,var(--panel-2));
    box-shadow:none; outline:1.5px dashed var(--warn-vivo);
    outline-offset:-1px}
  .caja.nota-modo:focus-within{background:color-mix(in srgb,var(--warn) 12%,var(--panel-2));
    box-shadow:0 0 0 2px var(--warn),
      0 0 0 6px color-mix(in srgb,var(--warn) 22%,transparent), var(--sombra-2)}
  .caja textarea{flex:1; min-width:0; resize:none; border:0; background:transparent;
    outline:none; padding:9px 4px; min-height:24px; max-height:140px;
    font:inherit; line-height:1.4; color:var(--text)}
  .caja textarea::placeholder{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  /* 🔴 16px EXACTOS EN MOVIL, Y NO ES UNA PREFERENCIA TIPOGRAFICA: iOS hace ZOOM
     automatico al enfocar cualquier campo que mida MENOS de 16px, y no lo deshace al
     salir. El cuerpo esta en 15px y el textarea hereda, asi que faltaba 1px.
     JC, 5-sep: «cuando cambio de nota a escribir al cliente tambien se amplia un poco el
     zoom y tengo que regresar manualmente… eso pasa tambien en la pantalla de login».
     ⚠️ La alternativa —`maximum-scale=1` en el viewport— tambien lo evita y ademas le
     PROHIBE a la persona hacer zoom en toda la pagina. Es una barrera de accesibilidad
     conocida y por eso no se usa: se sube el campo, no se le quita el zoom a nadie.
     Solo en movil: en escritorio el campo sigue heredando los 15px del cuerpo. */
  @media (max-width:960px){
    .caja textarea, .buscador input, .cli-form input, .cli-form textarea,
    .cli-form select{font-size:16px}
  }
  .ic-b{flex-shrink:0; display:grid; place-items:center; width:38px; height:38px;
    border-radius:50%; border:0; cursor:pointer; background:transparent;
    color:var(--tenue); padding:0}
  .ic-b:hover{color:var(--text); background:var(--panel)}
  .ic-b svg{width:20px;height:20px}
  /* 🔴 LA POSICION ES LA DE PRODUCCION: centrado, sin empujon (JC, 29-ago:
     «el dibujo esta lindo pero la posicion debe ser como esta ahora en
     produccion, y que despegue DE ESA POSICION»).
     CENTRADO, sin ningun ajuste. Lo mandaron tres correcciones de JC: primero
     le puse un empujon diagonal que nadie pidio, despues lo subi 2px leyendo mal
     un recorte, y al final el mando la captura entera de produccion — donde
     esta centrado a secas.
     LECCION, y es la misma que se repitio hoy: un ajuste «optico» que nadie
     pidio se paga dos veces, y un recorte no es evidencia suficiente para
     mover algo. La captura completa lo decidio en un segundo. */
  /* ════════════════════════════════════════════════════════════════════
     🔴 EL LOADER DE ENVIO — el avioncito despega (JC, 29-ago).
     Se me habia perdido la taza que se llenaba, que en produccion SI existe y
     esta LOCKED en `REFERENCIAS.md`. Error mio: no la porte.
     Y JC propuso cambiarla por el avion. Se acepta, con un motivo que no es de
     gusto: a 20px la taza llenandose casi no se lee, y la taza YA aparece dos
     veces en pantalla (el logo del carril y la marca de agua). El avion es el
     MISMO objeto del boton, y «despegar» es literalmente lo que esta pasando —
     el movimiento explica el estado sin que nadie lo aprenda.
     ⚠️ La taza NO se jubila: se reserva para un loader GRANDE (splash de la
     PWA), que es justo lo que `REFERENCIAS.md` ya dejaba anotado cuando se
     descarto el chorrito a tamaño boton.
     TRES COSAS TECNICAS QUE NO SON OPCIONALES:
       · Se anima `transform`, NUNCA `background-size`: WebKit no anima lo
         segundo, y esa leccion ya la pago el loader de la taza en iPhone.
       · Con `prefers-reduced-motion` el avion se queda quieto y baja la
         opacidad: sigue diciendo «estoy ocupado» sin moverse.
       · El boton queda DESHABILITADO mientras vuela — es la mitad visible del
         candado de envio en vuelo, que existe porque escribir durante el POST
         reabria el boton y el segundo submit mataba al primero.
     ════════════════════════════════════════════════════════════════════ */
  /* 🔴 EL AVION VA INCLINADO (JC, 29-ago: «esta centrado pero en un angulo, no
     completamente horizontal hacia la derecha»). Produccion usa el
     `paper-plane-TILT` de Phosphor, no el recto. Yo habia puesto el horizontal.
     No es un detalle menor para el motion: un avion inclinado apunta
     arriba-derecha, asi que el despegue en esa diagonal es coherente con el
     dibujo. Con el recto, la animacion iba en una direccion que el icono no
     señalaba. */
  .enviar.enviando{cursor:default}
  .enviar.enviando svg{animation:despegar .9s ease-in infinite}
  /* Sale del reposo (0,0), se va arriba-derecha —hacia donde apunta—, y vuelve
     a entrar desde abajo-izquierda mientras es invisible. Nunca se ve saltar. */
  @keyframes despegar{
    0%   {transform:translate(0,0) scale(1);         opacity:1}
    55%  {transform:translate(9px,-9px) scale(.86);  opacity:0}
    56%  {transform:translate(-8px,8px) scale(.86);  opacity:0}
    100% {transform:translate(0,0) scale(1);         opacity:1}
  }
  @media (prefers-reduced-motion:reduce){
    .enviar.enviando svg{animation:none; opacity:.55}
  }
  .enviar{background:var(--primary-fill); color:var(--primary-ink);
    transition:transform .1s ease, box-shadow .16s ease; box-shadow:var(--sombra-1)}
  .enviar:hover{transform:scale(1.07); box-shadow:var(--sombra-2)}
  .enviar:active{transform:scale(.95)}
  /* 🔴 EL AMBAR ES EL ASPECTO ENCENDIDO, y por eso va detras de `:not(:disabled)`.
     JC, 4-sep: «el composer para notas quedo raro; con el mismo concepto del canal
     normal, que se quite el circulito y se ponga cuando se escribe».
     Sin la compuerta, esta regla —tres clases— GANABA por especificidad sobre
     `.enviar:disabled` —dos— pero solo declaraba `background`. El `color` seguia
     viniendo del apagado, asi que quedaba un avion `--tenue` sobre relleno `--warn`:
       MEDIDO, claro 1.03 · oscuro 1.20   (umbral de icono 3.0; 1.0 = el mismo color)
     O sea invisible, que es exactamente lo que se veia en la captura de JC.
     Es el E27 en espejo: no se pierde la cascada por no ser especifico, se pierde por
     ganar en UNA propiedad y dejar las otras a otra regla. Decirlo con `:not(:disabled)`
     en vez de duplicar el apagado es lo que impide que vuelva a separarse. */
  .caja.nota-modo .enviar:not(:disabled){background:var(--warn); color:var(--warn-ink)}
  /* 🔴 EL AVION APAGADO. JC, 3-sep: «cuando no hay nada escrito es igual que cuando
     empiezo a escribir; se supone que debe estar atenuado».
     NO es una mejora: es un comportamiento de V1 que no se porto. Alla existe
     `.composer .enviar:disabled{opacity:.38}` (inbox.html:305) y aca no habia nada, asi
     que el boton se veia identico con la caja vacia y con texto. El `disabled` SI se
     ponia —`refrescarBoton()` lo maneja y el envio funciona—: lo que faltaba era que se
     VIERA.
     CON COLORES Y NO CON `opacity`, aunque V1 use opacidad: esta decidido en este repo
     (ver `.btn.apagado`). La opacidad apaga el fondo Y el contenido a la vez, y fabrica
     un par ilegible sin cambiar un solo color. Un estado apagado se dice con colores,
     que se pueden medir — y estos se midieron:
       icono --tenue sobre --panel-2 ......  5.44 claro · 5.74 oscuro   (umbral 3.0)
     🔴 VA SOBRE `:disabled`, el pseudo-selector que lee la PROPIEDAD real del boton. Una
     clase habria que acordarse de ponerla y quitarla, y olvidarse no da error.
     ⚠️ Y NO alcanza al `.btn:disabled` de la ficha ni al reves: el avion es `.ic-b
     .enviar`, no un `.btn`. Hay una prueba que fija esa separacion en las dos
     direcciones (`test_inbox_v2_composer.py`). */
  /* 🔴 TRANSPARENTE, no `--panel-2`. El 3-sep se puso el color de la caja para que el
     circulo «desapareciera», y funciona… mientras la caja SEA ese color. No lo es en
     modo nota (ambar), ni con el foco de la nota, ni arrastrando un archivo (`--soft`):
     ahi el mismo valor pinta un disco gris que no combina con nada.
     Transparente no imita el fondo: LO DEJA VER, asi que sirve para los cinco estados de
     la caja sin repetir un color en ningun sitio. Y ademas es lo correcto: `.ic-b` —la
     clase base que este boton comparte con el clip y el microfono— ya es
     `background:transparent; color:var(--tenue)`. Un boton apagado se ve como los otros
     iconos inactivos de su propia fila, que es lo que JC describio como «me gusto asi».
     MEDIDO sobre los cuatro fondos que puede tener la caja (umbral de icono 3.0):
       caja normal ......... claro 5.44 · oscuro 5.74
       caja en modo nota ... claro 4.98 · oscuro 4.75
       nota con el foco .... claro 4.64 · oscuro 4.59
     Guardado por `test_inbox_v2_composer.py`, que RECALCULA estos numeros desde los
     tokens: si alguien mueve la paleta, la prueba lo dice con su cifra. */
  .enviar:disabled{background:transparent; color:var(--tenue); cursor:default;
    box-shadow:none}
  .enviar:disabled:hover{transform:none; box-shadow:none}
  .enviar:disabled:active{transform:none}

  /* ══════════════════════════════════════════════════════════════════
     EL ÚNICO CAMBIO DE COMPOSER QUE SE DEFIENDE — y no es estético.
     ──────────────────────────────────────────────────────────────────
     Hoy, con la ventana de 24 h cerrada, el composer dice: «usa + Nueva
     (plantilla) para iniciar». Eso te SACA de la conversación: navegas,
     empiezas de cero, y el hilo que estabas leyendo se queda atrás.
     Acá la plantilla es un TERCER MODO del composer y se inserta sin
     moverse de sitio. Mismo destino, sin perder el contexto.
     ══════════════════════════════════════════════════════════════════ */
  /* 🔴 DOS BARRAS DE SCROLL (JC): el panel scrolleaba y el composer tambien.
     Ahora scrollea SOLO el panel y el composer no crece. */
  .plantillas{display:none; flex-direction:column; gap:2px;
    background:var(--panel-2); border-radius:var(--rc); padding:var(--s6)}
  /* El buscador queda FIJO y solo scrollea la lista: si el buscador se fuera
     con el scroll, con veinte plantillas habria que subir para poder buscar. */
  .plt-buscar{display:flex; align-items:center; gap:6px; flex-shrink:0;
    background:var(--panel); border-radius:9px; padding:6px var(--s8);
    margin-bottom:var(--s4)}
  .plt-buscar .ph{width:14px; height:14px; color:var(--tenue)}
  .plt-buscar input{flex:1; min-width:0; border:0; background:transparent; outline:none;
    color:var(--text); font:inherit; font-size:.8rem}
  /* ════════════════════════════════════════════════════════════════════
     🔴 EL TOPE SE MIDE, NO SE SUPONE. Tercer intento, y los dos anteriores
     fallaron por la misma razon: puse un numero a ojo (150px, luego 47px por
     fila) y JC siguio viendo cuatro. Atar el tope a una variable no sirve de
     nada si el valor de la variable tambien es una corazonada.
     Ahora el JS MIDE la primera fila renderizada con `offsetHeight` y calcula
     el tope desde ahi. Asi es cierto por construccion, aunque manana cambie la
     tipografia, el padding o el tamano de letra del sistema.
     ⚠️ La rendija de 12px es a proposito: sin ella nadie descubre que hay mas.
     Tres A LA VISTA, no tres en total.
     En movil son DOS: ahi el espacio es todo y el composer manda.
     ════════════════════════════════════════════════════════════════════ */
  /* 🔴 ESTAS DOS REGLAS SON LAS QUE ENCIENDEN EL PANEL, y me las lleve por
     delante en un corte por indices (JC: «ahora no sale ninguna plantilla»).
     La logica estaba intacta: el panel simplemente seguia en display:none.
     Leccion del dia, otra vez: un corte por indices borra lo que hay EN MEDIO,
     no solo lo que uno cree estar reemplazando. Verificar que sobrevivio, no
     asumirlo — y el chequeo esta al pie de este archivo. */
  .modo-plantilla .plantillas{display:flex}
  .modo-plantilla .caja{display:none}
  /* Tres a la vista + una rendija de 10px para que el scroll se descubra.
     Aritmetica pura: no depende de medir, ni de que corra JS. */
  .plt-lista{display:flex; flex-direction:column; gap:var(--plt-gap);
    max-height:calc(var(--plt-h) * 3 + var(--plt-gap) * 2);
    overflow-y:auto; overscroll-behavior:contain}
  /* 🔴 En movil DOS. Es lo que pidio JC y es donde de verdad importa: ahi el
     composer es lo unico que hay. */
  @media (max-width:960px){
    .plt-lista{max-height:calc(var(--plt-h) * 2 + var(--plt-gap))}
  }
  /* 🔴 CUARTO INTENTO, Y AHORA SIN MEDIR NADA (JC: «salen 4, me preocupa que
     no podamos limitarlas... hagamoslo bien»). Tiene razon en preocuparse: si
     el tope depende de una medicion, en movil un banco grande puede taparlo
     todo el dia que la medicion no corra.
     LA SALIDA es quitar la incognita: si la FILA tiene alto FIJO, el tope es
     aritmetica y no hace falta medir nada ni que corra ningun JS.
     Se consigue con dos cosas: alto fijo, y la vista previa recortada a UN
     renglon (que ademas es mejor: una plantilla se reconoce por su nombre y su
     arranque, no por leerla entera). */
  /* 🔴 QUINTO INTENTO, Y ESTA VEZ CON LA ARITMETICA ESCRITA. Los cuatro
     anteriores fallaron porque puse un numero sin comprobar que cuadrara:
     150px a ojo, luego 47px a ojo, luego una medicion que no siempre corria,
     luego 44px que NO LE ALCANZA AL CONTENIDO (por eso JC veia los nombres
     recortados arriba y abajo).
     Ahora el alto sale de sumar sus partes, y cada parte esta fijada:
        padding  8 + 8            = 16
        nombre   .8rem  x 1.25    = 16   (12.8px x 1.25)
        aire entre las dos lineas =  1
        vista previa .74rem x 1.3 = 15   (11.84px x 1.3)
        ─────────────────────────────
        TOTAL                     = 48px
     Si manana alguien cambia un tamano de letra, tiene que cambiar esta cuenta
     — por eso esta escrita aca y no en la cabeza de nadie.
     ⚠️ Y FUERA LA RENDIJA: JC la vio dos veces como «una cuarta plantilla»,
     porque 10px alcanzan para el primer renglon entero de la siguiente. La
     barra de scroll ya es la senal de que hay mas; la rendija sobraba y
     confundia. Tres en escritorio y DOS en movil, exactos. */
  :root{ --plt-h:48px; --plt-gap:2px }
  .plt{display:flex; align-items:center; gap:var(--s12); width:100%; border:0;
    height:var(--plt-h); flex-shrink:0; padding-top:8px; padding-bottom:8px;
    background:transparent; color:var(--text); font:inherit; font-size:.83rem;
    text-align:left; padding-left:var(--s12); padding-right:var(--s12);
    border-radius:10px; cursor:pointer;
    overflow:hidden}
  .plt:hover{background:var(--panel); box-shadow:var(--sombra-1)}
  /* 🔴 LA CATEGORIA DE LA PLANTILLA (JC, 29-ago: «hay manera de decir si es
     utilidad o marketing?»). Si, y el dato YA VIAJA: `whatsapp.py:159` le pide
     `category` a Meta al listar plantillas — pero `resumen_plantilla` la
     DESCARTA al armar el resumen del selector. Es una clave mas en ese dict,
     no un dato nuevo.
     Y no es cosmetico: MARKETING cuesta mas que UTILITY y tiene reglas mas
     estrictas. Elegir la plantilla a ciegas es elegir el precio a ciegas.
     MKT va teñida y UTIL neutra: lo caro es lo que tiene que saltar a la vista,
     no lo normal. */
  .plt .et.cat-util{background:var(--panel); color:var(--tenue)}
  .plt .et.cat-mkt{background:color-mix(in srgb,var(--warn-vivo) 18%,var(--panel));
    color:var(--text)}
  .plt .et{font:700 .58rem/1 var(--fm); letter-spacing:.04em; color:var(--tenue);
    background:var(--panel); border-radius:5px; padding:4px 6px; flex-shrink:0;
    box-shadow:var(--sombra-1)}
  .plt .cuerpo{min-width:0; overflow:hidden}
  .plt .cuerpo b{display:block; font:600 .8rem/1.25 var(--fb); height:16px; overflow:hidden;
    text-overflow:ellipsis; white-space:nowrap}
  .plt .cuerpo span{display:block; color:var(--muted); font:.74rem/1.3 var(--fb);
    height:15px; margin-top:1px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  /* Aviso de ventana cerrada — vive DENTRO del composer, no encima. */
  .ventana-cerrada{display:none; align-items:flex-start; gap:var(--s8);
    background:var(--panel-2);
    border-radius:var(--rc); padding:var(--s12); font-size:.78rem; color:var(--muted)}
  .cerrada .ventana-cerrada.wa{display:flex}
  .cerrada.sin-pl .ventana-cerrada.wa{display:none}
  .cerrada.sin-pl .ventana-cerrada.sin-plantilla{display:flex}
  .cerrada.sin-pl .modo-seg button.plantilla{display:none}
  /* 🔴 El reloj de este aviso NO va ambar (JC lo vio marron). La regla que
     acabamos de fijar dice que el ambar significa UNA sola cosa en todo el
     producto: lo interno no sale. Este aviso no es interno — es un limite de
     Meta. Si el ambar aparece aca, deja de significar algo alla. */
  .ventana-cerrada .ph{width:16px; height:16px; color:var(--primary); flex-shrink:0}
  /* 🔴 LOS ENLACES DE ESTOS AVISOS SALIAN EN AZUL DE NAVEGADOR (19-sep, medido: rgb(0,0,238)
     sobre rgb(36,42,52), ~1.5:1 en oscuro — ilegible). Nadie lo habia visto porque los que
     habia («Ir a la conversación abierta», «Probar el agente», «+ Nueva») salen en casos
     raros; el de «manda una plantilla» sale en CADA hilo vencido. La trampa del `<a>`, otra
     vez. Subrayado ademas del color: un enlace dentro de un parrafo no puede depender solo
     del color para distinguirse. */
  .ventana-cerrada a{color:var(--primary); font-weight:600;
    text-decoration:underline; text-underline-offset:2px}
  .ventana-cerrada .btn.chico{flex-shrink:0; align-self:center}
  /* 🔴 OTRO `opacity` MIO, cazado al revisar (misma familia que el «Guardar»):
     apagar el modo WhatsApp con `opacity:.4` apaga tambien SU TEXTO y fabrica
     un par ilegible. Un estado no disponible se dice con COLORES apagados, que
     si se pueden medir, y con `pointer-events` para que no se pueda tocar. */
  .cerrada .modo-seg button.wa{color:var(--tenue); pointer-events:none}
  .cerrada .modo-seg button.wa .ph{color:var(--tenue)}
  .ventana-cerrada b{color:var(--text)}

  /* ══════════ LA FICHA ══════════ */
  .ficha{position:relative}
  .ficha::before{content:""; position:absolute; inset:0; pointer-events:none; z-index:0;
    background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 700' fill='none' stroke='%231B4B7A' stroke-opacity='.22' stroke-width='1.2' stroke-linecap='round'%3E%3Cpath d='M126 300c26-30-22-52 4-82s-22-52 4-82'/%3E%3Cpath d='M170 296c30-34-26-60 4-94s-26-60 4-94'/%3E%3Cpath d='M214 302c24-28-20-48 4-76s-20-48 4-76'/%3E%3Cpath d='M96 348h150v52a58 58 0 0 1-58 58h-34a58 58 0 0 1-58-58z'/%3E%3Cpath d='M246 366h14a30 30 0 0 1 0 60h-14'/%3E%3Cpath d='M78 476h186'/%3E%3Cg transform='rotate(-4 171 400)' stroke-width='1.7' stroke-linejoin='round'%3E%3Cpath d='M170 426c-15-19-27-30-25-42 2-12 15-14 23-3 8-13 25-12 29 1 3 15-12 27-25 43'/%3E%3Cpath d='M148 386c3-6 9-9 15-6'/%3E%3C/g%3E%3C/svg%3E") no-repeat center 62%/330px auto}
  /* En oscuro se invierte a un fantasma claro (así lo vio JC y le gustó).
     En claro va en TERRACOTA y más marcada: en gris no se veía —lo cazó él— y
     un fantasma gris no es nuestra taza, es una mancha. */
    /* 🔴 EN CLARO LA TAZA VA EN AZUL (JC: «en claro no se nota mucho, quiza si la
     ponemos en azulito funciona mejor»). Tenia razon y ademas es mas coherente:
     con la interfaz en azul, un fantasma terracota era el unico rastro calido
     que quedaba fuera del logo — y ahi si competia en vez de acompañar.
     En OSCURO se queda como esta, que JC dijo que quedo hermosa. */
  :root[data-theme="dark"] .ficha::before{filter:invert(1) brightness(1.5) saturate(.4)}
  .ficha .scroll{position:relative; z-index:1; overflow-y:auto; padding:var(--s16)}
  .rot{font:700 .64rem/1 var(--fb); letter-spacing:.13em; text-transform:uppercase;
    color:var(--tenue); margin:0 0 var(--s8)}
  .campo{margin-bottom:var(--s8)}
  /* 🔴 EL FORMULARIO RESERVA SU PROPIA SEPARACION (JC, 31-ago: «el boton esta un
     poco sobre perfiles»). El formulario termina en «Guardar» y lo que viene
     despues —el rotulo PERFILES de la tumba— arrancaba a CERO pixeles, asi que al
     pasar el mouse el boton se levanta 1px y lo toca de verdad.
     Va en el FORMULARIO y no en `.rot`: ese rotulo se usa tambien dentro de
     `.hist-corto`, que ya trae su propio margen, y tocarlo alla movia una
     separacion que ya estaba bien. Los margenes de hermanos COLAPSAN, asi que
     contra `.hist-corto` (que tiene los mismos 24px) el hueco no cambia. */
  .cli-form{margin-bottom:var(--s24)}
  .campo label{display:block; font-size:.68rem; color:var(--tenue); margin-bottom:3px}
  /* ════════════════════════════════════════════════════════════════════
     🔴 EL EFECTO DEL HISTORIAL, COPIADO TAL CUAL (JC: «no ocurre como el
     historial, hagamoslo como en historial, ese efecto esta lindisimo»).
     Y me equivoque al diagnosticarlo la primera vez: lo llame «transparencia» y
     puse un `color-mix` al 78%. NO ES ESO. Las tarjetas del historial son
     OPACAS. Lo que hace el efecto son tres cosas, todas en `.hist`:
       · en reposo, PLANAS — sin sombra, se funden con el dibujo del fondo
       · al pasar el mouse, SE LEVANTAN 1px y ganan sombra
       · la activa vive en --panel con mas sombra: esta despegada siempre
     El fondo no cambia de opacidad en ningun momento. La sensacion de que la
     taza «se asoma» sale de que en reposo no hay sombra que separe la tarjeta
     del lienzo.
     LECCION: copiar el efecto que gusta, no la explicacion que uno se hace de
     el. Mirar el CSS del original cuesta un minuto; reinventarlo costo dos
     rondas. */
  /* 🔴 TERCERA PASADA, Y AHORA CON LA CAUSA ENTENDIDA (JC: «en historial se ve
     el humito y la taza, en cliente se pierde porque no esta lo suficientemente
     transparente»).
     La diferencia real entre las dos zonas no era la opacidad: era el ESPACIO.
     En el historial hay tres tarjetas separadas por 6px y la taza se ve POR LOS
     HUECOS. En cliente los campos van pegados con sus etiquetas y tapan la
     superficie entera — no queda hueco por donde asomarse.
     Por eso aca SI hace falta transparencia, y bastante mas de la que puse: al
     78% solo pasaba el 22% del dibujo, un trazo efectivo de 0.03. Nada. Al 55%
     pasa el 45%, y con el trazo subido a .22 la taza queda en 0.099 bajo el
     campo: visible sin competir con el texto.
     MEDIDO: la tinta sobre el campo en reposo da 12.18 en claro y 13.05 en
     oscuro. Se gana el dibujo sin gastar una decima de legibilidad.
     Y al ENFOCAR se vuelve opaco: escribiendo, el adorno estorba. */
  .campo input,.campo textarea{width:100%; border:0; border-radius:var(--rc);
    background:color-mix(in srgb,var(--panel-2) 55%,transparent);
    color:var(--text); padding:8px 11px; font:.85rem/1.4 var(--fb);
    transition:box-shadow .16s ease, transform .16s ease, background .14s}
  .campo input:hover,.campo textarea:hover{transform:translateY(-1px);
    background:var(--panel-2); box-shadow:var(--sombra-1)}
  /* Al enfocar, OPACO del todo: escribiendo, la legibilidad manda. */
  .campo input:focus,.campo textarea:focus{outline:none; background:var(--panel);
    box-shadow:0 0 0 2px var(--primary), 0 0 0 5px var(--foco-halo)}
  .campo textarea{min-height:62px; resize:vertical}
  /* ════════════════════════════════════════════════════════════════════
     🔴 EL HISTORIAL VUELVE ABAJO, Y CON TOPE (JC, 29-ago): «si hay mucho
     historial tenemos que estar seguros de que no va a ocupar toda la barra —
     por eso antes estaba abajo».
     Tenia razon dos veces. Primero: yo lo subi sin preguntar POR QUE estaba
     donde estaba, y estaba abajo por un motivo. Segundo, y mas grave: produccion
     muestra hasta 25 conversaciones. A ~40px cada una son 1000px — la ficha
     entera sepultada, y los cinco campos que SI se editan empujados fuera de
     la vista.
     Dos arreglos, y hacen falta los dos:
       · ORDEN: los campos del cliente primero (es lo que se edita), el
         historial despues. Como en produccion.
       · TOPE: cinco filas visibles y scroll propio. Es la misma leccion de los
         nueve chips de adjuntos y la de las plantillas — en esta pantalla,
         TODA lista que puede crecer necesita un techo.
     ⚠️ HUBO UNA EXCEPCION DE MOVIL Y SE QUITO (29-ago). Ponia el historial
     arriba en la hoja, y con cuatro conversaciones parecia buena idea. Con las
     25 reales sepultaba los cinco campos. El orden es el mismo en las tres
     anchuras.
     ════════════════════════════════════════════════════════════════════ */
  /* 🔴 SIN SCROLL INTERNO. Mi primera version metia un scroll propio de cinco
     filas, y era un mecanismo NUEVO compitiendo con uno probado. JC se acordo
     de la politica real y estaba en el codigo:
       · `web.py:692` capa el historial a 25 — «el contacto mas contactado es
         justo el que mas se abre, traer y pintar TODO degradaba cada apertura»
       · la cabecera muestra el TOTAL real, no lo pintado
       · y si hay mas: «Mostrando las N recientes — usa la busqueda de la lista
         para llegar a las anteriores»
     Eso ya resuelve las dos mitades: el rendimiento (25) y la barra que se
     desborda (los campos van ARRIBA, asi que el historial largo solo alarga el
     scroll de la ficha, no sepulta nada). Un scroll dentro de otro scroll
     ademas se pelea con la rueda del raton.
     La leccion, que es la misma de hoy: antes de inventar un tope, mirar si ya
     hay una politica — y por que esta donde esta. */
  :root{ --hist-h:46px }
  .hist-lista{display:flex; flex-direction:column}
  /* ════════════════════════════════════════════════════════════════════
     🔴 EL HISTORIAL SE COLAPSA (decision del 29-ago). JC dio dos opciones —
     alto fijo con scroll propio, o un boton que lo abre— y pidio ayuda para
     decidir. Va el BOTON, por tres razones en orden de peso:
       1. Un scroll DENTRO de otro scroll se pelea con la rueda: pasas el mouse
          por encima y no sabes cual se mueve. En trackpad es peor.
       2. La ficha se abre para EDITAR al cliente; el historial se consulta de
          vez en cuando. Lo que se usa poco no puede costar espacio siempre.
       3. Colapsado no hay scroll anidado en absoluto. Y expandido, lo pidio el
          usuario: ahi el scroll de la columna es esperado y no sorprende.
     Se muestran SIEMPRE las 3 mas recientes —la actual y las dos ultimas, que
     son las que de verdad se miran— y el boton abre las 25 que permite la
     politica de `web.py:692`. El total real (35) sigue a la vista arriba. */
  /* ════════════════════════════════════════════════════════════════════
     🔴 DOS PESTAÑAS EN LA FICHA — respuesta a JC (29-ago): «tiene que haber una
     manera OBVIA de volver a la ficha de cliente, porque abren el historial y
     se les olvida que existe».
     Se probaron dos cosas antes y las dos fallaron por el mismo motivo: eran
     una COLUMNA LARGA. Primero el historial abajo (hay que scrollear y al
     expandir 25 filas te pierdes); despues un boton de «ver mas» (al pie no se
     alcanza; en el encabezado se alcanza pero sigues en una columna donde el
     cliente queda arriba, fuera de vista).
     Las pestañas quitan el problema en vez de mitigarlo: **solo una cosa a la
     vez, y la otra siempre visible como pestaña.** No hay scroll que perderse,
     no hay nada que recordar, y volver es un clic que esta ahi todo el tiempo.
     ⚠️ Es el unico sitio donde el rediseno se aparta de produccion, que usa una
     columna. Se justifica porque produccion NO tiene el problema: alli el
     historial vive abajo y no se expande. En cuanto se expande, la columna deja
     de servir. Usa el mismo lenguaje de segmento que ya esta en el composer, el
     alcance y el orden — no es un patron nuevo que aprender.
     ════════════════════════════════════════════════════════════════════ */
  .ficha-tabs{display:flex; gap:2px; padding:var(--s12) var(--s16) 0; flex-shrink:0}
  .f-tab{flex:1; border:0; cursor:pointer; background:var(--panel-2);
    color:var(--muted); border-radius:10px 10px 0 0; padding:9px var(--s8);
    font:600 .78rem/1 var(--fb)}
  /* 🔴 EL ENFASIS DE LA PESTAÑA (JC: «no se nota claramente»). Tenia solo un
     cambio de fondo, que entre dos superficies vecinas casi no se lee. Ahora
     lleva TRES señales: superficie elevada, tinta plena en peso 700, y un filo
     de acento arriba — el mismo idioma de «el activo se lee mas fuerte». */
  .f-tab.act{background:var(--panel); color:var(--text); font-weight:700;
    box-shadow:inset 0 3px 0 var(--primary), var(--sombra-2)}
  .f-tab:not(.act):hover{color:var(--text)}
  .f-tab .num{opacity:.75; font-size:.72rem}
  /* 🔴 Reglas EXPLICITAS en los dos sentidos. La version anterior solo decia
     que ocultar, y confiaba en que lo otro estuviera visible «por defecto» —
     por eso quedaba estancada en el historial (JC). Un estado se declara
     entero, no por omision del contrario. */
  #bloque-cliente,#bloque-hist{display:none}
  /* 🔴 EL ATRIBUTO VIVE EN EL <aside>, no en `.scroll` (JC: «no veo mas
     elementos al pasar a Historial»). Estaba en `.scroll` y una insercion mia
     habia dejado `#bloque-hist` FUERA de ese contenedor, asi que el selector no
     lo alcanzaba y la regla que lo oculta si. El sintoma era una pestaña muerta,
     sin ningun error.
     El `<aside>` contiene los dos bloques pase lo que pase con la anidacion de
     adentro: el estado deja de depender de una estructura que se puede mover. */
  .ficha[data-p="cliente"] #bloque-cliente{display:block}
  .ficha[data-p="hist"] #bloque-hist{display:block}
  .hist-lista{max-height:none; overflow:visible}
  /* 🔴 EL HISTORIAL RECIENTE VIVE TAMBIEN EN LA PESTAÑA DE CLIENTE (JC): «que
     cuando selecciono cliente se vea lo del cliente y abajo algunos elementos
     de historial, y un boton de expandir que haga lo mismo que la pestaña».
     Es la mejor de las dos ideas juntas: las tres ultimas son las que de verdad
     se miran, asi que estan siempre; y el que necesita las 35 tiene DOS caminos
     al mismo sitio — la pestaña de arriba y este boton. Dos puertas, un destino:
     nadie se queda encerrado. */
  .hist-corto{margin-top:var(--s24)}
  .hist-expandir{display:flex; align-items:center; justify-content:center; gap:6px;
    width:100%; border:0; cursor:pointer; margin-top:var(--s6);
    background:var(--panel-2); color:var(--primary); border-radius:var(--rc);
    padding:9px var(--s12); font:600 .76rem/1 var(--fb)}
  .hist-expandir:hover{background:var(--panel); box-shadow:var(--sombra-1)}
  .hist-expandir .ph{width:15px; height:15px}
  .hist-lista[data-abierto="1"]{max-height:none}
  /* 🔴 EL AVISO DE «HAY MAS» NO SE PUEDE PERDER (JC, 29-ago). Es la unica
     pista de que existen conversaciones anteriores y de como llegar a ellas —
     sin el, un historial de 42 se lee como uno de 25 y nadie sospecha.
     Va PEGADO AL PIE de la lista: con 25 filas, uno que viviera al final del
     scroll podria no verse nunca. Pegado, esta siempre a la vista mientras se
     recorre, que es justo cuando la duda aparece.
     Texto de produccion, palabra por palabra (`inbox.html:1052`). */
  .hist-nota{position:sticky; bottom:0; margin:var(--s6) 0 0;
    background:var(--panel); padding:var(--s8) var(--s12);
    border-radius:var(--rc); box-shadow:var(--sombra-1);
    font-size:.7rem; color:var(--muted); line-height:1.35}
  /* 🔴 `.hist` SE PINTA SOBRE UN `<a>`, y el mockup usa `<div>`. Sin estas dos
     declaraciones las 25 filas del historial salen SUBRAYADAS y en azul de enlace.
     Es la CUARTA vez de la misma trampa: el CSS se copia verbatim del mockup, y el
     mockup no navega. Ya mordio con `.seg`, `.re` y `.rot/.nom/.tel`. */
  .hist{display:flex; align-items:center; gap:var(--s8); border-radius:var(--rc);
    text-decoration:none; color:var(--text);
    height:var(--hist-h); flex-shrink:0;
    padding:var(--s8) var(--s12); margin-bottom:var(--s6);
    background:color-mix(in srgb,var(--panel-2) 55%,transparent);
    font-size:.73rem; cursor:pointer;
    transition:box-shadow .16s ease, transform .16s ease, background .14s}
  .hist:hover{transform:translateY(-1px); background:var(--panel-2); box-shadow:var(--sombra-1)}
  .hist.act{background:var(--panel); box-shadow:var(--sombra-2)}
  .hist .est{font:700 .56rem/1 var(--fb); letter-spacing:.07em; text-transform:uppercase;
    color:var(--tenue); border-radius:999px; padding:3px 7px; background:var(--panel)}
  .hist.act .est{background:var(--soft); color:var(--primary)}

  /* Barra de comandos */
  .cmdf{position:fixed; inset:0; z-index:60; background:rgba(14,12,9,.55);
    display:none; align-items:flex-start; justify-content:center; padding-top:12vh;
    backdrop-filter:blur(2px)}
  .cmdf[data-a="1"]{display:flex}
  .cmd{width:min(620px,92vw); background:var(--panel); border-radius:20px;
    box-shadow:var(--sombra-3); overflow:hidden}
  .cmd input{width:100%; border:0; outline:none; background:transparent; color:var(--text);
    padding:var(--s20); font:1.02rem/1 var(--fb)}
  .cmd .gr{padding:var(--s4) var(--s12) var(--s8)}
  .cmd h4{margin:var(--s8) 0 var(--s4) var(--s8); font:700 .6rem/1 var(--fb);
    letter-spacing:.13em; text-transform:uppercase; color:var(--tenue)}
  .cmd .it{display:flex; align-items:center; gap:var(--s12); width:100%; border:0;
    background:transparent; color:var(--text); font:inherit; font-size:.87rem;
    padding:9px var(--s8); border-radius:10px; cursor:pointer; text-align:left}
  .cmd .it:hover,.cmd .it.mk{background:var(--soft)}
  .cmd .it .r{margin-left:auto; font:.66rem/1 var(--fm); color:var(--tenue)}
  .cmd .pie{display:flex; gap:var(--s12); padding:var(--s12) var(--s20);
    background:var(--panel-2); font-size:.66rem; color:var(--tenue)}

  /* ══════════ MÓVIL ══════════ */
  /* ════════════════════════════════════════════════════════════════════
     🔴 EL TRAMO INTERMEDIO — hueco real que encontro JC (29-ago) comparando el
     mockup contra produccion con la ventana a media pantalla.
     Mi unico corte estaba en 960 px, o sea que el diseno saltaba de ESCRITORIO
     COMPLETO a TELEFONO de golpe. Pero un escritorio a 1000-1300 px es lo
     normal: un portatil, o media pantalla. Ahi no se quiere la vista de
     telefono — se quiere escritorio que cede. Y la respuesta a su pregunta es
     NO: la ficha no puede exigir maximizar.
     LA ESCALERA, cediendo en el orden de siempre (primero lo repetible):
       >= 1280   las tres columnas fijas
       960-1280  la ficha se pliega y se abre con un boton, deslizandose sobre
                 el hilo. La LISTA SE QUEDA: es la que se usa todo el dia.
       < 960     la lista se va al carril y la ficha pasa a hoja inferior.
     ════════════════════════════════════════════════════════════════════ */
  .ficha-abrir{display:none}
  .ficha-abrir .ph{width:15px; height:15px}
  @media (max-width:1280px) and (min-width:961px){
    .lienzo{grid-template-columns:62px 320px 1fr; position:relative}
    .ficha-abrir{display:inline-flex}
    .ficha{position:absolute; top:var(--s12); right:var(--s12); bottom:var(--s12);
      width:308px; z-index:35; box-shadow:var(--sombra-3);
      transform:translateX(calc(100% + var(--s16)));
      transition:transform .22s cubic-bezier(.32,.72,0,1)}
    .ficha[data-a="1"]{transform:none}
  }
  @media (prefers-reduced-motion:reduce){ .ficha{transition:none} }

  .hojaf{display:none}
  @media (max-width:960px){
    /* Tres filas en movil: banner, carril y el panel. Con el banner oculto la primera
       colapsa a cero, asi que el caso normal queda EXACTAMENTE como antes. */
    .lienzo{grid-template-columns:1fr; gap:var(--s8); padding:var(--s8);
      grid-template-rows:auto auto 1fr}
    .notif-aviso{grid-row:1}
    .carril{grid-row:2}
    .col-lista,.col-hilo{grid-row:3}
    /* ══════════════════════════════════════════════════════════════════════
       EL CARRIL SE PARTE EN DOS, Y NO ES ESTETICA: SON DOS COSAS DISTINTAS
       ──────────────────────────────────────────────────────────────────────
       JC, 5-sep, en el telefono: «en movil no es intuitivo los loguitos, hay que saber
       para que sirve cada uno… me gustan los loguitos pero sacrificamos explicar para
       que sirve cada uno».
       Y no hay que inventar la solucion: PRODUCCION YA LA TIENE Y LA PIDIO EL MISMO
       (`BACKLOG §1239`, 21-jun) — una barra de filtros PERSISTENTE abajo, **con texto**.
       Lo que se hace acá es portarla, con el idioma del rediseño.

         · Los CUATRO FILTROS son la navegacion: se tocan todo el dia y son la unica
           forma de volver a la lista desde un hilo. Bajan a una barra fija abajo, al
           alcance del pulgar, con el icono ARRIBA y su nombre DEBAJO.
         · Las UTILIDADES (campañas, agente, tema, salir) son ocasionales. Se quedan
           arriba, y ahi el nombre va AL LADO del icono — que es lo que sale gratis con
           el marcado que ya existe, porque en fila el `<a>` y su `<span>` quedan
           uno junto al otro.
       ══════════════════════════════════════════════════════════════════════ */
    .carril{flex-direction:row; align-items:center; justify-content:flex-start;
      padding:var(--s6) var(--s8); gap:var(--s4); overflow-x:auto; overflow-y:hidden}
    /* 🔴 EL MUELLE SE QUEDA EN MOVIL, y antes se escondia. JC, 5-sep, comparando con
       produccion: «los botones viste que estan hacia la izquierda, se ven desalineados».
       Tenia razon y la referencia es su propia app: la cabecera de produccion pone la
       marca a la IZQUIERDA y los botones a la DERECHA. Sin el muelle, la taza y las
       utilidades quedaban todas apiñadas a la izquierda con medio ancho vacio al lado.
       `flex:1` entre medio hace exactamente eso, y ya existia — solo estaba apagado. */
    .carril .rail-sep{display:block; flex:1 0 auto; min-width:var(--s8)}
    /* El nombre AL LADO del icono, no debajo: en fila es lo que hace el marcado solo. */
    .carril > .rail-et{margin:0 var(--s8) 0 2px; font-size:11px; white-space:nowrap}
    .marca-taza{margin:0 var(--s6) 0 0; align-self:center; flex-shrink:0;
      width:22px; height:26px; background-size:20px auto}
    /* La palabra, como en produccion: «efi» en tinta y «core» en el acento de marca.
       🔴 El terracota es marca LOCKED (`REFERENCIAS.md`) y sobrevive a la paleta azul en
       exactamente dos sitios; este es uno. No se cambia por `--primary`. */
    .marca-nombre{display:inline; font:800 1.05rem/1 var(--fd); letter-spacing:-.02em;
      color:var(--text); margin-right:var(--s8); flex-shrink:0}
    .marca-nombre .co{color:var(--marca-co)}

    /* 🔴 EL CARRIL, MAS BAJO. JC, 5-sep: «el header quiza un poquin menos alto para
       acomodar todo como esta en produccion».
       Los botones bajan de 44px a 36px SOLO en la fila de arriba. El objetivo TACTIL se
       mantiene en 44 con un pseudo-elemento absoluto —el mismo patron que la barra de
       abajo— asi que se gana alto de pantalla SIN encoger lo que hay que tocar. Encoger
       el objetivo seria cambiar un problema por uno peor: en el telefono, un boton que
       falla al primer toque cuesta mas que 8px de alto. */
    .carril > .rail-b{width:36px; height:36px}
    .carril > .rail-b .ph{width:18px; height:18px}
    .carril > .rail-b::after{content:""; position:absolute; left:-4px; right:-4px;
      top:-4px; bottom:-4px}

    /* ── LA BARRA DE FILTROS, FIJA ABAJO ──────────────────────────────────
       `grid-auto-flow:column` con dos filas es lo que apila cada icono sobre SU nombre
       sin tocar el marcado: los hijos son `<a>` y `<span>` alternados, y asi caen
       icono-arriba / nombre-abajo, columna por columna.
       ⚠️ Va FIJA al viewport, no al final del documento: en la PWA de Android el alto
       del contenedor resuelve mas alto que el area visible y la barra caia bajo el
       fold. Es el mismo defecto que ya se pago en produccion, con su comentario. */
    .rail-filtros{
      display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
      grid-template-rows:auto auto; align-items:center; justify-items:center;
      position:fixed; left:0; right:0; bottom:0; z-index:50;
      background:var(--panel); box-shadow:0 -6px 18px rgba(0,0,0,.10);
      padding:var(--s6) var(--s8);
      padding-bottom:calc(var(--s6) + env(safe-area-inset-bottom));
    }
    .rail-filtros .rail-b{width:auto; min-width:44px; height:32px}
    .rail-filtros .rail-et{display:block; margin:0; font-size:10px;
      pointer-events:none}   /* el toque lo cobra el enlace, ver abajo */
    /* 🔴 EL NOMBRE ES HERMANO DEL ENLACE, NO ESTA DENTRO, asi que tocarlo NO NAVEGA.
       JC, 5-sep: «todavia cuando le doy a abiertas cerradas etc no sale la lista de
       chats». No era la lista: era que su dedo caia en la PALABRA, que es lo mas grande
       y lo natural para apuntar, y ahi no habia enlace. Solo el icono de arriba —32px—
       respondia.
       🔴 Y es peor que un boton que no hace nada: el filtro SI cambiaba de aspecto al
       tocar el icono, asi que la funcion parecia rota en vez de mal apuntada.
       El arreglo es el patron que ya usa este repo: un pseudo-elemento ABSOLUTO que
       estira el objetivo tactil SIN sumar alto ni mover un pixel de lo pintado. Cubre el
       icono Y su nombre, o sea la columna entera de la barra.
       ⚠️ La alternativa —meter el `<span>` DENTRO del `<a>`— cambia el marcado que
       comparte con escritorio, y escritorio esta aprobado y no se toca. */
    .rail-filtros .rail-b::after{content:""; position:absolute;
      left:-14px; right:-14px; top:-6px; bottom:-20px}
    /* El filo del activo, ARRIBA de la barra: abajo quedaria contra el borde de la
       pantalla y en varios telefonos lo tapa la barra de gestos del sistema. */
    .rail-filtros .rail-b.act::before{left:-6px; right:-6px; top:-7px; bottom:auto;
      width:auto; height:3px; border-radius:0 0 3px 3px}
    /* Y se le reserva el alto al lienzo para que NO tape el composer ni la ultima fila
       de la lista. `border-box`, asi que el padding no agranda la caja. */
    .lienzo{padding-bottom:calc(58px + env(safe-area-inset-bottom))}
    .rail-b.act::before{left:9px; right:9px; top:auto; bottom:-6px; width:auto; height:3px;
      border-radius:3px 3px 0 0}
    /* 🔴 EL ARREGLO DEL MOVIL (§0-H, reportado por JC el 30-ago y diferido por el).
       Aca decia `.col-lista{display:none}` a secas: la lista se escondia SIEMPRE, asi que
       en una sola columna ganaba el panel del hilo — que sin conversacion esta vacio.
       Resultado: «Seleccionar conversación» y NINGUNA lista de donde seleccionar. Girar el
       aparato no cambiaba nada, y cambiar de filtro tampoco.

       Es la receta de produccion, literal (`inbox.html:545-548`): una sola columna, y la
       clase `con-hilo` —que existe solo cuando hay una conversacion abierta— decide cual
       de los dos paneles se ve. Sin conversacion, la LISTA; con una, el HILO.

       ⚠️ El «volver» no es un boton: es el carril, que en movil es una fila horizontal
       siempre visible y cuyos filtros llevan a la lista. Misma decision que produccion
       (`BACKLOG §1238`/`§1239`, JC 21-jun). */
    .lienzo.con-hilo .col-lista{display:none}
    .lienzo:not(.con-hilo) .col-hilo{display:none}
    /* ══════════════════════════════════════════════════════════════════════
       LA HOJA DE LA FICHA — JC, 19-sep, con capturas del iPhone: «no funciona el
       scroll sino que scrolea el cuerpo del chat… al darle al tab historial queda
       como muy hacia abajo». MEDIDO a 390x844, y eran tres cosas de esta regla:
         · z 41 contra los 50 de la barra de filtros: la barra le TAPABA el pie
           (54 px; 88 en el iPhone, con la barra de gestos). Ahora la hoja es MODAL
           y va encima; el fondo la separa de la barra.
         · solo `max-height`: la hoja media lo que su contenido, asi que en Cliente
           NO HABIA NADA QUE SCROLLEAR (697 px de hoja, 633 de contenido — lo que
           faltaba estaba debajo de la barra, no fuera del scroll), y en Historial
           encogia a 252 px y se hundia. Con ALTO FIJO las dos pestañas miden lo
           mismo, no se mueven de debajo del dedo, y lo que sobra se scrollea.
         · el scroll de adentro no podia encogerse (ver `.ficha .scroll`).
       `vh` de respaldo y `dvh` despues, como el lienzo: gana la ultima que se
       entienda. */
    .ficha{position:fixed; left:0; right:0; bottom:0; z-index:52;
      height:86vh; height:86dvh;
      border-radius:22px 22px 0 0; box-shadow:var(--sombra-3);
      transform:translateY(101%); transition:transform .24s cubic-bezier(.32,.72,0,1)}
    .ficha[data-a="1"]{transform:none}
    /* El asa CIERRA (era un adorno `aria-hidden`). Es la salida para quien no atina
       al fondo y la unica que alcanza un lector de pantalla. Todo el ancho es toque. */
    .asa{display:block; width:100%; flex-shrink:0; border:0; background:transparent;
      cursor:pointer; padding:var(--s12) 0 var(--s4)}
    .asa i{display:block;width:38px;height:4px;border-radius:999px;
      background:var(--panel-2);margin:0 auto}
    /* 🔴 LA EXCEPCION DE MOVIL SE QUITO (JC, 29-ago): «con el historial largo se
       come todo el perfil del cliente».
       Yo habia puesto el historial primero en la hoja de movil, razonando que
       «lo primero es lo unico que se ve sin arrastrar». Con cuatro
       conversaciones se veia bien. Con veinticinco —que es el caso REAL, porque
       produccion capa en 25— empuja los cinco campos fuera de la pantalla y la
       ficha deja de servir para lo que se abre.
       LA LECCION: una excepcion que solo funciona con pocos datos no es una
       excepcion, es un defecto que todavia no se ha visto. Y probarla exigia
       datos realistas, no los cuatro de adorno que yo tenia puestos.
       Ahora el orden es el MISMO en las tres anchuras: Cliente, Guardar, la
       union, Historial. Como produccion. Una sola forma que recordar. */
    /* 🔴 `min-height:0` + `flex:1 1 auto`: el scroll es hijo de un flex en columna, y
       sin esto la regla del navegador es «no encojas por debajo de tu contenido» — el
       scroll no aparece nunca y el panel (`overflow:hidden`) corta lo que sobra.
       `overscroll-behavior:contain` impide que el gesto, al llegar al final, se le pase
       al hilo de atras. El pie reserva la barra de gestos del iPhone. */
    .ficha .scroll{display:flex; flex-direction:column; flex:1 1 auto; min-height:0;
      overscroll-behavior:contain;
      padding-bottom:calc(var(--s16) + env(safe-area-inset-bottom))}
    /* 🔴 EL FONDO. Este CSS existia desde el principio y el ELEMENTO NUNCA SE PINTO: el
       toque de al lado caia en el hilo y la hoja no tenia salida. Va entre la barra (50)
       y la hoja (52), asi que tambien tapa la barra: con la hoja abierta, un toque ahi la
       cierra en vez de navegar. `touch-action:none`: arrastrar sobre el fondo no mueve
       nada de atras. */
    .hojaf{display:block; position:fixed; inset:0; z-index:51; background:rgba(14,12,9,.45);
      opacity:0; pointer-events:none; transition:opacity .2s; touch-action:none}
    .hojaf[data-a="1"]{opacity:1; pointer-events:auto}
    /* ══════════════════════════════════════════════════════════════════════
       LA CABECERA DEL HILO, EN DOS FILAS — la distribucion de produccion
       ──────────────────────────────────────────────────────────────────────
       JC, 5-sep: «colocaste el numero pero no se alcanza a ver… mira como en produccion
       se acomoda tan bonito, y como se acomodan los botones cuando es tomar y cuando no
       hay tomar; quiero que la distribucion quede asi mismo pero con el nuevo look».

       El defecto: en UNA fila conviven el nombre, el canal+numero, el chip de asignacion
       y «Cerrar». Con «Tomar yo» ademas. No caben, y lo que cede es el subtitulo — o sea
       el numero, que es justo lo que se acababa de recuperar. Devolverlo y dejarlo en una
       fila era devolver algo que no se ve.

       La distribucion es la de produccion (`inbox.html`, `@media(max-width:760px)`):

           fila 1:  nombre + canal·numero        │  «Tomar yo» (si aplica)
           fila 2:                    chip «Atiende» + «Cerrar», a la derecha

       Y resuelve solo el caso que JC señalo —«cuando es tomar y cuando no hay tomar»—:
       sin «Tomar», la celda de arriba a la derecha queda vacia y el nombre se queda con
       todo el ancho. No hay dos reglas: hay una rejilla con nombres.
       ══════════════════════════════════════════════════════════════════════ */
    /* 🔴 CADA HIJO VA POR SU NOMBRE (19-sep). Aca decia `.hilo-top > .btn{grid-area:
       cerrar}`, y ese selector NUNCA alcanzo a «Cerrar»: vive dentro de un `<form>`, asi
       que el unico `.btn` hijo directo era «Cliente» — escondido. «Cerrar» caia donde lo
       dejara el auto-acomodo: ARRIBA sin «Tomar», ABAJO con «Tomar». Produccion lo
       prohibe por escrito: «"Cerrar" queda SIEMPRE en el mismo lugar» — y es el boton que
       cierra de un toque.
       Ahora hay tres columnas: el nombre ocupa las dos primeras arriba, y abajo van el
       chip, «Cliente» (visible: JC, «no se ve el boton de perfil de cliente») y «Cerrar»,
       siempre en la misma esquina. */
    .hilo-top{display:grid; grid-template-columns:1fr auto auto;
      grid-template-areas:"quien quien tomar" "chip cliente cerrar";
      row-gap:var(--s8); column-gap:var(--s8); align-items:center}
    .hilo-top .quien{grid-area:quien; min-width:0}
    .hilo-top .tomar-form{grid-area:tomar; justify-self:end}
    .hilo-top .pk-form{grid-area:chip; justify-self:end; min-width:0; max-width:100%}
    .hilo-top .ficha-abrir{grid-area:cliente; display:inline-flex}
    .hilo-top .cerrar-form{grid-area:cerrar; justify-self:end}
    /* 🔴 CON «Cliente» EN LA FILA, UN NOMBRE LARGO PARTIA EL CHIP EN DOS RENGLONES y la
       cabecera crecia 11 px (medido: «Tomar yo» ensancha la tercera columna y el chip se
       queda con 180 px). Cede el nombre de quien ATIENDE, con «…», y no el del paciente:
       ese es el que identifica. El completo sigue en el desplegable. */
    /* ⚠️ `min-width:0` en CADA nivel, no solo en el nombre: `.pk-form` es flex, y el
       `<details>` de adentro no encogia por debajo de su contenido (222 px en una caja de
       180). La caja del formulario se medía bien y el TEXTO se salia por encima de
       «Cliente» — lo cazo mirar la captura, no las medidas de las cajas. */
    .hilo-top .pk-v2{min-width:0; max-width:100%}
    /* 🔴 Y EL DESPLEGABLE, que colgaba del borde DERECHO del chip con 230 px de minimo.
       Con «Cliente» y «Cerrar» a su derecha el chip termina en x=204, asi que el
       desplegable arrancaba en x=-26: FUERA DE LA PANTALLA (medido). En el telefono
       cuelga del borde IZQUIERDO, y nunca mas ancho que la pantalla. */
    .hilo-top .pk-v2 .pk-pop{left:0; right:auto; max-width:calc(100vw - 2 * var(--s16))}
    .hilo-top .pastilla{max-width:100%}
    .hilo-top .pastilla .pk-nom{min-width:0; overflow:hidden; text-overflow:ellipsis;
      white-space:nowrap}
    /* Con la fila entera para el, el subtitulo ya no tiene que ceder. */
    .hilo-top .sub{overflow:visible; text-overflow:clip}
    .hilo-top .quien{cursor:pointer}
    .hilo-top h1::after{content:""; display:none}
    .msg{max-width:88%}
  }
  @media (min-width:961px){ .asa{display:none} }
  @media (prefers-reduced-motion:reduce){
    .conv,.btn,.enviar,.hist,.ficha,.hojaf{transition:none}
  }
  /* Compensacion optica del serif en tema oscuro (ver comentario en la paleta). */
  /* 🔴 EL PESO DEL NOMBRE EN OSCURO (JC, 29-ago): «las letras negras no se han
     corregido… la que esta ya seleccionada si esta perfecta».
     Su observacion da el diagnostico: la seleccionada era la UNICA en 800, y
     era la unica que se veia bien. El defecto no era de color — los 35 pares
     miden 14.30 sobre el panel y 10.05 sobre la fila activa, muy por encima del
     minimo. Era OPTICO: texto claro sobre fondo oscuro adelgaza por
     irradiacion, y a peso 700 el serif se afina hasta parecer apagado.
     Se corrige con PESO, no con color — subirle el color a algo que ya mide
     14:1 no arregla nada y rompe la escala de tinta.
     Todos los nombres pasan a 800; el seleccionado se distingue por superficie
     y tinta, que ya era el planteamiento. */
  /* 🔴 EL NOMBRE NO SELECCIONADO VA EN EL CELESTE DE LA MARCA (JC, 29-ago):
     «pueden ser del celeste de la burbuja cuando esta seleccionada, o del color
     del puntito de mensaje nuevo». Es --primary, el mismo de los dos.
     Medido: 9.06 sobre el panel — mas que el --muted que tenia (7.65), asi que
     ademas se gana legibilidad.
     ⚠️ LO QUE SE PAGA, dicho en voz alta: --primary deja de ser exclusivo de lo
     interactivo. Se acepta porque en una lista el nombre SI es lo que se toca,
     asi que el acento sigue significando «esto se abre».
     🔴 La tarjeta de enfasis NO se toca: JC dijo que ya esta optima. */
  /* 🔴 LA JERARQUIA, INVERTIDA POR JC (29-ago): «la letra del enfasis que sea
     la celeste con la burbuja celeste, pero las que NO estan seleccionadas que
     sean blancas».
     Yo lo tenia al reves. Su version es mejor y la razon se ve al medirla: en
     una lista donde casi todo esta sin seleccionar, el estado COMUN debe ser el
     mas legible —blanco, 14.30— y el acento se reserva para lo UNICO que esta
     activo. Asi el celeste vuelve a significar algo por ser escaso.
     Y ademas devuelve a --primary su papel: marcar lo activo, no todo.
       no seleccionada  blanco  #E8EBF0 sobre panel   14.30
       seleccionada     celeste #9CC0EA sobre burbuja  6.48
     El peso 800 se queda en las dos: eso arreglaba el adelgazamiento optico y
     no tiene que ver con el color. */
  /* 🔴 EL PESO ES EL MISMO EN LOS DOS TEMAS (JC, 29-ago: «en claro el enfasis
     esta como bold y en oscuro todo en bold; que quede como en claro, no
     cambiando colores sino el peso»).
     Yo habia subido TODOS los nombres a 700 en oscuro para compensar el
     adelgazamiento optico del serif claro sobre fondo oscuro. Esa compensacion
     ya no tiene sentido: **el serif se jubilo hace tres commits** y un sans no
     se adelgaza igual. Lo que quedo fue una regla huerfana que borraba la
     jerarquia justo en el tema donde mas se nota.
     LECCION: al quitar la causa, hay que ir a buscar los parches que la
     compensaban. Un arreglo sobrevive a su problema y se vuelve un defecto.
     Ahora el peso es el MISMO en claro y en oscuro: 600 lo normal, 700 lo
     activo. Lo unico que cambia por tema es el COLOR, como pidio JC. */
  :root[data-theme="dark"] .conv .quien{color:var(--text)}
  :root[data-theme="dark"] .conv.sel .quien{color:var(--primary)}
  /* La burbuja del seleccionado se tine de celeste, como pidio JC: el nombre y
     su fondo son la misma familia. */
  :root[data-theme="dark"] .conv.sel{
    background:color-mix(in srgb,var(--primary) 16%,var(--panel))}
  :root[data-theme="dark"] .conv .prev{color:var(--muted)}
  :root[data-theme="dark"] .hilo-top h1,
  :root[data-theme="dark"] .lista-top h2,
  :root[data-theme="dark"] .tarjeta .nom{font-weight:700}

  /* ══ SIN CONTRAPARTE EN EL MOCKUP ══════════════════════════════════════════
     El mockup no tiene mensajes con IMAGEN, con DOCUMENTO ni avisos flash, asi que
     estas cinco reglas no se pudieron copiar de ahi: estan portadas de produccion y
     traducidas a los tokens del rediseno.
     🔴 ES UNA DEUDA DE DISENO, NO UNA DECISION: tres de los trece tipos de mensaje
     del inventario -- imagen, audio y documento -- no tienen aspecto disenado. Lo que
     hay aca es funcional y sobrio, no aprobado. Cuando el mockup los disene, esto se
     reemplaza y vuelve a mandar D4 (copiar, no retipear). Anotado para JC.
     ════════════════════════════════════════════════════════════════════════════ */
  /* El aviso. Se va solo a los 6 s por JS: un aviso rancio se lee como actual. */
  .flash{margin:0 var(--s16) var(--s8); padding:7px 12px; border-radius:9px;
    font-size:.78rem; background:color-mix(in srgb,var(--danger) 12%,transparent);
    color:var(--danger)}
  .flash.ok{background:var(--soft); color:var(--primary)}
  /* Miniatura: tope de ancho para que una foto vertical no empuje la burbuja a toda
     la columna, y el cursor dice que amplia antes de tocarla. */
  .msg .img-zoom{display:block; width:fit-content; cursor:zoom-in}
  .msg .img-zoom img{max-width:min(240px,100%); height:auto; border-radius:10px;
    display:block}
  /* Documento: pastilla legible con el NOMBRE. Sin subrayado por lo mismo que .re:
     es un <a> viviendo donde el mockup solo puso texto. */
  .msg .doc-dl{display:inline-flex; align-items:center; gap:6px; max-width:100%;
    padding:5px 10px; border-radius:9px; background:var(--panel-2);
    color:var(--primary); font-size:.76rem; font-weight:600; text-decoration:none;
    overflow-wrap:anywhere}
  .msg .doc-dl:hover{box-shadow:var(--sombra-1)}
  /* Procedencia, no alarma: cursiva dentro de la meta y nada mas (ADR 0042). */
  .msg .meta .reenviado{font-style:italic}
  /* El visor. `place-items` + `display:grid` solo al abrir: sin la clase no existe. */
  .lightbox{position:fixed; inset:0; z-index:1000; display:none; place-items:center;
    cursor:zoom-out; background:rgba(0,0,0,.93);
    padding:max(16px,env(safe-area-inset-top)) 16px max(16px,env(safe-area-inset-bottom))}
  .lightbox.abierto{display:grid}
  .lightbox img{max-width:100%; max-height:100%; object-fit:contain; border-radius:10px}
