/* ═══════════════════════════════════════════════════════════════
   REGLA DEL SISTEMA · NINGUNA CÁMARA VA EN ESPEJO


   El informe de VERIDOM dice lo mismo en su página 9: "Sin espejo:
   la camara que RECOGE evidencia nunca va en espejo.

   ── LA PRECISION QUE COSTO TRES VUELTAS ──
 Eso NO es un espejo:
   es la imagen cruda, la que ve el otro. Un espejo hace lo contrario
   --te sigue al mismo lado y el texto sale al reves--.

   Asi que la regla se parte en dos, que es como debio nacer:

     · Lo que RECOGE evidencia --la camara trasera del titular, el video
       que mira el ejecutivo, la foto que se guarda-- NUNCA va en
       espejo. Si se voltea, el numero de la casa se lee al reves.

     · Lo que es VERSE A UNO MISMO --la ventanita del ejecutivo, la
       camara frontal del titular-- SI va en espejo, como FaceTime, Zoom
       y la camara selfie de cualquier telefono. Un autorretrato es un
       espejo, y sin el la gente cree que algo esta roto.

   Y donde la foto se guarda, el lienzo se voltea junto con la pantalla:
   lo que se encuadra tiene que ser lo que queda guardado".

   El argumento a favor del espejo existe —uno se encuadra mejor viéndose
   como en un espejo— y aquí se descarta a propósito. Pesan más dos cosas:

     · Lo que la persona ve tiene que ser lo que queda guardado. Con
       espejo, lo que mira en pantalla y la foto del expediente son
       imágenes distintas, y eso en una verificación de domicilio o en
       un enrolamiento de rostro es confusión pura.
     · Un número de casa, una placa o un comprobante se leen al revés.

   Si alguien va a reponer un espejo, que lo discuta antes: no es un
   detalle de estilo, es una regla.
   ═══════════════════════════════════════════════════════════════ */
/* =====================================================================
   LA HOJA DE ESTILO DEL PANEL
   =====================================================================

   Vivía DENTRO de `index.html`, en un `<style>` de 110 KB. Y ahí estaba
   la trampa que nos costó una vuelta el 2/oct/2026:

     · `?v=` rompe la caché de los .js — van con su versión en la
       dirección, así que el navegador los vuelve a bajar.
     · `index.html` NO lleva `?v=`: es la página, no un recurso con
       dirección propia. El navegador la sirve de su caché.

   Resultado: se publicó, el navegador bajó el `app.js` nuevo
   —con las tarjetas del tablero— y siguió usando el `index.html`
   viejo, SIN el CSS de esas tarjetas. Las tarjetas salieron como lo que
   son sin estilo: botones grises con el texto centrado. "¿Qué es esta
   porquería en mi diseño?" — y tenía toda la razón.

   Sacar el CSS aquí lo mete en el mismo trato que todo lo demás: va con
   `?v=` y se actualiza cuando se sube la versión. Las otras hojas del
   panel —kryptos-motor.css, veridom-acta.css— ya vivían así; ésta era
   la única que faltaba.

   OJO con el orden: este archivo se enlaza EXACTAMENTE donde estaba el
   `<style>`, para que la cascada quede igual que antes.
   ===================================================================== */
    /* ═══════════ LYKOS · Estilo B corporativo ═══════════
       Tokens del Design System v1.0. Un solo navy, un solo amarillo. */
    /* ── LOS COLORES SE DECIDEN AQUI, EN UN SOLO LUGAR ──

       Antes habia 167 colores escritos a mano regados por el codigo,
       repitiendo valores que ya tenian nombre: alguien escribia
       #041E42 donde iba --navy. Eso no se notaba... hasta que hay que
       cambiar un color, o poner un tema oscuro: los literales no
       obedecen a nadie y se quedan como estaban.

       Los valores de aqui son EXACTAMENTE los de siempre. Esta tanda no
       cambia como se ve el panel; cambia donde vive el color. */
    :root{
      --navy:#041E42; --navy-light:#0A2E5C; --amar:#FFC207; --amar-dark:#854F0B;
      /* La flecha del desplegable, en gris de texto secundario. */
      --flecha-campo:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%235D6B7E' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.8 6 6.4l5-4.6'/%3E%3C/svg%3E");
      /* ── EL FONDO CLARO, MEDIDO ──

         Era #F5F7FA y la tarjeta blanca encima daba 1.07 de contraste:
         practicamente el mismo color. Con #E7EDF4 sube a 1.18. No se
         oscurecio mas porque el texto secundario tambien cae con el
         fondo, y a partir de ahi deja de pasar el minimo legible. */
      --blanco:#F8FAFC; --bg:#E7EDF4; --bg-suave:#E7EDF4;
      --ok:#16C47F; --warn:#FF9F1C; --danger:#FF4D4F; --coral:#FF6B6B;
      --teal:#4ECDC4; --info:#3B82F6;
      /* El borde subio de #E8ECF1 a #DCE3EC: contra el fondo nuevo, el
         viejo daba 1.01 --o sea, desaparecia-- y la tarjeta se quedaba
         sin contorno. Ahora da 1.10 contra el fondo y 1.29 contra el
         blanco de la tarjeta: se ve en los dos lados.
         Y --text-3 de #626F7F a #5F6B7B, porque sobre el fondo nuevo el
         anterior caia a 4.35 y el minimo legible es 4.5. Ahora da 4.60. */
      --text:#041E42; --text-2:#5D6B7E; --text-3:#5F6B7B; --border:#DCE3EC;
      /* ── MEDIDO, NO ELEGIDO ──
         --text-3 era #9BA8B8 y daba 2.14 de contraste sobre las
         superficies claras. El minimo legible es 4.5. Paso a #626F7F
         (4.52) y, al oscurecerse el fondo, a #5F6B7B (4.60).
         Queda cerca de --text-2 (#5D6B7E), y eso no es descuido: la
         paleta clara nunca tuvo un tercer nivel de texto que se
         pudiera leer. El tercer nivel de verdad es el GRIS DE TRAZO,
         que no es texto y por eso puede ser claro. */
      --trazo-neutro:#9BA8B8;  /* barras, puntos y arcos: no se leen */
      --text-3-nav:#9BA8B8;    /* texto terciario SOBRE la barra navy */

      /* Las superficies y los colores de texto con estado. Tambien
         vivian sueltos; aqui llevan su mismo valor de hoy. */
      --superficie:#fff;      /* las tarjetas */
      --superficie-2:#FAFBFD; /* una superficie apenas hundida */
      --superficie-3:#EEF1F6; /* cabeceras de tabla, chips */
      --ok-txt:#0E8F5C;       /* verde de TEXTO: mas oscuro que --ok, para que se lea */
      --danger-txt:#C9302C;   /* rojo de TEXTO */
      --danger-borde:#f3c6c6; /* borde del boton de peligro */
      /* El campo de captura tiene color propio. En claro es el mismo
         blanco de la tarjeta y se distingue por el borde; en oscuro NO
         puede ser el mismo, porque entonces desaparece. */
      --campo-bg:#FFFFFF; --campo-borde:#DCE3EC;
      --amar-suave:#FFFDF5;   /* fondo de aviso, amarillo casi blanco */
      --teal-hondo:#2E7F86;   /* el verde-azul del saludo de la portada */
      --nav-bg:#041E42;       /* el fondo de la barra lateral y la de arriba */
      /* Texto SOBRE el amarillo. El amarillo es el mismo en los dos
         temas, asi que su texto tampoco puede cambiar: si usara
         --text, en oscuro quedaria letra clara sobre amarillo. */
      --sobre-amar:#041E42;

      --r-sm:8px; --r-md:11px; --r-lg:14px; --r-xl:18px; --r-2xl:24px; --r-full:9999px;
      --nav-w:232px; --tap:48px;
    }

    /* ══════════════ EL TEMA OSCURO ══════════════

       No sustituye al claro: se agrega. El panel sigue viéndose igual
       que siempre, y el oscuro aparece solo si el aparato de la persona
       está en oscuro, o si ella lo elige a mano.

       ── POR QUÉ NO ES "INVERTIR TODO" ──

       El navy es la marca, y hoy ya trabaja: la barra lateral es oscura
       y el contenido claro, y esa diferencia es la que te dice dónde
       estás parado. Si se invirtiera mecánicamente, la barra y el
       contenido quedarían del mismo color y se perdería la estructura.

       Entonces el contenido baja a un navy muy profundo, la barra se va
       un paso MÁS abajo todavía para seguir separándose, y el amarillo
       se queda de acento — que en oscuro luce mejor que en claro.

       Los verdes y rojos de TEXTO se aclaran: #0E8F5C sobre fondo
       oscuro no se lee. El de relleno no cambia, porque ahí el
       contraste lo da el fondo del propio chip.

       ── LAS TRES CAPAS ──

       1) `:root` de arriba  · el claro. Es el que manda si no hay nada.
       2) `@media` de abajo  · sigue al aparato, PERO se apaga si la
          persona eligió claro a mano (`[data-tema="claro"]`).
       3) `[data-tema="oscuro"]` · la elección manual, que gana siempre.  */

    @media (prefers-color-scheme: dark) {
      :root:not([data-tema="claro"]) {
        --navy:#0B1B2F; --navy-light:#14263F;
        --bg:#0E1E33; --bg-suave:#0E1E33; --blanco:#E8EEF6;
        --text:#E8EEF6; --text-2:#A7B6C9; --text-3:#8799AF;
        --trazo-neutro:#6F8299; --text-3-nav:#8799AF;  /* 3.56 -> 4.60 medido */
        --border:#1E3049;
        --superficie:#132540; --superficie-2:#101F36; --superficie-3:#182C49;
        --ok-txt:#4ADE9B; --danger-txt:#FF8A87; --danger-borde:#6B2F2F; --amar-dark:#FFD666;
        --flecha-campo:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23A7B6C9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.8 6 6.4l5-4.6'/%3E%3C/svg%3E");
        /* ── EL CAMPO SE TIENE QUE VER, Y ESTO ESTA MEDIDO ──
           Antes el campo y la tarjeta eran el MISMO #132540: contraste
           1.0 a 1, o sea ninguno. Lo unico que lo dibujaba era un borde
           #1E3049 que contra ese fondo da 1.15 a 1 — invisible.
           Ahora el campo se hunde (1.24 a 1) y el borde sube a 3.18 a 1,
           que es el minimo que la norma pide para que un elemento de
           interfaz se distinga de su fondo. Y la letra guia encima del
           campo queda en 4.84 a 1, que es lo que pide para texto. */
        --campo-bg:#08101C; --campo-borde:#4A73AC;
        --amar-suave:#2A2416;
        --teal-hondo:#1F5A61; --nav-bg:#08131F;
        color-scheme: dark;
      }
    }

    :root[data-tema="oscuro"] {
      --navy:#0B1B2F; --navy-light:#14263F;
      --bg:#0E1E33; --bg-suave:#0E1E33; --blanco:#E8EEF6;
      --text:#E8EEF6; --text-2:#A7B6C9; --text-3:#8799AF;
      --trazo-neutro:#6F8299; --text-3-nav:#8799AF;  /* 3.56 -> 4.60 medido */
      --border:#1E3049;
      --superficie:#132540; --superficie-2:#101F36; --superficie-3:#182C49;
      --ok-txt:#4ADE9B; --danger-txt:#FF8A87; --danger-borde:#6B2F2F; --amar-dark:#FFD666;
        --flecha-campo:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23A7B6C9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.8 6 6.4l5-4.6'/%3E%3C/svg%3E");
        /* ── EL CAMPO SE TIENE QUE VER, Y ESTO ESTA MEDIDO ──
           Antes el campo y la tarjeta eran el MISMO #132540: contraste
           1.0 a 1, o sea ninguno. Lo unico que lo dibujaba era un borde
           #1E3049 que contra ese fondo da 1.15 a 1 — invisible.
           Ahora el campo se hunde (1.24 a 1) y el borde sube a 3.18 a 1,
           que es el minimo que la norma pide para que un elemento de
           interfaz se distinga de su fondo. Y la letra guia encima del
           campo queda en 4.84 a 1, que es lo que pide para texto. */
        --campo-bg:#08101C; --campo-borde:#4A73AC;
      --amar-suave:#2A2416;
      --teal-hondo:#1F5A61; --nav-bg:#08131F;
      color-scheme: dark;
    }

    /* La barra lateral se va un paso más abajo que el contenido, para
       que la separación siga existiendo en oscuro. */
    @media (prefers-color-scheme: dark) {
      /* La barra de arriba del movil acompana a la lateral. Ya no hace
         falta repetir el color: las dos leen --nav-bg. */
      :root:not([data-tema="claro"]) .topbar { background:var(--nav-bg) }
    }
    :root[data-tema="oscuro"] .topbar { background:var(--nav-bg) }
    *{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
    html,body{margin:0;height:100%}
    body{background:var(--bg);color:var(--text);font-family:Inter,-apple-system,"Segoe UI",Roboto,sans-serif;font-size:14px}
    .hidden{display:none!important}
    .mono{font-family:ui-monospace,"JetBrains Mono",SFMono-Regular,Menlo,monospace}

    /* ── LOGIN ── */
    #login{position:fixed;inset:0;display:grid;place-items:center;background:var(--navy);padding:20px;z-index:60}
    #lyk-aurora-canvas{position:fixed;z-index:0;pointer-events:none;display:block;will-change:filter}
    .login-card{position:relative;z-index:2;background:rgba(4,30,66,.72);
      backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
      border:1px solid rgba(248,250,252,.14);border-radius:var(--r-2xl);
      padding:38px 32px;width:100%;max-width:390px;box-shadow:0 20px 60px rgba(0,0,0,.4);text-align:center}
    .login-card h1{color:var(--blanco)}
    .login-card p.sub{color:var(--text-3-nav)}
    .login-card label{color:var(--text-3-nav)}
    .login-card .fi{background:rgba(248,250,252,.07);border-color:rgba(248,250,252,.18);color:var(--blanco)}
    .login-card .fi::placeholder{color:var(--text-2)}
    .login-card img{width:220px;max-width:78%;height:auto;margin:0 auto 26px;display:block}
    .login-card h1{font-size:19px;font-weight:800;margin:0 0 6px;letter-spacing:-.3px}
    .login-card p.sub{font-size:13px;color:var(--text-2);margin:0 0 24px}
    .login-card .field{text-align:left}

    /* ── LAYOUT ── */
    #app{display:flex;min-height:100dvh}
    /* ── SIN AIRE ARRIBA, Y AQUI ESTA EL PORQUE ──
       Tenia `padding:16px 10px 14px`. Esos 16 de arriba parecen
       inofensivos y eran la rendija por la que los botones volvian a
       salir por encima del logo al deslizar.
       `position:sticky; top:0` pega el elemento al borde del CONTENIDO,
       y el contenido empieza despues del padding. Asi que el logo se
       quedaba 16px mas abajo del tope, y esos 16px son area de scroll:
       lo que subia reaparecia ahi, arriba del logo.
       El aire no desaparece, cambia de dueno: ahora lo pone el propio
       logo como relleno suyo. Se ve igual y ya no hay rendija. */
    #tabs{width:var(--nav-w);flex-shrink:0;background:var(--nav-bg);padding:0 10px 14px;
      display:flex;flex-direction:column;gap:1px;position:sticky;top:0;height:100dvh;overflow-y:auto}

    /* ── UN SOLO RODADERO, CON EL LOGO CLAVADO ──

       Primer intento: partir el menú en dos y dejar que rodara sólo la
       lista, con el logo y el pie fuera. Resolvía el problema
       reportado —no subía hasta donde está el logotipo— y creaba uno
       peor: la zona que responde al dedo se encogía a una ventanita de
       432 px para 837 px de contenido, sin barra visible, cortando a
       media sección. Y sobre el pie, que se comía 188 px fijos del
       costado, no rodaba nada. "No se desliza, se ve mal".

       El logo no necesitaba salirse del rodadero: necesitaba quedarse
       QUIETO dentro de él. Eso es `position:sticky`, y ya estaba
       escrito para el menú plegado; sólo que el menú abierto no lo
       tenía. Así que vuelve a rodar la columna entera —de arriba abajo,
       como siempre— y el logo se queda arriba pase lo que pase.

       `.nav-lista` se queda, pero sólo como cajón: lo que de verdad
       arregló los botones muertos fue que el repintado tenga su propio
       contenedor y no arrase con `#tabs`. Rodar nunca fue su trabajo. */
    /* `flex:0 0 auto` y no el reparto por defecto: un hijo de flex
       ENCOGE si no cabe, y al encoger la lista sus botones se salían de
       ella sin que `#tabs` los contara. El resultado era una columna que
       decía medir exactamente lo que la ventana —`scrollHeight` igual a
       `clientHeight`— cortada a media sección y sin nada que rodar. Al
       negarle el encogimiento, la lista empuja, `#tabs` ve el alto real
       y aparece el desplazamiento. */
    #tabs .nav-lista{ display:flex; flex-direction:column; gap:1px; flex:0 0 auto; }

    /* El logo se queda arriba al deslizar, abierto o plegado. El fondo
       es obligatorio: sin él los renglones se le ven por debajo. */
    #tabs .marca{
      position:sticky; top:0; z-index:9; background:var(--nav-bg);
    }

    /* ── QUE SE VEA QUE SE PUEDE DESLIZAR ──
       macOS esconde la barra hasta que uno ya está deslizando, así que
       una lista cortada parece una lista terminada. Una barra delgada y
       permanente es la diferencia entre "falta contenido" y "está
       roto". */
    #tabs::-webkit-scrollbar{ width:8px; }
    #tabs::-webkit-scrollbar-track{ background:transparent; }
    #tabs::-webkit-scrollbar-thumb{
      background:rgba(248,250,252,.22); border-radius:99px;
      border:2px solid transparent; background-clip:content-box;
    }
    #tabs::-webkit-scrollbar-thumb:hover{ background:rgba(248,250,252,.38);
      background-clip:content-box; }
    #tabs{ scrollbar-width:thin; scrollbar-color:rgba(248,250,252,.22) transparent; }
    #nav-pie{margin-top:auto;flex:0 0 auto;padding-top:12px;border-top:0.5px solid rgba(248,250,252,.12)}
    #nav-pie .quien-nav{display:block;padding:8px 12px 10px;color:var(--text-3-nav);font-size:11.5px;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    #nav-pie button{display:flex;align-items:center;gap:11px;min-height:var(--tap);padding:0 12px;
      border:0;background:transparent;color:var(--blanco);font-family:inherit;font-size:13.5px;
      font-weight:500;border-radius:var(--r-md);cursor:pointer;width:100%;text-align:left}
    #nav-pie button:hover{background:rgba(248,250,252,.08)}

    /* ── MI JORNADA, EN EL PIE DE LA LATERAL ──
       Entrar al sistema registra la entrada. Esto le dice que quedó
       registrada y le da por dónde cerrarla. Va ARRIBA de la cuenta y
       de "Salir" a propósito: cerrar la jornada y cerrar sesión son
       cosas distintas, y si estuvieran pegadas se confundirían. */
    #jor-caja{padding:2px 0 10px;border-bottom:0.5px solid rgba(248,250,252,.10);
      margin-bottom:8px}
    /* fijo: la lateral es azul marino en los dos temas */
    #jor-caja .jor-txt{display:block;padding:2px 12px 8px;color:#C7D4E3;
      font-size:11.5px;line-height:1.45}
    /* fijo: sobre el azul marino de la lateral */
    #jor-caja .jor-cerrada{color:#8593A5}
    /* ── EL RECORDATORIO, SOBRE LA BARRA OSCURA ──
       Esta caja vive dentro del menu lateral, que es navy. Los tokens de
       superficie clara no sirven aqui: se usan tintes translucidos, como
       el resto de la barra. Dos tonos, porque son dos situaciones: falta
       poco, y ya se paso. El segundo dice la CONSECUENCIA, no solo el
       hecho — si la cierra el sistema, la hora la pone una regla. */
    #jor-caja .jor-aviso{display:flex;align-items:flex-start;gap:7px;
      margin:0 12px 9px;padding:8px 10px;border-radius:8px;
      font-size:11.5px;line-height:1.42}
    #jor-caja .jor-aviso i{font-size:14px;flex:none;margin-top:1px}
    #jor-caja .jor-pronto{background:rgba(255,194,7,.16);color:#FFD666;
      border:1px solid rgba(255,194,7,.30)}
    #jor-caja .jor-tarde{background:rgba(255,77,79,.18);color:#FF8A87;
      border:1px solid rgba(255,77,79,.38)}
    body.nav-plegada #jor-caja .jor-aviso{display:none}
    #jor-caja .jor-btn{display:flex;align-items:center;gap:11px;min-height:var(--tap);
      padding:0 12px;border:0;background:rgba(248,250,252,.06);color:var(--blanco);
      font-family:inherit;font-size:13.5px;font-weight:500;border-radius:var(--r-md);
      cursor:pointer;width:100%;text-align:left}
    #jor-caja .jor-btn:hover{background:rgba(248,250,252,.13)}
    #jor-caja .jor-btn:disabled{opacity:.55;cursor:default}
    body.nav-plegada #jor-caja .jor-txt{display:none}
    body.nav-plegada #jor-caja .jor-btn{justify-content:center;padding:0}
    body.nav-plegada #jor-caja .jor-btn .lbl{display:none}
    /* ── PLEGADA, EL ICONO SE QUEDABA MUDO · 12/oct/2026 ──
 Se mantiene el botón —pero plegada se
       ocultan el texto Y el aviso, así que lo único que quedaba era un
       reloj sin nada que lo distinguiera de cualquier otro icono.

       Un punto encima, del mismo color que el aviso que se ocultó: el
       ámbar de "cierra pronto" y el rojo de "ya se te pasó". Es lo
       mismo que hace el badge de los pendientes, y por eso se reconoce
       sin explicarlo. Desplegada no hace falta: ahí está el aviso. */
    body.nav-plegada #jor-caja .jor-btn{position:relative}
    body.nav-plegada #jor-caja .jor-btn.pronto::after,
    body.nav-plegada #jor-caja .jor-btn.urge::after{
      content:"";position:absolute;top:8px;right:14px;width:8px;height:8px;
      border-radius:99px;box-shadow:0 0 0 2px var(--nav-bg,#0B1B33)}
    body.nav-plegada #jor-caja .jor-btn.pronto::after{background:var(--amar)}
    body.nav-plegada #jor-caja .jor-btn.urge::after{background:#FF4D4F;
      animation:jorLate 1.8s ease-in-out infinite}
    @keyframes jorLate{0%,100%{opacity:1}50%{opacity:.45}}
    @media (prefers-reduced-motion:reduce){
      body.nav-plegada #jor-caja .jor-btn.urge::after{animation:none}
    }

    /* En móvil el pie se oculta y esto vive dentro del menú "Más",
       sobre fondo claro: por eso cambian los colores, no el contenido. */
    #jor-caja-movil:empty{display:none}
    #jor-caja-movil .jor-txt{display:block;padding:2px 14px 8px;
      font-size:12px;color:var(--text-2);line-height:1.45}
    #jor-caja-movil .jor-cerrada{color:var(--text-3)}
    #jor-caja-movil .jor-btn{display:flex;align-items:center;gap:11px;
      min-height:var(--tap);padding:0 14px;border:0;background:transparent;
      color:var(--text);font-family:inherit;font-size:14px;font-weight:500;
      cursor:pointer;width:100%;text-align:left}
    #jor-caja-movil .jor-btn:disabled{opacity:.55;cursor:default}
    /* La marca es el boton de plegar, pero no debe PARECER un boton.
       Heredaba de `#tabs button` el fondo al pasar el mouse: eso era la
       caja transparente que aparecia alrededor del logo. Se apaga.
       El aviso de que es pulsable lo da el cursor y el tooltip. */
    /* El lector biométrico. El óvalo amarillo es la misma señal que la
       app del guardia: "pon tu cara aquí". */
    .lec-cam{position:relative;width:100%;max-width:300px;margin:0 auto;
      aspect-ratio:3/4;/* fijo: detras de la camara va negro, no es del tema */background:#000;border-radius:var(--r-lg);overflow:hidden}
    .lec-cam video{width:100%;height:100%;object-fit:cover}
    .lec-guia{position:absolute;inset:12% 16%;border:3px solid rgba(255,194,7,.85);
      border-radius:50%;pointer-events:none}
    .lec-txt{position:absolute;left:0;right:0;bottom:0;padding:10px;
      background:linear-gradient(transparent,rgba(0,0,0,.72));color:var(--superficie);
      font-size:13px;font-weight:600;text-align:center}

    /* ══════════ EL LECTOR A PANTALLA COMPLETA ══════════

       Se reporto con la captura de una cara cortada a la mitad dentro
       de un recuadro: el biometrico que abria no era el que se habia
       construido.

       Tenia razon. Arriba, `.lec-cam` mide `max-width:300px` y vive
       DENTRO de la tarjeta del formulario: un cuadrito de 300 px
       apretado entre el campo de la clave y el pie de pagina. A 300 px
       de ancho con proporcion 3/4, la cara de alguien que sostiene el
       telefono a la altura del pecho entra por la mitad. Y el ovalo
       --`inset:12% 16%`, porcentajes de ese cuadrito-- queda alto y
       angosto, sin relacion con donde de verdad cae un rostro.

       Esta clase no cambia el lector: cambia DONDE se dibuja. El mismo
       video, el mismo motor, la misma guia, pero ocupando la pantalla
       como la camara de Instagram. Se aplica solo donde hace falta
       --el acceso al sistema-- y los otros cinco lugares que usan
       `bloqueLector` siguen exactamente igual: un cambio global habria
       movido el enrolamiento de Capital Humano sin que nadie lo pidiera.

       El velo y la camara llegan a los bordes; lo demas vive en una
       columna centrada. Es la especificacion de `Capas.html`. */
    /* Navy Lykos, no negro. Una pantalla blanca de un segundo se
       siente eterna. El blanco venia de un marco que el
       sitio bloqueaba; el negro de aqui era mio y tampoco es de la
       marca. Mientras algo carga, lo que se ve es azul Lykos. */
    .lec-full{position:fixed;inset:0;z-index:9990;background:#041E42;
      display:flex;flex-direction:column;align-items:center;justify-content:center}
    /* La camara manda sobre el recuadro: sin tope de ancho, sin
       proporcion impuesta y sin esquinas redondeadas. */
    .lec-full .lec-caja{width:100%;height:100%}
    .lec-full .lec-cam{max-width:none;width:100%;height:100%;margin:0;
      aspect-ratio:auto;border-radius:0}
    .lec-full .lec-cam video{width:100%;height:100%;object-fit:cover;
      /* Espejo, como cualquier camara frontal: si no, la persona se ve
         al reves y mueve la cabeza para el lado contrario. */
      transform:scaleX(-1)}
    /* ── EL OVALO, DONDE DE VERDAD CAE UNA CARA ──
       Ancho del 64% de la pantalla y proporcion 1:1.3, centrado a la
       altura del 42%: un poco arriba del centro geometrico, que es
       donde queda la cabeza de quien sostiene el telefono. No son
       porcentajes de un cuadrito: son de la pantalla real. */
    .lec-full .lec-guia{position:absolute;inset:auto;left:50%;top:42%;
      width:64vw;height:calc(64vw * 1.3);max-width:330px;max-height:429px;
      transform:translate(-50%,-50%);
      border:3px solid rgba(255,194,7,.9);border-radius:50%;
      box-shadow:0 0 0 100vmax rgba(4,30,66,.55);pointer-events:none}
    /* ── EL TEXTO VA ARRIBA DE LA GUIA, NO ENCIMA ──
       Ya me paso: lo puse sobre el ovalo y quedo tapando la cara. El
       arreglo no era achicar la letra --eso lo intente y quedo
       ilegible-- era ponerlo donde no estorba. */
    .lec-full .lec-txt{position:absolute;left:16px;right:16px;
      top:max(24px, env(safe-area-inset-top));bottom:auto;
      padding:14px 16px;background:rgba(4,30,66,.72);border-radius:14px;
      backdrop-filter:blur(10px);color:#F8FAFC;
      font-size:16px;font-weight:700;line-height:1.35;text-align:center}
    /* ── NADIE SE QUEDA ENCERRADO ──
       A pantalla completa desaparece la tarjeta, y con ella el boton de
       Volver. Sin esta salida, quien no logre que lo lean se queda sin
       forma de retroceder mas que recargando la pagina.

       El texto y la forma NO se inventan: son los del diseño aprobado
       --"No puedo identificarme", borde fino y fondo transparente--.
       Yo le habia puesto "Cancelar" con otro estilo y se cacho de
       inmediato: un boton abajo que tampoco era el aprobado en el
       banco. Esta pantalla es el RESPALDO del biometrico nuevo; si
       alguna vez sale, tiene que verse como la buena. */
    .lec-full .lec-salir{position:absolute;left:50%;transform:translateX(-50%);
      bottom:calc(28px + env(safe-area-inset-bottom));
      /* igual que el `exit` del diseño: 52 px, borde de 1.5, radio 16,
         fondo transparente y peso 600 */
      min-height:52px;padding:0 22px;border-radius:16px;
      border:1.5px solid rgba(248,250,252,.38);background:transparent;
      color:#F8FAFC;font-family:inherit;font-size:16px;font-weight:600;
      display:flex;align-items:center;justify-content:center;gap:8px;
      cursor:pointer}

    #tabs .marca{ display:flex; align-items:center; gap:8px; width:100%;
      /* EL RECUADRO QUE NO SE IBA.
         `.marca` es un <button>, asi que heredaba `border-radius` de la
         regla general `#tabs button`. Ese redondeo, con el
         `border-bottom` que llevaba, dibujaba un rectangulo de esquinas
         curvas alrededor del logo: eso es lo que se venia senalando
         como "boton transparente", y en oscuro quedo
         evidente. Se quitan el redondeo Y la linea: el rotulo del
         primer grupo ya separa lo suficiente.
         Y el relleno era 2px arriba contra 12px abajo, asi que el logo
         quedaba pegado al techo. Ahora es parejo: centrado de verdad,
         no solo de lado a lado. */
      padding:28px 4px 12px; margin-bottom:6px; border:0; border-radius:0;
      background:transparent; cursor:pointer;
      /* Se queda arriba mientras la lista de secciones corre por debajo:
         asi el logo siempre esta a la mano para plegar el menu, sin
         tener que subir a buscarlo. El fondo navy es obligatorio — sin
         el, los renglones se verian pasar por atras. */
      /* ── POR QUE EL MISMO FONDO QUE LA BARRA ──
         Llevaba `var(--navy)` mientras la barra usa `--nav-bg`. En claro
         eran el mismo color y no se notaba; con el tema oscuro la barra
         bajo un tono y la marca no, asi que quedo un rectangulo mas
         claro pegado arriba — el "boton transparente" que se venia
         senalando desde hace dias, ahora evidente.
         Siguen el mismo token: no pueden volver a separarse. */
      position:sticky; top:0; z-index:9; background:var(--nav-bg); }
    /* Centrado. Iba pegado a la izquierda y con la barra desplegada
       dejaba un hueco a la derecha que lo hacia ver desalineado. */
    #tabs .marca{ justify-content:center; }
    #tabs .marca img{ height:36px; width:auto; max-width:100%; object-fit:contain;
      object-position:center; }
    #tabs .marca .iso{ display:none; height:30px; width:auto; }
    /* ── EL LOGO ES OPACO, SIEMPRE ──
       Aqui decia `opacity:.85` en `:hover` Y en `:focus`. El problema es
       que la marca ES el boton que pliega el menu: al tocarlo queda
       ENFOCADA, y el foco no se va solo. Asi que el logo se quedaba al
       85% de opacidad de forma permanente.

       Y `opacity` no atenua solo el dibujo: atenua el elemento entero,
       su fondo incluido. Por eso el isotipo se veia "transparentoso" y
       los iconos del menu se alcanzaban a ver pasando por debajo al
       deslizar. Justo lo que no debe pasar en algo que flota.

       El guino de hover ahora es `brightness`, que aclara sin dejar ver
       a traves. El aro amarillo del foco se queda: es lo que necesita
       quien navega con teclado. */
    #tabs .marca{ opacity:1; }
    #tabs .marca:hover{ background:var(--nav-bg); filter:brightness(1.15); }
    #tabs .marca:focus{ background:var(--nav-bg); }
    #tabs .marca:focus-visible{ outline:2px solid var(--amar); outline-offset:-2px; }
    #tabs button{display:flex;align-items:center;gap:11px;min-height:var(--tap);padding:0 12px;
      border:0;background:transparent;color:var(--blanco);font-family:inherit;font-size:13.5px;
      font-weight:500;border-radius:var(--r-md);cursor:pointer;text-align:left;width:100%;
      transition:background .15s}
    #tabs button:hover{background:rgba(248,250,252,.08)}
    #tabs button.act{background:var(--amar);color:var(--sobre-amar);font-weight:800}
    #tabs .nav-grupo{color:var(--text-3-nav);font-size:10px;letter-spacing:1px;padding:16px 12px 5px;
      text-transform:uppercase}
    #tabs .badge{margin-left:auto;background:var(--danger);color:var(--blanco);font-size:10px;
      font-weight:800;padding:2px 7px;border-radius:var(--r-full)}

    .contenido{flex:1;min-width:0;display:flex;flex-direction:column}
    /* LA BARRA BLANCA DE ARRIBA SOLO EXISTE EN MOVIL.
       En escritorio no cargaba nada: el titulo repetia el renglon que
       el menu lateral ya marca como activo, la campana nunca se
       conecto a nada, y el correo y el boton de Salir de esta barra
       estan ocultos desde siempre porque viven en el pie del menu.
       Eran 61px de alto pegados arriba a cambio de nada.
       En movil SI hace falta: ahi es la barra azul con el logotipo y
       el boton del menu, y su regla vive en la media query de 768px. */
    .topbar{display:none}
    .topbar{align-items:center;gap:12px;padding:16px 22px;border-bottom:0.5px solid var(--border);
      background:var(--superficie);position:sticky;top:0;z-index:20}
    .topbar h1{font-size:17px;font-weight:800;margin:0;letter-spacing:-.2px}
    .topbar .logo-movil{display:none}
    .topbar .quien{margin-left:auto;font-size:12px;color:var(--text-2);border:0.5px solid var(--border);
      padding:8px 13px;border-radius:var(--r-md)}
    /* Botón de icono: sin fondo, hereda el color de su barra */
    .icon-btn{width:44px;height:44px;border:0;background:transparent;color:var(--text-2);
      font-size:21px;border-radius:var(--r-md);cursor:pointer;display:inline-flex;
      align-items:center;justify-content:center;flex-shrink:0;position:relative}
    .icon-btn:hover{background:var(--bg)}
    #main{flex:1;padding:20px 22px 40px}
    #menu-btn{display:none}

    /* ── COMPONENTES ── */
    .panel{background:var(--superficie);border:0.5px solid var(--border);border-radius:var(--r-lg);
      padding:17px 19px;margin-bottom:14px}
    h2{font-size:15px;font-weight:800;margin:0 0 12px;letter-spacing:-.2px;
      display:flex;align-items:center;gap:9px}
    h2::before{content:"";width:3px;height:15px;background:var(--amar);border-radius:2px}
    /* ── LA CABECERA DE PANEL ──
       `com-head` la usan comercial.js, capital-humano.js, nomina.js y el
       dashboard, y su marcado siempre es el mismo: un bloque con el
       título a la izquierda y otro con los botones a la derecha. Nunca
       tuvo UNA SOLA regla de CSS, así que los dos bloques se apilaban y
       el botón caía debajo del título en todas esas pantallas. No es un
       componente nuevo: es el que ya existía, con el layout que su
       marcado da por hecho. */
    /* ══ EL TABLERO ══


       Y: "las cards del dashboard no las quiero con la línea esa
       amarilla antes de cada título". La barra amarilla es la firma de
       una SECCIÓN de trabajo; en un tablero, donde todo son tarjetas, se
       vuelve ruido repetido doce veces.

       Y lo tercero, dicho a media obra: "cuidadito con el modo oscuro,
       que lo haga bien". Por eso aquí no hay un solo color escrito a
       mano. Todo sale de los tokens, que ya se invierten con el tema;
       si una gráfica necesitara un color que no existe como token, el
       error es el color, no el token. */
    /* ── LA REJILLA, SIN HUECOS ──


       `grid-auto-flow: dense` es exactamente eso: cuando una tarjeta
       grande deja un hueco, el navegador mete ahí la siguiente que
       quepa en vez de dejarlo vacío. Sin dense, la rejilla respeta el
       orden a costa de agujeros.

       El ANCHO lo manda el peso —la importancia del tema, que decide el
       servidor— y el ALTO lo manda la forma: una dona no cabe en el
       alto de un número, y estirar un número al alto de una dona deja
       aire tonto adentro. */
    .dash-grid{display:grid;gap:13px;margin-bottom:16px;
      grid-template-columns:repeat(4, minmax(0,1fr));
      grid-auto-rows:150px; grid-auto-flow:dense}
    .dash-card{background:var(--superficie);border:0.5px solid var(--border);
      border-radius:var(--r-lg);padding:15px 17px;display:flex;flex-direction:column;
      min-width:0;overflow:hidden}
    /* Ancho por importancia */
    .dash-card.p4{grid-column:span 2} .dash-card.p3{grid-column:span 2}
    .dash-card.p2{grid-column:span 1} .dash-card.p1{grid-column:span 1}
    /* Alto por forma */
    .dash-card.f-numero{grid-row:span 1}
    .dash-card.f-dona, .dash-card.f-barras{grid-row:span 2}
    .dash-card.f-embudo{grid-row:span 2}
    /* Un embudo de siete etapas no cabe en el alto de uno de tres: el
       pie se encimaba con el primer renglon. El alto lo pide la tarjeta
       segun cuantas filas trae. */
    .dash-card.alto-3{grid-row:span 3}
    /* Sin huecos en la rejilla, y se dijo claro.
       Quedaban porque una tarjeta SIN DATOS seguia ocupando el
       alto de su grafica para enseñar una frase de dos renglones. Lo
       que no tiene nada que dibujar se encoge al tamaño minimo y deja
       que la rejilla densa acomode a las demas. */
    .dash-card.vacia{grid-column:span 1 !important;grid-row:span 1 !important}
    .dash-card.vacia .dash-cuerpo{justify-content:flex-start}
    /* Una cifra sola NO necesita dos renglones por ser importante:
       necesita ancho y tamano de letra. Con dos renglones quedaba la
       mitad de la tarjeta en aire. */
    .dash-card.p4.f-numero{grid-row:span 1}

    /* Sin barra amarilla: en el tablero el titulo es un rotulo, no una seccion.

 Tenia razon, y fue en el
       commit de homologar las tarjetas: le puse 800 a un rotulo de 14px.
       800 es peso de titular grande; a tamano chico engorda el trazo y
       hace que doce tarjetas griten a la vez. 700 marca la jerarquia sin
       emborronar. */
    .dash-card h3{margin:0;font-size:14px;font-weight:700;letter-spacing:-.1px;
      line-height:1.3}
    .dash-card h3::before{content:none}
    .dash-card .pie{display:block;font-size:11.5px;color:var(--text-2);margin:3px 0 12px}
    .dash-head{display:flex;align-items:flex-start;justify-content:space-between;
      gap:10px;margin-bottom:2px}
    .dash-cuerpo{flex:1;min-height:0;display:flex;flex-direction:column;
      justify-content:center;overflow:hidden}

    /* La cifra grande, que es una tarjeta más y no otro componente. */
    /* El número salía apachurrado y había que bajarlo de tamaño.
       Eran dos cosas: 40px de alto con -1.5px de interletra, que en un
       solo dígito se come el aire a los lados y lo deja ovalado; y que
       la cifra es un dato, no un cartel — no necesita gritar para que se
       vea. Baja de tamaño y suelta la interletra. */
    .d-num{font-size:30px;font-weight:800;line-height:1.05;letter-spacing:-.3px;
      font-variant-numeric:tabular-nums;align-self:flex-start}
    .dash-card.p4 .d-num{font-size:42px}
    /* ── Y CUANDO EL "NUMERO" ES UNA PALABRA, NECESITA MAS AIRE ──
       El 1.05 de arriba esta pensado para digitos, que no bajan de la
       linea base. Una palabra si: la "p" de "Captura" se salia de la caja
       y el overflow:hidden de .dash-cuerpo se la cortaba a la mitad.
       Un peso menos, tambien: una palabra en 800 es un grito. */
    .d-num.es-texto{font-size:22px;font-weight:700;letter-spacing:-.2px;
      line-height:1.3;padding-bottom:2px}
    .dash-card.p4 .d-num.es-texto{font-size:26px}
    .d-num.v-ok{color:var(--ok)} .d-num.v-warn{color:var(--amar-dark)}
    .d-num.v-danger{color:var(--danger)} .d-num.v-navy{color:var(--text)}

    /* La bienvenida es contexto, no dato: ocupa dos columnas y un renglón. */
    .dash-hola{grid-column:span 2;grid-row:span 1;border:0;color:#fff;
      background:linear-gradient(115deg, var(--navy) 0%, var(--navy-light) 45%,
                 var(--teal-hondo, #1F5A61) 100%);
      display:flex;flex-direction:column;justify-content:center;
      border-radius:var(--r-lg);padding:18px 22px;overflow:hidden}
    /* El color va en el H1, no heredado de la tarjeta: hay una regla
       global de encabezados que gana por especificidad y lo pintaba de
       navy oscuro... sobre un degradado navy oscuro. El saludo salia
       invisible. `.c-hero h1` ya lo resolvia asi desde antes. */
    .dash-hola h1{margin:0;font-size:24px;font-weight:800;letter-spacing:-.4px;
      color:#FFFFFF}
    .dash-hola p{margin:5px 0 0;font-size:13px;color:rgba(255,255,255,.86)}

    @media (max-width:1100px){ .dash-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
    @media (max-width:640px){
      .dash-grid{grid-template-columns:1fr;grid-auto-rows:auto}
      .dash-card, .dash-hola{grid-column:span 1 !important;grid-row:span 1 !important}
      .dash-card.f-dona, .dash-card.f-barras, .dash-card.f-embudo{min-height:250px}
    }

    /* ── BARRAS ──    /* ── BARRAS ──  verticales, con su valor encima. */
    .g-barras{display:flex;align-items:flex-end;gap:8px;height:120px}
    .g-b{flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;
      justify-content:flex-end;height:100%;min-width:0}
    /* 11px en 800 es el caso de libro del trazo engordado: a ese tamano
       el peso extra no se lee como enfasis, se lee como mancha. */
    .g-b-v{font-size:11px;font-weight:700;color:var(--text-2);
      font-variant-numeric:tabular-nums}
    .g-b-barra{width:100%;border-radius:7px 7px 0 0;min-height:4px;
      background:var(--trazo-neutro);transition:height .4s cubic-bezier(.22,.61,.36,1)}
    .g-b-l{font-size:10px;color:var(--text-3);text-align:center;width:100%;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .g-b.t-ok    .g-b-barra{background:var(--ok)}
    .g-b.t-warn  .g-b-barra{background:var(--amar)}
    .g-b.t-danger .g-b-barra{background:var(--danger)}
    .g-b.t-navy  .g-b-barra{background:var(--teal)}
    .g-b.vacia   .g-b-barra{background:var(--border)}

    /* ── BARRAS HORIZONTALES ── para embudos: la etiqueta cabe entera. */
    .g-emb{display:flex;flex-direction:column;gap:9px}
    .g-e{display:grid;grid-template-columns:1fr auto;gap:4px 10px;align-items:center}
    .g-e-l{font-size:12px;color:var(--text-2);grid-column:1}
    .g-e-v{font-size:13px;font-weight:800;grid-column:2;grid-row:1;
      font-variant-numeric:tabular-nums}
    .g-e-pista{grid-column:1 / -1;height:8px;border-radius:999px;
      background:var(--superficie-2);overflow:hidden}
    /* `display:block` no es adorno: la barra es un <span>, y en un span
       en linea el `height` no hace nada. La barra existia, medía lo
       correcto y no se veía. */
    .g-e-barra{display:block;height:100%;border-radius:999px;background:var(--teal);
      min-width:2px;transition:width .45s cubic-bezier(.22,.61,.36,1)}

    /* ── DONA ──  SVG, sin librerias. El agujero deja el total al centro. */
    .g-dona{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
    .g-dona svg{flex:none;transform:rotate(-90deg)}
    .g-dona .hueco{font-size:22px;font-weight:800;fill:var(--text);
      transform:rotate(90deg);transform-origin:center}
    .g-dona .hueco-l{font-size:9px;font-weight:700;fill:var(--text-3);
      transform:rotate(90deg);transform-origin:center;letter-spacing:.5px}
    .g-leyenda{display:flex;flex-direction:column;gap:7px;min-width:0;flex:1}
    .g-le{display:flex;align-items:center;gap:8px;font-size:12px;min-width:0}
    .g-le i{width:10px;height:10px;border-radius:3px;flex:none;background:var(--trazo-neutro)}
    .g-le.t-ok i{background:var(--ok)} .g-le.t-warn i{background:var(--amar)}
    .g-le.t-danger i{background:var(--danger)} .g-le.t-navy i{background:var(--teal)}
    .g-le span{color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .g-le b{margin-left:auto;font-variant-numeric:tabular-nums}
    .arco{stroke:var(--trazo-neutro)}
    .arco.t-ok{stroke:var(--ok)} .arco.t-warn{stroke:var(--amar)}
    .arco.t-danger{stroke:var(--danger)} .arco.t-navy{stroke:var(--teal)}
    .g-vacia{color:var(--text-3);font-size:12.5px;padding:18px 0;text-align:center}

    .com-head{display:flex;align-items:flex-start;justify-content:space-between;
      gap:14px;flex-wrap:wrap;margin-bottom:14px}
    .com-head > :last-child{display:flex;gap:8px;align-items:center;flex-wrap:wrap}


    /* El filtro de la lista de prospectos. Antes eran once pildoras que
       llenaban dos renglones; lo que decian --donde se acumula la
       gente-- ahora lo dice el embudo del tablero, dibujado. Aqui queda
       el control, que es lo unico que el tablero no puede hacer. */
    .pl-filtro{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin:14px 0 4px}
    .pl-filtro label{font-size:11px;font-weight:800;letter-spacing:.5px;
      text-transform:uppercase;color:var(--text-3)}
    .pl-f-lbl{font-size:11px;font-weight:800;letter-spacing:.5px;
      text-transform:uppercase;color:var(--text-3)}
    /* El desplegable es NUESTRO. Un <select> abre la lista del sistema
       operativo —gris, con su tipografia y su palomita— y eso es
       exactamente lo que no va: "no quiero nada del navegador del
       dispositivo, todo debe ser de nuestro sistema". Misma regla que ya
       prohibia alert, confirm y prompt. */
    .pl-menu{position:relative}
    .pl-boton{display:inline-flex;align-items:center;gap:9px;min-height:var(--tap);
      padding:0 12px;border:1px solid var(--border);border-radius:var(--r-md);
      background:var(--superficie);font:inherit;font-size:13.5px;font-weight:700;
      color:var(--text);cursor:pointer;min-width:230px}
    .pl-boton:hover{border-color:var(--trazo-neutro)}
    .pl-boton b{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--text-2)}
    .pl-boton i{color:var(--text-3);font-size:15px}
    .pl-ops{position:absolute;z-index:40;top:calc(100% + 5px);left:0;min-width:100%;
      background:var(--superficie);border:1px solid var(--border);
      border-radius:var(--r-md);box-shadow:0 14px 34px rgba(4,30,66,.20);
      padding:5px;max-height:330px;overflow:auto}
    .pl-op{display:flex;align-items:center;gap:10px;width:100%;min-height:36px;
      padding:0 10px;border:0;border-radius:8px;background:none;font:inherit;
      font-size:13px;color:var(--text);cursor:pointer;text-align:left}
    .pl-op:hover{background:var(--superficie-2)}
    .pl-op b{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--text-3);
      font-size:12.5px}
    .pl-op.act{background:var(--amar);color:var(--sobre-amar);font-weight:800}
    .pl-op.act b{color:var(--sobre-amar)}
    .kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:11px;margin-bottom:14px}
    .kpi-card{background:var(--superficie);border:0.5px solid var(--border);border-radius:var(--r-lg);padding:14px 16px}
    .kpi-card .lbl{font-size:10.5px;color:var(--text-2);letter-spacing:.6px;text-transform:uppercase;
      font-weight:800;display:block}
    .kpi-card .val{font-size:27px;font-weight:800;margin-top:6px;display:block;letter-spacing:-.5px}
    .kpi-card .val small{font-size:14px;color:var(--text-3);font-weight:600}
    .kpi-card .pie{font-size:11.5px;color:var(--text-2);margin-top:3px;display:block}
    .v-ok{color:var(--ok)} .v-warn{color:var(--warn)} .v-danger{color:var(--danger)} .v-navy{color:var(--text)}

    .fila{display:flex;align-items:center;gap:11px;padding:12px 0;border-bottom:0.5px solid var(--border)}
    .fila:last-child{border-bottom:0}
    .fila .ico{width:34px;height:34px;border-radius:var(--r-sm);display:grid;place-items:center;flex-shrink:0}
    .fila .txt{flex:1;min-width:0;font-size:13.5px}
    .fila .meta{font-size:11.5px;color:var(--text-2)}

    .btn{min-height:var(--tap);padding:0 18px;border-radius:var(--r-md);border:1px solid var(--amar);
      background:var(--amar);color:var(--sobre-amar);font-family:inherit;font-size:14px;font-weight:800;
      cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:7px}
    .btn:active{transform:translateY(1px)}
    .btn.ghost{background:var(--superficie);border-color:var(--border);color:var(--text)}
    /* El escalon intermedio. Cuando hay tres acciones, una sola puede ir
       en amarillo solido —la recomendada— pero dejar las otras dos en
       gris las iguala, y no son iguales: una es una accion de verdad y
       la otra es irse. El contorno amarillo dice "esto tambien hace
       algo", sin competir con la principal. */
    .btn.contorno{background:var(--superficie);border-color:var(--amar);color:var(--amar)}
    .btn.danger{background:var(--superficie);border-color:var(--danger-borde);color:var(--danger)}
    .btn.sm{min-height:44px;font-size:13px;padding:0 15px}
    .field{margin-bottom:14px}
    /* ── EL TEXTO DE AYUDA DE UN DIÁLOGO ──
       `cd-ayuda` ya se usaba en dos lugares de `capital-humano.js` y NO
       tenía regla: se pintaba igual que el resto y no se leía como
       ayuda. Es la misma familia de la trampa A.1 del doc 65 --algo que
       no falla, sencillamente no hace nada-- sólo que con una clase en
       vez de una variable. */
    .cd-dlg-p{margin:0 0 var(--e-4);font:var(--t-cuerpo);color:var(--texto-2)}
    .cd-dlg-p b{color:var(--texto);font-weight:600}
    .cd-ayuda{margin:var(--e-1) 0 0;font:var(--t-sec);color:var(--texto-3)}
    label{display:block;font-size:12px;font-weight:800;color:var(--text-2);margin-bottom:6px;
      text-transform:uppercase;letter-spacing:.4px}
    .fi{width:100%;min-height:var(--tap);padding:12px 14px;border:1px solid var(--campo-borde);
      border-radius:var(--r-md);background:var(--campo-bg);font-family:inherit;font-size:15px;color:var(--text);outline:none}


    .fi:focus{border-color:var(--amar);box-shadow:0 0 0 3px rgba(255,194,7,.18)}
    textarea.fi{min-height:96px;resize:vertical}
    .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px}
    .toolbar{display:flex;gap:11px;align-items:flex-end;flex-wrap:wrap;margin-bottom:14px}
    .toolbar .field{margin-bottom:0}
    /* ══ EL PERFIL POR DIMENSIONES ══
       Cada barra es el resultado de la persona; la marca vertical es lo
       que pide el perfil. El color NO sale del valor absoluto —"ansiedad
       70" no dice nada— sino de la DISTANCIA al objetivo del puesto, que
       es lo que vuelve accionable el dato. */
    .kd-lista{display:flex;flex-direction:column;gap:14px}
    .kd-fila{min-width:0}
    .kd-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
    .kd-nom{font-size:13px;font-weight:700}
    .kd-val{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums}
    .kd-val.t-ok{color:var(--ok-txt)} .kd-val.t-warn{color:var(--amar-dark)}
    .kd-val.t-danger{color:var(--danger-txt)}
    .kd-pista{position:relative;height:9px;border-radius:999px;margin:6px 0 4px;
      background:var(--superficie-2);overflow:visible}
    .kd-barra{display:block;height:100%;border-radius:999px;background:var(--trazo-neutro)}
    .kd-barra.t-ok{background:var(--ok)} .kd-barra.t-warn{background:var(--amar)}
    .kd-barra.t-danger{background:var(--danger)}
    /* La marca del objetivo: una línea, no un punto. Un punto se
       confunde con el final de la barra. */
    .kd-obj{position:absolute;top:-4px;width:2px;height:17px;border-radius:1px;
      background:var(--text);transform:translateX(-1px)}
    .kd-pie{font-size:11.5px;color:var(--text-3)}

    /* ══ LAS SIETE QUE VENÍAN PRESTADAS ══

       `kryptos-motor.css` —la hoja de la pantalla del candidato, que
       siempre es clara— definía estas clases con nombres genéricos y
       colores fijos, y el panel las usaba sin tener las suyas. En tema
       oscuro salían blancas. Esa hoja ya no se carga aquí; estas son las
       definiciones del panel, con sus tokens, que sí se invierten.

       `.msg.avi` también faltaba: se usaba en cuatro lugares del informe
       de evaluación y nunca existió, así que caía al `.msg` pelón. */
    .card{background:var(--superficie);border:0.5px solid var(--border);
      border-radius:var(--r-lg);padding:18px;margin-bottom:16px}
    .alert{padding:12px 14px;border-radius:var(--r-md);display:flex;align-items:flex-start;
      gap:10px;font-size:12.5px;margin-bottom:10px;border-left:3px solid var(--amar);
      background:var(--superficie-2);color:var(--text-2)}
    .app{display:block;min-height:100vh}
    .chip{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;
      border-radius:var(--r-full);font-size:10.5px;font-weight:800;letter-spacing:.4px;
      text-transform:uppercase;background:var(--superficie-3);color:var(--text-2)}
    .section-title{font-size:11px;font-weight:800;color:var(--text-3);letter-spacing:1.2px;
      text-transform:uppercase;margin-bottom:10px;display:flex;align-items:center;gap:8px}
    .tab{padding:11px 18px;font-size:12.5px;font-weight:700;color:var(--text-2);
      cursor:pointer;border-bottom:3px solid transparent;margin-bottom:-2px}
    .tab.on,.tab.act{color:var(--text);border-bottom-color:var(--amar)}
    .tip{background:var(--navy);color:#fff;padding:12px 14px;border-radius:var(--r-md);
      font-size:12px;line-height:1.45}
    /* El aviso del informe: ni error ni confirmación, una advertencia. */
    .msg.avi{background:var(--amar-suave);color:var(--amar-dark);
      border-color:rgba(255,194,7,.32)}

    .msg{padding:12px 15px;border-radius:var(--r-md);font-size:13.5px;margin:10px 0;
      background:var(--superficie-2);color:var(--text-2);border:0.5px solid var(--border)}
    .msg.ok{background:rgba(22,196,127,.1);color:var(--ok-txt);border-color:rgba(22,196,127,.3)}
    .msg.err{background:rgba(255,77,79,.09);color:var(--danger-txt);border-color:rgba(255,77,79,.28)}
    .tag{display:inline-flex;align-items:center;gap:5px;padding:5px 11px;border-radius:var(--r-full);
      font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;
      background:var(--superficie-3);color:var(--text-2)}
    .tag::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
    .tag.verde{background:rgba(22,196,127,.14);color:var(--ok-txt)}
    /* El nivel LATENTE del CRO: ni bien ni mal, vigilado. Ver la nota
       en `motor.js` sobre por que el riesgo mas bajo dejo de ir en
       verde. */
    .tag.teal{background:rgba(46,110,119,.14);color:#1C4C53}
    .tag.ambar{background:rgba(255,159,28,.16);color:var(--amar-dark)}
    .tag.rojo{background:rgba(255,77,79,.12);color:var(--danger-txt)}
    .tag.neutro{background:var(--superficie-3);color:var(--text-2)}

    table{width:100%;border-collapse:collapse;font-size:13.5px}
    th{background:var(--navy);color:var(--blanco);font-weight:800;font-size:11.5px;
      text-transform:uppercase;letter-spacing:.4px;padding:11px 13px;text-align:left}
    th:first-child{border-radius:var(--r-sm) 0 0 0} th:last-child{border-radius:0 var(--r-sm) 0 0}
    td{padding:12px 13px;border-bottom:0.5px solid var(--border)}
    tbody tr:hover{background:var(--superficie-2)}

    /* ── MODAL → BOTTOM SHEET EN MÓVIL ── */
    .modal{position:fixed;inset:0;background:rgba(4,30,66,.5);z-index:80;
      display:flex;align-items:center;justify-content:center;padding:20px;overflow:auto}
    .modal>.panel{width:100%;max-width:580px;max-height:88dvh;display:flex;flex-direction:column;
      margin:0;border-radius:var(--r-2xl);padding:0;overflow:hidden}
    .modal .m-head{padding:18px 22px;border-bottom:0.5px solid var(--border);flex-shrink:0}
    .modal .m-body{padding:18px 22px;overflow-y:auto;flex:1;-webkit-overflow-scrolling:touch}
    .modal .m-foot{padding:14px 22px;border-top:0.5px solid var(--border);background:var(--superficie);
      display:flex;gap:10px;flex-shrink:0}
    .modal .m-foot .btn{flex:1}

    #kpis{display:flex;gap:9px;flex-wrap:wrap;margin-bottom:13px}
    .kpi{font-size:12px;font-weight:800;padding:7px 13px;border-radius:var(--r-full);
      background:var(--superficie-3);color:var(--text-2)}
    .kpi.v{background:rgba(22,196,127,.14);color:var(--ok-txt)}
    .kpi.a{background:rgba(255,159,28,.16);color:var(--amar-dark)}
    .kpi.r{background:rgba(255,77,79,.12);color:var(--danger-txt)}

    /* ══════ iPad: lateral colapsada a iconos ══════ */
    /* ══════ Tablet (iPad): barra de iconos QUE SE PUEDE ABRIR ══════


       Tenia razon y la causa estaba aqui. Este bloque forzaba 68px y
       escondia las etiquetas SIN MIRAR si el menu estaba plegado o no,
       asi que tocar el logo --que es el boton de plegar, ver app.js--
       alternaba una clase que en esta franja no cambiaba nada. El menu
       quedaba en iconos para siempre, y un icono sin etiqueta en un
       sistema de treinta pantallas es una adivinanza.

       Un iPad en vertical mide entre 744 y 834 px, asi que cae justo
       aqui. En horizontal pasa de 1080 y se comporta como PC.

       Ahora la franja respeta `body.nav-plegada`, igual que el
       escritorio: plegado son iconos, desplegado se abre. Y al abrirse
       se MONTA SOBRE el contenido en vez de empujarlo, porque a 820 px
       un lateral de 232 px mas la pantalla deja el contenido
       inservible. Es el patron que ya espera quien usa una tablet. */
    @media (max-width: 1080px) and (min-width: 600px) and (min-height: 480px){
      /* ── PLEGADO · la barra de iconos ── */
      body.nav-plegada{ --nav-w:68px }
      body.nav-plegada #tabs{padding:14px 8px;align-items:center}
      body.nav-plegada #tabs .marca{ justify-content:center; padding:2px 0 12px; }
      body.nav-plegada #tabs .marca img{ object-position:center; }
      body.nav-plegada #tabs .marca .completo{ display:none; }
      body.nav-plegada #tabs .marca .iso{ display:block; }
      body.nav-plegada #tabs button{justify-content:center;padding:0;
        width:44px;height:44px;min-height:44px;gap:0;position:relative}
      body.nav-plegada #tabs button span.lbl{display:none}
      body.nav-plegada #tabs .nav-grupo{display:none}
      body.nav-plegada #tabs .badge{position:absolute;margin:0;
        transform:translate(13px,-13px);padding:1px 5px;font-size:9px}

      /* ── DESPLEGADO · se monta encima, no empuja ──
         El hueco de 68px se deja reservado con el padding de #main para
         que el contenido no salte cuando el menu se abre y se cierra. */
      body:not(.nav-plegada) #tabs{
        position:fixed; top:0; left:0; z-index:60; width:264px;
        box-shadow:0 18px 50px rgba(2,6,23,.45);
      }
      body:not(.nav-plegada) #main{ padding-left:calc(68px + 18px) }

      /* El velo: tocar fuera cierra, que es lo que uno intenta primero.
         Lo crea app.js una sola vez; aqui solo se le dice cuando se ve. */
      #nav-velo{
        position:fixed; inset:0; z-index:55; border:0; padding:0;
        background:rgba(2,6,23,.45); cursor:pointer;
        opacity:0; pointer-events:none; transition:opacity .16s;
      }
      body:not(.nav-plegada) #nav-velo{ opacity:1; pointer-events:auto }
    }

    /* Fuera de la franja de tablet el velo no existe: en PC el menu
       convive con el contenido y en movil hay barra inferior. */
    #nav-velo{ display:none }
    @media (max-width: 1080px) and (min-width: 600px) and (min-height: 480px){ #nav-velo{ display:block } }

    /* ══════ Móvil: navegación inferior ══════ */
    @media (max-width: 599px), (max-height: 479px){
      #app{flex-direction:column}
      #tabs{position:fixed !important;bottom:0 !important;left:0;right:0;top:auto !important;
        width:100%;height:auto;
        flex-direction:row;justify-content:space-between;align-items:stretch;
        padding:6px 4px calc(env(safe-area-inset-bottom) + 8px);
        background:var(--superficie);border-top:0.5px solid var(--border);gap:0;z-index:50;overflow:visible}
      #tabs .marca{display:none}
      #tabs .nav-grupo{display:none}
      /* ── POR QUÉ SE AMONTONABAN A LA IZQUIERDA ──


         La barra es `flex` en fila, pero a los botones nunca se les
         dijo que repartieran el ancho. Sin eso, cada uno mide lo que
         mide su texto y todos se pegan al inicio, dejando el hueco a la
         derecha. Y como el número de botones cambia según el rol y
         según lo que quepa, el resultado era distinto —y feo de otra
         manera— para cada persona.

         `flex:1 1 0` reparte el ancho en partes iguales entre los que
         estén visibles, sean tres o siete. `min-width:0` es el que
         permite que una etiqueta larga se recorte en vez de empujar a
         las demás; sin él, flex respeta el ancho del contenido y el
         reparto se rompe otra vez. */
      #tabs button{flex:1 1 0;min-width:0;flex-direction:column;gap:3px;min-height:56px;padding:6px 2px;
        font-size:10px;font-weight:600;color:var(--text-3);border-radius:var(--r-sm);position:relative}
      #tabs button.act{background:transparent;color:var(--text);font-weight:800}
      #tabs button .ti{font-size:22px}
      #tabs button.oculto{display:none}
      #tabs .badge{position:absolute;top:2px;right:calc(50% - 20px);margin:0;padding:1px 5px;font-size:9px}
      #tabs button.solo-movil{display:flex !important;flex-direction:column}
      .solo-escritorio{display:none !important}
      #nav-plegar{ display:none !important; }
      body.nav-plegada #tabs{width:100%;padding:6px 4px calc(env(safe-area-inset-bottom) + 8px);align-items:stretch}
      body.nav-plegada #tabs button{flex:1 1 0;min-width:0;width:auto;height:auto;min-height:56px;flex-direction:column;gap:3px}
      body.nav-plegada #tabs button .lbl{display:block}
      /* El pie (cuenta y salir) NO va en la barra inferior: vive en el menú "Más" */
      #nav-pie{display:none}
      /* Los iconos deben verse: en móvil la barra es blanca */
      #tabs button .ti{font-size:22px;display:block}
      /* Una etiqueta larga —"Atención al empleado"— se recorta con
         puntos suspensivos. Antes empujaba a las demás y descuadraba la
         barra entera por un solo renglón de texto. */
      #tabs button .lbl{display:block;font-size:10px;line-height:1.2;text-align:center;
        max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
      .topbar{display:flex;padding:9px 12px 9px 15px;background:var(--nav-bg);gap:6px}
      .topbar h1{display:none}
      .topbar .icon-btn{color:var(--blanco);margin-left:auto}
      .topbar .icon-btn:hover{background:rgba(248,250,252,.1)}
      .topbar .icon-btn + #menu-btn{margin-left:0}
      .topbar .logo-movil{display:block;height:34px;width:auto;max-width:58vw;object-fit:contain;object-position:left center}
      #menu-btn{color:var(--blanco)}
      .topbar .quien{display:none}
      #menu-btn{display:inline-flex;align-items:center;justify-content:center;
        width:44px;height:44px;border:0;background:transparent;color:var(--blanco);font-size:22px;
        margin-left:0;cursor:pointer;border-radius:var(--r-md);flex-shrink:0}
      #menu-btn:hover{background:rgba(248,250,252,.1)}
      #main{padding:15px 15px 90px}
      .panel{padding:15px}
      .kpis{grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}
      /* ── UNA CIFRA NO SE PARTE EN DOS RENGLONES · 13/oct/2026 ──
         Con 23px fijos, «$410,788.48» en dos columnas de un teléfono de
         375px se rompía como «$410,788.» / «48», y eso no es una cifra:
         es dos. Se vio en Facturación, pero el componente lo usa también
         el tablero, así que el arreglo va aquí.

         `clamp` la encoge lo justo para que quepa --nunca por debajo de
         17px, que sigue leyéndose-- y `nowrap` garantiza que no se
         corte. El `min-width:0` es lo que permite que una celda de una
         rejilla se achique por debajo de su contenido; sin él, el
         `nowrap` desbordaría la tarjeta. */
      .kpi-card{min-width:0}
      /* 19px y un pelo de `letter-spacing` negativo: con eso
         «$410,788.48» --once caracteres, la cifra más larga que sale en
         este panel-- entra completa en media pantalla de 375px.

         Nada de `text-overflow:ellipsis`: la primera versión lo puso y
         dejaba «$410,7…», que es PEOR que partirla en dos renglones.
         Una cifra cortada no se lee mal: se lee equivocada. */
      .kpi-card .val{font-size:19px;letter-spacing:-.6px;white-space:nowrap}
      .grid{grid-template-columns:1fr}
      .toolbar{flex-direction:column;align-items:stretch}
      .toolbar .field,.toolbar .btn{width:100%}
      table{display:block;overflow-x:auto;white-space:nowrap}

      /* ── LA LISTA DE CANDIDATOS, EN TARJETAS ──


         Y no era arreglable deslizando: son ocho columnas y una de
         ellas --Rostro-- trae un párrafo entero explicando por qué
         fallaron los intentos. Al llegar a la derecha ya perdiste el
         nombre de quién estás mirando, que es el único dato que ancla
         todo lo demás.

         Cada renglón pasa a ser una tarjeta: el nombre de título, y
         debajo cada dato con la etiqueta de su columna --que viene en
         `data-col`, porque el encabezado de la tabla desaparece--. Se
         lee de arriba abajo, como todo lo demás del teléfono, sin
         deslizar a los lados.

         Va atada a `.ch-tabla` a propósito: las demás tablas del sistema
         siguen deslizándose como estaban. */
      .ch-tabla, .ch-tabla tbody, .ch-tabla tr, .ch-tabla td{
        display:block; width:100%; white-space:normal;
      }
      .ch-tabla thead{ display:none; }
      .ch-tabla{ overflow:visible; border-spacing:0; }

      .ch-tabla tr{
        background:var(--superficie); border:1px solid var(--border);
        border-radius:var(--r-lg); padding:14px 16px; margin-bottom:12px;
      }

      /* Cada dato: su etiqueta a la izquierda, su valor a la derecha.
         `gap` y no un ancho fijo, para que "Evaluación" no obligue a
         "Código" a dejar un hueco del mismo tamaño. */
      .ch-tabla td{
        border:0; padding:7px 0;
        display:flex; align-items:flex-start; justify-content:space-between;
        gap:14px; font-size:13.5px;
      }
      .ch-tabla td + td{ border-top:1px solid var(--border); }
      .ch-tabla td::before{
        content:attr(data-col);
        flex:0 0 auto; color:var(--text-3);
        font-size:11px; font-weight:800; letter-spacing:.06em;
        text-transform:uppercase; padding-top:2px;
      }
      .ch-tabla td{ text-align:right; flex-wrap:wrap; }

      /* ── LA CELDA DE ROSTRO TRAE DOS COSAS ──
         Una etiqueta --o un botón-- y, debajo, el párrafo que explica
         por qué fallaron los intentos. Como eran dos hijos sueltos del
         mismo flex, se repartían el ancho y la etiqueta quedaba de una
         letra por renglón: "S I N P R O B A R" en vertical.

         La etiqueta se queda en el renglón del rótulo y se niega a
         encogerse; el párrafo baja a ocupar el ancho completo, que es
         donde se puede leer. */
      .ch-tabla td > .tag,
      .ch-tabla td > .btn{ flex:0 0 auto; white-space:nowrap; }
      .ch-tabla td > div,
      .ch-tabla td > p{
        flex:1 0 100%; text-align:left; margin-top:6px;
      }

      /* El nombre es el título de la tarjeta, no un dato más. */
      .ch-tabla td.ch-td-nombre{
        display:block; text-align:left; border-top:0;
        padding:0 0 10px; margin-bottom:4px;
        border-bottom:1px solid var(--border);
        font-size:16px; font-weight:800; color:var(--text);
      }
      .ch-tabla td.ch-td-nombre::before{ content:none; }

      /* Acciones: ancho completo y alto de dedo, al pie de la tarjeta. */
      .ch-tabla td.ch-td-acciones{ display:block; padding:12px 0 0; }
      .ch-tabla td.ch-td-acciones::before{ content:none; }
      .ch-tabla td.ch-td-acciones .btn{
        width:100%; min-height:46px; justify-content:center;
      }
      /* El modal se vuelve hoja inferior con el guardar SIEMPRE visible */
      .modal{align-items:flex-end;padding:0}
      .modal>.panel{max-width:none;max-height:92dvh;border-radius:var(--r-2xl) var(--r-2xl) 0 0}
      .modal .m-head{padding:16px 18px 14px}
      .modal .m-head::before{content:"";display:block;width:38px;height:4px;border-radius:2px;
        background:var(--border);margin:0 auto 12px}
      .modal .m-body{padding:16px 18px}
      .modal .m-foot{padding:12px 18px calc(env(safe-area-inset-bottom) + 14px)}
      .login-card{padding:32px 24px}
      .login-card img{width:190px}

      /* ═══════════════════════════════════════════════════════════
         QUE NADA SE SALGA DE ANCHO


         Buena parte de eso no es el zoom: es que algo mide más que la
         pantalla. Cuando un solo elemento se pasa de ancho, la página
         entera se puede correr de lado, y entonces TODO aparece
         desplazado —incluida la barra de abajo, que está pegada al
         borde izquierdo de la página y no al de la pantalla—. Al
         acercar y alejar, ese corrimiento se nota muchísimo más.

         Aquí no se tapa el síntoma, se quitan las causas que ya
         estaban en el archivo:
         ═══════════════════════════════════════════════════════════ */

      /* 1 · `clip` y no `hidden`: `hidden` crea un contenedor de
             desplazamiento y rompe todo lo que esté pegado con
             `position:sticky`. `clip` recorta sin romper nada. */
      html, body{ overflow-x:clip; max-width:100%; }

      /* 2 · Un botón con 230px de ancho mínimo no cabe en un teléfono
             de 360. Se le deja crecer y encogerse. */
      .pl-boton{ min-width:0; width:100%; }

      /* 3 · Un correo largo, una URL o una CLABE no tienen dónde
             partirse y empujan su tarjeta hasta sacarla de la
             pantalla. Que se partan. */
      .panel, .card, .msg, .r-nota, td, th{ overflow-wrap:anywhere; }

      /* 4 · Una imagen sin tope se lleva el ancho de la página. */
      img, video, canvas, svg{ max-width:100%; height:auto; }

      /* 5 · Los campos heredan un ancho propio del navegador que no
             respeta al padre. */
      input, select, textarea{ max-width:100%; }
    }
    /* Menú "Más" en móvil */
    /* ── POR QUÉ EL `overscroll-behavior` NO ES ADORNO ──


       Es el enganche de scroll. La hoja tiene su propio desplazamiento;
       al llegar al tope, el teléfono le PASA el gesto a la página de
       atrás —que también se desplaza— y el dedo deja de mover lo que
       uno está mirando. Se siente como que se trabó, pero lo que pasó
       es que se está moviendo otra cosa, debajo.

       `contain` corta ese paso: al llegar al borde, el gesto se queda
       en la hoja y no se va a ningún lado. `touch-action:pan-y` le dice
       al navegador que aquí sólo se sube y se baja, para que no tenga
       que adivinar al principio del gesto —ese titubeo es parte de lo
       que se siente como traba—. */
    #mas-menu{position:fixed;inset:0;background:rgba(4,30,66,.5);z-index:70;display:none;align-items:flex-end;
      overscroll-behavior:none;touch-action:none}
    #mas-menu.abierto{display:flex}
    #mas-menu .hoja{background:var(--superficie);width:100%;border-radius:var(--r-2xl) var(--r-2xl) 0 0;
      padding:18px 16px calc(env(safe-area-inset-bottom) + 20px);max-height:70dvh;overflow-y:auto;
      overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}
    #mas-menu .hoja::before{content:"";display:block;width:38px;height:4px;border-radius:2px;
      background:var(--border);margin:0 auto 16px}
    #mas-menu button{display:flex;align-items:center;gap:12px;width:100%;min-height:var(--tap);
      padding:0 14px;border:0;background:transparent;font-family:inherit;font-size:15px;
      font-weight:600;color:var(--text);border-radius:var(--r-md);cursor:pointer;text-align:left}
    #mas-menu button:active{background:var(--bg)}
/* ====== RONDINES · estilo corporativo (Design System Lykos) ====== */
    .r-preg{ font-size:15px; font-weight:700; color:var(--text); margin:22px 0 12px; }

    /* Tarjetas de nivel de vigilancia */
    .r-niveles{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:12px; }
    .r-nivel{
      background:var(--superficie); border:1.5px solid var(--border); border-radius:var(--r-lg);
      padding:16px; text-align:left; font-family:inherit; cursor:pointer;
      display:flex; flex-direction:column; gap:4px;
      transition:border-color .15s, box-shadow .15s, background .15s;
    }
    .r-nivel:hover:not(:disabled){ border-color:var(--border); background:var(--superficie-2); }
    .r-nivel:disabled{ cursor:default; opacity:.75; }
    .r-nivel.sel{ border-color:var(--amar); background:var(--amar-suave); box-shadow:0 0 0 3px rgba(255,194,7,.16); }
    .r-nivel-t{ font-size:15px; font-weight:700; color:var(--text); }
    .r-nivel-n{ font-size:12px; font-weight:700; color:var(--amar-dark, var(--amar-dark)); letter-spacing:.2px; }
    .r-nivel-d{ font-size:12.5px; color:var(--text-2); line-height:1.45; margin-top:2px; }

    /* Horario */
    .r-horario{ display:flex; align-items:flex-end; gap:12px; flex-wrap:wrap; }
    .r-horario .field{ margin-bottom:0; }
    .r-horario .fi{ min-width:120px; }
    .r-flecha{ color:var(--text-2); font-size:18px; padding-bottom:11px; }
    .r-nota{ font-size:12.5px; color:var(--text-2); }
    .r-horario .r-nota{ flex-basis:100%; margin-top:2px; }

    /* Checks explicados */
    .r-check{ display:flex; align-items:flex-start; gap:10px; margin-top:16px;
      padding:13px 15px; background:var(--superficie-2); border:1px solid var(--border);
      border-radius:var(--r-md); cursor:pointer; }
    .r-check input{ margin-top:2px; width:16px; height:16px; flex-shrink:0; }
    .r-check b{ font-size:14px; color:var(--text); font-weight:700; }
    .r-check small{ font-size:12.5px; color:var(--text-2); }

    /* Ajustes avanzados (plegados) */
    .r-avz{ margin-top:20px; border-top:1px solid var(--border); padding-top:14px; }
    .r-avz summary{ cursor:pointer; font-size:13.5px; font-weight:700; color:var(--text-2);
      list-style:none; display:inline-flex; align-items:center; gap:7px; user-select:none; }
    .r-avz summary::-webkit-details-marker{ display:none; }
    .r-avz summary::before{ content:"▸"; font-size:11px; transition:transform .15s; }
    .r-avz[open] summary::before{ transform:rotate(90deg); }
    .r-avz summary:hover{ color:var(--text); }
    .r-avz-body{ margin-top:14px; }

    /* Resumen de la noche */
    .r-resumen{ display:flex; align-items:center; gap:16px; padding:16px 18px;
      background:var(--superficie-2); border:1px solid var(--border); border-radius:var(--r-lg); margin-bottom:14px; }
    .r-big{ font-size:34px; font-weight:800; color:var(--text); line-height:1; }
    .r-big span{ font-size:19px; color:var(--text-2); font-weight:600; }
    .r-res-txt b{ display:block; font-size:15px; color:var(--text); }
    .r-res-txt small{ font-size:12.5px; color:var(--text-2); }

    /* Línea de recorridos */
    .r-linea{ display:flex; flex-direction:column; gap:1px; }
    .r-item{ display:flex; align-items:center; gap:14px; padding:13px 15px;
      background:var(--superficie); border:1px solid var(--border); border-left:3px solid var(--border);
      border-radius:var(--r-md); }
    .r-item.verde{ border-left-color:var(--ok); }
    .r-item.ambar{ border-left-color:var(--warn); }
    .r-item.rojo{  border-left-color:var(--danger); }
    /* Badge neutro para 'pendiente' — nunca verde (verde = cumplido) */
    .tag.neutro{ background:var(--superficie-3); color:var(--text-2); border-color:var(--border); }
    .tag.neutro::before{ background:var(--trazo-neutro); }
    .r-hora{ font-size:15px; font-weight:800; color:var(--text); font-variant-numeric:tabular-nums; min-width:52px; }
    .r-punto{ flex:1; font-size:14px; color:var(--text); }

    @media (max-width:760px){
      .r-niveles{ grid-template-columns:1fr; }
      .r-flecha{ display:none; }              /* en móvil los campos se apilan */
      .r-horario{ gap:10px; }
      .r-horario .field{ flex:1 1 100%; }
      .r-horario .fi{ min-width:0; }
      .r-item{ flex-wrap:wrap; gap:8px 12px; }
      .r-resumen{ padding:14px; }
    }

    
/* ====== USUARIOS · estilo corporativo Lykos ====== */
    .u-fila{ display:flex; align-items:center; gap:14px; padding:14px 16px;
      background:var(--superficie); border:1px solid var(--border); border-radius:var(--r-md);
      margin-bottom:8px; flex-wrap:wrap; }
    .u-fila.off{ opacity:.55; background:var(--superficie-2); }
    .u-datos{ flex:1; min-width:180px; display:flex; flex-direction:column; gap:2px; }
    .u-nom{ font-size:14.5px; font-weight:700; color:var(--text); }
    .u-nom em{ font-style:normal; font-weight:500; color:var(--text-2); font-size:12.5px; }
    .u-mail{ font-size:12.5px; color:var(--text-2); }
    .u-rol{ min-width:150px; max-width:190px; padding:9px 11px; font-size:13px; }
    .u-acts{ display:flex; gap:7px; flex-wrap:wrap; }

    /* Caja del enlace de invitación */
    .u-linkbox{ margin-top:16px; padding:16px; background:var(--amar-suave);
      border:1.5px solid var(--amar); border-radius:var(--r-lg);
      display:flex; flex-direction:column; gap:10px; }
    .u-lbl{ font-size:11.5px; font-weight:800; letter-spacing:.5px;
      text-transform:uppercase; color:var(--amar-dark); }
    .u-url{ display:block; background:var(--superficie); border:1px solid var(--border);
      border-radius:var(--r-md); padding:11px 13px; font-size:12.5px;
      color:var(--text); word-break:break-all; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; }
    .u-linkbox .u-acts{ margin-top:2px; }

    @media (max-width:760px){
      .u-fila{ gap:10px; }
      .u-rol{ max-width:none; flex:1 1 100%; }
      .u-acts{ flex:1 1 100%; }
      .u-acts .btn{ flex:1; justify-content:center; }
    }

    
/* ====== MAPA de ubicación (Leaflet / OpenStreetMap) ====== */
    .mapa-caja{ width:100%; height:320px; border-radius:var(--r-md);
      border:1px solid var(--border); overflow:hidden; background:var(--bg); }
    .mapa-acts{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin:12px 0; }
    .mapa-buscar{ display:flex; gap:8px; flex:1; min-width:260px; }
    .mapa-buscar .fi{ flex:1; }
    .mapa-ayuda{ color:var(--text-2); font-size:12.5px; margin:6px 0; line-height:1.5; }
    .mapa-coords{ margin-top:8px; font-size:12.5px; color:var(--text-2);
      font-variant-numeric:tabular-nums; }
    .mapa-radio{ margin-top:16px; padding-top:14px; border-top:1px solid var(--border); }
    .mapa-radio label{ display:block; font-size:13px; color:var(--text); margin-bottom:8px; }
    .mapa-radio label b{ color:var(--amar-dark); }
    #radio-slider{ width:100%; accent-color:var(--amar); }
    /* El mapa dentro del modal necesita quedar por debajo de los controles */
    .leaflet-container{ font-family:inherit; }
    @media (max-width:760px){
      .mapa-caja{ height:260px; }
      .mapa-buscar{ min-width:100%; }
      .mapa-acts .btn{ flex:1; justify-content:center; }
    }

    
/* ====== REPORTES · bandeja ====== */
    .rep-card{ background:var(--superficie); border:1px solid var(--border);
      border-left:3px solid var(--border); border-radius:var(--r-md);
      padding:15px 17px; margin-bottom:9px; }
    .rep-card.rojo{ border-left-color:var(--danger); }
    .rep-card.ambar{ border-left-color:var(--warn); }
    .rep-top{ display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin-bottom:7px; }
    .rep-folio{ font-size:12.5px; font-weight:800; color:var(--text);
      font-family:ui-monospace,SFMono-Regular,Menlo,monospace; letter-spacing:.3px; }
    .rep-sev{ font-size:11.5px; color:var(--text-2); text-transform:capitalize; }
    .rep-cat{ font-size:14px; font-weight:700; color:var(--text);
      text-transform:capitalize; margin-bottom:4px; }
    .rep-cat b{ color:var(--amar-dark); font-weight:700; }
    .rep-desc{ font-size:13.5px; color:var(--text); line-height:1.5; margin-bottom:7px; }
    .rep-meta{ font-size:12px; color:var(--text-2); }
    .rep-alerta{ color:var(--danger); font-weight:600; }
    .rep-nota{ margin-top:8px; padding:9px 12px; background:var(--superficie-2);
      border-left:2px solid var(--border); border-radius:4px;
      font-size:12.5px; color:var(--text-2); }
    .rep-acts{ display:flex; gap:7px; margin-top:11px; flex-wrap:wrap; }

    /* ====== ÍNDICE DE EXPOSICIÓN ====== */
    .exp-card{ display:flex; gap:18px; align-items:flex-start; background:var(--superficie);
      border:1px solid var(--border); border-radius:var(--r-lg);
      padding:16px 18px; margin-bottom:10px; }
    .exp-izq{ display:flex; flex-direction:column; align-items:center; gap:7px;
      min-width:76px; }
    .exp-num{ font-size:32px; font-weight:800; line-height:1; color:var(--text); }
    .exp-num.verde{ color:var(--ok-txt); } .exp-num.ambar{ color:var(--amar-dark); } .exp-num.rojo{ color:var(--danger); }
    .exp-der{ flex:1; min-width:0; }
    .exp-sitio{ font-size:15px; font-weight:700; color:var(--text); margin-bottom:7px; }
    .exp-barra{ height:7px; background:var(--superficie-3); border-radius:4px; overflow:hidden; margin-bottom:8px; }
    .exp-barra span{ display:block; height:100%; border-radius:4px; background:var(--trazo-neutro); }
    .exp-barra span.verde{ background:var(--ok); }
    .exp-barra span.ambar{ background:var(--warn); }
    .exp-barra span.rojo{ background:var(--danger); }
    .exp-meta{ font-size:12.5px; color:var(--text-2); }
    .exp-desc{ font-size:12.5px; color:var(--text-2); margin-top:3px; font-style:italic; }

    /* ====== Tendencia ====== */
    .tend{ display:flex; align-items:flex-end; gap:10px; padding:16px 4px 0;
      overflow-x:auto; min-height:170px; }
    .tend-col{ display:flex; flex-direction:column; align-items:center; gap:5px;
      min-width:52px; justify-content:flex-end; }
    .tend-val{ font-size:12.5px; font-weight:800; color:var(--text); }
    .tend-barra{ width:30px; background:var(--amar);
      border-radius:5px 5px 0 0; }
    .tend-sem{ font-size:10.5px; color:var(--text-2); white-space:nowrap; }

    @media (max-width:760px){
      .exp-card{ gap:12px; padding:14px; }
      .exp-num{ font-size:27px; }
      .exp-izq{ min-width:60px; }
      .rep-acts .btn{ flex:1; justify-content:center; }
    }

  

    /* ── Compatibilidad con el modal que ya pinta app.js (.box) ── */
    .modal>.box{width:100%;max-width:580px;max-height:88dvh;background:var(--superficie);
      border-radius:var(--r-2xl);padding:22px;overflow-y:auto;-webkit-overflow-scrolling:touch}
    .modal>.box h2{margin-top:0}
    .modal>.box .row-actions{position:sticky;bottom:-22px;background:var(--superficie);
      padding:14px 0 0;margin-top:16px;border-top:0.5px solid var(--border)}
    /* La fila de botones NO se define aqui: vive mas abajo, en un solo
       lugar. Estaba escrita dos veces en este archivo y una tercera en
       capital-humano.js — tres reglas para el mismo boton. */
    #modal-close{position:absolute;top:14px;right:14px;width:40px;height:40px;
      border:0;background:transparent;font-size:22px;color:var(--text-2);cursor:pointer}

    /* ── Miniaturas y badges de geocerca del app.js original ──


       Tres cosas distintas se arreglan aquí y en `app.js`:

       1. La miniatura rota enseñaba la palabra «selfie» --el texto
          alternativo del `<img>`-- con el icono de imagen partida al
          lado. Ahora nace como un marco vacío con la silueta de una
          persona, y la foto entra encima cuando llega. Si no llega,
          se queda el marco: un hueco limpio dice «no hay foto» mejor
          que una palabra suelta en medio de la tabla.

       2. Proporción 4:5, la misma de la credencial y del expediente.
          Una selfie de teléfono es vertical; metida en un cuadrado se
          recortaba por el centro y salían la frente y los ojos.

       3. En el comparador, las dos fotos van en marcos del MISMO
          tamaño. Antes cada una traía su proporción y quedaban
          disparejas, que es lo que no se puede enseñar. */
    .thumb{width:44px;height:55px;border-radius:var(--r-sm);object-fit:cover;
      object-position:center 30%;
      border:0.5px solid var(--border);background:var(--bg);
      display:block;cursor:pointer}
    /* El marco que se ve mientras la foto llega, y el que se queda si no
       llega nunca. `font-size:0` mata el texto alternativo. */
    .thumb:not([src]){font-size:0;position:relative;
      background:var(--bg) center/20px no-repeat
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235D6B7E' stroke-width='1.5' stroke-linecap='round'><circle cx='12' cy='8' r='3.2'/><path d='M5 20c0-3.6 3.1-5.6 7-5.6s7 2 7 5.6'/></svg>");
      cursor:default}

    /* ── La foto en grande ──
       Se limita por ALTO, no por ancho: una selfie vertical al 100% del
       ancho se sale por abajo de la ventana y el cliente acaba viendo
       media cara. `contain` la enseña entera, sin recortar. */
    .foto-grande{display:block;margin:0 auto;max-width:100%;max-height:68vh;
      width:auto;height:auto;object-fit:contain;border-radius:12px;
      background:var(--bg)}

    /* ── El comparador de rostros ── */
    .cmp{display:flex;gap:18px;flex-wrap:wrap;justify-content:center;margin:4px 0 2px}
    .cmp-c{text-align:center}
    .cmp-rot{font-size:12px;color:var(--text-2);margin-bottom:8px;
      letter-spacing:.02em}
    /* Marco de tamaño fijo: las dos fotos se ven iguales aunque vengan
       de cámaras distintas. `cover` llena el marco y el encuadre alto
       deja la cara donde se mira, no la frente. */
    .cmp-f{width:200px;height:250px;border-radius:12px;object-fit:cover;
      object-position:center 30%;border:0.5px solid var(--border);
      background:var(--bg);display:block}
    .cmp-f.vacio{display:flex;align-items:center;justify-content:center;
      color:var(--text-3);font-size:12px;text-align:center;padding:12px}
    .dentro{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:var(--r-full);
      font-size:11px;font-weight:800;background:rgba(22,196,127,.14);color:var(--ok-txt)}
    .fuera{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:var(--r-full);
      font-size:11px;font-weight:800;background:rgba(255,77,79,.12);color:var(--danger-txt)}

    @media print{
      #tabs,.topbar,.btn{display:none!important}
      body{background:var(--superficie)}
    }

    @media (max-width:768px){
      .modal>.box{max-width:none;max-height:92dvh;border-radius:var(--r-2xl) var(--r-2xl) 0 0;padding:18px}
      .modal>.box .row-actions{bottom:-18px;padding-bottom:calc(env(safe-area-inset-bottom) + 6px)}
    }


    /* ═══ ESTILO C · Corp-Startup ═══
       Acento reservado a 3 puntos del sistema: el saludo de Inicio, la acción
       de gestionar roles, y el índice de exposición. Uno por pantalla: si
       aparece dos veces en la misma vista, deja de destacar. */
    .c-hero{
      background:linear-gradient(118deg,var(--navy) 0%,var(--navy-light) 46%,var(--teal-hondo) 78%,var(--teal) 100%);
      border-radius:var(--r-2xl); padding:22px 26px; margin-bottom:16px;
      position:relative; overflow:hidden;
    }

    /* ── EL MEDIDOR, AL LADO DEL SALUDO ──
       Va en la misma tarjeta del saludo a proposito: un indicador que
       hay que ir a buscar no cambia la conducta de nadie; uno que esta
       donde uno entra todos los dias, si.
       En pantalla angosta se va abajo, centrado: apretarlo al lado del
       titulo dejaria los dos ilegibles. */
    .c-hero-med{ display:flex; align-items:center; gap:22px; flex-wrap:wrap; }
    .c-hero-txt{ flex:1; min-width:210px; }
    #ini-medidor{ display:flex; gap:18px; flex-wrap:wrap; }
    /* ── EL MEDIDOR DENTRO DEL SALUDO DEL TABLERO ──
       Se mudó aquí al fundir "Inicio" con el tablero. Nació para el
       `.c-hero`, que lo pone al LADO del texto; `.dash-hola` apila en
       columna, así que necesita su propio aire arriba y una raya que lo
       separe del saludo — sin ella parecía un pie del título. Los
       colores ya servían: las dos cajas son navy. */
    .dash-hola #ini-medidor{ margin-top:14px; padding-top:13px;
      border-top:0.5px solid rgba(248,250,252,.16); gap:22px; }
    .dash-hola:has(#ini-medidor .med-uno){ grid-row:span 2 }
    .med-uno{ display:grid; grid-template-columns:auto 1fr; grid-template-rows:auto auto auto;
      column-gap:11px; align-items:center; min-width:150px; }
    .med-svg{ width:64px; height:64px; grid-row:1/4; display:block; }
    .med-pct{ font-size:19px; font-weight:900; line-height:1.1; align-self:end;
      font-variant-numeric:tabular-nums; }
    .med-lbl{ font-size:12px; font-weight:800; color:var(--blanco); letter-spacing:.02em;
      line-height:1.3; }
    .med-det{ font-size:11.5px; color:rgba(248,250,252,.72); line-height:1.3;
      align-self:start; font-variant-numeric:tabular-nums; }
    @media (max-width:640px){
      .c-hero-med{ gap:16px; }
      #ini-medidor{ width:100%; justify-content:flex-start; }
    }
    .c-hero::after{
      content:""; position:absolute; inset:0;
      background:radial-gradient(120% 160% at 88% -30%, rgba(78,205,196,.30), transparent 62%);
      pointer-events:none;
    }
    .c-hero h1{ margin:0; font-size:22px; font-weight:800; color:var(--blanco);
      letter-spacing:-.5px; position:relative; z-index:1 }
    .c-hero p{ margin:5px 0 0; font-size:13.5px; color:rgba(248,250,252,.72);
      position:relative; z-index:1 }

    .btn-c{
      min-height:var(--tap); padding:0 22px; border:0; border-radius:var(--r-md);
      background:linear-gradient(112deg,var(--navy) 0%,var(--navy-light) 48%,var(--teal-hondo) 100%);
      color:var(--blanco); font-family:inherit; font-size:14px; font-weight:800;
      cursor:pointer; display:inline-flex; align-items:center; justify-content:center; gap:8px;
    }
    .btn-c:hover{ filter:brightness(1.12) }
    .btn-c:active{ transform:translateY(1px) }

    .exp-hero{
      background:linear-gradient(126deg,var(--navy) 0%,var(--navy-light) 52%,var(--teal-hondo) 100%);
      border-radius:var(--r-2xl); padding:20px 24px; margin-bottom:14px;
      display:flex; align-items:center; gap:22px; position:relative; overflow:hidden;
    }
    .exp-hero::after{
      content:""; position:absolute; inset:0;
      background:radial-gradient(110% 150% at 92% -20%, rgba(78,205,196,.32), transparent 60%);
      pointer-events:none;
    }
    .exp-hero-num{ font-size:46px; font-weight:800; color:var(--blanco); line-height:1;
      letter-spacing:-1.5px; position:relative; z-index:1 }
    .exp-hero-txt{ position:relative; z-index:1; min-width:0 }
    .exp-hero-txt b{ display:block; font-size:16px; font-weight:800; color:var(--blanco) }
    .exp-hero-txt small{ display:block; font-size:12.5px; color:rgba(248,250,252,.72); margin-top:3px }

    @media (max-width:768px){
      .c-hero{ padding:18px 19px; border-radius:var(--r-xl) }
      .c-hero h1{ font-size:19px }
      .exp-hero{ padding:17px 19px; gap:16px; border-radius:var(--r-xl) }
      .exp-hero-num{ font-size:38px }
    }

    /* ====== MASTER STAFFING · cuadrícula semanal ====== */
    .ms-tabla{ width:100%; border-collapse:separate; border-spacing:3px; font-size:12.5px; }
    .ms-tabla th{ background:var(--navy); color:var(--blanco); font-weight:800;
      font-size:10.5px; text-transform:uppercase; letter-spacing:.4px;
      padding:9px 6px; text-align:center; border-radius:var(--r-sm); }
    .ms-tabla th:first-child{ text-align:left; padding-left:12px; }
    .ms-tabla th small{ font-weight:600; opacity:.75; font-size:9.5px; }
    .ms-lbl{ background:var(--superficie); border:0.5px solid var(--border); border-radius:var(--r-sm);
      padding:9px 12px; vertical-align:middle; }
    .ms-lbl b{ display:block; font-size:13px; color:var(--text); }
    .ms-lbl small{ display:block; font-size:11px; color:var(--text-2); margin-top:2px; }
    .ms-cel{ text-align:center; padding:8px 4px; border-radius:var(--r-sm);
      min-width:62px; vertical-align:middle; background:var(--superficie);
      border:0.5px solid var(--border); }
    .ms-cel.verde{  background:rgba(22,196,127,.13); border-color:rgba(22,196,127,.3); }
    .ms-cel.ambar{  background:rgba(255,159,28,.14); border-color:rgba(255,159,28,.32); }
    .ms-cel.rojo{   background:rgba(255,77,79,.11);  border-color:rgba(255,77,79,.28); }
    .ms-cel.neutro{ background:var(--bg); }
    .ms-cel.vacio{  background:transparent; border-style:dashed; color:var(--text-3); }
    .ms-letra{ display:block; font-size:14px; font-weight:800; color:var(--text); line-height:1.1; }
    .ms-elem{ display:block; font-size:10.5px; color:var(--text-2); margin-top:2px;
      white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:70px; }
    .ms-tarde{ display:block; font-size:10px; font-weight:800; color:var(--danger); }
    .ms-leyenda{ display:flex; gap:8px; flex-wrap:wrap; margin-top:14px;
      padding-top:12px; border-top:0.5px solid var(--border); }
    .ms-leg{ font-size:11px; font-weight:600; padding:4px 10px; border-radius:var(--r-full);
      background:var(--bg); color:var(--text-2); }
    .ms-leg.verde{  background:rgba(22,196,127,.13); color:var(--ok-txt); }
    .ms-leg.ambar{  background:rgba(255,159,28,.15); color:var(--amar-dark); }
    .ms-leg.rojo{   background:rgba(255,77,79,.11);  color:var(--danger-txt); }
    @media (max-width:768px){
      .ms-tabla{ font-size:11.5px; }
      .ms-cel{ min-width:52px; padding:7px 3px; }
      .ms-elem{ display:none; }
      .ms-lbl{ min-width:150px; }
    }


    /* ═══ LATERAL COLAPSABLE ═══
       Qué se ve en cada dispositivo:
         PC    · lateral 232px (o 68px si el usuario la pliega) + botón de plegar
         iPad  · lateral 68px de iconos + botón de plegar
         Móvil · sin lateral: barra inferior de 5 pestañas + botón "Más"
       El botón "Más" NUNCA aparece en PC ni iPad; el de plegar, nunca en móvil. */
    #nav-plegar{ position:absolute; top:18px; right:8px; width:30px; height:30px;
      border:0; border-radius:var(--r-sm); background:rgba(248,250,252,.1);
      color:var(--text-3); cursor:pointer; display:grid; place-items:center; font-size:16px;
      transition:background .15s; }
    
    /* La visibilidad por dispositivo necesita la misma especificidad que
       #tabs button, o esa regla gana y todo se ve en todos lados. */
    #tabs button.solo-movil{ display:none; }
    #tabs button.solo-escritorio,
    #nav-plegar.solo-escritorio{ display:grid; }

    /* ── ESTADO PLEGADO · SÓLO DONDE HAY LATERAL ──
       Lo elige el usuario tocando el logo y se recuerda.

       ── ESTE BLOQUE NO TENÍA MEDIA QUERY, Y ERA UN BUG DE VERDAD ──
       `body.nav-plegada` no es una clase de escritorio: `montarNav` la
       pone en cuanto `lyk_nav_plegada` vale "1", en el aparato que sea.
       Y en el teléfono NO hay lateral: `#tabs` es la barra de abajo.

       Sin la media query, `body.nav-plegada #tabs{ width:68px }` pesa lo
       mismo que el `width:100%` del bloque de móvil y va DESPUÉS en el
       archivo, así que ganaba: a cualquiera que hubiera plegado el menú
       alguna vez en su computadora, la barra del teléfono le salía de
       68 px amontonada en la esquina de abajo a la izquierda, con los
       botones de 10 px de ancho. Lo mismo el alto de 46 px y el
       `display:none` de las etiquetas.

       Ahora el bloque entero vive donde tiene sentido: de tableta para
       arriba. */
    @media (min-width: 600px) and (min-height: 480px){
      /* Estado plegado (lo elige el usuario y se recuerda) */
      body.nav-plegada #tabs{ width:68px; padding:0 8px 14px; align-items:center; }
      /* ── EL ISOTIPO TAMBIEN FLOTA ──
         Con el menu abierto el logo se queda arriba al deslizar; plegado
         se iba con el scroll. No era que faltara el sticky: es que
         `.marca` ES un <button>, y tres lineas mas abajo esta
         `body.nav-plegada #tabs button{ ... position:relative }` — puesta
         ahi para colgarle el puntito de aviso a los botones. Esa regla
         pesa mas que el `position:sticky` de la marca y lo pisaba.

         Se vuelve a declarar aqui, nombrando el boton por su clase para
         que gane sin depender del orden: `button.marca` pesa mas que
         `button` a secas. */
      body.nav-plegada #tabs button.marca{ justify-content:center; padding:26px 0 12px;
        position:sticky; top:0; z-index:9; background:var(--nav-bg); }
      body.nav-plegada #tabs .marca img{ object-position:center; }
      body.nav-plegada #tabs .marca .completo{ display:none; }
      body.nav-plegada #tabs .marca .iso{ display:block; }
      body.nav-plegada #tabs button{ justify-content:center; padding:0; width:46px;
        height:46px; min-height:46px; gap:0; position:relative; }
      body.nav-plegada #tabs button .lbl{ display:none; }
      body.nav-plegada #tabs .nav-grupo{ display:none; }
      body.nav-plegada #tabs .badge{ position:absolute; top:5px; right:5px; margin:0;
        padding:1px 5px; font-size:9px; }
    
        /* chevron-right */
      body.nav-plegada #nav-pie .quien-nav{ display:none; }
      body.nav-plegada #nav-pie button{ justify-content:center; padding:0; }
      body.nav-plegada #nav-pie button .lbl{ display:none; }
    }

    /* ====== ENROLAMIENTO FACIAL ====== */
    .enr-panel{ background:var(--superficie-2); }
    .enr-estado{ font-size:13.5px; padding:11px 14px; border-radius:var(--r-md);
      background:var(--superficie-3); color:var(--text-2); margin:10px 0 12px; line-height:1.45; }
    .enr-estado.ok{ background:rgba(22,196,127,.12); color:var(--ok-txt); font-weight:600; }
    .enr-estado.err{ background:rgba(255,77,79,.1); color:var(--danger-txt); font-weight:600; }
    .enr-acts{ display:flex; gap:10px; flex-wrap:wrap; }
    .enr-acts .btn{ min-height:var(--tap); }
    .enr-acts label.btn{ cursor:pointer; }

    .enr-cam-wrap{ margin-top:14px; }
    .enr-cam-marco{ position:relative; width:100%; max-width:280px; aspect-ratio:224/280;
      margin:0 auto; border-radius:var(--r-lg); overflow:hidden;
      /* fijo: el visor de la camara es negro en los dos temas */
      background:#000;
      border:2px solid var(--border); }
    .enr-cam-marco video{ width:100%; height:100%; object-fit:cover; display:block; }
    /* Esto lo puse yo el 3/oct con el argumento de que "uno espera
       verse como en un espejo". Quedó rechazado sin excepciones y
       el informe de VERIDOM ya decía lo mismo. La clase se deja sin
       efecto en vez de borrarla, para que si alguien la pone en otro
       lado tampoco haga nada. */
    .enr-cam-marco video.espejo{ transform:none; }
    .enr-guia{ position:absolute; inset:0; pointer-events:none; }
    .enr-guia::before{ content:""; position:absolute; left:50%; top:46%;
      width:64%; aspect-ratio:3/4; transform:translate(-50%,-50%);
      border:2px dashed rgba(255,194,7,.6); border-radius:50%; }
    .enr-cam-txt{ position:absolute; left:50%; bottom:12px; transform:translateX(-50%);
      background:rgba(4,30,66,.86); color:var(--blanco); font-size:14px; font-weight:700;
      padding:9px 15px; border-radius:var(--r-md); white-space:nowrap; }
    .enr-cam-acts{ display:flex; gap:10px; justify-content:center; margin-top:12px; }

    .enr-crop-wrap{ margin-top:14px; }
    .enr-crop{ position:relative; width:224px; height:280px; margin:10px auto;
      overflow:hidden; border-radius:var(--r-lg);
      /* fijo: fondo del visor, no cambia con el tema */
      background:#0b1220;
      cursor:grab; touch-action:none; border:2px solid var(--navy); }
    .enr-crop:active{ cursor:grabbing; }
    .enr-crop img{ position:absolute; left:0; top:0; transform-origin:0 0;
      max-width:none; user-select:none; -webkit-user-drag:none; }
    .enr-crop-ovalo{ position:absolute; inset:0; pointer-events:none; }
    .enr-crop-ovalo::before{ content:""; position:absolute; left:50%; top:46%;
      width:70%; aspect-ratio:3/4; transform:translate(-50%,-50%);
      border:2px dashed rgba(255,194,7,.7); border-radius:50%; }
    .enr-zoom{ display:flex; align-items:center; gap:12px; max-width:280px;
      margin:0 auto; font-size:13px; color:var(--text-2); }
    .enr-zoom input{ flex:1; min-height:var(--tap); }
    .enr-valid{ text-align:center; font-size:12.5px; margin:10px 0;
      padding:8px 12px; border-radius:var(--r-sm); }
    .enr-valid.bien{ background:rgba(22,196,127,.12); color:var(--ok-txt); }
    .enr-valid.regular{ background:rgba(255,159,28,.14); color:var(--amar-dark); }
    .enr-valid.mal{ background:rgba(255,77,79,.1); color:var(--danger-txt); font-weight:700; }
    .enr-crop-acts{ display:flex; gap:10px; justify-content:center; }

    .enr-final{ display:flex; align-items:center; gap:14px; margin-top:14px;
      padding:14px; background:var(--superficie); border:0.5px solid var(--border);
      border-radius:var(--r-lg); }
    .enr-final img{ width:72px; height:90px; object-fit:cover;
      border-radius:var(--r-md); border:2px solid var(--ok); }
    .enr-final-txt{ flex:1; min-width:0; }
    .enr-final-txt b{ display:block; font-size:14px; color:var(--text); }
    .enr-final-txt small{ display:block; font-size:12.5px; color:var(--text-2);
      margin-top:3px; line-height:1.4; }

    /* ══════ LA FILA DE BOTONES · UN SOLO LUGAR ══════

       Esta regla estaba escrita tres veces: dos en este archivo y una
       en capital-humano.js. Cada copia arreglaba el problema de un dia
       sin saber de las otras dos, y entre las tres decidian cosas
       distintas sobre el mismo boton. Aqui esta la unica.

       `stretch` y no `center`: asi todos los botones de la fila miden
       exactamente lo mismo de alto, venga el texto que venga. Con
       `center` cada uno tomaba su altura natural y bastaba un detalle
       para que uno quedara un pelo mas alto que el de al lado.

       `flex:1 1 150px` con `wrap`: cada boton pide 150px y crece para
       repartirse el sobrante. Si no caben, saltan de linea EN VEZ DE
       APLASTARSE. Se midio: antes, apretados, el texto se recortaba a
       partir de los 165px por boton y nadie se enteraba. Ahora no puede
       pasar — o caben, o se acomodan abajo. */
    .row-actions{ display:flex; flex-wrap:wrap; gap:10px; align-items:stretch; }
    .row-actions .btn{ flex:1 1 150px; min-width:0; min-height:var(--tap);
      white-space:nowrap; padding:0 14px; }
    @media (max-width:768px){
      /* En el bottom sheet se apilan: lado a lado el ghost se desborda
         encima del primario y el texto queda ilegible.

         `flex:0 0 auto` no es opcional. En fila, `flex-basis` es el
         ANCHO; en columna es el ALTO. Al apilar, el `1 1 150px` de
         arriba se convertia en "cada boton mide 150 de alto" y el pie
         se comia media pantalla. Se vio midiendo en 375px: 339x150 por
         boton. Aqui se le devuelve su altura natural. */
      .modal .row-actions{ flex-direction:column-reverse; gap:9px; }
      .modal .row-actions .btn{ flex:0 0 auto; width:100%; justify-content:center; }
      /* El label que hace de botón (subir archivo) también */
      .enr-acts{ flex-direction:column; }
      .enr-acts .btn{ width:100%; justify-content:center; }
      .enr-cam-acts .btn, .enr-crop-acts .btn{ flex:1; justify-content:center; }
    }
    /* ══════ PATRÓN DE SALIDA · en todo el sistema ══════
       Toda pantalla que se abre debe tener una salida VISIBLE, sin
       buscarla. Antes solo estaba "Cancelar" al final del formulario:
       si el formulario era largo, había que bajar hasta el fondo para
       poder salir, y eso se siente como estar atorado. */
    /* La X flota FUERA de la card, en la esquina de la pantalla: siempre
       en el mismo lugar, sin competir con los botones del formulario.
       Antes iba dentro y se encimaba con el "Cancelar" de la barra. */
    .modal{ position:relative; }
    .modal-x{ position:fixed; top:18px; right:18px; width:46px; height:46px;
      border:0; border-radius:50%; background:rgba(4,30,66,.9);
      backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
      color:var(--blanco); font-size:26px; line-height:1; cursor:pointer;
      z-index:120; display:grid; place-items:center;
      box-shadow:0 4px 16px rgba(0,0,0,.28); transition:background .15s; }
    .modal-x:hover{ background:var(--navy); }
    @media (max-width:768px){
      /* En el bottom sheet va sobre la hoja, no en la esquina de la pantalla */
      .modal-x{ top:auto; bottom:calc(100dvh - 100px); right:14px;
        width:42px; height:42px; font-size:24px; }
    }

    /* ── EL CÓDIGO QR DE ENTRADA ──

       El comentario de antes decía "fondo blanco siempre, aunque el tema
       esté oscuro", y luego el fondo lo sacaba de `--superficie`, que en
       oscuro vale #132540. O sea: decía una cosa y hacía la otra. Eso es
       el marco oscuro que se veía a los lados y abajo del código.

       Va blanco de verdad, escrito a mano y no por token: un lector de
       códigos necesita contraste real, no el del tema del sistema. Y sin
       borde: el propio código trae su margen blanco, que es el silencio
       que la cámara necesita alrededor. La caja se ajusta al código, no
       al revés — por eso el alto fijo, para que no brinque la pantalla
       mientras se genera. */
    /* La caja mide EXACTAMENTE lo que mide el codigo. Antes tenia alto
       fijo y ancho libre —se estiraba a lo ancho de la columna, 281px
       contra 222 de alto— y ese sobrante era el marco que se veia a los
       costados. Ahora es cuadrada y se centra sola. */
    .qr-lienzo{ display:flex; align-items:center; justify-content:center;
      /* fijo: una camara necesita contraste real, no el del tema */
      background:#fff; border:0; border-radius:6px;
      padding:9px; margin:6px auto 4px; width:222px; height:222px;
      max-width:100%; overflow:hidden; }
    /* ── `max-width:none` NO ES ADORNO, ES LO QUE HACE QUE EL CODIGO SE LEA ──

       Arriba, en la linea del logo, vive `.login-card img{ width:220px;
       max-width:78%; height:auto }`. Se escribio para el logo, pero el
       codigo QR tambien es un <img> dentro de un `.login-card`, asi que
       le pegaba igual. Las dos reglas tienen la misma fuerza, y como
       esta va despues gana en `width`... pero NO en `max-width`, porque
       aqui no se declaraba.

       Resultado: el codigo se aplastaba al 78% de ancho y se quedaba con
       todo el alto. 159 de ancho por 204 de alto, dentro de una caja de
       222. Ese sobrante blanco a los costados ERA el marco que se veia.

       Se probo si asi de aplastado se podia leer, y si: el lector de
       Chrome lo decodifico deformado igual que cuadrado. O sea que esto
       no estaba rompiendo la entrada, solo se veia mal. Queda escrito
       aqui para no volver a contarlo mas grave de lo que fue.

       Se declaran los tres —ancho, alto y max-width— para que ninguna
       regla de mas arriba lo vuelva a deformar. */
    .qr-lienzo img{ width:204px; height:204px; max-width:none; margin:0;
      image-rendering:pixelated; display:block; }
    /* fijo: va sobre el blanco del codigo, no sobre la superficie */
    .qr-lienzo .qr-vencido{ text-align:center; color:#5D6B7E; font-size:13px;
      line-height:1.5; padding:0 14px; }
    .qr-cuenta{ font-size:12px; color:var(--text-3); text-align:center; margin-top:2px; }
    /* El boton que el mensaje "Toca Generar otro" prometia y no existia. */
    .qr-otro{ display:none; width:100%; margin-top:8px; }
    .qr-otro.se-ve{ display:block; }

    /* ── LA NÓMINA ──

       Todo con los tokens del panel, ninguno a mano. Cada color escrito
       aquí sería una copia que el día que cambie la paleta se queda
       vieja: es exactamente como nacieron los tres problemas de color
       que llevamos —el informe, los campos y el cuestionario. */

    /* Las fechas del periodo: corte, pago y el fondo de cinco días. */
    /* ── EL SELECTOR DE LÍNEA ──
       Cada quincena tiene DOS nóminas con su propio embudo: la operativa
       y la administrativa. La pantalla mostraba sólo una y no decía que
       la otra existiera, así que la gente de oficina parecía no estar.
       Se dibujan las dos siempre, con cuánta gente lleva cada una. */
    /* ══ EL AUDITOR ══
       Una notificación que nazca de forma lateral, de derecha a
       izquierda, en la parte inferior derecha de la pantalla, para que
       me avise siempre de bugs". Nace fuera de la pantalla y entra. */
    #aud-pila{ position:fixed; right:16px; bottom:16px; z-index:120;
               display:flex; flex-direction:column; gap:10px; align-items:flex-end;
               pointer-events:none; max-width:min(380px, calc(100vw - 32px)); }
    .aud-aviso{ pointer-events:auto; width:min(380px, calc(100vw - 32px));
                background:var(--superficie); color:var(--text);
                border:1px solid var(--danger-borde, var(--border));
                border-left:4px solid var(--rojo, #dc2626);
                border-radius:var(--r-md); padding:12px 14px;
                box-shadow:0 12px 32px rgba(4,30,66,.22);
                transform:translateX(calc(100% + 24px)); opacity:0;
                transition:transform .32s cubic-bezier(.22,.61,.36,1), opacity .32s ease; }
    .aud-aviso.entra{ transform:translateX(0); opacity:1; }
    .aud-a-top{ display:flex; align-items:center; gap:7px; font-size:13.5px; }
    .aud-a-top > i{ color:var(--rojo, #dc2626); font-size:16px; }
    .aud-a-que{ font-size:12.5px; color:var(--text-2); margin-top:5px; }
    .aud-a-msg{ font-size:12px; color:var(--text-3); margin-top:5px;
                word-break:break-word; max-height:3.6em; overflow:hidden; }
    .aud-a-pie{ font-size:11.5px; color:var(--rojo, #dc2626); font-weight:700; margin-top:5px; }
    .aud-a-acc{ margin-top:9px; }
    .aud-ir{ background:var(--navy); color:#fff; border:0; border-radius:7px;
             padding:6px 12px; font:700 12px inherit; cursor:pointer; }
    .aud-x{ margin-left:auto; background:none; border:0; color:var(--text-3);
            cursor:pointer; font-size:15px; line-height:1; padding:0; }
    .aud-rol{ background:var(--superficie-2); color:var(--text-3); border-radius:4px;
              padding:1px 6px; font-size:10.5px; font-weight:800; text-transform:uppercase; }

    /* El número junto a "Auditoría" en el menú. */
    .aud-num{ margin-left:auto; background:var(--rojo, #dc2626); color:#fff;
              border-radius:999px; min-width:19px; height:19px; line-height:19px;
              text-align:center; font-size:11px; font-weight:800; padding:0 5px; }

    /* La sección. */
    .aud-fila{ border:1px solid var(--border); border-left:3px solid var(--rojo, #dc2626);
               border-radius:var(--r-md); padding:12px 14px; margin-bottom:10px;
               background:var(--superficie); }
    .aud-f-top{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:13.5px; }
    .aud-cuando{ color:var(--text-3); font-size:12px; }
    .aud-f-que{ font-size:13px; color:var(--text-2); margin-top:5px; }
    .aud-f-msg{ font-size:12.5px; margin-top:6px; word-break:break-word; }
    .aud-f-det{ margin:8px 0 0; padding:9px 11px; background:var(--superficie-2);
                border-radius:6px; font:11px/1.5 ui-monospace,Menlo,monospace;
                color:var(--text-3); white-space:pre-wrap; max-height:150px;
                overflow:auto; }
    .aud-f-pie{ font-size:11px; color:var(--text-3); margin-top:7px; }
    .aud-f-acc{ margin-top:9px; }

    .nom-lineas{ display:flex; gap:10px; flex-wrap:wrap; margin:14px 0 4px; }
    .nom-linea{ flex:1 1 220px; text-align:left; cursor:pointer;
                background:var(--superficie-2); border:1px solid var(--border);
                border-radius:var(--r-md); padding:11px 13px 12px 40px;
                position:relative; font:inherit; color:var(--text);
                transition:border-color .15s ease, background .15s ease; }
    .nom-linea:hover{ border-color:var(--trazo-neutro); }
    .nom-linea > i{ position:absolute; left:13px; top:12px; font-size:17px;
                    color:var(--text-3); }
    .nom-linea.act{ border-color:var(--navy); background:var(--superficie);
                    box-shadow:inset 3px 0 0 var(--navy); }
    .nom-linea.act > i{ color:var(--navy); }
    .nl-t{ display:block; font-size:14px; font-weight:800; }
    .nl-q{ display:block; font-size:11.5px; color:var(--text-3); margin-top:1px; }
    .nl-n{ display:block; font-size:12px; color:var(--text-2); margin-top:5px;
           font-weight:700; }

    /* ── LA REJILLA DE ASISTENCIA ──
       Un renglón por persona, una columna por día: la forma de la hoja
       "Rotación 30 días" del Diseñador de Jornada. La primera columna se
       queda fija porque con 15 o 30 días la tabla se va de lado y sin el
       nombre pegado no se sabe de quién es el renglón. */
    .as-caja{ overflow:auto; border:1px solid var(--border); border-radius:var(--r-md);
              margin:16px 0 10px; background:var(--superficie); }
    .as-tabla{ border-collapse:separate; border-spacing:0; width:auto; min-width:100%; }
    .as-tabla th, .as-tabla td{ border-bottom:1px solid var(--border); }
    .as-q{ position:sticky; left:0; z-index:2; background:var(--superficie);
           text-align:left; padding:8px 12px; min-width:188px;
           border-right:1px solid var(--border); }
    .as-q b{ display:block; font-size:13px; }
    .as-q span{ display:block; font-size:11px; color:var(--text-3); margin-top:1px; }
    .as-qh{ z-index:3; font-size:11px; letter-spacing:.4px; text-transform:uppercase;
            color:var(--text-3); }
    .as-tabla thead th{ position:sticky; top:0; z-index:1; background:var(--superficie-2); }
    .as-tabla thead .as-q{ z-index:4; background:var(--superficie-2); }
    .as-dh{ width:40px; min-width:40px; padding:5px 0 6px; text-align:center; }
    .as-dd{ display:block; font-size:9.5px; font-weight:800; color:var(--text-3); }
    .as-dn{ display:block; font-size:12px; font-weight:800; }
    .as-dh.fin .as-dd, .as-dh.fin .as-dn{ color:var(--text-3); }
    .as-dh.hoy{ box-shadow:inset 0 -3px 0 var(--amar); }
    /* Los colores salen del ERP isla: azul marino para el día devengado,
       rojo para la falta, naranja para la ausencia que sí se paga. Las
       incidencias se pintan por su NATURALEZA —pago, descuento,
       ausencia— que la manda el catálogo, no por una lista escrita aquí. */
    .as-c{ text-align:center; font-size:10px; font-weight:800; padding:7px 0;
           width:40px; min-width:40px; letter-spacing:.3px; cursor:pointer; }
    .as-c:hover{ outline:2px solid var(--navy); outline-offset:-2px; }
    .as-dev{ background:var(--navy); color:#fff; }
    .as-hoy{ background:var(--amar); color:var(--navy); }
    .as-des{ background:var(--superficie-2); color:var(--text-3); }
    .as-nge{ background:#374151; color:#fff; }
    .as-fal{ background:var(--danger); color:var(--danger-txt); }
    .as-pro{ color:var(--text-3); }
    .as-pag{ background:#2563eb; color:#fff; }        /* incidencia que PAGA */
    .as-dto{ background:#7c3aed; color:#fff; }        /* descuento */
    .as-aus-paga{ background:#ea580c; color:#fff; }   /* ausencia que se paga */
    .as-aus{ background:#b45309; color:#fff; }        /* ausencia que no se paga */
    .as-tot{ text-align:center; font-size:12.5px; font-weight:800; width:52px;
             border-left:1px solid var(--border); }
    .as-toth{ font-size:10px; letter-spacing:.3px; text-transform:uppercase;
              color:var(--text-3); font-weight:800; padding:6px 0; }
    .as-leyenda{ display:flex; gap:14px; flex-wrap:wrap; font-size:12px;
                 color:var(--text-2); }
    .as-leyenda span{ display:inline-flex; align-items:center; gap:6px; }
    .as-ley-mas{ color:var(--text-3); }
    /* La leyenda reusaba `.as-c`, que trae el `padding:7px 0` de la celda
       de la rejilla. Con una altura fija de 20px, ese padding empujaba
       el texto FUERA de la caja: "DEV" salia colgando por abajo del
       cuadro azul. Aqui se anula el padding y se centra de verdad. */
    .as-leyenda i{ display:inline-flex; align-items:center; justify-content:center;
                   box-sizing:border-box; width:38px; height:22px; padding:0;
                   border-radius:4px; font-style:normal; border:1px solid var(--border);
                   font-size:10px; font-weight:800; cursor:default; letter-spacing:.3px; }

    .nom-fechas{ display:flex; gap:10px; flex-wrap:wrap; margin:14px 0 18px; }
    .nom-f{ flex:1 1 130px; background:var(--superficie-2); border:1px solid var(--border);
            border-radius:var(--r-md); padding:11px 13px; }
    .nom-f span{ display:block; font-size:11px; font-weight:800; letter-spacing:.4px;
                 text-transform:uppercase; color:var(--text-3); margin-bottom:3px; }
    .nom-f b{ font-size:15px; color:var(--text); }
    /* El fondo se marca: son los días de salario ya trabajado que la
       empresa tiene en mano, y es la única garantía cuando alguien se va
       debiendo equipo. Que se vea. */
    .nom-f-i{ border-color:var(--amar-dark); }

    /* La tira de pasos. El que va se resalta; los hechos llevan palomita. */
    .nom-tira{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:16px; }
    .nom-paso{ display:flex; align-items:center; gap:7px; padding:7px 12px;
               border:1px solid var(--border); border-radius:999px;
               background:var(--superficie-2); font-size:12.5px; }
    .nom-paso .nom-p-n{ width:20px; height:20px; border-radius:50%; flex-shrink:0;
                        display:flex; align-items:center; justify-content:center;
                        font-size:11px; font-weight:800;
                        background:var(--border); color:var(--text-2); }
    .nom-paso.hecho{ opacity:.72; }
    .nom-paso.hecho .nom-p-n{ background:var(--ok-txt, #16a34a); color:#fff; }
    .nom-paso.ahora{ border-color:var(--amar); background:var(--amar-suave); }
    .nom-paso.ahora .nom-p-n{ background:var(--amar); color:var(--sobre-amar); }
    .nom-paso.pend{ opacity:.55; }

    /* Qué está pasando ahora, en palabras. */
    .nom-ahora{ background:var(--superficie-2); border:1px solid var(--border);
                border-left:3px solid var(--amar); border-radius:var(--r-md);
                padding:13px 15px; margin-bottom:14px; }
    .nom-a-t{ font-weight:800; font-size:15px; color:var(--text); margin-bottom:3px; }
    .nom-a-d{ font-size:13.5px; color:var(--text-2); line-height:1.5; }

    /* Un conteo NO es una cifra de dinero: esto sí lo ve Capital Humano. */
    .nom-dato{ font-size:14px; color:var(--text-2); margin-bottom:14px; }
    .nom-dato b{ font-size:20px; color:var(--text); }

    /* Los dos vistos buenos. */
    .nom-vbs{ display:flex; gap:10px; flex-wrap:wrap; margin-bottom:14px; }
    .nom-vb{ flex:1 1 220px; display:flex; align-items:center; gap:11px;
             border:1px solid var(--border); border-radius:var(--r-md);
             padding:12px 14px; background:var(--superficie-2); }
    .nom-vb i{ font-size:21px; color:var(--text-3); flex-shrink:0; }
    .nom-vb.ok{ border-color:var(--ok-txt, #16a34a); }
    .nom-vb.ok i{ color:var(--ok-txt, #16a34a); }
    .nom-vb div{ flex:1; min-width:0; }
    .nom-vb b{ display:block; font-size:14px; color:var(--text); }
    .nom-vb span{ font-size:12.5px; color:var(--text-2); }

    /* Los totales. Esta caja SOLO se pinta en Dispersión, y a esa
       pantalla solo entra quien tiene el permiso de cifras. */
    .nom-tot{ display:flex; gap:10px; flex-wrap:wrap; margin-bottom:12px; }
    .nom-t{ flex:1 1 150px; background:var(--superficie-2); border:1px solid var(--border);
            border-radius:var(--r-md); padding:13px 15px; }
    .nom-t span{ display:block; font-size:11px; font-weight:800; letter-spacing:.4px;
                 text-transform:uppercase; color:var(--text-3); margin-bottom:4px; }
    .nom-t b{ font-size:19px; color:var(--text); }
    .nom-t.fuerte{ border-color:var(--amar); }
    .nom-t.fuerte b{ color:var(--text); }

    @media (max-width:640px){
      .nom-f{ flex:1 1 calc(50% - 5px); }
      .nom-t{ flex:1 1 calc(50% - 5px); }
      .nom-paso .nom-p-t{ display:none; }
      .nom-paso.ahora .nom-p-t{ display:inline; }
    }

    /* ── EL INFORME KRYPTOS ES UN DOCUMENTO, Y SE QUEDA CLARO ──

       `kryptos-motor.css` trae su propio juego de colores, y ahi esta la
       trampa: comparte NOMBRE con tres del panel —--text, --bg,
       --border—. Sus tarjetas son blancas fijas, pero el texto lo saca
       de `--text`... que en oscuro se vuelve claro. Resultado: letra
       casi blanca sobre tarjeta blanca, en once lugares del informe.

       No se arregla oscureciendo el informe: es un documento que se
       imprime, se descarga y se le manda al cliente, y tiene que verse
       igual siempre — como el codigo QR, que tambien se queda blanco a
       proposito.

       Entonces aqui dentro los tres tokens vuelven a su valor claro,
       pase lo que pase con el tema de afuera. */
    #inf-cuerpo, .kryptos-doc {
      --text:#1a1a1a; --bg:#f8fafc; --border:#e5e7eb;
      /* El informe se imprime y se manda al cliente: #9ca3af daba 2.32. */
      --text-2:#6b7280; --text-3:#69707C; --trazo-neutro:#9ca3af;
      --superficie:#fff; --superficie-2:#fafbfc; --superficie-3:#f1f5f9;
      --navy:#041E42;
      color-scheme: light;
      /* fijo: el informe se imprime y se manda al cliente: siempre claro */
      background:#fff; color:#1a1a1a;
      border-radius:var(--r-md);
    }

    /* ── EL CUESTIONARIO TAMBIÉN, Y POR OTRA RAZÓN ──

       El arreglo de arriba cubrió el INFORME. El CUESTIONARIO vive en
       otro contenedor —`#kryptos-engine`— y se quedó fuera. Se vio
       enfrente de una candidata: salían los números 1, 2, 3, 4 y
       ningún texto.

       La causa es distinta y vale la pena escribirla, porque no se
       encuentra leyendo la hoja de colores.

       `.q-o` es un <button>. Los botones NO heredan `color`: lo toman
       del sistema operativo. El panel declara `color-scheme: dark`, así
       que el color de sistema para texto de botón se vuelve BLANCO. Y
       `.q-o` pinta su fondo `#fff` a mano pero nunca dice de qué color
       va su letra. Blanco sobre blanco.

       Por eso el NÚMERO sí se veía: `.q-n` es un <span> con
       `color:#5D6B7E` propio. El texto de la opción es un <span> sin
       color, que hereda del botón, que hereda del sistema.

       El cuestionario lo contesta un CANDIDATO en un aparato que se le
       entrega. Tiene que verse igual siempre, como el informe: no puede
       depender de si quien se lo prestó traía el tema oscuro. */
    #kryptos-engine, #kr-overlays{ color:var(--kr-txt); }
    /* Los <button> NO heredan color --lo toman del sistema-- asi que cada
       uno tiene que decirlo. Es lo que dejo el texto blanco sobre blanco. */
    #kryptos-engine button, #kr-overlays button,
    #kryptos-engine input, #kr-overlays input,
    #kryptos-engine select, #kr-overlays select,
    #kryptos-engine textarea, #kr-overlays textarea{ color:var(--kr-txt); }
    #kryptos-engine .bp, #kr-overlays .bp{ color:#041E42; }

    /* ── LA VENTANA TIENE DOS AUDIENCIAS, Y NO SE VEN IGUAL ──

       Aquí estaba mi error de criterio: traté toda la ventana como si
       fuera del candidato, y la dejé fija en claro. Pero son dos cosas
       distintas dentro del mismo marco.

         · Las instrucciones y los pasos los ves TÚ, en tu panel, con tu
           tema. Ver una tarjeta blanca dentro de un panel oscuro se ve
           mal y no tiene razón de ser.
         · El CUESTIONARIO lo ve la candidata, en un aparato que le
           prestas. Ese sí tiene que verse igual siempre: no puede
           depender de cómo traía el tema quien se lo dio.

       Entonces las tarjetas del operador siguen el tema —con los mismos
       tokens del panel, para que empaten exacto— y el cuestionario se
       queda claro y lo dice a mano.

       Los tokens se declaran una vez y las reglas los usan: así, el día
       que el panel cambie sus colores, esto cambia con él en lugar de
       quedarse con una copia vieja. */
    #kryptos-engine, #kr-overlays{
      --kr-sup:#ffffff; --kr-sup-2:#f8fafc; --kr-txt:#02132C;
      --kr-txt-2:#5D6B7E; --kr-bd:#E8ECF1; --kr-amar-on:#FFFDF5;
    }
    @media (prefers-color-scheme: dark){
      :root:not([data-tema="claro"]) #kryptos-engine,
      :root:not([data-tema="claro"]) #kr-overlays{
        --kr-sup:#132540; --kr-sup-2:#101F36; --kr-txt:#E8EEF6;
        --kr-txt-2:#A7B6C9; --kr-bd:#1E3049; --kr-amar-on:#2A2416;
      }
    }
    :root[data-tema="oscuro"] #kryptos-engine,
    :root[data-tema="oscuro"] #kr-overlays{
      --kr-sup:#132540; --kr-sup-2:#101F36; --kr-txt:#E8EEF6;
      --kr-txt-2:#A7B6C9; --kr-bd:#1E3049; --kr-amar-on:#2A2416;
    }

    /* Lo que ves tú: sigue el tema. */
    #kryptos-engine .card, #kr-overlays .card,
    #kryptos-engine .stepcard, #kr-overlays .stepcard,
    #kryptos-engine .ci-c, #kr-overlays .ci-c{
      background:var(--kr-sup); color:var(--kr-txt); border-color:var(--kr-bd);
    }
    #kryptos-engine .card *, #kr-overlays .card *,
    #kryptos-engine .stepcard *, #kr-overlays .stepcard *{ border-color:var(--kr-bd); }
    /* Los avisos: el fondo sigue el tema, el color del borde es el que
       dice de qué tipo son y ese no se toca. */
    #kryptos-engine .note, #kr-overlays .note{
      background:var(--kr-sup-2); color:var(--kr-txt);
    }
    #kryptos-engine .note b, #kr-overlays .note b{ color:var(--kr-txt); }
    #kryptos-engine .ch, #kr-overlays .ch{ background:var(--kr-sup); color:var(--kr-txt); }
    #kryptos-engine .ch-t, #kr-overlays .ch-t{ color:var(--kr-txt); }
    #kryptos-engine .ch-s, #kr-overlays .ch-s{ color:var(--kr-txt-2); }

    /* ── EL CUESTIONARIO TAMBIÉN SIGUE EL TEMA ──

       Aquí lo había dejado claro a propósito, con el argumento de que la
       candidata debe verlo siempre igual. Se pidió el tema oscuro dos
       veces y yo sostuve mi criterio; el resultado fue que en la
       pantalla de quien lo revisaba no cambió nada. El argumento podía
       ser razonable y aun así estaba de más: es su sistema y su
       operación, y una ventana blanca dentro de un panel oscuro se ve
       mal aunque haya una razón escrita debajo.

       Todo sigue el tema. Lo único que no se mueve es la barra de
       progreso, que es navy en los dos porque su texto va en blanco. */
    #kryptos-engine .qcard, #kr-overlays .qcard{
      background:var(--kr-sup); color:var(--kr-txt);
      border:1px solid var(--kr-bd);
    }
    #kryptos-engine .q-t, #kr-overlays .q-t{ color:var(--kr-txt); }
    #kryptos-engine .q-o, #kr-overlays .q-o{
      background:var(--kr-sup-2); color:var(--kr-txt); border-color:var(--kr-bd);
    }
    #kryptos-engine .q-o:hover, #kr-overlays .q-o:hover{
      border-color:var(--empresa-principal, #FFC207); }
    /* La elegida se marca con amarillo en los dos temas: es la marca de
       Lykos y en oscuro necesita su propio fondo para no perderse. */
    #kryptos-engine .q-o.on, #kr-overlays .q-o.on{
      /* ── POR QUE AQUI NO VA EL TEAL DE ORVIK ──
         La elegida se marca con el color de la OPERATIVA: la regla
         madre pone el «reporte KRYPTOS» entre los documentos que van
         con la empresa, y esta es la prueba que contesta SU candidato.
         Lo que cambia es que ya no esta escrito a mano: se cuelga de
         `--empresa-principal`, que escribe el motor de marca al
         entrar, con el amarillo de Lykos de respaldo porque es quien
         opera hoy. */
      border-color:var(--empresa-principal, #FFC207);
      background:var(--kr-amar-on); color:var(--kr-txt);
    }
    #kryptos-engine .q-n, #kr-overlays .q-n{
      background:var(--kr-bd); color:var(--kr-txt-2);
    }
    #kryptos-engine .q-o.on .q-n, #kr-overlays .q-o.on .q-n{
      background:var(--empresa-principal, #FFC207);
      color:var(--empresa-sobre-principal, #041E42);
    }
    /* Las matrices de figuras: el SVG se dibuja con el color del texto. */
    #kryptos-engine .mx-c, #kr-overlays .mx-c,
    #kryptos-engine .mx-o, #kr-overlays .mx-o{
      background:var(--kr-sup-2); color:var(--kr-txt); border-color:var(--kr-bd);
    }
    #kryptos-engine .mx-o.on, #kr-overlays .mx-o.on{
      border-color:var(--empresa-principal, #FFC207);
      background:var(--kr-amar-on);
    }
    /* La barra de progreso no cambia: navy con letra blanca en los dos. */
    #kryptos-engine .prog, #kr-overlays .prog{ background:#041E42; }
    #kryptos-engine .prog *, #kr-overlays .prog *{ color:#fff; }

    /* ── Y QUE SE VEA LA ÚLTIMA OPCIÓN ──
       En la foto la cuarta opción quedaba cortada por abajo. El
       cuestionario tiene cuatro respuestas: si la última no se ve, la
       pregunta está mal hecha. El cuerpo scrollea y deja aire al final
       para que ninguna quede pegada al borde. */
    #kryptos-engine{ max-height:none; overflow:visible; }
    #kryptos-engine .qcard{ margin-bottom:16px; }
    #kryptos-engine .q-ops{ padding-bottom:8px; }

    /* ── LOS CAMPOS SE DEFIENDEN DE LA HOJA DEL INFORME ──

       `kryptos-motor.css` pinta de blanco TODOS los inputs del
       documento, no solo los suyos: sus selectores son de elemento
       —input[type="date"], select, textarea— y alcanzan a todo el
       panel. Encima pesan mas que `.fi`, asi que ganaban.

       En claro nunca se noto, porque blanco era justo lo que se queria.
       Con el tema oscuro quedo campo blanco con letra casi blanca: eso
       es lo que se fotografio en "Semana del".

       Aqui `.fi` pesa lo mismo y va despues, asi que gana. Y `select`
       lleva su flecha propia: la del navegador se pinta con el color
       del texto y en oscuro se perdia sobre el campo.  */
    input.fi, select.fi, textarea.fi{
      background:var(--campo-bg); color:var(--text); border-color:var(--campo-borde);
    }

    /* ── VA DESPUES DE LA REGLA DE ARRIBA, Y NO ES CAPRICHO ──
       Primero lo puse junto a `.fi`, 1400 lineas mas arriba. La flecha
       no aparecia: `background:var(--campo-bg)` de aqui es el ATAJO, y
       un atajo reescribe TODAS las partes de la propiedad --incluida
       `background-image`-- aunque solo se nombre el color. Mi flecha
       se borraba sin que nada avisara.
       Medido en el navegador: `background-image` salia `none` y
       `background-position` volvia a `0% 0%`. */
    /* ══════════ LA FLECHA DEL DESPLEGABLE · 20/oct ══════════

       Medido: `.fi` daba `padding-right:14px` y el `<select>` se
       quedaba con `appearance:auto`, o sea la flecha NATIVA del
       navegador. Esa se dibuja a unos 6 px del borde y no se puede
       mover: no es un elemento, la pinta el sistema.

       Por eso se apaga el aspecto nativo y la flecha pasa a ser un
       fondo que si se coloca: 16 px de aire a la derecha, y 42 de
       `padding` para que el texto largo --«Construcción / Obra», «Otro
       lugar · lo escribo»-- nunca se le meta debajo.

       EL COLOR SIGUE AL TEMA. Un `data:` URI no admite `var()`, asi
       que la flecha entera es un token y se declara dos veces, una por
       tema, igual que `--fondo-tinte`. Escribirla con un gris fijo
       habria repetido el error del tema oscuro que ya se pago aqui.

       Y vale para las tres pantallas sin una sola media query: es
       `padding` y posicion, no medidas fijas. */
    select.fi{
      -webkit-appearance:none; -moz-appearance:none; appearance:none;
      padding-right:42px;
      background-image:var(--flecha-campo);
      background-repeat:no-repeat;
      background-position:right 16px center;
      background-size:12px 8px;
    }
    /* El calendario y el reloj tambien venian pegados: son nativos y no
       se pueden recolocar, pero si se les puede dar aire. */
    input.fi[type="date"]::-webkit-calendar-picker-indicator,
    input.fi[type="time"]::-webkit-calendar-picker-indicator,
    input.fi[type="datetime-local"]::-webkit-calendar-picker-indicator{
      margin-right:4px; opacity:.75;
    }
    input.fi[type="date"], input.fi[type="time"], input.fi[type="datetime-local"]{
      padding-right:12px;
    }
    input.fi::placeholder, textarea.fi::placeholder{ color:var(--text-3); opacity:1 }
    /* El calendarito y las flechas nativas siguen al tema. */
    input.fi[type="date"], input.fi[type="time"], input.fi[type="number"]{ color-scheme:inherit }

    /* ══════════ LA ENTRADA EN COMPUTADORA ══════════
       Dos columnas: a la izquierda la marca, una frase y los tres pasos;
       a la derecha el código. Es la forma que la gente ya reconoce de
       WhatsApp Web — no hay que enseñarles nada nuevo. */
    .login-card.ancha{ max-width:760px; }
    .pc-cols{ display:grid; grid-template-columns:1.15fr .85fr; gap:34px; align-items:center; }
    .pc-logo{ width:176px; margin:0 0 20px; display:block; }
    .pc-frase{ font-size:20px; font-weight:800; line-height:1.35; margin:0 0 18px;
      color:var(--blanco); letter-spacing:-.3px; }
    .pc-pasos{ margin:0; padding:0; list-style:none; counter-reset:paso; }
    .pc-pasos li{ counter-increment:paso; position:relative; padding:0 0 14px 38px;
      font-size:13.5px; line-height:1.5; color:rgba(248,250,252,.78); }
    .pc-pasos li:last-child{ padding-bottom:0 }
    .pc-pasos li::before{ content:counter(paso); position:absolute; left:0; top:-2px;
      width:26px; height:26px; border-radius:50%; display:grid; place-items:center;
      border:1.5px solid rgba(255,194,7,.55); color:var(--amar);
      font-size:12.5px; font-weight:800; }
    .pc-pasos li b{ color:var(--blanco); font-weight:700 }
    /* ── EN CUÁL VA ──

       Sin esto, quien escanea no sabe si la computadora se enteró, y es
       justo donde se perdió una persona del equipo.

       Pero la primera versión gritaba: discos sólidos amarillo y verde
       sobre una pantalla cuyo lenguaje es el contrario —aros finos
       ámbar sobre azul marino—, y el paso ya hecho quedaba con
       `opacity:.55`, que apaga el texto Y el aro a la vez hasta dejarlo
       casi ilegible sobre el navy. Marcar un avance no es encender una
       luz: es mover el peso. El que va se engrosa y se aclara; el hecho
       se apaga sin desaparecer. */
    .pc-pasos li.va{ color:var(--blanco) }
    .pc-pasos li.va::before{ background:rgba(255,194,7,.16); border-color:var(--amar);
                             color:var(--amar);
                             box-shadow:0 0 0 4px rgba(255,194,7,.10) }
    /* El hecho NO lleva `opacity` en el <li>: eso apagaba también el aro.
       Se baja el color del texto, y nada más. */
    .pc-pasos li.hecho{ color:rgba(248,250,252,.45) }
    .pc-pasos li.hecho b{ color:rgba(248,250,252,.58) }
    .pc-pasos li.hecho::before{ content:"\2713"; background:transparent;
                                border-color:rgba(248,250,252,.26);
                                color:rgba(248,250,252,.52); font-size:13px }
    .pc-qr{ min-width:0 }

    /* El número de la confirmación: grande, porque hay que leerlo de
       lejos mientras se tiene el teléfono en la mano. */
    .num-grande{ font-size:58px; font-weight:800; text-align:center; letter-spacing:.06em;
      color:var(--amar); margin:14px 0 6px; font-variant-numeric:tabular-nums; }
    .nums-fila{ display:flex; gap:10px; }
    .nums-fila button{ flex:1; min-height:60px; font-size:24px; font-weight:800;
      font-variant-numeric:tabular-nums; border-radius:var(--r-md);
      border:1.5px solid rgba(248,250,252,.22); background:rgba(248,250,252,.06);
      color:var(--blanco); font-family:inherit; cursor:pointer; }
    .nums-fila button:hover{ border-color:var(--amar) }

    @media (max-width:760px){
      .login-card.ancha{ max-width:none }
      .pc-cols{ grid-template-columns:1fr; gap:22px }
      .pc-frase{ font-size:17px }
    }

    /* La barra superior: el botón de salir debe leerse como botón */
    .topbar .btn.ghost{ background:var(--superficie); border:0.5px solid var(--border);
      color:var(--text); font-weight:600; }
        /* ═══ Corrección · tarjeta flotante y una sola salida ═══
       .modal se había quedado en position:relative por una regla escrita
       más abajo, y eso la sacaba de flotar: caía dentro de la página y el
       botón Guardar quedaba fuera de vista. Aquí recupera position:fixed.
       Y se oculta el botón "Cancelar": comparte identificador con un icono
       de 40x40, por eso salía gigante y desbordado. La salida queda en la
       X que app.js agrega a todos los formularios. */
    .modal{ position:fixed; }
    .modal>.box{ position:relative; }
    .modal>.box #modal-close{ display:none; }


    /* ══════ VERIFICACIÓN FACIAL ══════
       La MISMA pantalla que ve el guardia al entrar a su turno.
       Copiada de apps/guardia/index.html — si cambia allá, cambia
       aquí. Quien prueba su rostro al darse de alta debe reconocer
       la pantalla el día que la use de verdad, a las seis de la
       mañana y con prisa. */
    body.fui-abierto{ overflow:hidden; }
    /* El fondo navy es solo el respaldo mientras la cámara abre: una vez
       que hay video, el video es el fondo. Por eso el orden de capas
       importa — sin z-index explícito el video quedaba tapado. */
    .fui{ position:fixed; inset:0; z-index:999; background:#021631;
      display:flex; flex-direction:column; }

    /* Rayas diagonales de fondo, del diseño */
    /* Las rayas van DEBAJO del video: son textura de fondo mientras la
       cámara abre, no una capa sobre la imagen. */
    .fui-rayas{ position:absolute; inset:0; z-index:0; opacity:.5;
      pointer-events:none;
      background:repeating-linear-gradient(115deg,
        rgba(248,250,252,.028) 0 1px, transparent 1px 9px); }

    /* El video, sobre las rayas y bajo todo lo demás */
    /* CON espejo, a propósito: sin él, mover la mano derecha la muestra
       del lado izquierdo (como si alguien te viera de frente) y uno pelea
       con el encuadre. El espejo es solo visual — el canvas captura los
       bytes originales, así que la foto guardada y el vector facial no
       se ven afectados. */
    /* contain, no cover: cover recorta para llenar la pantalla y en un
       teléfono alto eso se siente como zoom — hay que echarse para atrás
       para caber. Con contain se ve el encuadre completo de la cámara. */
    .fui-video{ position:absolute; inset:0; z-index:1; width:100%; height:100%;
      /* ── NI FRANJAS NI ACERCAMIENTO ──
         `contain` dejaba franjas negras; `cover` recortaba casi la mitad
         del ancho en un telefono y la cara salia encimada. Las dos
         estaban mal porque el problema no era el ajuste: era el FONDO.
         La imagen buena va completa (`contain`) y detras va ella misma
         desenfocada llenando el resto. Es como resuelven esto Netflix,
         YouTube o Instagram. */
      object-fit:contain; background:transparent; }

    /* El fondo: la misma imagen, desenfocada y oscurecida. La escala de
       mas evita que el desenfoque deje los bordes transparentes. */
    .fui-fondo{ position:absolute; inset:0; z-index:0; width:100%; height:100%;
      object-fit:cover; transform:scale(1.18);
      filter:blur(26px) brightness(.42) saturate(1.15);
      opacity:0; transition:opacity .6s ease; pointer-events:none; }
    .fui[data-video="listo"] .fui-fondo{ opacity:1; }
    /* Velo navy sobre el video: el rostro se ve, pero el marco resalta */
    /* Sin velo: se ve la cámara limpia, como la app de cámara del
       teléfono. El degradado oscurecía los bordes y se veía sucio. */
    .fui-vel{ display:none; }

    /* ── CUATRO VARIABLES QUE NO EXISTÍAN EN EL PANEL · 27-oct ──

       Este bloque se copió de la app del guardia, donde `--lyk-yellow`,
       `--lyk-navy`, `--radius-full` y `--radius-lg` SÍ están definidas
       (`apps/guardia/orvik-guardia.css` y su `index.html`). En el panel
       no existen, y una variable que no existe no falla: desaparece
       --trampa A.1 del doc 65--.

       No era cosmético. Un `stroke` que apunta a una variable sin valor deja el
       trazo en su inicial, que es `none`: **el anillo de progreso del
       rostro no se dibujaba** en el panel. Y los dos radios caían a 0.

       El cambio es a su equivalente exacto, tomado del propio mapeo de
       la app del guardia y del catálogo de tokens:

         --lyk-yellow  → --marca-principal   (la guardia lo mapea así)
         --lyk-navy    → --marca-banda       (#070F1E, su mismo respaldo)
         --radius-full → --r-pildora         (999)
         --radius-lg   → --r-control-g       (14, el mismo número)

       Ninguno es una elección mía: los cuatro tenían equivalente. */
    .fui-top{ position:relative; z-index:3; display:flex; align-items:center;
      justify-content:space-between; gap:12px;
      padding:calc(env(safe-area-inset-top) + 16px) 20px 0; }
    .fui-h{ font-size:13px; font-weight:800; letter-spacing:1.2px;
      text-transform:uppercase; color:var(--marca-principal); }
    .fui-x{ width:46px; height:46px; border:0; background:rgba(248,250,252,.12);
      color:#F8FAFC; font-size:26px; line-height:1; border-radius:50%;
      cursor:pointer; display:grid; place-items:center; }

    .fui-svg{ position:absolute; left:0; right:0; top:0; bottom:0;
      width:100%; height:100%; z-index:2; pointer-events:none; }
    /* El óvalo ocupaba casi toda la pantalla y obligaba a pegarse mucho.
       Más chico, el rostro entra completo sin acercarse tanto. */

    /* Esquinas en diagonal: solo dos, arriba-izq y abajo-der */
    .fui-esq{ fill:none; stroke:#F8FAFC; stroke-width:3; stroke-linecap:round;
      opacity:.92; }

    /* Óvalo exterior: punteado grueso */
    .fui-ov-ext{ fill:none; stroke:rgba(248,250,252,.95); stroke-width:3.4;
      transform:none;
      stroke-dasharray:16 10; transition:stroke .3s, opacity .3s;
      filter:drop-shadow(0 0 6px rgba(255,255,255,.28)); }
    /* Óvalo interior: punteado fino */
    .fui-ov-int{ fill:none; stroke:rgba(248,250,252,.5); stroke-width:1.8;
      transform:none;
      stroke-dasharray:5 7; transition:stroke .3s, opacity .3s; }

    .fui-anillo{ fill:none; stroke:var(--marca-principal); stroke-width:6;
      /* Ninguna rotación. Un óvalo alto girado 90° se convierte en
         uno ancho y forma una cruz sobre el rostro. */
      transform:none;
      stroke-linecap:round; opacity:0;
      filter:drop-shadow(0 0 10px rgba(255,194,7,.55));
      transition:stroke-dashoffset .25s linear, stroke .3s, opacity .3s; }

    .fui-abajo{ position:relative; z-index:3; margin-top:auto;
      padding:0 20px calc(env(safe-area-inset-bottom) + 22px);
      display:flex; flex-direction:column; gap:12px; align-items:center; }

    .fui-msg{ width:100%; max-width:340px; text-align:center;
      background:rgba(4,30,66,.9); backdrop-filter:blur(12px);
      -webkit-backdrop-filter:blur(12px);
      border:1.5px solid rgba(255,194,7,.3); border-radius:var(--r-control-g);
      padding:15px 20px; transition:border-color .3s, background .3s; }
    .fui-msg .fui-t{ display:block; font-size:20px; font-weight:800;
      color:#F8FAFC; line-height:1.25; }
    .fui-msg .fui-s{ display:block; font-size:14px; font-weight:600;
      color:rgba(248,250,252,.76); margin-top:5px; }

    /* Botón blanco del diseño */
    .fui-btn{ width:100%; max-width:340px; min-height:56px; border:0;
      border-radius:var(--r-pildora); background:#F8FAFC; color:var(--marca-banda);
      font-family:inherit; font-size:17px; font-weight:800; cursor:pointer; }
    .fui-btn:active{ transform:translateY(1px); }
    .fui-btn.oculto{ visibility:hidden; }
    /* En el panel la prueba arranca sola y NUNCA hay boton que tocar.
       `visibility:hidden` guarda el lugar —que es lo correcto en la app
       del guardia, donde el boton reaparece— pero aqui dejaba un hueco
       permanente debajo del mensaje. Se colapsa solo de este lado; el
       JS sigue siendo el mismo archivo. */
    .fui-btn.oculto{ display:none; }
    .fui-alt{ border:0; background:transparent; color:rgba(248,250,252,.62);
      font-family:inherit; font-size:13.5px; font-weight:600; min-height:46px;
      cursor:pointer; text-decoration:underline; text-underline-offset:3px; }

    /* ── buscando ── */
    .fui[data-estado="buscando"] .fui-ov-ext{ animation:fui-latir 2.2s ease-in-out infinite; }
    @keyframes fui-latir{ 0%,100%{opacity:.55} 50%{opacity:1} }

    /* ── ajustando ── */
    .fui[data-estado="ajustando"] .fui-ov-ext{ stroke-dasharray:none;
      stroke:var(--marca-principal); opacity:1; animation:none; }
    .fui[data-estado="ajustando"] .fui-ov-int{ stroke:rgba(255,194,7,.35); }

    /* ── analizando ── */
    .fui[data-estado="analizando"] .fui-ov-ext{ stroke-dasharray:none;
      stroke:rgba(248,250,252,.22); opacity:1; animation:none; }
    .fui[data-estado="analizando"] .fui-anillo{ opacity:1; }

    /* ── ok ── */
    .fui[data-estado="ok"] .fui-ov-ext{ stroke-dasharray:none;
      stroke:#16C47F; opacity:1; animation:none; }
    .fui[data-estado="ok"] .fui-ov-int{ stroke:rgba(22,196,127,.4); }
    .fui[data-estado="ok"] .fui-anillo{ opacity:1; stroke:#16C47F;
      filter:drop-shadow(0 0 14px rgba(22,196,127,.7)); }
    .fui[data-estado="ok"] .fui-msg{ background:rgba(11,74,54,.92);
      border-color:rgba(22,196,127,.6); }
    .fui[data-estado="ok"] .fui-svg{ animation:fui-pulso .5s ease-out; }
    @keyframes fui-pulso{ 0%{transform:scale(1)} 45%{transform:scale(1.03)} 100%{transform:scale(1)} }

    /* ── falla ── coral, no rojo crítico ── */
    .fui[data-estado="falla"] .fui-ov-ext{ stroke-dasharray:none;
      stroke:#FF6B6B; opacity:1; animation:none; }
    .fui[data-estado="falla"] .fui-ov-int{ stroke:rgba(255,107,107,.35); }
    .fui[data-estado="falla"] .fui-anillo{ opacity:1; stroke:#FF6B6B; }
    .fui[data-estado="falla"] .fui-msg{ background:rgba(74,20,22,.92);
      border-color:rgba(255,107,107,.55); }
    .fui[data-estado="falla"] .fui-svg{ animation:fui-negar .45s ease-in-out; }
    @keyframes fui-negar{ 0%,100%{transform:translateX(0)}
      25%{transform:translateX(-8px)} 75%{transform:translateX(8px)} }

    @media (max-height:700px){
      /* Pantalla corta (iPhone SE, iPhone 8): el óvalo llegaba hasta el mensaje.
         Se sube el marco y se compacta el pie. */
      /* En 568px de alto el óvalo no cabe completo sobre el mensaje:
         se escala en vez de solo subirlo, o queda cortado arriba. */
      .fui-svg{ bottom:36%; transform:scale(.82); transform-origin:center 42%; }
      .fui-msg{ padding:11px 14px; }
      .fui-msg .fui-t{ font-size:16px; }
      .fui-msg .fui-s{ font-size:12.5px; }
      .fui-btn{ min-height:48px; font-size:15px; }
      .fui-abajo{ gap:9px; padding-bottom:calc(env(safe-area-inset-bottom) + 14px); }
      .fui-alt{ min-height:44px; font-size:12.5px; }
    }

    /* ══════ RITMO ══════
       Una verificación que termina en 300 ms no se siente confiable: se
       siente como si no hubiera revisado nada. Y una capa que aparece de
       golpe sobre negro parece un error, no una pantalla.

       Esto es lo que hace cualquier casa seria —Face ID, la alta digital
       de un banco—: fondo de marca desde el primer milisegundo, el video
       entra con fundido cuando de verdad hay imagen, y cada fase dura lo
       suficiente para leerse. Los tiempos minimos viven en lector.js. */
    @keyframes fui-entra{ from{ opacity:0 } to{ opacity:1 } }
    @keyframes fui-sale { from{ opacity:1 } to{ opacity:0 } }
    .fui{ animation:fui-entra .26s ease-out both; }
    .fui.fui-sale{ animation:fui-sale .24s ease-in both; pointer-events:none; }

    /* El video no existe hasta que hay un cuadro de verdad. Mientras,
       se ve el fondo con sus rayas: eso es una pantalla esperando, no
       una pantalla rota. */
    .fui-video{ opacity:0; transition:opacity .5s ease; }
    .fui[data-video="listo"] .fui-video{ opacity:1; }

    /* Mientras enciende, el ovalo respira. Quieto parece congelado. */
    @keyframes fui-respira{ 0%,100%{ opacity:.45 } 50%{ opacity:.9 } }
    .fui:not([data-video="listo"]) .fui-ov-ext,
    .fui:not([data-video="listo"]) .fui-ov-int{ animation:fui-respira 1.6s ease-in-out infinite; }

    /* El anillo se llena, no salta. */
    .fui-anillo{ transition:stroke-dashoffset .4s linear; }
    /* =================================================================
       LAS GUÍAS DEL SISTEMA
       =================================================================
       Reescrito el 27-oct contra el lienzo aprobado
       `docs/mockups-orvik/guias-del-sistema-aprobado.html`.

       QUÉ ESTABA MAL, Y NO ERA SÓLO EL VIDRIO
       ---------------------------------------
       · la ventana salía en vidrio, que el `02` §A.3 prohíbe en
         tarjetas, y en oscuro se volvía ilegible;
       · el velo y la sombra estaban escritos a mano --`rgba(4,30,66,.66)`,
         que es el navy de Lykos--: color de marca en el código, que el
         anexo prohíbe, y que en tema claro teñía de azul la pantalla;
       · las letras iban en 10.5, 11, 13, 13.5, 14.5 y 16.5 px, con
         pesos de 800. El `08` §2 tiene cuatro tamaños y ninguno es
         ésos;
       · y nada contenía la lista, así que los renglones se salían de la
         tarjeta.

       Ahora: sólo tokens, cuatro tamaños, y la LISTA es la que se
       desplaza --`min-height:0` y `overflow-y:auto`, con cada renglón
       en `flex:none`-- para que nada se salga del borde.

       El recorte sigue siendo una sombra ENORME sobre un elemento
       chico: lo que se ve por el hueco es la página tal cual, sin
       copiarla. Clonar el botón para «resaltarlo» es lo que hace que
       los tours se vean falsos.
       ================================================================= */
    #tut-capa{position:fixed;inset:0;z-index:9000;pointer-events:none}
    /* El velo se DERIVA del fondo del tema, como el de los diálogos y
       el del menú lateral (`consola-orvik.css`). Así acompaña al tema
       solo y no hay un color de marca escrito a mano. */
    #tut-capa{--tut-velo:color-mix(in srgb, var(--fondo) 62%, transparent)}
    #tut-velo{position:absolute;inset:0;background:var(--tut-velo)}
    #tut-hueco{position:absolute;border-radius:var(--r-control);pointer-events:none;
      box-shadow:0 0 0 9999px var(--tut-velo);
      outline:2px solid var(--marca-principal);outline-offset:2px;
      transition:top .22s ease,left .22s ease,width .22s ease,height .22s ease}

    /* ── LA TARJETA ── */
    #tut-card{position:absolute;pointer-events:auto;z-index:9001;
      width:min(380px,calc(100vw - 32px));
      max-height:min(86vh,640px);
      display:flex;flex-direction:column;overflow:hidden;
      background:var(--superficie);color:var(--texto);
      border:1px solid var(--linea);border-radius:var(--r-contenedor);
      box-shadow:var(--sombra-3);
      padding:var(--e-5)}
    /* La ventana del catálogo es más ancha y no lleva relleno propio:
       su cabecera, su lista y su pie traen el suyo, porque la lista
       tiene que llegar al borde para que el desplazamiento se vea. */
    #tut-card.ancha{width:560px;max-width:calc(100vw - 32px);padding:0}
    #tut-card.centrada{top:50%;left:50%;transform:translate(-50%,-50%);
      right:auto;bottom:auto}

    /* ── LA CABECERA DEL CATÁLOGO ── */
    .tut-cab{display:flex;align-items:flex-start;gap:var(--e-3);
      padding:var(--e-6) var(--e-6) var(--e-4);flex:none}
    .tut-cab .tx{flex:1;min-width:0}
    /* ── SIN LA RAYITA DE ACENTO ──
       `panel.css` le pone a todo `h2` una barrita de 3 px por delante,
       y la consola se la quita dentro de `#app`. La ventana de guías
       vive FUERA de `#app` --su capa cuelga de `body`, para poder
       flotar sobre cualquier pantalla-- así que esa regla no la
       alcanzaba y la rayita salía. El lienzo no la tiene. */
    .tut-cab h2{margin:0;font:var(--t-seccion)}
    .tut-cab h2::before{content:none}
    .tut-cab p{margin:var(--e-1) 0 0;font:var(--t-sec);color:var(--texto-2)}
    .tut-cerrar{width:var(--toque);height:var(--toque);border-radius:var(--r-pildora);
      border:1px solid var(--linea);background:var(--control);color:var(--texto-2);
      display:inline-flex;align-items:center;justify-content:center;
      font-size:20px;flex:none;cursor:pointer}

    /* ── LA LISTA, QUE ES LA QUE SE DESPLAZA ── */
    .tut-lista{flex:1;min-height:0;overflow-y:auto;
      padding:0 var(--e-4) var(--e-4);display:flex;flex-direction:column;gap:var(--e-2)}
    .tut-grupo{font:var(--t-sec);color:var(--texto-3);padding:var(--e-3) var(--e-2) var(--e-1);flex:none}
    .tut-guia{flex:none;display:grid;grid-template-columns:40px minmax(0,1fr) auto;
      gap:var(--e-1) var(--e-3);align-items:center;
      padding:var(--e-3) var(--e-4);min-height:64px;width:100%;text-align:left;
      border:1px solid var(--linea);border-radius:var(--r-tarjeta);
      background:var(--superficie);font:inherit;color:inherit;cursor:pointer}
    .tut-guia:hover{background:var(--superficie-2)}
    .tut-guia .ic{width:40px;height:40px;border-radius:var(--r-control);
      background:var(--marca-suave);color:var(--marca-principal-texto);
      display:flex;align-items:center;justify-content:center;font-size:20px;grid-row:span 2}
    .tut-guia b{font:var(--t-cuerpo-f);min-width:0}
    .tut-guia small{font:var(--t-sec);color:var(--texto-2);grid-column:2;min-width:0}
    .tut-guia .der{grid-column:3;grid-row:1 / span 2;display:flex;align-items:center;gap:var(--e-2)}
    .tut-guia .der i{color:var(--texto-3);font-size:20px}
    .tut-estado{display:inline-flex;align-items:center;gap:6px;height:28px;
      padding:0 var(--e-3);border-radius:var(--r-pildora);
      font:var(--t-sec);font-weight:600;white-space:nowrap}
    .tut-e-va{background:var(--marca-suave);color:var(--marca-principal-texto)}
    .tut-e-ok{background:var(--exito-suave);color:var(--exito-texto)}
    .tut-e-nueva{background:var(--proceso-suave);color:var(--proceso-texto)}
    /* «Empezar de nuevo» va DENTRO del renglón, en su segunda línea: en
       un cuadro aparte se salía de la tarjeta. */
    .tut-guia .linea2{grid-column:2 / span 2;display:flex;gap:var(--e-3);
      align-items:center;margin-top:var(--e-1)}
    .tut-denuevo{font:var(--t-sec);font-weight:600;color:var(--marca-principal-texto);
      min-height:var(--toque);display:inline-flex;align-items:center;gap:6px;
      padding:0 var(--e-2);margin-left:calc(var(--e-2) * -1);
      border-radius:var(--r-control);background:none;border:0;cursor:pointer}
    .tut-pie{border-top:1px solid var(--linea);padding:var(--e-3) var(--e-6);
      font:var(--t-sec);color:var(--texto-3);flex:none}

    /* ── LA TARJETA DE UN PASO ── */
    .tut-cuenta{font:var(--t-sec);color:var(--texto-3)}
    .tut-t{font:var(--t-tarjeta);margin:0}
    .tut-d{font:var(--t-cuerpo);color:var(--texto-2);margin:var(--e-2) 0 0}
    .tut-d b{color:var(--texto);font-weight:600}
    .tut-barra{height:4px;border-radius:var(--r-pildora);background:var(--pista);
      overflow:hidden;margin-top:var(--e-3)}
    .tut-barra i{display:block;height:100%;background:var(--marca-principal);border-radius:inherit}
    .tut-espera{margin-top:var(--e-3);font:var(--t-sec);
      color:var(--marca-principal-texto);display:flex;align-items:center;gap:6px}
    /* Cuando el blanco no aparece, la tarjeta lo DICE. Va en neutro, no
       en rojo: que un paso no encuentre su botón no es una alerta del
       sistema. */
    .tut-sinblanco{margin-top:var(--e-3);padding:var(--e-3);
      border-radius:var(--r-control);background:var(--superficie-2);
      font:var(--t-sec);color:var(--texto-2)}
    .tut-b{display:flex;align-items:center;gap:var(--e-2);margin-top:var(--e-4);flex:none}
    .tut-b button{min-height:var(--toque);padding:0 var(--e-4);
      border-radius:var(--r-control);font:var(--t-sec);font-weight:600;
      border:1px solid var(--linea);background:var(--control);color:var(--texto-2);
      cursor:pointer;white-space:nowrap}
    .tut-x{margin-right:auto;border-color:transparent!important;
      background:none!important;color:var(--texto-3)!important}
    .tut-ok{background:var(--marca-principal)!important;
      color:var(--marca-sobre-principal)!important;border-color:transparent!important}

    html.tut-abierto{overflow-x:hidden}

    /* ── CELULAR · HOJA INFERIOR SÓLIDA ──
       El corte es 599, el mismo de la navegación: antes era 520 y había
       un tramo --de 521 a 599-- donde el menú ya era de celular y la
       guía seguía creyéndose de escritorio. Dirección pidió el 26-oct
       que la hoja NO lleve vidrio. */
    @media(max-width:599px){
      #tut-card,#tut-card.ancha,#tut-card.centrada{
        top:auto!important;bottom:0!important;left:0!important;right:0!important;
        transform:none!important;width:auto!important;max-width:none;
        max-height:86vh;
        border-radius:28px 28px 0 0;border-bottom:0;
        padding:var(--e-4) var(--e-4) var(--e-6)}
      #tut-card.ancha{padding:0 0 var(--e-2)}
      #tut-card::before{content:"";display:block;width:40px;height:5px;
        border-radius:var(--r-pildora);background:var(--linea-fuerte);
        margin:0 auto var(--e-3);flex:none}
      .tut-cab{padding:var(--e-4) var(--e-4) var(--e-3)}
      .tut-lista{padding:0 var(--e-4) var(--e-6)}
      .tut-b button{flex:1 1 0}
      .tut-x{flex:0 1 auto}
    }

    /* El punto de aviso: sale sólo mientras no haya abierto la guía.
       Vive en el pie del menú, junto a Tema — la barra de arriba está
       oculta en escritorio, así que ahí el botón era invisible. */
    #nav-pie [data-guia]{position:relative}
    #guia-punto{position:absolute;top:8px;left:26px;width:7px;height:7px;
      border-radius:50%;background:var(--amar);
      box-shadow:0 0 0 2px var(--nav-bg)}
    body.nav-plegada #guia-punto{left:auto;right:9px;top:7px}
    /* ── AQUÍ VIVÍA EL CATÁLOGO VIEJO DE GUÍAS ──

       Se retira el 27-oct. Era el SEGUNDO bloque de estilos del
       tutorial en este archivo, más abajo que el otro, así que ganaba
       por orden: `.tut-lista{display:flex}` pisaba la rejilla nueva y
       la pastilla de estado se montaba encima de la descripción.

       Lo encontré al mirar la captura, no al leer el código: el banco
       decía «listo» --no se salía nada-- y la ventana se veía mal de
       todos modos. Una prueba que mide el ancho no mide si está bien
       puesto.

       Llevaba además lo que el lienzo prohíbe: letras de 9.5, 11.5 y
       13.5 px, pesos de 800, MAYÚSCULAS y el amarillo de Lykos escrito
       a mano. El estilo bueno está arriba, con el resto del tutorial.

       `.mas-grupo` NO era del tutorial --son los encabezados del menú
       de móvil-- y se queda. */

    /* Los encabezados del menú de móvil. Mismo peso que en la barra
       lateral: quien conoce una conoce la otra. */
    .mas-grupo{padding:14px 14px 5px;font:var(--t-sec);color:var(--texto-3)}
    .mas-grupo:first-child{padding-top:4px}
  
    /* ── LAS GRÁFICAS DEL INSTRUMENTO, DENTRO DEL INFORME ──
       Las dibuja el motor de KRYPTOS (svgUmbrales, svgRadar y las
       demás) y traen sus propias clases desde kryptos-motor.css, que
       esta página ya carga. Aquí sólo se les da caja y aire, y se
       fuerza que el SVG se achique en pantallas angostas: nacieron
       para una hoja carta y el informe se lee también en un teléfono. */
    .inf-graf { margin: 10px 0 0; }
    .inf-graf svg { width: 100%; height: auto; max-width: 100%; display: block; }
    .inf-graf table { width: 100%; border-collapse: collapse; }

    /* ── LAS TABLAS DE PARAMETROS DEL INFORME ──

       `krTablaAjustes` y `krTablaDimensiones` emiten `<table class="t">`, y
       todo el CSS de esa clase en `kryptos-motor.css` esta limitado a
       `#kryptos-engine` y `#kr-overlays`. El informe del panel se dibuja en
       `#inf-cuerpo`, que no es ninguno de los dos: sin estas reglas las dos
       tablas salen sin bordes, sin encabezado y en letra diminuta.

       Las medidas no son las de la isla a proposito: alla son puntos para
       una hoja carta (7.8pt) y aqui se lee en pantalla, a veces en un
       telefono. Los colores salen de los tokens que `#inf-cuerpo` ya fija
       en claro mas arriba, asi que no hay cifra de color repetida aqui. */
    #inf-cuerpo table.t {
      width:100%; border-collapse:collapse; font-size:12.5px; margin-top:2px;
    }
    #inf-cuerpo table.t th {
      background:var(--navy); color:#fff; padding:7px 8px;
      font-size:10.5px; text-transform:uppercase; letter-spacing:.3px;
      text-align:center; font-weight:700;
    }
    #inf-cuerpo table.t td {
      padding:6px 8px; border-bottom:1px solid var(--border); text-align:center;
    }
    #inf-cuerpo table.t .lft { text-align:left; }
    #inf-cuerpo table.t .der { text-align:right; }
    #inf-cuerpo table.t tr.tot { background:var(--superficie-3); font-weight:700; }
    #inf-cuerpo table.t .ref { font-size:11px; color:var(--text-2); }
    /* En telefono la columna de referencia es lo primero que se sacrifica:
       es contexto academico, no un dato que se consulte. */
    @media (max-width:560px){
      #inf-cuerpo table.t { font-size:11.5px; }
      #inf-cuerpo table.t th { font-size:9.5px; padding:6px 5px; }
      #inf-cuerpo table.t td { padding:5px 5px; }
      #inf-cuerpo table.t .ref,
      #inf-cuerpo table.t th:last-child { display:none; }
    }
    /* El informe usa `--rojo` para el chip de la videollamada sin
       ubicación; el panel no la tenía definida y `var()` sin valor no
       falla: desaparece. */
    :root { --rojo: #b3261e; }
  
    /* ═══ EL BORRADOR DEL CONTRATO (contratos.js) ═══
       El documento se lee como un documento, no como una pantalla: una
       sola columna angosta, texto justificado y la tipografia del
       sistema. No se imita una hoja de Word --no es un simulacro de
       impresion-- pero si se respeta que lo que se esta leyendo es un
       contrato. */
    .ct-cab{display:flex;align-items:center;justify-content:space-between;gap:14px;
      padding:12px 14px;border:0.5px solid var(--border);border-radius:12px;
      background:var(--superficie-2,var(--superficie));margin-bottom:4px}
    .ct-cab > div:first-child{display:flex;flex-direction:column;gap:2px;min-width:0}
    .ct-cab b{font-size:15px;font-weight:700;color:var(--text)}
    .ct-cab span{font-size:11.5px;color:var(--text-2)}
    .ct-cuenta{text-align:right;flex:0 0 auto}
    .ct-cuenta b{display:block;font-size:26px;font-weight:800;line-height:1.1;
      color:var(--danger)}
    .ct-cuenta b:empty, .ct-cuenta b{font-variant-numeric:tabular-nums}

    .ct-doc{max-height:58vh;overflow:auto;padding:22px 26px;margin:10px 0;
      border:0.5px solid var(--border);border-radius:12px;
      background:var(--superficie);color:var(--text);
      font-size:13px;line-height:1.65;text-align:justify;hyphens:auto}
    .ct-doc p{margin:0 0 10px}
    .ct-title{font-size:15px;font-weight:800;text-align:center;letter-spacing:.02em;
      margin:0 0 16px;line-height:1.35}
    .ct-sub{font-size:13px;font-weight:700;text-align:center;margin:14px 0 8px}
    .ct-h{font-size:13.5px;font-weight:700;margin:18px 0 8px;
      padding-bottom:5px;border-bottom:0.5px solid var(--border)}
    .ct-center{text-align:center;font-weight:700;margin:16px 0 10px;letter-spacing:.04em}
    .ct-clause{margin:0 0 10px}
    .ct-pb{font-weight:600;margin:12px 0 8px}
    .ct-lista{margin:0 0 10px;padding-left:20px}
    .ct-lista li{margin:0 0 5px}
    .ct-anexo{margin-top:24px;padding-top:4px;border-top:1.5px solid var(--border)}
    .ct-firma{margin:18px 0 6px;text-align:center;font-size:12px;color:var(--text-2);
      white-space:pre-line}
    .ct-campos{margin:0 0 10px;padding:10px 12px;border:0.5px dashed var(--border);
      border-radius:8px;font-size:12px;color:var(--text-2);white-space:pre-line}

    /* Un hueco sin dato NO se borra ni se deja la llave cruda: se marca.
       El borrador existe justo para que el agujero se vea antes de que
       alguien mande el documento a firmar. */
    .ct-hueco{background:var(--danger-suave,rgba(220,38,38,.12));color:var(--danger);
      font-weight:700;padding:0 4px;border-radius:4px;white-space:nowrap}
    /* Un bloque que el generador no supo dibujar tampoco desaparece
       callado: eso fue lo que dejo al motor de nomina sin ver un solo
       dia trabajado. */
    .ct-raro{border-left:3px solid var(--amar);padding-left:10px;
      background:rgba(255,194,7,.07)}
    .ct-pendiente{margin:0 0 10px;padding:10px 12px;border-radius:8px;
      border:0.5px dashed var(--border);background:rgba(127,127,127,.06);
      font-size:12px;color:var(--text-2);text-align:left}

    @media (max-width:760px){
      .ct-doc{max-height:52vh;padding:16px 15px;font-size:12.5px;text-align:left}
      .ct-cab{flex-direction:column;align-items:flex-start}
      .ct-cuenta{text-align:left}
    }

/* ═══════════════════════════════════════════════════════════════
   POSTULACIONES DE LA WEB

   Prefijo `po-` en todo. No es manía: en la web de candidatos usé
   `.pf` para mis campos y resultó que ya era de las tarjetas de
   perfil del sitio — los campos heredaron su borde y el texto de
   ayuda se veía como un recuadro vacío. Un prefijo propio cuesta
   tres letras y evita exactamente eso.

   Los colores salen de las variables del panel, así que el modo
   oscuro funciona sin escribir una sola regla más: cuando el tema
   cambia, estas tarjetas cambian con él.
   ═══════════════════════════════════════════════════════════════ */
.po-filtros { display: flex; gap: 8px; flex-wrap: wrap; }

.po-card {
  background: var(--superficie);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px 18px;
  margin-bottom: 12px;
}
/* Quien lleva más de un día esperando se marca por el costado, no con
   un fondo de color: el fondo grita y en una lista larga todo grita. */
.po-fria { border-left: 3px solid var(--warn); }

.po-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 10px;
}
.po-nombre { font-size: 15.5px; font-weight: 700; color: var(--text); }
.po-folio { display: block; font-size: 12px; color: var(--text-3); margin-top: 2px; }

.po-estado {
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  padding: 4px 10px; border-radius: var(--r-full); white-space: nowrap;
}
/* El texto va con `--amar-dark`, que cambia con el tema. Puesto a
   mano (--navy) daba 1.12:1 de contraste en modo oscuro —texto azul
   marino sobre marrón oscuro—: la etiqueta desaparecía. El fondo se
   invierte con el tema y el color fijo no; tienen que moverse los dos. */
.po-nueva      { background: var(--amar-suave); color: var(--amar-dark); }
.po-contactada { background: var(--superficie-2); color: var(--text-2); }
/* Fondo con una variable, no con un rgba() a mano: el translúcido
   daba 3.60:1 en claro. Las variables ya están medidas para los dos
   temas; un color inventado hay que medirlo cada vez. */
/* El verde se fue del TEXTO a un punto. Como texto no pasaba de
   3.98:1 en tema claro por más vueltas que le di: un verde lo
   bastante vivo para leerse como "listo" es, por definición, un verde
   claro, y un verde claro sobre fondo claro no se lee. El punto da la
   señal de color y el texto se queda con un gris que sí contrasta. */
.po-invitada   { background: var(--superficie-2); color: var(--text-2);
                 display: inline-flex; align-items: center; gap: 6px; }
.po-invitada::before {
  content: ""; width: 7px; height: 7px; border-radius: var(--r-full);
  background: var(--ok); flex: none;
}
/* `--text-3` daba 2.14:1 en tema claro. Una etiqueta apagada sigue
   teniendo que leerse: apagada no es invisible. Con `--text-2` son
   4.79 en claro y 6.81 en oscuro. */
.po-descartada { background: var(--superficie-3); color: var(--text-2); }

.po-datos {
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  font-size: 13.5px; color: var(--text-2); margin-bottom: 12px;
}
.po-datos i { color: var(--text-3); margin-right: 5px; }

.po-aviso {
  font-size: 13px; color: var(--text-2); background: var(--superficie-2);
  border-radius: var(--r-md); padding: 8px 12px; margin-bottom: 12px;
}
.po-aviso i { color: var(--warn); margin-right: 6px; }

.po-motivo {
  font-size: 13px; color: var(--text-3); font-style: italic;
  margin-bottom: 12px;
}

.po-acts { display: flex; flex-wrap: wrap; gap: 8px; }

.po-vacio {
  text-align: center; padding: 48px 20px; color: var(--text-3);
}
.po-vacio i { font-size: 38px; display: block; margin-bottom: 10px; opacity: .6; }
.po-vacio p { margin: 0; font-size: 14px; }

/* En teléfono el estado se va debajo del nombre: en una columna de
   340px, nombre y etiqueta lado a lado parten el nombre en tres. */
@media (max-width: 560px) {
  .po-top { flex-direction: column; gap: 6px; }
  .po-acts .btn { flex: 1 1 auto; justify-content: center; }
}

/* ── EL BOTÓN QUE ACABA DE COPIAR ──
   Copiaba bien y no se notaba: el aviso salía en una caja de mensajes
   que está en otra parte de la ventana, y el ojo está en el botón que
   se acaba de apretar. Dos segundos de verde y vuelve solo.
   El color sale de las variables, así que el modo oscuro va incluido. */
.btn.copiado,
.copiado {
  background: var(--ok) !important;
  border-color: var(--ok) !important;
  color: var(--sobre-amar) !important;
}
.copiado i { color: inherit !important; }

/* ═══════════════════════════════════════════════════════════════════
   QUE CADA CLIC SE SIENTA · regla única para todos los botones


   La auditoría del 3/oct le dio la razón con números: 141 selectores de
   botón en los cuatro archivos del sistema y solo 7 tenían `:active`.

   Y el detalle que lo explica: casi todos tenían `:hover`, que en una
   pantalla táctil NO EXISTE. En computadora el botón se iluminaba al
   pasar el mouse y daba la sensación de estar vivo; en el teléfono no
   pasaba absolutamente nada. El mismo botón, dos experiencias
   distintas, y la peor era la del que anda en campo.

   ── POR QUÉ UNA SOLA REGLA Y NO 141 PARCHES ──
   Porque un botón que se agregue mañana nace con esto puesto. Arreglar
   141 selectores a mano garantiza que el 142 salga muerto otra vez.

   ── QUÉ HACE, EN ORDEN DE IMPORTANCIA ──
   1. `:active` — el botón se hunde un poco y se oscurece. Es lo que la
      mano espera: que la cosa ceda cuando la aprietas.
   2. `tap-highlight` transparente — iOS y Android pintan un recuadro
      gris encima que no es nuestro y se ve ajeno. Se quita, pero SOLO
      porque lo reemplazamos por algo mejor; quitarlo sin dar nada a
      cambio dejaría el botón peor que antes.
   3. `touch-action: manipulation` — quita los ~300 ms que el navegador
      espera por si el toque era un doble-tap para hacer zoom. Es la
      razón de que un botón "tarde en responder" sin estar lento.
   4. `:focus-visible` — quien usa teclado ve dónde está parado. Solo
      con teclado: `:focus` a secas deja el anillo pegado tras un clic.
   5. `:disabled` — no reacciona. Un botón apagado que se hunde al
      tocarlo miente sobre lo que va a pasar.
   ═══════════════════════════════════════════════════════════════════ */
button, .btn, a.btn, label.btn, [role="button"],
input[type="submit"], input[type="button"] {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform .09s ease, filter .09s ease,
              background-color .14s ease, border-color .14s ease;
}

button:not(:disabled):not([aria-disabled="true"]):active,
.btn:not(:disabled):not([aria-disabled="true"]):active,
a.btn:active, label.btn:active, [role="button"]:active,
input[type="submit"]:not(:disabled):active,
input[type="button"]:not(:disabled):active {
  transform: scale(.96);
  filter: brightness(.92);
}

button:focus-visible, .btn:focus-visible, a.btn:focus-visible,
label.btn:focus-visible, [role="button"]:focus-visible,
input[type="submit"]:focus-visible, input[type="button"]:focus-visible {
  /* El foco usa su propio token. Escrito a mano era el amarillo de
     Lykos en TODA la consola, incluido el anillo del teclado.
     `--amar` es el respaldo por si esta hoja se carga sin
     `orvik-tokens.css`. */
  outline: 2px solid var(--foco, var(--amar));
  outline-offset: 2px;
}

button:disabled, .btn:disabled, [aria-disabled="true"] {
  cursor: not-allowed;
}
button:disabled:active, .btn:disabled:active, [aria-disabled="true"]:active {
  transform: none;
  filter: none;
}

/* El `:hover` solo donde hay un puntero de verdad. En una pantalla
   táctil el navegador lo deja "pegado" después de tocar: el botón se
   queda iluminado como si el dedo siguiera encima. */
@media (hover: none) {
  button:hover, .btn:hover, a.btn:hover, label.btn:hover {
    filter: none;
  }
}

/* Para quien pidió menos movimiento en su teléfono, el color sigue
   avisando aunque el botón no se mueva. */
@media (prefers-reduced-motion: reduce) {
  button, .btn, a.btn, label.btn, [role="button"] { transition: filter .09s ease; }
  button:active, .btn:active, a.btn:active, label.btn:active, [role="button"]:active {
    transform: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   LA SALA DE VERIDOM · las medidas salen del informe de construcción

   "Pantalla del ejecutivo — sala": video a la izquierda, panel de
   control de 330 px a la derecha. En móvil, video arriba a 42vh,
   panel abajo y botones a 46 px.

   Antes esto vivía dentro de una tarjeta modal y por eso el contenido
   se deslizaba sin tope, el botón principal decía "Cerrar" y el video
   quedaba tan chico que había que buscarlo arrastrando.
   ═══════════════════════════════════════════════════════════════════ */
/* ── QUE EL VIDEO LLENE LO QUE HAY ──


   El video se quedaba con su propia proporción y dejaba el hueco. En
   una pantalla de computadora eso es media pantalla desperdiciada
   justo en lo único que hay que mirar de cerca: la fachada, el número
   de la casa, el comprobante.

   Ahora la sala ocupa el alto de la ventana y el video se estira para
   llenarlo. `min-height:0` en los hijos no es adorno: sin eso un hijo
   de flex se niega a encogerse por debajo de su contenido y el panel
   de la derecha empuja la altura hacia abajo en vez de desplazarse por
   dentro. Es la causa número uno de "me quedó un hueco raro" en un
   diseño de flex.

   El `contain` del video se queda: llenar el alto no puede significar
   recortar evidencia. Si la cámara es vertical y el hueco horizontal,
   sobra negro a los lados — y está bien, porque lo que no puede pasar
   es perder el número de la casa por un recorte. */
.sala {
  display: flex; flex-direction: column; gap: 14px;
  /* `height` y no `min-height`: con `min-height` la sala crece cuando
     el panel tiene diez pasos, se pasa del alto de la ventana y la
     página entera se desplaza — que es exactamente el hueco que se
     quería quitar. Con alto fijo, el que se desplaza es el panel por
     dentro, y el video se queda quieto donde se ve.
     Se resta SOLO el relleno de `#main` —20 arriba y 40 abajo—. El
     encabezado de la sala va DENTRO de ella, así que restarlo también
     dejaba sesenta y pico de píxeles muertos al fondo: el mismo hueco
     que se estaba quitando, más chico.

     ── Y SE RECUPERAN LOS 40 DE ABAJO ──
     Un video vertical sólo puede crecer A LO ALTO: su ancho sale de la
     proporción. Así que cada píxel de alto que se le quita a un margen
     muerto se convierte también en ancho. Los 40 px de relleno al pie
     de `#main` no los usa nadie en esta pantalla, así que se cancelan
     con el margen negativo y se le pasan al video. */
  height: calc(100dvh - 32px);
}
/* Los márgenes negativos son SÓLO de la sala de video, no del documento
   de revisión: ahí no hay nada que apretar y jalarían el contenido. */
.sala:not(.doc) { margin: -10px -4px -28px; }

/* ── LA MISMA CAJA, DOS TRABAJOS DISTINTOS ──
   La sala de la videollamada se topa al alto de la ventana a proposito:
   el video se queda quieto y lo que rueda es el panel de al lado. La
   pantalla de REVISION usa la misma caja y no es lo mismo: es un
   documento largo que crece con las fotos.

   Con el tope puesto, su contenido se desbordaba por fuera de `#app`.
   La pagina se desplazaba, pero `#app` seguia midiendo una ventana, y
   `#tabs` --que es `sticky` dentro de `#app`-- se acababa ahi: la
   franja navy se cortaba a media altura y abajo quedaba blanco. */
.sala.doc { height: auto; }

.sala-top { display: flex; align-items: center; gap: 14px; }
.sala-quien { min-width: 0; }
.sala-quien b { display: block; font-size: 16px; color: var(--text); }
.sala-quien span { display: block; font-size: 12.5px; color: var(--text-3); }

.sala-cuerpo { display: flex; gap: 16px; align-items: stretch; flex: 1 1 auto; min-height: 0; }

.sala-video {
  /* ── LA CAJA TOMA LA FORMA DEL VIDEO, NO AL REVÉS ──

     Era un rectángulo negro fijo. El video del titular viene de un
     TELÉFONO, o sea vertical, así que quedaba en una columna al centro
     con negro a los lados, y como la caja se topaba a 60vh sobraba otra
     franja negra abajo. Pintar de negro lo que sobra no es adaptarse a
     la pantalla.

     Recortarlo para que llene —`object-fit:cover`— tampoco: ese video
     ES la evidencia y lo que se recorte puede ser el número de la casa.

     Entonces se mide el video de verdad (`medirRemoto` en
     capital-humano.js deja su proporción en `--vk-ar`) y la caja la
     adopta. El negro desaparece porque ya no sobra nada, y el ancho que
     se libera se lo queda el panel de los pasos. El 3/4 de reserva es
     la forma de un teléfono vertical: lo que llega el 95% de las veces. */
  flex: 0 1 auto; min-width: 0; min-height: 0;
  background: transparent; overflow: visible;
  display: flex; align-items: center; justify-content: flex-start;
}
.sala-video .vk {
  flex: 0 0 auto; display: block;
  aspect-ratio: var(--vk-ar, 9 / 16);
  height: 100%; width: auto;
  max-width: 100%; max-height: 100%;
  background: #000; border-radius: var(--r-lg); overflow: hidden;
}
.sala-video .vk video#vkRemoto {
  width: 100%; height: 100%; object-fit: contain; background: #000;
}

/* ── LA VISTA PREVIA DEL EJECUTIVO, MÁS CHICA ──
   Los 210 px venían del informe de VERIDOM y tapaban un buen pedazo de
   la esquina de arriba. A 150 sigue sirviendo para lo que existe
   —saber cómo te está viendo el titular— y estorba la mitad. */
.sala-video .vk #vkLocalCaja { width: 150px; }
@media (max-width: 767px) { .sala-video .vk #vkLocalCaja { width: 104px; } }

.sala-panel {
  /* Se queda con TODO lo que sobre. Tenía un tope de 560 px y lo que
     pasaba de ahí quedaba en blanco al borde derecho de la pantalla:
     media pantalla desperdiciada al lado de una lista de diez botones
     apretados. Un video vertical no puede ensancharse sin recortar la
     evidencia, así que el ancho libre sólo tiene un buen destino. */
  flex: 1 1 330px; min-width: 330px; min-height: 0;
  background: var(--superficie); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 16px;
  overflow-y: auto; overscroll-behavior: contain;
}
.sala-acts { display: flex; gap: 10px; margin-bottom: 14px; }
.sala-acts .btn { flex: 1 1 0; min-width: 0; justify-content: center; }

/* ── EL IPAD EN VERTICAL: MANDA EL ANCHO ──

   Con el reparto de escritorio puesto, en un iPad vertical de 820 el
   panel se lleva sus 330 y al video le quedan 390 de ancho contra 1090
   de alto. La regla de escritorio saca el ancho DEL ALTO --`height:100%;
   width:auto`--, así que pedía 613, el `max-width` lo recortaba a 374 y
   la proporción se rompía: medido, 374x1090 con dos franjas negras
   enormes arriba y abajo.

   En vertical la restricción que manda es la otra. Se le da el ancho y
   el alto sale de la proporción: 390 x 693, sin una sola franja. Lo que
   sobra de alto se lo queda el panel, que es donde hace falta.

   En HORIZONTAL no se toca: ahí vuelve a mandar el alto, igual que en
   computadora. Y la versión de escritorio no se toca en absoluto — esto
   vive dentro de su propia franja de anchos. */
@media (min-width: 768px) and (max-width: 1080px) and (orientation: portrait) {
  .sala-video { flex: 1 1 auto; align-items: flex-start; }
  .sala-video .vk { width: 100%; height: auto; max-height: 100%; }
}

/* ── LA FRONTERA ESTÁ EN 767, NO EN 900 ──
   Un iPad en vertical mide entre 768 y 834 px, así que con el corte en
   900 le tocaba el armazón del TELÉFONO: una sola columna, las cámaras
 partidas a la mitad y la lista debajo.

   Tiene espacio de sobra para las dos columnas, así que de 768 para
   arriba se queda con el reparto de escritorio. Lo que sigue es sólo del
   teléfono. */
@media (max-width: 767px) {
  /* En teléfono manda el contenido, no la ventana: la sala crece lo
     que necesite y se desplaza la página entera. Amarrarla al alto de
     la pantalla dejaría el panel con su propio desplazamiento dentro
     de otro, que es justo lo que se siente como que "se traba". */
  /* `height:auto` además de quitar el mínimo: si no, la caja conserva
     el alto de la ventana mientras su contenido mide el doble. Se veía
     bien de casualidad —el contenido se desbordaba visible— pero la
     caja mentía sobre su tamaño, y de ahí salen los bugs raros cuando
     alguien le agrega algo encima mañana. */
  .sala { height: auto; min-height: 0; }
  .sala-cuerpo { flex-direction: column; align-items: stretch; }
  /* ── EL ALTO MANDA, Y EL ANCHO LO SIGUE ──
     Primero se intentó al revés: ancho al 100% y `max-height:64vh` de
     tope. No funciona: el tope recorta el ALTO pero el ancho se queda
     en 100%, con lo que la proporción se rompe y vuelven las barras
     negras a los lados --medido: caja 346x540 para un video de 720x1280.

     Se le fija el alto al contenedor y la caja saca su ancho de la
     proporción del video, igual que en escritorio. Los 64vh son para
     que el primer paso del recorrido alcance a asomarse, que es lo que
     dice qué pedirle. */
  /* ── EN EL TELÉFONO, DOS FRANJAS Y NADA MÁS ──

     Copiado de la isla: `minmax(230px, 46vh)` para el video y el resto
     para la lista, que es la única que rueda. El mínimo de 230 px es lo
     que necesita el video para que se distinga un número de casa; el
     tope en vh es para que en un teléfono chico la lista no quede
     reducida a un renglón.

     El video deja de ser `sticky`: ya no hace falta cuando nada se
     desplaza debajo de él. Un primer intento lo pegó arriba con la
     página desplazándose; resolvía no perder de vista al candidato pero
     todo cabeceaba al deslizar. */
  /* ── EL ARMAZÓN FIJO ES SÓLO DEL TELÉFONO ──

     En computadora la sala ya estaba resuelta y se queda como estaba:
     alto de ventana, dos columnas, nada que tocar. Esto es sólo para el
     teléfono, que es donde se vio que la pantalla se desliza hacia
     arriba un poco.

     Copiado de la isla (lykos-veridom, public/index.html): su sala es
     `position:fixed; inset:0` y reparte el espacio ella misma, así que
     la página NO se desplaza. Lo único que rueda es la lista de pasos.

     Un intento anterior dejó que la página se desplazara con el video
     pegado arriba; resolvía no perder de vista al candidato, pero el
     encabezado se iba y el video lo perseguía: todo cabeceaba. Una
     llamada en la que la pantalla se mueve mientras le hablas a alguien
     parado en su banqueta no se siente firme.

     `z-index:60` la pone sobre la barra inferior (50): durante una
     llamada no se navega, y para salir están "Volver" y "Colgar", que
     siempre se ven. La revisión --`.doc`-- queda fuera: ésa sí es un
     documento largo que se lee desplazándose. */
  .sala:not(.doc) {
    position: fixed; inset: 0; z-index: 60;
    height: auto; margin: 0; padding: 12px 14px;
    background: var(--bg); gap: 10px;
  }
  .sala:not(.doc) .sala-top { flex: 0 0 auto; }

  /* El cuerpo: dos franjas y nada más. `minmax(230px, 46vh)` es de la
     isla --230 es lo que necesita el video para que se distinga un
     número de casa, y el tope en vh evita que en un teléfono chico la
     lista quede en un renglón--. */
  .sala:not(.doc) .sala-cuerpo {
    display: grid; grid-template-columns: 1fr;
    /* 42vh y no 46: dos azulejos verticales a media pantalla necesitan
       menos alto que uno solo centrado --en un teléfono de 390, cada uno
       mide 178 de ancho y pide 316 de alto--. Lo que sobraba se iba en
       negro arriba y abajo; ahora se va a la lista. */
    grid-template-rows: minmax(230px, 42vh) 1fr;
    gap: 10px; min-height: 0; overflow: hidden;
  }
  .sala-video {
    width: 100%; min-width: 0; min-height: 0;
    display: flex; align-items: center; justify-content: center;
  }
  /* ── EN EL TELÉFONO, LAS DOS CÁMARAS LADO A LADO ──


     Y tenía razón por algo que no es de gusto: la ventanita encimada
     vive en la esquina SUPERIOR DERECHA, que es justo donde se encuadra
     un número de casa. Tapaba cerca del 10% del video, y precisamente el
     pedazo que más se mira.

     Dos azulejos verticales iguales necesitan menos alto que uno solo
     centrado, así que además se liberan unos 50 px para la lista: un
     renglón más sin deslizar.

     Lo que cuesta: el video del titular se angosta de 218 a 190 px en un
     teléfono de 390. Se decidió con el número delante —13%
     menos de ancho pesa menos que una esquina ciega—.

     EN COMPUTADORA NO CAMBIA NADA: allá sobra espacio y la ventanita
     encimada funciona bien. Esto es sólo del teléfono. */
  .sala-video .vk {
    height: 100%; width: 100%; max-width: 100%;
    aspect-ratio: auto;
    display: flex; gap: 6px;
    background: transparent; border-radius: 0; overflow: visible;
    /* El renglón de abajo se RESERVA, no se encima. Con la ventanita
       flotando, el aviso de la llamada podía taparle una esquina y nadie
       se quejaba; con dos azulejos cruzaba por encima de los dos y además
       chocaba con la leyenda "Así te ve el titular". Reservado, cada cosa
       tiene su sitio.

       36 y no 30: el aviso mide 32 --9 de relleno arriba, 9 abajo y el
       renglón de texto-- y con 30 se comía 3 píxeles del borde de los
       azulejos. Medido, no calculado a ojo. */
    padding-bottom: 36px;
  }

  /* Cada azulejo se queda con la mitad exacta y respeta la forma de SU
     cámara: `contain`, que nunca recorta. `min-width:0` porque un hijo
     de flex no se encoge por debajo de su contenido sin eso, y los dos
     se saldrían de la franja. */
  .sala-video .vk video#vkRemoto,
  .sala-video .vk #vkLocalCaja {
    flex: 1 1 0; min-width: 0; width: auto; height: 100%;
    border-radius: var(--r-md); overflow: hidden; background: #000;
  }
  .sala-video .vk video#vkRemoto { object-fit: contain; }

  /* Deja de flotar: ahora es un azulejo más, no un sello. Se mantiene
     `relative` para que su leyenda "Así te ve el titular" siga colgando
     de él. */
  .sala-video .vk #vkLocalCaja {
    position: relative; inset: auto; top: auto; right: auto;
    border: 0; z-index: auto;
  }
  .sala-video .vk #vkLocal {
    width: 100%; height: 100%; aspect-ratio: auto; object-fit: contain;
  }

  /* El aviso de la llamada cruza los dos: es el estado de la llamada,
     no de una cámara. Y los chips de ubicación se quedan arriba a la
     izquierda, que es el azulejo del titular — que es de quien hablan. */
  .sala-video .vk .est { border-radius: 0 0 var(--r-md) var(--r-md); }
  /* La lista es LO ÚNICO que rueda. `min-height:0` porque un hijo de
     rejilla tampoco se encoge por debajo de su contenido sin esto, y
     entonces se desborda en vez de desplazarse. */
  .sala-panel {
    width: 100%; min-height: 0; overflow-y: auto;
    overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  }
  /* 46 px: se aprieta con el pulgar mientras la otra mano sostiene el
     teléfono y se habla. */

}

/* ── Los chips sobre el video, el progreso y los pasos ──
   Arriba a la izquierda, uno debajo del otro, como lo fijó el informe.
   Estaban a la derecha y se encimaban con el recuadro de la cámara. */
.vk-chips {
  position: absolute; left: 10px; top: 10px; z-index: 3;
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
}
.vk-chip {
  font-size: 11.5px; font-weight: 700; color: #fff;
  padding: 5px 10px; border-radius: var(--r-full);
  background: rgba(4, 30, 66, .82); backdrop-filter: blur(4px);
  white-space: nowrap;
}
.vk-chip.bien  { background: rgba(10, 125, 60, .92); }
.vk-chip.medio { background: rgba(176, 106, 0, .92); }
.vk-chip.malo  { background: rgba(179, 38, 30, .92); }

/* ── CUANDO LA COLUMNA ES ANCHA, LA LISTA VA EN DOS ──


   Quitarle el tope al panel resolvió el hueco en blanco pero creó otro
   problema: a 928 px de ancho, el nombre del paso queda a la izquierda
   y su botón "Capturar" a un metro de distancia. Usar la pantalla no es
   estirar un renglón hasta que no se pueda seguir con la vista.

   Un video vertical no puede ensancharse sin recortar la evidencia, así
   que el ancho libre sólo tiene un destino honesto: que quepan más
   pasos a la vista. En dos columnas los diez se ven de un golpe, sin
   rodar, y cada botón se queda junto a su nombre.

   El corte es a 1300 px porque es donde el panel pasa de ~700, que es
   el ancho a partir del cual el renglón se siente estirado. */
@media (min-width: 1300px) {
  #vk-pasos { display: grid; grid-template-columns: 1fr 1fr; column-gap: 22px; }
  #vk-pasos .vk-prog { grid-column: 1 / -1; }
}

.vk-prog { margin-bottom: 12px; }
.vk-prog b { display: block; font-size: 13px; margin-bottom: 7px; color: var(--text); }
/* La barra dice de un vistazo lo que el número dice leyendo. */
.vk-barra {
  height: 6px; border-radius: var(--r-full);
  background: var(--superficie-2); overflow: hidden;
}
.vk-barra i { display: block; height: 100%; background: var(--amar); transition: width .3s ease; }

/* ── LA MINIATURA DE LO QUE SE ACABA DE TOMAR ──
   Ocupa el lugar del icono, no se suma: una palomita al lado de la foto
   dice dos veces lo mismo y la foto lo dice mejor. 4:3 recortada para
   que las diez queden parejas aunque el video venga vertical; el
   detalle se ve al tocarla, que abre el visor del expediente. */
.vk-mini {
  width: 54px; height: 40px; flex: 0 0 auto;
  object-fit: cover; border-radius: 6px; cursor: zoom-in;
  background: var(--superficie-2); border: 1px solid var(--border);
  transition: transform .12s;
}
.vk-mini:hover { transform: scale(1.06); }
@media (max-width: 900px) { .vk-mini { width: 62px; height: 46px; } }

.vk-paso {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-top: 0.5px solid var(--border);
}
/* El título arriba y la etiqueta debajo, no uno al lado del otro.
   En línea, "tomada 3 veces" se partía en dos dentro de la píldora y se
   veía roto: la columna mide 330 px y ahí no caben título, etiqueta y
   botón en el mismo renglón. */
.vk-paso-t {
  flex: 1; min-width: 0; font-size: 13.5px; color: var(--text);
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
}
.vk-paso-t .tag { margin-left: 0; white-space: nowrap; }
/* El botón no lleva medidas en el atributo `style`: ahí nacía el
   problema de que en teléfono se quedara en 36 px pese a la regla de
   46. Un estilo escrito en el elemento le gana siempre a la hoja. */
.vk-paso .btn { width: auto; padding: 0 14px; min-height: 36px; flex: none; }
/* El tamaño de DEDO no se reparte por maqueta sino por tacto: el iPad se
   toca igual que el teléfono aunque use el reparto de escritorio. Por eso
   esto se queda en 900 y no baja a 767. */
@media (max-width: 1080px) {
  .sala-panel .btn { min-height: 46px; }
}
@media (max-width: 900px) {
  .vk-paso .btn { min-height: 46px; padding: 0 18px; }
  /* El video va pegado arriba y ocupa 56vh, así que un renglón traído
     con `scrollIntoView` aterrizaría DEBAJO de él, tapado. Esto le dice
     al navegador cuánto espacio reservar por encima: el alto del video
     más un respiro. Sin esto, la lista se acomoda sola en un paso que
     no se alcanza a ver, que es peor que no acomodarla. */
  /* Ya no hay que reservar espacio por un video pegado: el video vive
     en su propia franja y la lista rueda dentro de la suya. Sólo un
     respiro para que el renglón no quede al ras. */
  .vk-paso { scroll-margin: 12px 0 24px; }
}

/* ═══════════════════════════════════════════════════════════════════
   EL MENÚ PLEGADO POR ÁREAS

   Hereda todo de `#tabs button`: mismo alto, misma tipografía, mismo
   amarillo al estar activo. Lo único nuevo son la sangría de los hijos,
   la flechita del área y el amarillo de los iconos de sección.

   POR QUÉ LOS ICONOS DE SECCIÓN VAN EN AMARILLO Y LOS DE ÁREA NO:
   el amarillo marca lo que lleva a algún lado. Un área no te lleva: solo
   abre. Si los dos fueran amarillos el ojo no distinguiría el cajón de
   lo que hay dentro, y volveríamos al problema que esto vino a resolver
   — una lista larga de cosas que parecen todas iguales.
   ═══════════════════════════════════════════════════════════════════ */
#tabs .nav-area .nav-flecha { margin-left: auto; opacity: .5; font-size: 16px; }
#tabs .nav-hijos { display: none; }
#tabs .nav-hijos.abierta { display: block; }
#tabs .nav-hijo {
  margin-left: 14px; width: calc(100% - 14px);
  min-height: 44px; font-size: 13px;
}
/* El amarillo sólo mientras NO está activa: ahí el fondo ya es amarillo
   y el icono se volvería invisible sobre sí mismo. */
#tabs .nav-hijo .ti:first-child { color: var(--amar); }

/* ── EL SEGUNDO RÓTULO ──
   Tres secciones llevan un nombre corto que SÓLO usa la barra del
   teléfono, porque el largo no cabe en 69 px (ver `BARRA_CORTO` en
   app.js). Los dos viven en el mismo botón y la hoja de estilos decide
   cuál se enseña: aquí, el largo. El bloque de móvil lo invierte.

   Que la decisión sea de CSS y no de JavaScript importa: si fuera un
   `if` del ancho de pantalla, girar el teléfono o abrir el panel en una
   ventana angosta dejaría el rótulo equivocado hasta el siguiente
   repintado. */
#tabs .lbl-corto { display: none; }
#tabs .nav-hijo.act .ti:first-child { color: var(--sobre-amar); }

/* ── PLEGADO · sólo los iconos de las áreas (PC Y TABLETA) ──
   Con el menú en 68 px no cabe ni una etiqueta. Mostrar ahí los hijos
   dejaba una columna de iconos sin jerarquía —área, flecha, hijo,
   hijo, hijo, área…— con el doble de altura que la pantalla. Se
   enseñan las áreas; tocar una abre el menú ya en esa área.

   ── ESTO ESTABA SÓLO EN TABLETA, Y ERA EL BUG ──
   La regla nació dentro de `(max-width:1080px) and (min-width:769px)`,
   así que el menú plegado de ESCRITORIO —que vive en otro bloque, el
   de `min-width:1081px`— se quedó sin ella: ahí salían los iconos de
   área, las flechitas y TODOS los hijos apilados. Y era también la
   causa del atorón del iPad: la columna medía más que la pantalla, el
   menú se quedaba con scroll propio y se peleaba con el de la página.

   Ahora va una sola vez, para todo lo que no es teléfono. En el
   teléfono el menú no se "pliega": es otra caja —la barra de abajo— y
   ahí los hijos SÍ se usan, que es lo del bloque siguiente. */
@media (min-width: 600px) and (min-height: 480px){
  body.nav-plegada #tabs .nav-hijos { display: none !important; }
  body.nav-plegada #tabs .nav-area .nav-flecha { display: none; }

  /* Plegado, el renglón activo está escondido: la única referencia
     que queda de dónde estás es su área. Mismo amarillo y mismo
     contraste que una sección activa, para no inventar un estado. */
  body.nav-plegada #tabs .nav-area.act-area{
    background:var(--amar); color:var(--sobre-amar);
  }
  body.nav-plegada #tabs .nav-area.act-area .ti{ color:var(--sobre-amar); }

  /* Nada de `overscroll-behavior` aquí: cuando la columna llega a su
     tope, el gesto sigue de largo a la página, que es lo que uno
     espera de una barra lateral. Amarrarlo se sentía como un atorón. */
}

/* ── EN EL TELÉFONO LA BARRA NO SE PLIEGA POR ÁREAS ──
   Abajo no hay un menú plegado: hay cuatro atajos y "Más". Los
   titulares de área no pintan nada ahí —un icono de carpeta junto a
   cuatro destinos es un botón que no lleva a ningún lado— y las áreas
   completas ya viven dentro de "Más", agrupadas.

   El cajón que envuelve a los hijos se vuelve TRANSPARENTE
   (`display:contents`): desaparece del reparto y sus hijos compiten
   por el ancho junto a los demás botones, como si no existiera. Así
   la barra no depende de qué área esté abierta: se pintan los cuatro
   con atajo —`EN_BARRA` en app.js— y los demás llevan `.oculto`.

   Va DESPUÉS de las reglas del acordeón a propósito: `#tabs .nav-hijo`
   pesa lo mismo escrito arriba o abajo, así que gana el último. Escrito
   en el bloque de móvil de arriba, el margen y el alto del acordeón le
   pasaban por encima y la barra volvía a descuadrarse. */
@media (max-width: 599px), (max-height: 479px){
  /* Abajo no hay cajón que ruede: la barra reparte los botones a lo
     ancho, así que la lista se vuelve transparente igual que `.nav-hijos`. */
  #tabs .nav-lista{ display:contents !important; }
  #tabs .nav-area{ display:none !important; }
  #tabs .nav-hijos{ display:contents !important; }
  #tabs .nav-hijo{
    margin-left:0; width:auto; min-height:56px; font-size:10px;
  }
  /* El amarillo de los iconos es de la lateral, que es navy siempre.
     Abajo la barra es blanca en tema claro y un amarillo ahí no se
     lee: el icono toma el color del botón, como los demás. */
  #tabs .nav-hijo .ti:first-child,
  #tabs .nav-hijo.act .ti:first-child{ color:inherit; }

  /* Abajo manda el corto: "Tablero" en vez de "Tablero gen…".

     El selector nombra el `span` a propósito. Compite con
     `body.nav-plegada #tabs button .lbl{display:block}` de más arriba,
     que pesa (1 id, 2 clases, 2 tipos); `#tabs button .lbl-largo` pesa
     (1,1,1) y perdía, así que los dos rótulos salían uno encima del
     otro. Con `span.lbl.lbl-largo` empata en peso y gana por ir
     después, que es como se resuelve un empate. */
  #tabs button span.lbl.lbl-largo{ display:none; }
  #tabs button span.lbl.lbl-corto{ display:block; }
}

/* ── LA HOJA DEL TELÉFONO ──
   Otra caja: sube desde abajo y sigue el tema claro u oscuro, mientras
   la lateral es navy siempre. El plegado es el mismo. */
#mas-menu .mas-area .mas-flecha { margin-left: auto; opacity: .5; font-size: 17px; }
#mas-menu .mas-hijos { display: none; }
#mas-menu .mas-hijos.abierta { display: block; }
#mas-menu .mas-hijo { padding-left: 28px; font-size: 14px; }
#mas-menu .mas-hijo .ti:first-child { color: var(--amar-dark); }
/* ── DÓNDE ESTÁS PARADO ──
   La hoja enseña TODAS las secciones, también las que ya tienen atajo
   en la barra de abajo: es el único mapa por áreas que hay en el
   teléfono, y antes le faltaban justo las que más se usan. Para que
   nadie dude de si está viendo la misma pantalla dos veces, el renglón
   de la sección abierta va resaltado. `--amar-suave` y `--amar` existen
   en los dos temas, así que esto no desaparece en oscuro. */
#mas-menu .mas-hijo.act, #mas-menu .mas-sola.act {
  background: var(--amar-suave); font-weight: 700; color: var(--text);
  box-shadow: inset 3px 0 0 var(--amar);
}

/* ── QUIÉN ESTÁ RECLUTANDO ──
   Con varios reclutadores hace falta ver quién produce. Lo anterior a
   hoy sale como "Sin registrar": nadie guardaba quién daba de alta. */
.ch-quien-caja{ border:1px solid var(--border,#e3e8ef); border-radius:14px;
  padding:12px 14px; margin:0 0 14px; background:var(--superficie,#fff); }
.ch-quien-caja h3{ margin:0 0 9px; font-size:12.5px; font-weight:700; letter-spacing:.3px;
  text-transform:uppercase; color:var(--text-3,#64748b); }
.ch-quien-filas{ display:flex; flex-direction:column; gap:7px; }
.ch-quien-f{ display:flex; flex-wrap:wrap; align-items:baseline; gap:10px; }
.ch-quien-f .n{ font-weight:700; color:var(--text,#0f172a); }
.ch-quien-f .r{ font-size:12px; color:var(--text-3,#64748b); }
.ch-quien-f .c{ margin-left:auto; font-size:13.5px; color:var(--text,#0f172a); }
.ch-quien-f .k{ font-size:12.5px; color:var(--text-3,#64748b); flex-basis:100%; }

/* ═══════════════════════════════════════════════════════════════════
   EL REDISEÑO · aprobado el 13/oct/2026
   Se muda aquí desde docs/propuesta-diseno.css, que era la demo.

   Son reglas sobre los componentes COMPARTIDOS, así que alcanzan a las
   41 pantallas de una vez. No hay un solo color escrito a mano: todo
   sale de los tokens de arriba.
   ═══════════════════════════════════════════════════════════════════ */

/* ── 1 · EL BOTÓN PRINCIPAL NO OCUPA EL ANCHO DE LA PANTALLA ──────────
   Un botón amarillo de borde a borde no es una llamada a la acción: es
   una pared. Y cuando todo grita, nada se oye. En el teléfono sí se
   estiran --ahí manda el pulgar--, pero en orden: primero cancelar. */
.row-actions{justify-content:flex-end}
.row-actions .btn{flex:0 0 auto;min-width:132px}
.modal .m-foot .btn{flex:0 0 auto;min-width:132px}
.modal > .box .row-actions{gap:10px}
@media (max-width:560px){
  .row-actions .btn,
  .modal .m-foot .btn{flex:1 1 auto;min-width:0}
}

/* ── 2 · LA TARJETA ENTERA RESPONDE ───────────────────────────────────
   La fila completa reacciona al pasar por encima: menos ruido que un
   botón «Ver» repetido en cada renglón, y más área que tocar.

   El hover NO lleva borde amarillo, aunque así se enseñó en la demo: el
   amarillo de la marca pinta y enciende, no delinea --está dicho y
   repetido--. Se resuelve con el fondo hundido y el borde un punto más
   marcado, que es lo mismo sin romper la regla. */
.u-fila, .cli-f, .doc-f{
  transition:border-color .16s ease, background-color .16s ease, transform .16s ease}
.u-fila:hover, .cli-f:hover, .doc-f:hover{
  background:var(--superficie-2);
  border-color:var(--text-3)}

/* ── 3 · LAS CAPAS SE NOTAN ───────────────────────────────────────────
   El fondo, la tarjeta y el borde ya eran tres tokens distintos; lo que
   faltaba es que se vieran. Una sombra muy baja levanta la tarjeta sin
   convertirla en una caja pesada. */
.panel{box-shadow:0 1px 2px rgba(4,30,66,.04), 0 8px 24px rgba(4,30,66,.04)}
.kpi-card{transition:border-color .16s ease, transform .16s ease}
.kpi-card:hover{border-color:var(--text-3);transform:translateY(-1px)}

/* ── 4 · EL FORMULARIO EN DOS COLUMNAS DE VERDAD ──────────────────────
   `.grid` ya era de dos columnas y casi nadie las usaba: cada campo
   lleva `grid-column:1/-1` y ocupa la fila entera.

   Esta regla deja que un campo corto --una fecha, un CP, un teléfono--
   se ponga de a dos marcándolo con `.corto`. Hoy NINGUNA pantalla usa
   esa clase todavía, así que la regla está puesta y no cambia nada: el
   formulario se angosta pantalla por pantalla, cuando se toque cada
   una. Ponerla ahora evita que el día que alguien escriba `.corto` no
   funcione y no se sepa por qué. */
.grid > .field.corto{grid-column:auto}
@media (max-width:640px){
  .grid > .field.corto{grid-column:1 / -1}
}

/* ── 5 · LAS ETIQUETAS, MÁS CALLADAS ──────────────────────────────────
   Estaban en mayúsculas con espaciado, que grita un poco. La etiqueta
   acompaña; el dato manda. */
.field > label{
  text-transform:none;
  letter-spacing:0;
  font-size:13px;
  font-weight:600;
  color:var(--text-2)}

/* ── 6 · LA SECCIÓN, MENOS GRITADA ─────────────────────────────────── */
.com-sec{
  font-size:12px;
  letter-spacing:.02em;
  text-transform:none;
  font-weight:800}

    /* ── EL AVISO DE QUE LA GUÍA NO GUARDA NADA ──
       Estaba escrito en línea dentro de `tutorial.js`, con el navy de
       Lykos y sus propios tamaños de letra: en oscuro era un bloque
       azul marino flotando sobre la pantalla. Mismos tokens que todo
       lo demás, y los mismos cuatro tamaños. */
    .tut-nogurda{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);
      z-index:2147483000;max-width:min(460px,92vw);
      background:var(--superficie);color:var(--texto);
      border:1px solid var(--linea);border-radius:var(--r-tarjeta);
      box-shadow:var(--sombra-3);padding:var(--e-4);
      font:var(--t-cuerpo)}
    .tut-nogurda b{display:block;font:var(--t-cuerpo-f);margin-bottom:var(--e-1)}
    .tut-nogurda .que{display:block;margin-top:var(--e-2);
      font:var(--t-sec);color:var(--texto-3)}
