  :root {
    --bg: #f6f3ee;
    --bg-elev: #fbf9f5;
    --bg-2: #efeae0;
    --surface: #ffffff;
    --ink: #1a1f1d;
    --ink-2: #3a4441;
    --ink-3: #6b7572;
    --ink-4: #9aa39f;
    --line: #e6e0d6;
    --line-2: #d8d1c4;

    --teal-50: #e8f4f1;
    --teal-100: #c8e4dc;
    --teal-200: #8fc9bc;
    --teal-400: #2f8b78;
    --teal-500: #1f7766;
    --teal-600: #155b4f;
    --teal-700: #0f4439;
    --teal-900: #062520;

    /* El acento para texto e iconos SUELTOS, sin tinte `--teal-50` detras. Es el
       unico token de la rampa que cambia con el tema; ver applyAccent() en
       js/shell.js, que lo reescribe segun el acento elegido. */
    --teal-txt: #155b4f;

    --sand: #ead9b8;
    /* El texto que va SOBRE `--sand`. Estaba escrito a pelo dentro de
       `.chip-sand` (#6b5a2e), asi que en tema oscuro —donde `--sand` pasa a ser
       un marron casi negro— quedaba marron sobre marron: ilegible. Se veia poco
       porque el chip solo salia en el archivo de cuentas; desde que el ciclo de
       vida tiene una columna propia, sale en toda fila que este de salida. */
    --sand-fg: #6b5a2e;
    --coral: #c66a4a;
    --amber: #c89a3a;
    --amber-bg: #f3e9d0;
    --amber-fg: #7a5b1a;
    --rose: #b6535a;
    --rose-bg: #f1d8da;
    --rose-fg: #7d2e34;
    /* El verde de las paletas por NOMBRE (hoy, el de Tableros). No es
       `--teal-*`: aquella es la rampa del ACENTO, que la persona cambia desde
       Personalizar, y por eso una columna marcada «Verde» se veia azul con el
       acento azul puesto —que era el de fabrica—. Un color elegido por su
       nombre tiene que salir de ese nombre y de nada mas. Ver PENDIENTE.md
       §26.1. */
    --green: #4f8a46;
    --green-bg: #dcead6;
    --green-fg: #3a6431;

    --blue-bg: #dde7f0;
    --blue-fg: #1f4f6b;
    --plum-bg: #e8d8e9;
    --plum-fg: #5a2b62;

    /* El pilotito de presencia del chat. Tiene tokens PROPIOS y no reaprovecha
       `--teal-400` y `--blue-fg` como hacia antes, por una razon muy concreta:
       en un punto de siete pixeles el verde de la marca —un teal— y el azul se
       confunden, y en tema oscuro (#5ec0a9 contra #8ec0db) se confundian del
       todo. «En linea» y «fichado» son justo los dos estados que no pueden
       parecerse: uno dice que contesta ahora y el otro que igual tarda una
       hora. Verde verde y azul azul, y separados tambien en claridad. */
    --pres-online:  #16a34a;
    --pres-away:    #d68a12;
    --pres-fichado: #2563eb;
    --pres-off:     #9aa39f;

    --r-1: 4px; --r-2: 8px; --r-3: 12px; --r-4: 16px; --r-5: 24px;
    --sh-1: 0 1px 2px rgba(20,30,28,0.04);
    --sh-2: 0 4px 14px rgba(20,30,28,0.06), 0 1px 2px rgba(20,30,28,0.04);
    --sh-3: 0 24px 48px -16px rgba(15,68,57,0.20), 0 8px 16px -8px rgba(20,30,28,0.08);

    --f-sans: 'Geist', ui-sans-serif, system-ui, sans-serif;
    --f-mono: 'Geist Mono', ui-monospace, monospace;
    --f-display: 'Fraunces', Georgia, serif;

    --side-w: 248px;
    --header-h: 60px;
  }

  [data-theme="dark"] {
    --bg: #0e1816;
    --bg-elev: #131e1c;
    --bg-2: #182622;
    --surface: #15211e;
    --ink: #e8efec;
    --ink-2: #c1ccc7;
    --ink-3: #8aa099;
    --ink-4: #5d706a;
    --line: #1f2d29;
    --line-2: #283834;

    --teal-50: rgba(47,139,120,0.12);
    --teal-100: rgba(47,139,120,0.20);
    --teal-200: #4ba491;
    --teal-400: #5ec0a9;
    --teal-500: #6dd2ba;
    --teal-600: #7ddcc6;
    --teal-700: #1f7766;
    --teal-txt: #7ddcc6;

    --sand: #2a2317;
    --sand-fg: #d9c08a;
    --green: #7fb56a;
    --green-bg: rgba(79,138,70,0.18);
    --green-fg: #9ed184;
    --amber-bg: rgba(200,154,58,0.16);
    --amber-fg: #e3b864;
    --rose-bg: rgba(182,83,90,0.16);
    --rose-fg: #e08f95;
    --blue-bg: rgba(31,79,107,0.20);
    --blue-fg: #8ec0db;
    --plum-bg: rgba(122,67,134,0.20);
    --plum-fg: #c79bd0;

    /* Los mismos cuatro, subidos de claridad para que se vean sobre un fondo
       oscuro sin dejar de ser el mismo color. */
    --pres-online:  #4ade80;
    --pres-away:    #fbbf24;
    --pres-fichado: #7ea8ff;
    --pres-off:     #6b7d77;
  }

  /* -----------------------------------------------------------------------
     TEMA «CORE» · el tercero, y el que lleva los colores del logotipo.
     -----------------------------------------------------------------------
     No es «otro oscuro»: es CLARO, pero frio y tenido de la turquesa de la
     marca, para que se distinga de un vistazo del claro calido de arena. Con
     dos temas oscuros habria que mirar dos veces para saber en cual estas.

     Solo redefine los NEUTROS y las sombras. Todo lo demas —la rampa del
     acento, los chips de ambar, rosa, azul y ciruela— sirve igual que en
     `light` porque los dos son claros; redeclararlos aqui seria copiarlos.

     La rampa `--teal-*` NO se pone aqui a proposito, y no es un olvido: la
     escribe aplicarPersonalizacion() como estilo en linea sobre <html>, que le
     gana a este bloque. Ver la cabecera de js/personalizacion.js.
     -------------------------------------------------------------------- */
  [data-theme="core"] {
    --bg: #eaf5f4;
    --bg-elev: #f4faf9;
    --bg-2: #d9ecea;
    --surface: #ffffff;
    --ink: #0c2f33;
    --ink-2: #23545a;
    --ink-3: #5a8188;
    --ink-4: #8fadb2;
    --line: #d2e7e4;
    --line-2: #b8dad5;

    /* Las sombras del tema claro son marron grisaceo (rgba(20,30,28,…)). Sobre
       un fondo turquesa se ven sucias: una sombra tiene que ser el fondo mas
       oscuro, no otro color. Estas son el mismo azul verdoso del texto. */
    --sh-1: 0 1px 2px rgba(12,47,51,0.05);
    --sh-2: 0 4px 14px rgba(12,47,51,0.07), 0 1px 2px rgba(12,47,51,0.05);
    --sh-3: 0 24px 48px -16px rgba(10,75,76,0.22), 0 8px 16px -8px rgba(12,47,51,0.10);

    /* La arena calida del tema claro desentona con todo lo de arriba. Este es
       el mismo papel, girado hacia el verde. */
    --sand: #dfeee0;
    --sand-fg: #40603f;
  }

  * { box-sizing: border-box; }
  html, body { margin: 0; padding: 0; height: 100%; }

  /* El color de fondo se declara TAMBIEN en <html> y no solo en <body>, y eso
     es lo que hace posible el fondo personalizado: con un fondo puesto, <body>
     se vuelve transparente y la trama se pinta en un pseudo-elemento fijo a
     `z-index: -1`, que queda por encima del lienzo de <html> y por debajo de
     todo lo demas. Si el color viviera solo en <body>, ese -1 lo dejaria detras
     del propio fondo del cuerpo y no se veria nada. */
  html { background: var(--bg); }
  body {
    font-family: var(--f-sans);
    background: var(--bg); color: var(--ink);
    font-size: 14px; line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
  }
  button { font: inherit; color: inherit; }

  /* ----- El fondo del area de trabajo -----
     La clase la pone y la quita aplicarFondo() (js/personalizacion.js), que
     tambien escribe las cuatro variables. Se ve SOLO en el area de trabajo, y
     no porque se recorte: la barra lateral y la cabecera tienen fondo opaco
     propio, asi que lo tapan. Es deliberado — una trama por detras del menu
     hace ilegibles las entradas, y ese menu se lee cien veces al dia. */
  body.con-fondo { background: transparent; }
  body.con-fondo::before {
    content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
    background-image: var(--fondo-img);
    background-size: var(--fondo-size, cover);
    background-repeat: var(--fondo-repeat, no-repeat);
    background-position: center;
    opacity: var(--fondo-op, .35);
  }
  /* Un fondo es adorno: quien pide no animar tampoco quiere que se mueva al
     hacer scroll. Aqui no hay animacion, pero si la hubiera iria en este mismo
     sitio; la regla se deja escrita para que no acabe en otro fichero. */
  @media (prefers-reduced-motion: reduce) {
    body.con-fondo::before { transition: none; }
  }

  /* ===== LOGIN ===== */
  .auth {
    position: fixed; inset: 0; display: grid;
    grid-template-columns: 1fr 1fr;
    background: var(--bg);
  }
  .auth-art {
    background: linear-gradient(150deg, #062520 0%, var(--teal-700) 50%, #2f8b78 100%);
    color: #fff; padding: 48px; position: relative; overflow: hidden;
    display: flex; flex-direction: column; justify-content: space-between;
  }
  .auth-art .topline { display: flex; align-items: center; gap: 10px; font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(255,255,255,0.7); }
  .auth-art h1 { font-family: var(--f-display); font-weight: 400; font-size: 56px; line-height: 1.05; letter-spacing: -0.025em; max-width: 14ch; margin: 0 0 16px; }
  .auth-art h1 em { font-style: italic; color: var(--teal-200); }
  .auth-art p { font-size: 14px; color: rgba(255,255,255,0.78); max-width: 38ch; margin: 0; }
  .auth-art .waves { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; pointer-events: none; opacity: .35; }
  .auth-art .stat { display: flex; gap: 24px; font-family: var(--f-mono); font-size: 11px; color: rgba(255,255,255,0.6); letter-spacing: 0.06em; }
  .auth-art .stat strong { display: block; color: #fff; font-family: var(--f-display); font-size: 24px; font-weight: 500; letter-spacing: -0.01em; }

  .auth-form { display: flex; align-items: center; justify-content: center; padding: 32px; }
  .auth-card { width: 100%; max-width: 380px; }
  .auth-card .logo-row { display: flex; align-items: center; gap: 10px; margin-bottom: 32px; }
  .auth-card h2 { font-family: var(--f-display); font-weight: 500; font-size: 28px; margin: 0 0 6px; letter-spacing: -0.02em; }
  .auth-card .sub { color: var(--ink-3); margin: 0 0 24px; font-size: 13px; }
  .auth-card .demo {
    background: var(--teal-50); border: 1px solid var(--teal-100);
    border-radius: var(--r-2); padding: 10px 12px; font-family: var(--f-mono); font-size: 11px;
    color: var(--teal-700); margin-bottom: 18px; display: flex; align-items: center; gap: 10px;
  }
  .auth-card .demo .lucide { width: 14px; height: 14px; flex-shrink: 0; }
  .auth-card form { display: flex; flex-direction: column; gap: 14px; }
  .auth-card .field { display: flex; flex-direction: column; gap: 6px; }
  .auth-card label { font-size: 12px; color: var(--ink-2); font-weight: 500; }
  .auth-card .err { color: var(--rose); font-size: 12px; min-height: 1em; }
  .auth-card .meta-foot { margin-top: 20px; font-family: var(--f-mono); font-size: 10px; color: var(--ink-4); letter-spacing: 0.08em; text-align: center; text-transform: uppercase; }
  .auth-link {
    border: 0; background: transparent; color: var(--teal-txt); cursor: pointer; padding: 2px 0;
    font-size: 12px; font-weight: 500;
  }
  .auth-link:hover { text-decoration: underline; }

  /* ===== APP SHELL ===== */
  .app { display: grid; grid-template-columns: var(--side-w) 1fr; height: 100vh; }

  /* Sidebar */
  /* `--side-bg` no lo declara ningun tema: solo existe si alguien lo elige en
     el panel de personalizacion. Mientras no lo haga, el respaldo deja la barra
     exactamente donde estaba. Lo mismo con `--header-bg` mas abajo. El motivo
     de que sean tokens propios y no `--bg-elev` y `--bg` a secas esta en
     COLORES_PISABLES, en js/personalizacion.js. */
  .side {
    background: var(--side-bg, var(--bg-elev)); border-right: 1px solid var(--line);
    display: flex; flex-direction: column;
    overflow: hidden;
  }
  /* ----- Tinta de la barra y de la cabecera -----
     Elegir un color de barra lateral oscuro en un tema claro dejaba el menu
     escrito en gris marengo sobre verde botella: ilegible. Se arregla aqui y
     no en el JavaScript por una propiedad del lenguaje que hace todo el
     trabajo — las variables CSS SE HEREDAN, asi que redefinir `--ink` sobre
     `.side` cambia de golpe las treinta y tantas reglas de dentro que la leen,
     sin tocar ni una.

     La clase la pone aplicarPersonalizacion() midiendo la LUMINANCIA del color
     elegido (ver esOscuro en js/personalizacion.js), y solo cuando ese color
     esta pisado: sin eleccion propia no hay clase y manda el tema, como
     siempre.

     Los filetes van en blanco o negro translucido a proposito. Un `--line`
     fijo se ve como una raya de otro color en cuanto el fondo se aparta de lo
     previsto; un blanco al 12 % es el mismo fondo un poco mas claro, sea cual
     sea. */
  body.side-oscura .side, body.cab-oscura .header {
    --ink: #eef3f1; --ink-2: #cad6d2; --ink-3: #9db1ab; --ink-4: #7d918c;
    --line: rgba(255,255,255,0.14); --line-2: rgba(255,255,255,0.22);
    --bg-2: rgba(255,255,255,0.09); --bg-elev: rgba(255,255,255,0.07);
    --surface: rgba(255,255,255,0.07);
  }
  body.side-clara .side, body.cab-clara .header {
    --ink: #1a1f1d; --ink-2: #3a4441; --ink-3: #6b7572; --ink-4: #9aa39f;
    --line: rgba(0,0,0,0.10); --line-2: rgba(0,0,0,0.17);
    --bg-2: rgba(0,0,0,0.05); --bg-elev: rgba(0,0,0,0.04);
    --surface: rgba(255,255,255,0.55);
  }

  .side-top { padding: 16px 16px 8px; }
  /* ----- La marca -----
     El cuadradito con la ola dentro es el icono de aplicacion del logotipo, y
     la palabra va al lado. El SVG lo pinta logoCore() en js/personalizacion.js;
     aqui solo esta la caja.

     Los tres colores salen de `--logo-a`, `--logo-b` y `--logo-txt`, que
     escribe aplicarPersonalizacion(): sin eleccion propia, los dos primeros
     son el acento y el tercero el color del texto, asi que el logo acompana al
     resto de la aplicacion sin que nadie lo toque. */
  .logo { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; }
  .logo-mark {
    width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
    background: linear-gradient(140deg, var(--logo-a), var(--logo-b));
    display: grid; place-items: center;
    box-shadow: 0 1px 3px rgba(0,0,0,.18);
  }
  /* Sobre la franja oscura del acceso el degradado del acento se pierde: alli
     el cuadradito es un vidrio, y la ola blanca es lo unico que se ve. */
  .logo-mark.sobre-oscuro {
    background: rgba(255,255,255,0.16);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18);
  }
  .logo-words { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
  /* Sin cursiva y sin serif: el logotipo es una geometrica espaciada, y
     `--f-display` (Fraunces) es justo lo contrario. `--f-sans` con mucho
     `letter-spacing` es lo mas parecido que hay cargado, y no anade una
     tipografia mas al arranque. */
  .logo-text {
    font-family: var(--f-sans); font-weight: 400; font-size: 19px;
    letter-spacing: 0.24em; line-height: 1.05; text-indent: 0.24em;
    color: var(--logo-txt, var(--ink));
  }
  .logo-text.claro { color: #fff; }
  .logo-sub {
    font-family: var(--f-mono); font-size: 8.5px; letter-spacing: 0.14em;
    text-transform: uppercase; color: var(--ink-4); white-space: nowrap;
  }
  .core-ola { display: block; overflow: visible; }

  .org-pick {
    margin: 14px 16px 8px; padding: 8px 10px; border: 1px solid var(--line);
    border-radius: var(--r-2); display: flex; align-items: center; gap: 10px;
    background: var(--surface); cursor: pointer; font-size: 12px;
  }
  .org-pick .av {
    width: 24px; height: 24px; border-radius: 5px;
    background: var(--teal-700); color:#fff; display: grid; place-items: center;
    font-family: var(--f-display); font-size: 13px;
  }
  .org-pick .info { flex: 1; min-width: 0; }
  .org-pick .info .name { color: var(--ink); font-weight: 500; }
  .org-pick .info .sub { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); letter-spacing: 0.04em; }
  .org-pick .lucide { width: 14px; height: 14px; color: var(--ink-3); }

  .org-pick.switchable { cursor: pointer; position: relative; }
  .org-pick.switchable:hover { border-color: var(--teal-200); background: var(--teal-50); color: var(--teal-700); }
  .org-pick.switchable:hover .info .name,
  .org-pick.switchable:hover .info .sub,
  .org-pick.switchable:hover .lucide { color: var(--teal-700); }
  .org-pick.open { border-color: var(--teal-500); }

  .org-menu {
    position: absolute; top: calc(100% + 6px); left: 0; right: 0;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-2); box-shadow: var(--shadow-3);
    padding: 6px; z-index: 50; cursor: default;
    display: flex; flex-direction: column; gap: 2px;
  }
  .org-menu-hd {
    font-family: var(--f-mono); font-size: 10px; color: var(--ink-3);
    letter-spacing: 0.06em; text-transform: uppercase;
    padding: 8px 8px 4px;
  }
  .org-menu-item {
    display: flex; align-items: center; gap: 10px;
    padding: 8px; border-radius: var(--r-1); border: 0;
    background: transparent; cursor: pointer; text-align: left;
    font: inherit; color: var(--ink); width: 100%;
  }
  .org-menu-item:hover { background: var(--teal-50); }
  .org-menu-item.active { background: var(--teal-100); }
  .org-menu-item:hover, .org-menu-item.active,
  .org-menu-item:hover .info .name, .org-menu-item.active .info .name,
  .org-menu-item:hover .info .sub, .org-menu-item.active .info .sub { color: var(--teal-700); }
  .org-menu-item .av.sm {
    width: 22px; height: 22px; border-radius: 5px; color: #fff;
    display: grid; place-items: center; font-family: var(--f-display);
    font-size: 12px; flex-shrink: 0;
  }
  .org-menu-item .info { flex: 1; min-width: 0; }
  .org-menu-item .info .name { color: var(--ink); font-weight: 500; font-size: 12px; }
  .org-menu-item .info .sub { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); letter-spacing: 0.04em; }
  .org-menu-item .lucide { width: 14px; height: 14px; color: var(--teal-500); }
  .nav { flex: 1; overflow-y: auto; padding: 8px 12px 16px; }
  .nav-group { font-family: var(--f-mono); font-size: 10px; color: var(--ink-4); text-transform: uppercase; letter-spacing: 0.08em; padding: 14px 8px 6px; }
  /* La cabecera de grupo es además el botón de plegar. Hereda todo lo de
     arriba para que la barra tenga la misma cara donde el grupo no se puede
     plegar (el portal, que trae uno solo). */
  .nav-group.toggle {
    display: flex; align-items: center; gap: 6px; width: 100%; text-align: left;
    border: 0; background: transparent; cursor: pointer;
    font: inherit; font-family: var(--f-mono); font-size: 10px;
    color: var(--ink-4); text-transform: uppercase; letter-spacing: 0.08em;
  }
  .nav-group.toggle:hover { color: var(--ink-2); }
  /* Plegado y con la vista activa dentro: la cabecera hace de entrada
     iluminada, para que no pierdas el sitio al replegar donde estás. */
  .nav-group.toggle.active { color: var(--teal-500); font-weight: 600; }
  /* El grupo de la compañía se llama como la compañía: un nombre largo se corta
     aquí en vez de empujar la flecha o el contador fuera de la barra. */
  .nav-group-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .nav-group-caret { width: 12px; height: 12px; flex-shrink: 0; transition: transform .15s ease; }
  .nav-group.collapsed .nav-group-caret { transform: rotate(-90deg); }
  /* El contador que un grupo plegado se trae de dentro. */
  .nav-group.toggle .badge {
    margin-left: auto; background: var(--amber-bg); color: var(--amber-fg);
    font-size: 10px; padding: 1px 6px; border-radius: 999px;
    letter-spacing: 0; font-weight: 500;
  }
  .nav-item {
    display: flex; align-items: center; gap: 10px; padding: 8px 10px;
    border-radius: 7px; font-size: 13px; color: var(--ink-2); cursor: pointer;
    border: 0; background: transparent; width: 100%; text-align: left;
    transition: background .12s ease;
  }
  .nav-item .lucide { width: 16px; height: 16px; color: var(--ink-3); flex-shrink: 0; }
  .nav-item .badge {
    margin-left: auto; background: var(--amber-bg); color: var(--amber-fg);
    font-size: 10px; padding: 2px 6px; border-radius: 999px; font-weight: 500;
  }
  .nav-item:hover { background: rgba(31,119,102,0.08); color: var(--ink); }
  .nav-item.active { background: var(--teal-700); color: #fff; }
  .nav-item.active .lucide { color: #fff; }
  .nav-item.active .badge { background: rgba(255,255,255,0.18); color: #fff; }
  .nav-item .nav-caret { width: 13px; height: 13px; opacity: 0.5; margin-left: auto; }
  .nav-item .badge ~ .nav-caret { margin-left: 4px; }
  .nav-item.active .nav-caret { opacity: 0.8; }

  /* Desplegable de sub-secciones del menú lateral.
     Se monta en <body> con position:fixed porque .side recorta el desbordamiento. */
  .nav-fly {
    position: fixed; z-index: 80; min-width: 216px;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-2); padding: 6px;
    box-shadow: 0 12px 32px rgba(28, 38, 36, 0.14), 0 2px 6px rgba(28, 38, 36, 0.06);
    display: flex; flex-direction: column; gap: 1px;
    animation: navFlyIn .12s ease;
  }
  @keyframes navFlyIn { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
  .nav-fly-hd {
    font-family: var(--f-mono); font-size: 10px; color: var(--ink-3);
    letter-spacing: 0.06em; text-transform: uppercase; padding: 8px 10px 6px;
  }
  .nav-fly-item {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: var(--r-1); border: 0;
    background: transparent; cursor: pointer; text-align: left;
    font: inherit; font-size: 13px; color: var(--ink); width: 100%;
  }
  .nav-fly-item .lucide { width: 15px; height: 15px; color: var(--ink-3); flex-shrink: 0; }
  /* El fondo va con su texto SIEMPRE (ver «CUIDADO CON LOS TOKENS DE ACENTO»,
     más abajo): `--teal-50` lo escribe applyAccent() en :root y sigue siendo un
     pastel CLARO en tema oscuro, así que una fila resaltada sin fijar el color
     queda blanca sobre blanca y no se lee. */
  .nav-fly-item:hover { background: var(--teal-50); color: var(--teal-700); }
  .nav-fly-item:hover .lucide { color: var(--teal-600); }
  .nav-fly-item.active { background: var(--teal-50); color: var(--teal-700); font-weight: 500; }
  .nav-fly-item.active .lucide { color: var(--teal-600); }

  .side-bottom {
    padding: 12px 16px; border-top: 1px solid var(--line);
    display: flex; align-items: center; gap: 10px;
  }
  .side-bottom .name { font-size: 12px; flex: 1; min-width: 0; }
  .side-bottom .name strong { display: block; color: var(--ink); }
  .side-bottom .name span { color: var(--ink-3); font-family: var(--f-mono); font-size: 10px; }

  /* Main */
  .main { display: flex; flex-direction: column; min-width: 0; height: 100vh; }
  .header {
    height: var(--header-h); flex-shrink: 0;
    border-bottom: 1px solid var(--line);
    display: flex; align-items: center; padding: 0 24px; gap: 16px;
    background: var(--header-bg, var(--bg));
    position: sticky; top: 0; z-index: 10;
  }
  .header .crumb { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em; }
  .header .crumb b { color: var(--ink); font-weight: 500; }
  .header .search {
    flex: 1; max-width: 360px; margin-left: 24px;
    display: flex; align-items: center; gap: 8px;
    background: var(--bg-elev); border: 1px solid var(--line); border-radius: 8px;
    padding: 0 12px; height: 36px;
  }
  .header .search input { flex: 1; border: 0; background: transparent; outline: none; color: var(--ink); font-size: 13px; font-family: inherit; }
  .header .search .lucide { width: 14px; height: 14px; color: var(--ink-3); }
  .header .search kbd { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); border: 1px solid var(--line); padding: 1px 5px; border-radius: 3px; background: var(--surface); }
  .header .h-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
  .icon-btn {
    width: 34px; height: 34px; border-radius: 8px;
    border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
    display: grid; place-items: center; cursor: pointer; position: relative;
  }
  .icon-btn .lucide { width: 16px; height: 16px; }
  .icon-btn:hover { background: var(--bg-elev); }
  .icon-btn .pulse { position: absolute; top: 6px; right: 6px; width: 7px; height: 7px; border-radius: 50%; background: var(--coral); border: 1.5px solid var(--surface); }

  .content { flex: 1; overflow-y: auto; padding: 24px 32px 64px; }

  /* Common */
  .page-head { display: flex; align-items: flex-end; gap: 16px; margin-bottom: 20px; }
  .page-head h1 { font-family: var(--f-display); font-size: 32px; font-weight: 500; margin: 0; letter-spacing: -0.02em; }

  .btn {
    display: inline-flex; align-items: center; gap: 8px;
    height: 36px; padding: 0 14px; border-radius: 8px;
    font-size: 13px; font-weight: 500;
    border: 1px solid transparent; cursor: pointer; transition: all .15s ease;
    white-space: nowrap;
  }
  .btn .lucide { width: 14px; height: 14px; }
  .btn-primary { background: var(--teal-600); color: #fff; }
  [data-theme="dark"] .btn-primary { background: var(--teal-500); color: #062520; }
  .btn-primary:hover { background: var(--teal-700); }
  .btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--line-2); }
  .btn-secondary:hover { background: var(--bg-elev); }
  .btn-ghost { background: transparent; color: var(--ink-2); }
  .btn-ghost:hover { background: var(--bg-elev); }
  .btn-danger { background: transparent; color: var(--rose); border: 1px solid var(--rose-bg); }
  .btn-danger:hover { background: var(--rose-bg); }
  .btn-sm { height: 28px; padding: 0 10px; font-size: 12px; border-radius: 6px; }

  .card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-3); padding: 20px;
  }
  .card-hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; gap: 12px; }
  .card-hd h3 { font-size: 13px; font-weight: 500; color: var(--ink-3); margin: 0; text-transform: uppercase; letter-spacing: 0.06em; }
  .card-hd .h-actions { display: flex; gap: 6px; }

  .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
  .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
  .grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }

  .kpi {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-3); padding: 18px 20px;
    display: flex; flex-direction: column; gap: 8px; position: relative; overflow: hidden;
  }
  .kpi .label { font-size: 11px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 500; }
  .kpi .value { font-family: var(--f-display); font-size: 36px; font-weight: 500; line-height: 1; letter-spacing: -0.02em; }
  .kpi .value .unit { font-family: var(--f-sans); font-size: 13px; color: var(--ink-3); margin-left: 6px; font-weight: 400; }
  .kpi .delta { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--teal-500); font-weight: 500; }
  .kpi .delta.down { color: var(--rose); }
  .kpi .delta.warn { color: var(--amber-fg); }
  .kpi .delta .lucide { width: 12px; height: 12px; }
  .kpi .accent { position: absolute; top: 0; left: 0; bottom: 0; width: 3px; }
  .kpi.k1 .accent { background: var(--teal-500); }
  .kpi.k2 .accent { background: var(--amber); }
  .kpi.k3 .accent { background: var(--coral); }
  .kpi.k4 .accent { background: var(--teal-700); }

  .chip {
    display: inline-flex; align-items: center; gap: 6px;
    height: 22px; padding: 0 8px; border-radius: 999px;
    font-size: 11px; font-weight: 500; line-height: 1;
    background: var(--teal-50); color: var(--teal-700);
    white-space: nowrap;
  }
  /* Aqui hubo un `[data-theme="dark"] .chip { color: var(--teal-500); }` que
     bajaba el contraste de 9.78 a 4.79 en vez de subirlo: daba por hecho que en
     oscuro `--teal-500` seria el verde menta claro del bloque de arriba, y no lo
     es —applyAccent() lo pisa con #1f7766, que es oscuro—. El pastel de
     `--teal-50` tampoco se oscurece, asi que la pareja de la regla de arriba ya
     se lee en los dos temas y no hace falta tocar nada. */
  .chip-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
  .chip-amber { background: var(--amber-bg); color: var(--amber-fg); }
  .chip-sand { background: var(--sand); color: var(--sand-fg); }
  .chip-rose { background: var(--rose-bg); color: var(--rose-fg); }
  .chip-blue { background: var(--blue-bg); color: var(--blue-fg); }
  .chip-plum { background: var(--plum-bg); color: var(--plum-fg); }
  .chip-neutral { background: var(--bg-2); color: var(--ink-2); }

  .avatar {
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--teal-100); color: var(--teal-700);
    display: inline-grid; place-items: center;
    font-size: 12px; font-weight: 600;
    border: 1px solid rgba(15,68,57,0.08);
    flex-shrink: 0;
  }
  /* Igual que en `.chip`: el `color: var(--teal-500)` que habia aqui dejaba las
     iniciales en 4.01 sobre el pastel de `--teal-100`, frente a los 8.18 que da
     el `--teal-700` de la regla de arriba. Solo se conserva el borde. */
  [data-theme="dark"] .avatar { border-color: rgba(255,255,255,0.04); }
  .avatar.sm { width: 24px; height: 24px; font-size: 10px; }
  .avatar.lg { width: 40px; height: 40px; font-size: 14px; }
  .avatar.xl { width: 56px; height: 56px; font-size: 18px; }
  .avatar-stack { display: inline-flex; }
  .avatar-stack .avatar { margin-left: -8px; box-shadow: 0 0 0 2px var(--surface); }
  .avatar-stack .avatar:first-child { margin-left: 0; }

  .field { display: flex; flex-direction: column; gap: 6px; }
  .field label { font-size: 12px; color: var(--ink-2); font-weight: 500; }
  .input, .select, .textarea {
    height: 38px; padding: 0 12px; border-radius: 8px;
    border: 1px solid var(--line-2); background: var(--surface);
    font: inherit; font-size: 13px; color: var(--ink); width: 100%;
    transition: border-color .15s ease, box-shadow .15s ease;
  }
  .input:focus, .select:focus, .textarea:focus {
    outline: none; border-color: var(--teal-500);
    box-shadow: 0 0 0 3px rgba(31,119,102,0.12);
  }
  .textarea { height: auto; padding: 10px 12px; resize: vertical; min-height: 80px; }

  /* Table
   * `.tbl` y `.table` son el MISMO componente. Los módulos nuevos (gastos,
   * encuestas, denuncias, administración) se escribieron con `class="table"`,
   * que no existía en esta hoja: sus tablas salían sin bordes, sin cabecera y
   * sin relleno en las celdas — de ahí que las columnas se vieran pegadas unas
   * a otras. En vez de reescribir cuatro pantallas, el selector acepta las dos
   * clases y ya está. */
  table.tbl, table.table { width: 100%; border-collapse: collapse; font-size: 13px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); overflow: hidden; }
  table.tbl thead th, table.table thead th {
    text-align: center; font-size: 11px; font-weight: 500; color: var(--ink-3);
    text-transform: uppercase; letter-spacing: 0.06em;
    padding: 10px 14px; border-bottom: 1px solid var(--line);
    background: var(--bg-elev); user-select: none;
  }
  table.tbl thead th.sortable, table.table thead th.sortable { cursor: pointer; }
  table.tbl thead th.sortable:hover, table.table thead th.sortable:hover { color: var(--ink); }
  table.tbl thead th .sort-arrow, table.table thead th .sort-arrow { opacity: 0.4; margin-left: 4px; }
  table.tbl thead th.sorted .sort-arrow, table.table thead th.sorted .sort-arrow { opacity: 1; color: var(--teal-500); }
  table.tbl tbody td, table.table tbody td {
    padding: 12px 14px; border-bottom: 1px solid var(--line);
    color: var(--ink-2); vertical-align: middle;
  }
  table.tbl tbody tr:last-child td, table.table tbody tr:last-child td { border-bottom: 0; }
  table.tbl tbody tr:hover, table.table tbody tr:hover { background: var(--bg); }
  table.tbl tbody tr.selected, table.table tbody tr.selected { background: var(--teal-50); }
  /* Una tabla pegada al borde de una tarjeta no lleva su propio marco: el de la
   * tarjeta ya está ahí y los dos juntos dibujan una línea doble. */
  .card > table.tbl, .card > table.table { border: 0; border-radius: 0; }
  /* Columnas numéricas y de acciones: se marcan con clase, no con `style` suelto
   * en cada celda, que es como acababan unas alineadas y otras no.
   *
   * Solo afectan al CUERPO. Los encabezados van todos centrados sobre su
   * columna, sin excepciones: una cabecera que se alinea distinto según el tipo
   * de dato es justo lo que hacía que la fila de arriba pareciera desordenada. */
  table.tbl tbody td.num, table.table tbody td.num { text-align: right; font-family: var(--f-mono); font-size: 11.5px; white-space: nowrap; }
  /* Fechas y códigos: monoespaciados para que las cifras cuadren en vertical,
   * pero alineados a la izquierda — una fecha no es una cantidad y alinearla a
   * la derecha la separa de la columna a la que acompaña. */
  table.tbl tbody td.mono, table.table tbody td.mono { text-align: left; font-family: var(--f-mono); font-size: 11.5px; white-space: nowrap; }
  table.tbl tbody td.acts, table.table tbody td.acts { text-align: center; }
  table.tbl .acts, table.table .acts { width: 44px; }
  .emp-cell { display: flex; align-items: center; gap: 10px; }
  .emp-cell .name { color: var(--ink); font-weight: 500; }
  .emp-cell .email { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); }

  /* Row-action popover (… button on each table row)
   *
   * `z-index: 130` y no 90. Con 90 quedaba por DEBAJO del chat flotante (95) y
   * de la ventana de llamada (96): el botón de «Opciones» de la cabecera del
   * chat abría su menú justo encima del panel, o sea escondido detrás de él, y
   * desde fuera parecía que el botón no hacía nada. Un menú anclado tiene que
   * pintarse sobre lo que sea que lo ha abierto, incluido un diálogo (100). */
  .row-menu {
    position: fixed; z-index: 130; min-width: 200px;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-2); padding: 6px;
    box-shadow: 0 12px 32px rgba(28, 38, 36, 0.12), 0 2px 6px rgba(28, 38, 36, 0.06);
    display: flex; flex-direction: column; gap: 1px;
  }
  .row-menu-hd {
    font-family: var(--f-mono); font-size: 10px; color: var(--ink-3);
    letter-spacing: 0.06em; text-transform: uppercase;
    padding: 8px 10px 6px;
  }
  .row-menu-item {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: var(--r-1); border: 0;
    background: transparent; cursor: pointer; text-align: left;
    font: inherit; font-size: 13px; color: var(--ink); width: 100%;
  }
  .row-menu-item:hover { background: var(--teal-50); }
  .row-menu-item .lucide { width: 14px; height: 14px; color: var(--ink-3); flex-shrink: 0; }
  .row-menu-item:hover .lucide { color: var(--teal-600); }
  .row-menu-item.danger { color: var(--rose-fg); }
  .row-menu-item.danger .lucide { color: var(--rose-fg); }
  .row-menu-item.danger:hover { background: var(--rose-bg); }
  .row-menu-sep { height: 1px; background: var(--line); margin: 4px 2px; }
  /* Una línea que explica por qué falta una opción. No es pulsable, así que no
   * se pinta como si lo fuera: sin hover, sin cursor de mano. */
  .row-menu-note {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 8px 10px; font-size: 12px; line-height: 1.45;
    color: var(--ink-3); max-width: 240px;
  }
  .row-menu-note .lucide { width: 14px; height: 14px; color: var(--ink-4); flex-shrink: 0; margin-top: 2px; }

  /* Toolbar */
  .toolbar {
    display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
    flex-wrap: wrap;
  }
  .toolbar .search-mini {
    display: flex; align-items: center; gap: 8px;
    background: var(--surface); border: 1px solid var(--line-2); border-radius: 8px;
    padding: 0 10px; height: 34px; min-width: 220px;
  }
  .toolbar .search-mini input { border: 0; background: transparent; outline: none; color: var(--ink); font-size: 13px; font-family: inherit; flex: 1; }
  .toolbar .search-mini .lucide { width: 14px; height: 14px; color: var(--ink-3); }
  .toolbar .filter-pill {
    height: 34px; padding: 0 12px; border-radius: 8px; border: 1px solid var(--line-2);
    background: var(--surface); display: inline-flex; align-items: center; gap: 6px;
    font-size: 13px; color: var(--ink-2); cursor: pointer;
  }
  .toolbar .filter-pill.active { background: var(--teal-50); color: var(--teal-700); border-color: var(--teal-200); }
  .toolbar .filter-pill .lucide { width: 12px; height: 12px; }
  .toolbar .right { margin-left: auto; display: flex; gap: 8px; }
  .bulk-bar {
    display: flex; align-items: center; gap: 12px;
    background: var(--teal-700); color: #fff; padding: 8px 14px; border-radius: 8px;
    margin-bottom: 12px; font-size: 13px;
  }
  .bulk-bar .btn-ghost { color: #fff; }
  .bulk-bar .btn-ghost:hover { background: rgba(255,255,255,0.1); }

  /* Toast */
  .toast-wrap { position: fixed; bottom: 24px; right: 24px; display: flex; flex-direction: column; gap: 8px; z-index: 1000; }
  .toast {
    background: var(--surface); border: 1px solid var(--line);
    box-shadow: var(--sh-3); padding: 12px 16px; border-radius: 10px;
    display: flex; align-items: center; gap: 12px; min-width: 280px;
    animation: toast-in .3s ease;
  }
  .toast .lucide { width: 16px; height: 16px; color: var(--teal-500); }
  .toast.err .lucide { color: var(--rose); }
  .toast.warn .lucide { color: var(--amber-fg); }
  .toast.warn { border-color: var(--amber-bg); }
  .toast .msg { font-size: 13px; color: var(--ink); }
  .toast .sub { font-size: 11px; color: var(--ink-3); }
  @keyframes toast-in { from { transform: translateY(8px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

  /* Modal */
  .modal-backdrop {
    position: fixed; inset: 0; background: rgba(10,15,14,0.5);
    backdrop-filter: blur(2px); z-index: 100;
    display: flex; align-items: center; justify-content: center; padding: 24px;
    animation: fade-in .15s ease;
  }
  @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
  .modal {
    background: var(--surface); border-radius: var(--r-4);
    width: 100%; max-width: 560px; max-height: 90vh; overflow-y: auto;
    box-shadow: var(--sh-3); border: 1px solid var(--line);
    animation: modal-in .2s ease;
  }
  @keyframes modal-in { from { transform: scale(.96); opacity: 0; } to { transform: scale(1); opacity: 1; } }
  .modal.lg { max-width: 720px; }
  .modal-hd {
    padding: 20px 24px; border-bottom: 1px solid var(--line);
    display: flex; align-items: center; justify-content: space-between;
  }
  .modal-hd h3 { font-family: var(--f-display); font-size: 22px; font-weight: 500; margin: 0; letter-spacing: -0.01em; }
  .modal-bd { padding: 24px; }
  .modal-ft { padding: 16px 24px; border-top: 1px solid var(--line); display: flex; justify-content: flex-end; gap: 8px; }

  /* Dashboard specific */
  .dash-greeting {
    background: linear-gradient(135deg, var(--teal-700), var(--teal-500) 80%);
    color: #fff; border-radius: var(--r-4); padding: 24px 28px;
    position: relative; overflow: hidden;
    display: flex; align-items: center; gap: 24px;
    margin-bottom: 20px;
  }
  .dash-greeting svg.waves { position: absolute; right: -10%; top: -20%; width: 60%; height: 200%; opacity: 0.15; pointer-events: none; }
  .dash-greeting .gtxt { flex: 1; min-width: 0; position: relative; z-index: 1; }
  /* El saludo es el encabezado principal de Inicio y del Dashboard, así que es
     `h1`: esas dos pantallas se quedaban sin ninguno. Tamaño sin cambios. */
  .dash-greeting h1 { font-family: var(--f-display); font-size: 26px; font-weight: 500; margin: 0; letter-spacing: -0.01em; }
  .dash-greeting p { font-size: 13px; color: rgba(255,255,255,0.78); margin: 4px 0 0; }
  .dash-greeting .actions { position: relative; z-index: 1; display: flex; gap: 8px; }
  .dash-greeting .pill {
    background: rgba(255,255,255,0.14); color: #fff; padding: 8px 14px; border-radius: 999px;
    font-size: 12px; display: inline-flex; align-items: center; gap: 8px; cursor: pointer; border: 1px solid rgba(255,255,255,0.18);
  }
  .dash-greeting .pill:hover { background: rgba(255,255,255,0.22); }
  .dash-greeting .pill .lucide { width: 14px; height: 14px; }

  .chart-wrap { position: relative; height: 240px; }
  .chart-wrap.tall { height: 280px; }
  .chart-empty {
    height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px; text-align: center; padding: 0 24px; color: var(--ink-3);
    font-size: 12.5px; line-height: 1.5; max-width: 42ch; margin: 0 auto;
  }
  .chart-empty .lucide { width: 22px; height: 22px; opacity: 0.45; }

  .task-list { display: flex; flex-direction: column; gap: 8px; }
  .task {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px; border: 1px solid var(--line);
    border-radius: 8px; background: var(--bg-elev);
  }
  .task .check {
    width: 18px; height: 18px; border-radius: 5px; border: 1.5px solid var(--ink-4);
    flex-shrink: 0; cursor: pointer; display: grid; place-items: center;
  }
  .task .check.done { background: var(--teal-600); border-color: var(--teal-600); color: #fff; }
  .task .check .lucide { width: 12px; height: 12px; opacity: 0; }
  .task .check.done .lucide { opacity: 1; }
  .task .info { flex: 1; min-width: 0; }
  .task .info .title { font-size: 13px; color: var(--ink); }
  .task .info .meta { font-size: 11px; color: var(--ink-3); font-family: var(--f-mono); }
  .task.done .info .title { text-decoration: line-through; color: var(--ink-3); }

  .timeline-item {
    display: flex; gap: 12px; padding: 10px 0;
    border-bottom: 1px dashed var(--line);
  }
  .timeline-item:last-child { border-bottom: 0; }
  .timeline-item .when {
    font-family: var(--f-mono); font-size: 11px; color: var(--ink-3);
    min-width: 56px;
  }
  .timeline-item .what { font-size: 13px; color: var(--ink-2); flex: 1; }
  .timeline-item .what b { color: var(--ink); font-weight: 500; }

  /* Calendar */
  .cal-nav { display: flex; gap: 4px; }
  .cal-grid {
    display: grid; grid-template-columns: repeat(7, 1fr);
    border: 1px solid var(--line); border-radius: var(--r-3);
    background: var(--surface); overflow: hidden;
  }
  .cal-dow {
    padding: 8px; font-size: 11px; color: var(--ink-3); text-transform: uppercase;
    letter-spacing: 0.06em; font-weight: 500;
    background: var(--bg-elev); border-bottom: 1px solid var(--line); text-align: center;
  }
  .cal-cell {
    min-height: 96px; padding: 6px 8px; border-right: 1px solid var(--line);
    border-bottom: 1px solid var(--line); position: relative;
    display: flex; flex-direction: column; gap: 3px;
  }
  .cal-cell:nth-child(7n) { border-right: 0; }
  .cal-cell.muted { background: var(--bg-elev); }
  .cal-cell.muted .day-num { color: var(--ink-4); }
  .cal-cell.today .day-num {
    background: var(--teal-600); color: #fff; border-radius: 50%;
    width: 22px; height: 22px; display: grid; place-items: center;
  }
  .cal-cell.weekend { background: var(--bg-elev); }
  .day-num { font-family: var(--f-mono); font-size: 12px; color: var(--ink-2); margin-bottom: 2px; align-self: flex-start; padding: 2px 4px; }
  .cal-event {
    font-size: 10.5px; padding: 2px 6px; border-radius: 4px;
    background: var(--teal-50); color: var(--teal-700);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    cursor: pointer; line-height: 1.4;
  }
  .cal-event.t-vac { background: var(--amber-bg); color: var(--amber-fg); }
  .cal-event.t-sick { background: var(--blue-bg); color: var(--blue-fg); }
  .cal-event.t-mat { background: var(--plum-bg); color: var(--plum-fg); }
  .cal-event.t-perm { background: var(--teal-50); color: var(--teal-700); }
  .cal-event.more { background: transparent; color: var(--ink-3); padding-left: 0; }
  .cal-event .co-tag {
    display: inline-block; font-family: var(--f-mono); font-size: 8.5px;
    font-weight: 600; padding: 0 3px; margin-right: 4px;
    border-radius: 2px; line-height: 1.4;
    background: rgba(0,0,0,0.06); opacity: 0.7;
  }

  /* Approval list */
  .req-row {
    display: grid; grid-template-columns: 200px 100px 1fr 120px auto;
    gap: 16px; padding: 14px 16px;
    border-bottom: 1px solid var(--line); align-items: center;
  }
  .req-row:last-child { border-bottom: 0; }
  .req-row .who { display: flex; gap: 10px; align-items: center; }
  .req-row .who .meta { line-height: 1.3; }
  .req-row .who .name { color: var(--ink); font-weight: 500; font-size: 13px; }
  .req-row .who .role { font-size: 11px; color: var(--ink-3); }
  .req-row .dates { font-family: var(--f-mono); font-size: 11px; color: var(--ink-2); }
  .req-row .dates b { color: var(--ink); }
  .req-row .actions { display: flex; gap: 6px; }

  /* Documents */
  .doc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
  .doc-card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-3); padding: 16px;
    display: flex; flex-direction: column; gap: 8px;
    cursor: pointer; transition: all .15s ease;
    position: relative;
  }
  .doc-card:hover { border-color: var(--teal-500); transform: translateY(-1px); box-shadow: var(--sh-2); }
  .doc-card .ic {
    width: 40px; height: 48px; border-radius: 4px 4px 4px 4px;
    background: var(--teal-50); border: 1px solid var(--teal-100);
    color: var(--teal-700);
    display: grid; place-items: center; position: relative;
  }
  .doc-card .ic::after {
    content: ""; position: absolute; top: 0; right: 0;
    width: 10px; height: 10px;
    background: linear-gradient(225deg, var(--bg) 50%, transparent 50%);
  }
  .doc-card .ic .lucide { width: 18px; height: 18px; }
  .doc-card.t-contract .ic { background: var(--amber-bg); color: var(--amber-fg); border-color: rgba(200,154,58,0.3); }
  .doc-card.t-payslip .ic { background: var(--blue-bg); color: var(--blue-fg); border-color: rgba(31,79,107,0.3); }
  .doc-card.t-cert .ic { background: var(--plum-bg); color: var(--plum-fg); border-color: rgba(122,67,134,0.3); }
  .doc-card .name { font-size: 13px; color: var(--ink); font-weight: 500; line-height: 1.3; }
  .doc-card .meta { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); }
  .doc-card .ft { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }

  .doc-folder {
    display: flex; align-items: center; gap: 12px; padding: 10px 12px;
    border: 1px solid var(--line); border-radius: 8px; background: var(--bg-elev);
    cursor: pointer; font-size: 13px;
  }
  .doc-folder.active { background: var(--teal-50); border-color: var(--teal-200); color: var(--teal-700); }
  .doc-folder .lucide { width: 16px; height: 16px; color: var(--ink-3); }
  .doc-folder.active .lucide { color: var(--teal-500); }
  .doc-folder .count { margin-left: auto; font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); }

  /* Kanban */
  .kanban { display: grid; grid-template-columns: repeat(5, minmax(220px, 1fr)); gap: 12px; overflow-x: auto; }
  .kan-col { background: var(--bg-elev); border: 1px solid var(--line); border-radius: var(--r-3); padding: 12px; min-height: 400px; }
  .kan-col h4 { margin: 0 0 12px; font-size: 12px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; display: flex; align-items: center; justify-content: space-between; }
  .kan-col h4 span { background: var(--surface); padding: 2px 8px; border-radius: 999px; color: var(--ink-2); font-size: 10px; font-family: var(--f-mono); border: 1px solid var(--line); }
  .kan-card { background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 12px; margin-bottom: 8px; cursor: pointer; }
  .kan-card:hover { border-color: var(--teal-500); }
  .kan-card .name { font-size: 13px; color: var(--ink); font-weight: 500; margin-bottom: 4px; }
  .kan-card .role { font-size: 11px; color: var(--ink-3); margin-bottom: 8px; }
  .kan-card .ft { display: flex; align-items: center; justify-content: space-between; margin-top: 8px; }
  .kan-card .ft .chip { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .kan-empty { padding: 14px 8px; text-align: center; color: var(--ink-4); font-size: 11px; border: 1px dashed var(--line-2); border-radius: 8px; }

  /* Recruiting · vacancy list + layout */
  .rec-layout { display: grid; grid-template-columns: 340px 1fr; gap: 16px; align-items: flex-start; }
  .vac-item {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 10px 10px; border: 1px solid transparent; border-radius: 10px;
    background: transparent; cursor: pointer; font: inherit; text-align: left;
    transition: background .15s ease, border-color .15s ease;
  }
  .vac-item:hover { background: var(--bg-elev); }
  .vac-item.active { background: var(--teal-50); border-color: var(--teal-200); }
  .vac-item.is-closed .vac-title { color: var(--ink-3); }
  .vac-ico { width: 32px; height: 32px; flex-shrink: 0; border-radius: 8px; background: var(--surface); border: 1px solid var(--line); display: grid; place-items: center; color: var(--ink-3); }
  /* El icono se pone su PROPIO fondo `--surface`, no hereda el tinte `--teal-50`
     de la fila activa: por eso aqui va el acento suelto y no el texto oscuro. */
  .vac-item.active .vac-ico { background: var(--surface); color: var(--teal-txt); border-color: var(--teal-200); }
  .vac-ico .lucide { width: 15px; height: 15px; }
  .vac-title { display: block; font-size: 13px; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .vac-sub { display: block; font-size: 11px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .vac-item .count { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px; flex-shrink: 0; }
  .vac-menu { opacity: 0; border: 0; background: transparent; color: var(--ink-3); cursor: pointer; padding: 4px; border-radius: 6px; display: grid; place-items: center; flex-shrink: 0; transition: opacity .15s ease, background .15s ease; }
  .vac-menu .lucide { width: 15px; height: 15px; }
  .vac-item:hover .vac-menu, .vac-item.active .vac-menu { opacity: 1; }
  .vac-menu:hover { background: var(--line); color: var(--ink); }
  .cand-hint { display: flex; gap: 10px; align-items: flex-start; padding: 12px; background: var(--teal-50); border-radius: 8px; font-size: 12px; line-height: 1.5; color: var(--teal-700); }
  .cand-hint .lucide { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; }

  @media (max-width: 880px) {
    .rec-layout { grid-template-columns: 1fr; }
  }

  /* Onboarding */
  .ob-row { display: grid; grid-template-columns: 240px 1fr 80px 120px; gap: 16px; padding: 14px 16px; border-bottom: 1px solid var(--line); align-items: center; }
  .ob-row:last-child { border-bottom: 0; }
  .progress { height: 6px; background: var(--bg-2); border-radius: 999px; overflow: hidden; }
  .progress > div { height: 100%; background: var(--teal-500); border-radius: 999px; transition: width .3s ease; }

  /* Mobile */
  .mobile-nav { display: none; }
  .menu-btn { display: none; }

  @media (max-width: 1024px) {
    :root { --side-w: 0px; }
    .side { position: fixed; top: 0; bottom: 0; left: -260px; width: 260px; z-index: 50; transition: left .2s ease; box-shadow: var(--sh-3); }
    .side.open { left: 0; }
    .app { grid-template-columns: 1fr; }
    .menu-btn { display: grid; }
    .header .search { display: none; }
    .grid-4 { grid-template-columns: repeat(2, 1fr); }
    .grid-3 { grid-template-columns: 1fr; }
    .auth { grid-template-columns: 1fr; }
    .auth-art { display: none; }
  }
  @media (max-width: 640px) {
    .content { padding: 16px; padding-bottom: 88px; }
    .header { padding: 0 16px; }
    .grid-4 { grid-template-columns: 1fr 1fr; }
    .page-head { flex-direction: column; align-items: flex-start; gap: 6px; }
    .page-head h1 { font-size: 26px; }
    .req-row { grid-template-columns: 1fr; gap: 8px; }
    .ob-row { grid-template-columns: 1fr; gap: 6px; }
    .kanban { grid-template-columns: minmax(220px, 1fr); grid-auto-flow: column; grid-auto-columns: minmax(220px, 1fr); }
    .cal-cell { min-height: 60px; }
    .cal-event { display: none; }

    .mobile-nav {
      display: flex; position: fixed; bottom: 0; left: 0; right: 0;
      background: var(--surface); border-top: 1px solid var(--line);
      padding: 8px 8px calc(8px + env(safe-area-inset-bottom));
      z-index: 30; justify-content: space-around;
    }
    .mobile-nav button {
      flex: 1; background: transparent; border: 0; padding: 6px 4px;
      display: flex; flex-direction: column; align-items: center; gap: 3px;
      font-size: 10px; color: var(--ink-3); cursor: pointer;
    }
    .mobile-nav button .lucide { width: 18px; height: 18px; }
    .mobile-nav button.active { color: var(--teal-txt); }
    .mobile-nav button.active .lucide { color: var(--teal-txt); }
  }

  /* =========================================================================
   * PANEL DE PERSONALIZACION
   * -------------------------------------------------------------------------
   * Era una tarjeta de 280px en la esquina con cuatro controles. Con tres
   * temas, acento libre, seis colores pisables, ocho fondos y el logotipo ya no
   * cabia: pasa a ser un cajon de altura completa pegado al borde derecho.
   *
   * SE CONSERVAN el id `#tweaks` y la clase `.tweaks`, y no por pereza: el
   * protocolo de modo edicion los busca por nombre (ver js/app.js) y las reglas
   * de impresion los esconden por clase. Renombrarlos obligaba a tocar los dos
   * sitios sin ganar nada.
   *
   * Z-INDEX 98. Antes era 90, que lo dejaba POR DEBAJO del chat (95 y 96), y eso
   * se decidio a proposito cuando el panel era una tarjeta pequena en la misma
   * esquina que el chat: alli el chat tenia que ganar. Con un cajon de altura
   * completa la decision se invierte —el boton del chat quedaria flotando
   * ENCIMA de los controles—, asi que sube por encima del chat y se queda por
   * debajo del fondo de los modales (100), que sigue teniendo que taparlo todo.
   * Por eso tambien desaparecio `body.has-chat .tweaks { bottom: 92px }`: ya no
   * comparten esquina.
   * ====================================================================== */
  .pz-bd {
    position: fixed; inset: 0; z-index: 97; display: none;
    background: rgba(10,25,24,0.32);
  }
  .pz-bd.open { display: block; }

  .tweaks {
    position: fixed; top: 0; right: 0; bottom: 0; width: 392px; max-width: 100%;
    background: var(--surface); border-left: 1px solid var(--line);
    box-shadow: var(--sh-3); z-index: 98;
    display: none; flex-direction: column;
  }
  .tweaks.open { display: flex; }

  .pz-top {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 16px 16px 12px; border-bottom: 1px solid var(--line); flex-shrink: 0;
  }
  .pz-top-txt { flex: 1; min-width: 0; }
  .pz-top h4 { margin: 0; font-family: var(--f-display); font-size: 19px; font-weight: 500; }
  .pz-top p { margin: 2px 0 0; font-size: 11.5px; color: var(--ink-3); }

  .pz-cuerpo { flex: 1; overflow-y: auto; padding: 4px 16px 16px; }

  /* Los dos bloques del panel. El titulo es de la familia de titulares y no del
     monoespaciado de los `h5`: tiene que leerse como un cambio de capitulo y no
     como una etiqueta mas de las de dentro. */
  .pz-grupo { padding: 16px 0 2px; }
  .pz-grupo.con-linea { border-top: 1px solid var(--line-2); margin-top: 6px; padding-top: 18px; }
  .pz-grupo h3 {
    margin: 0; font-family: var(--f-display); font-size: 17px; font-weight: 500;
    letter-spacing: -0.01em;
  }
  .pz-grupo p { margin: 3px 0 0; font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }
  .pz-grupo p em { font-style: italic; color: var(--ink-2); }
  /* El primer apartado de cada bloque no lleva el filete de arriba: ya lo pone
     el encabezado del bloque. */
  .pz-grupo + .pz-sec { padding-top: 10px; }
  .pz-sec { padding: 14px 0; border-bottom: 1px solid var(--line); }
  .pz-sec:last-child { border-bottom: 0; }
  .pz-sec h5 {
    margin: 0 0 8px; font-family: var(--f-mono); font-size: 10px; font-weight: 500;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);
  }
  .pz-nota { margin: 0 0 10px; font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }
  .oculta { display: none !important; }

  /* ----- Tema ----- */
  .pz-temas { display: grid; gap: 6px; }
  .pz-tema {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-2);
    background: var(--bg-elev); cursor: pointer; text-align: left;
  }
  .pz-tema:hover { border-color: var(--line-2); }
  /* `--teal-50` es SIEMPRE el tono claro, tambien en tema oscuro: la rampa del
     acento no se invierte (ver la cabecera de js/personalizacion.js). Asi que
     lo que va encima tiene que ser `--teal-700`, como en el resto de la hoja.
     Con `--ink` —que en oscuro es casi blanco— el nombre del tema elegido
     desaparecia sobre su propio recuadro. */
  .pz-tema.activo { border-color: var(--teal-500); background: var(--teal-50); }
  .pz-tema.activo .pz-tema-txt b { color: var(--teal-700); }
  .pz-tema.activo .pz-tema-txt span { color: var(--teal-600); }
  .pz-tema-mini {
    width: 40px; height: 28px; border-radius: 5px; flex-shrink: 0;
    border: 1px solid var(--line-2); position: relative; overflow: hidden;
  }
  .pz-tema-mini i { position: absolute; left: 4px; top: 5px; width: 20px; height: 18px; border-radius: 2px; }
  .pz-tema-mini b { position: absolute; right: 4px; bottom: 5px; width: 10px; height: 10px; border-radius: 50%; }
  .pz-tema-txt { display: flex; flex-direction: column; min-width: 0; }
  .pz-tema-txt b { font-size: 13px; font-weight: 500; }
  .pz-tema-txt span { font-size: 11px; color: var(--ink-3); }

  /* ----- Acento ----- */
  .pz-sws { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
  .pz-sw {
    width: 28px; height: 28px; border-radius: 50%; cursor: pointer; padding: 0;
    border: 2px solid transparent; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12);
  }
  .pz-sw.activo { border-color: var(--ink); }
  .pz-sw:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
  /* La ruedecita del sistema es un <input type="color"> escondido DENTRO del
     circulo: a ese control no hay forma de darle estilo, y taparlo con el
     circulo dejandole recibir el clic es lo unico que funciona igual en los
     tres navegadores. */
  .pz-sw-libre {
    position: relative; display: grid; place-items: center; overflow: hidden;
    box-shadow: inset 0 0 0 1px var(--line-2);
  }
  .pz-sw-libre .lucide { width: 14px; height: 14px; color: var(--ink-3); pointer-events: none; }
  .pz-sw-libre.activo .lucide { color: #fff; }
  .pz-sw-libre input { position: absolute; inset: -6px; opacity: 0; cursor: pointer; border: 0; padding: 0; }
  .pz-rampa { display: flex; margin-top: 10px; border-radius: var(--r-1); overflow: hidden; height: 12px; }
  .pz-rampa span { flex: 1; }

  /* ----- Colores de la interfaz ----- */
  .pz-color {
    display: flex; align-items: center; gap: 10px;
    padding: 7px 0; border-bottom: 1px dashed var(--line);
  }
  .pz-color:last-of-type { border-bottom: 0; }
  .pz-color label { flex: 1; min-width: 0; display: flex; flex-direction: column; cursor: pointer; }
  .pz-color label b { font-size: 12.5px; font-weight: 500; }
  .pz-color label span { font-size: 11px; color: var(--ink-3); }
  /* Un punto detras del nombre marca los que estan pisados. Sin el, la unica
     senal era el boton de deshacer habilitado, que es un gris contra otro
     gris. */
  .pz-color.puesto label b::after {
    content: "·"; color: var(--teal-txt); font-weight: 700; margin-left: 5px;
  }
  .pz-color-ctl { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
  /* Un <input type="color"> trae un marco propio distinto en cada navegador. Se
     le quita todo y se le pone el mismo borde que a los demas controles. */
  .pz-color-ctl input[type="color"],
  .pz-logo-ctl input[type="color"] {
    -webkit-appearance: none; appearance: none;
    width: 34px; height: 28px; padding: 0; cursor: pointer;
    border: 1px solid var(--line-2); border-radius: var(--r-1); background: transparent;
  }
  .pz-color-ctl input[type="color"]::-webkit-color-swatch-wrapper,
  .pz-logo-ctl input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
  .pz-color-ctl input[type="color"]::-webkit-color-swatch,
  .pz-logo-ctl input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 2px; }
  .pz-color-ctl input[type="color"]::-moz-color-swatch,
  .pz-logo-ctl input[type="color"]::-moz-color-swatch { border: 0; border-radius: 2px; }

  .pz-quitar {
    width: 26px; height: 26px; border-radius: var(--r-1); flex-shrink: 0;
    border: 1px solid var(--line); background: transparent; color: var(--ink-3);
    display: grid; place-items: center; cursor: pointer;
  }
  .pz-quitar .lucide { width: 13px; height: 13px; }
  .pz-quitar:hover:not(:disabled) { color: var(--ink); border-color: var(--line-2); }
  .pz-quitar:disabled { opacity: .3; cursor: default; }

  /* ----- Fondo ----- */
  .pz-fondos { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .pz-fondo {
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: 9px 4px; border: 1px solid var(--line); border-radius: var(--r-2);
    background: var(--bg-elev); cursor: pointer; font-size: 10.5px; color: var(--ink-2);
  }
  .pz-fondo .lucide { width: 16px; height: 16px; color: var(--ink-3); }
  .pz-fondo:hover { border-color: var(--line-2); }
  /* Mismo motivo que en .pz-tema.activo: sobre `--teal-50` va `--teal-700`. */
  .pz-fondo.activo { border-color: var(--teal-500); background: var(--teal-50); color: var(--teal-700); }
  .pz-fondo.activo .lucide { color: var(--teal-700); }

  .pz-imagen { margin-top: 10px; }
  .pz-imagen-btns { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
  .pz-subir {
    display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
    height: 32px; padding: 0 12px; border-radius: var(--r-2);
    border: 1px solid var(--line-2); background: var(--bg-elev); font-size: 12px;
  }
  .pz-subir .lucide { width: 14px; height: 14px; }
  .pz-quitar-img {
    border: 0; background: transparent; color: var(--rose); cursor: pointer;
    font-size: 12px; padding: 4px 2px; text-decoration: underline;
  }
  .pz-rango { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
  .pz-rango label { font-size: 12px; color: var(--ink-2); flex-shrink: 0; }
  .pz-rango input[type="range"] { flex: 1; accent-color: var(--teal-500); min-width: 0; }
  .pz-rango output { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); width: 36px; text-align: right; }

  /* ----- Logotipo ----- */
  .pz-logo-prev {
    display: flex; align-items: center; gap: 10px;
    padding: 14px; border: 1px solid var(--line); border-radius: var(--r-2);
    background: var(--bg-elev); margin-bottom: 10px;
  }
  .pz-logo-ctl { display: flex; align-items: flex-end; gap: 10px; }
  .pz-logo-ctl label { display: flex; flex-direction: column; gap: 4px; align-items: center; }
  .pz-logo-ctl label span { font-size: 10px; color: var(--ink-3); }
  .pz-logo-ctl .pz-quitar { margin-left: auto; }

  /* ----- Interfaz ----- */
  .pz-fila { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 6px 0; }
  .pz-fila label { font-size: 12.5px; color: var(--ink-2); }
  .pz-fila .select { width: auto; height: 30px; padding: 0 8px; font-size: 12px; }

  .pz-pie { padding: 12px 16px; border-top: 1px solid var(--line); flex-shrink: 0; }
  .pz-pie .btn { width: 100%; justify-content: center; }

  @media (max-width: 640px) {
    /* `left: 0` y no `width: 100vw`: en un movil con barra de desplazamiento
       superpuesta las dos medidas no coinciden y el cajon se salia por la
       derecha, dejando los botones de deshacer fuera de la pantalla. Anclado
       por los dos lados no hay nada que calcular. */
    .tweaks { left: 0; width: auto; border-left: 0; }
    .pz-fondos { grid-template-columns: repeat(2, 1fr); }
  }

  /* ----- Muestras de color en un formulario -----
     La rejilla de botones que sustituye al desplegable de «Color». El punto de
     cada uno se pinta con el color de verdad: elegir «Ambar» de una lista
     desplegable obligaba a aceptarlo y mirar la columna para saber que era.

     El color lo pone quien usa el control, con una clase suya sobre el boton
     (ver `.tb-sw-*` en el bloque de Tableros). Este armazon no sabe de colores:
     asi sirve para la siguiente paleta que aparezca. */
  .ff-sws { display: flex; flex-wrap: wrap; gap: 6px; }
  .ff-sw {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 10px 6px 8px; border-radius: var(--r-2);
    border: 1px solid var(--line); background: var(--bg-elev);
    font-size: 12px; color: var(--ink-2); cursor: pointer;
  }
  .ff-sw:hover { border-color: var(--line-2); }
  .ff-sw.activo { border-color: var(--ink-3); box-shadow: inset 0 0 0 1px var(--ink-3); color: var(--ink); }
  .ff-sw:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
  .ff-sw:disabled { opacity: .5; cursor: default; }
  .ff-sw-dot {
    width: 15px; height: 15px; border-radius: 5px; flex-shrink: 0;
    background: var(--bg-2); box-shadow: inset 0 0 0 1px var(--line-2);
  }
  /* «Sin color» no puede ser un cuadro vacio identico al de al lado: lleva una
     diagonal, que es como se dibuja «ninguno» en cualquier editor. */
  /* La ruedecita del sistema, escondida bajo el boton. Mismo truco que el
     acento libre del panel: a un <input type="color"> no se le puede dar
     estilo, asi que se tapa y se le deja recibir el clic. */
  .ff-sw-libre { position: relative; overflow: hidden; }
  .ff-sw-libre input[type="color"] {
    position: absolute; inset: -6px; opacity: 0; cursor: pointer; border: 0; padding: 0;
  }
  .ff-sw-dot.vacio {
    background:
      linear-gradient(to top right, transparent calc(50% - 1px), var(--ink-4) calc(50% - 1px),
                      var(--ink-4) calc(50% + 1px), transparent calc(50% + 1px)),
      var(--surface);
  }

  /* Empty state */
  .empty { padding: 48px 24px; text-align: center; color: var(--ink-3); }
  .empty .lucide { width: 32px; height: 32px; color: var(--ink-4); margin-bottom: 8px; }

  /* Misc */
  .pill-btn { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; background: var(--bg-elev); border: 1px solid var(--line); font-size: 11px; color: var(--ink-2); cursor: pointer; }
  .balance-bar { display: flex; gap: 4px; height: 8px; border-radius: 4px; overflow: hidden; background: var(--bg-2); margin-top: 6px; }
  .balance-bar > div { background: var(--teal-500); }
  .scroll-x { overflow-x: auto; }

  hr.div { border: 0; border-top: 1px dashed var(--line); margin: 12px 0; }

  .checkbox { width: 16px; height: 16px; border: 1.5px solid var(--ink-4); border-radius: 4px; display: inline-grid; place-items: center; cursor: pointer; flex-shrink: 0; }
  .checkbox.on { background: var(--teal-600); border-color: var(--teal-600); color: #fff; }
  .checkbox.on::after { content: "✓"; font-size: 11px; line-height: 1; }

  /* Search palette */
  .palette-bd { position: fixed; inset:0; background: rgba(10,15,14,0.45); backdrop-filter: blur(2px); z-index: 200; display:flex; justify-content:center; padding-top:10vh; }
  .palette { width: 560px; max-width: 92vw; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--sh-3); overflow:hidden; max-height: 70vh; display:flex; flex-direction:column; }
  .palette-input { display:flex; align-items:center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line); }
  .palette-input input { flex:1; border:0; outline:0; font:inherit; font-size:15px; background:transparent; color:var(--ink); }
  .palette-input .lucide { width:16px; height:16px; color: var(--ink-3); }
  .palette-results { overflow-y:auto; padding: 8px; }
  .palette-section { padding: 8px 8px 4px; font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); letter-spacing: 0.08em; text-transform: uppercase; }
  .palette-item { display:flex; align-items:center; gap:10px; padding: 8px 10px; border-radius: 7px; cursor:pointer; font-size: 13px; }
  .palette-item:hover, .palette-item.active { background: var(--teal-50); color: var(--teal-700); }
  .palette-item .lucide { width: 14px; height: 14px; color: var(--ink-3); }
  .palette-item .meta { margin-left:auto; font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); }


  /* Notificaciones (la campana de la cabecera).
   *
   * OJO: la fila sin leer se marcaba con `background: var(--teal-50)` y dejaba
   * los textos en `--ink` / `--ink-3` / `--ink-4`. Ver «CUIDADO CON LOS TOKENS
   * DE ACENTO» más abajo: `applyAccent()` (js/shell.js) escribe la rampa CLARA
   * del acento en línea sobre `:root`, y un estilo en línea le gana al bloque
   * `[data-theme="dark"]`, así que en tema oscuro `--teal-50` es un pastel
   * CLARO y `--ink` es casi blanco. Resultado: pastel claro con letra clara,
   * y las notificaciones no se leían.
   *
   * Aquí no se usa la pareja fondo+texto de la casa porque la fila lleva TRES
   * tonos de texto (título, cuerpo y hora) y fijarlos todos a `--teal-700` los
   * aplana. Se marca como `.sd-general-fila`: fondo `--bg-2` —que sí es del
   * tema— y una barra de acento a la izquierda en `--teal-txt`, el único token
   * de la rampa que se invierte con el tema. Funciona en claro, en oscuro y con
   * los cuatro acentos. */
  .notif-pop {
    position: fixed; top: 56px; right: 24px; width: 360px; max-width: 92vw;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-3); box-shadow: var(--sh-3); overflow: hidden;
    z-index: 150;
  }
  .notif-head {
    padding: 14px 16px; border-bottom: 1px solid var(--line);
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
  }
  .notif-head strong { font-size: 14px; color: var(--ink); }
  .notif-head .notif-count { font-size: 11px; color: var(--ink-3); margin-top: 2px; }
  .notif-list { max-height: 60vh; overflow-y: auto; }
  .notif-vacio { padding: 28px 16px; text-align: center; color: var(--ink-3); font-size: 13px; }

  .notif-item {
    position: relative; display: flex; gap: 10px; padding: 12px 16px;
    border-bottom: 1px solid var(--line); background: var(--surface);
  }
  .notif-item:last-child { border-bottom: 0; }
  .notif-item.sin-leer { background: var(--bg-2); }
  .notif-item.sin-leer::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
    background: var(--teal-txt);
  }
  .notif-ico {
    width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
    background: var(--bg-elev); border: 1px solid var(--line);
    display: grid; place-items: center;
  }
  /* `--teal-txt` y no `--teal-600`: el 600 lo escribe applyAccent() oscuro en
   * los dos temas y sobre `--bg-elev` en oscuro no se ve. */
  .notif-ico .lucide { width: 14px; height: 14px; color: var(--teal-txt); }
  .notif-cuerpo { min-width: 0; flex: 1; }
  .notif-titulo { font-size: 13px; font-weight: 500; color: var(--ink); }
  .notif-texto { font-size: 12px; color: var(--ink-2); margin-top: 2px; }
  .notif-hora { font-size: 11px; color: var(--ink-3); margin-top: 4px; }
  /* Sobre `--bg-2`, `--ink-3` se queda en 4.02 en tema claro y AA pide 4.5 a
   * este tamaño. Un escalón más de tinta solo en las filas sin leer: la
   * jerarquía la sigue marcando el tamaño, y de paso las no leídas se leen algo
   * más fuertes, que es lo que son. */
  .notif-item.sin-leer .notif-hora { color: var(--ink-2); }
  /* El pie del desplegable: el interruptor de los avisos del sistema. Va aquí
     y no en Tweaks porque es donde se mira cuando uno se pregunta «¿por qué no
     me he enterado de esto?». Solo se pinta si el navegador puede cumplirlo
     (ver notifsEscritorioDisponible() en js/components.js). */
  .notif-pie {
    padding: 11px 16px 13px; border-top: 1px solid var(--line);
    background: var(--bg-elev);
  }
  .notif-pie label {
    display: flex; align-items: center; gap: 8px;
    font-size: 12.5px; color: var(--ink); cursor: pointer;
  }
  .notif-pie input { cursor: pointer; }
  /* Dos casillas en el pie —escritorio y sonido—, cada una con su nota debajo.
     El selector va sobre la NOTA y no `label + label`: entre las dos casillas
     hay siempre una nota, así que las etiquetas nunca son hermanas contiguas.
     Y así, cuando el navegador no admite avisos de escritorio y la casilla de
     sonido queda la primera, no se le cuela un margen de más. */
  .notif-pie-nota + label { margin-top: 10px; }
  .notif-pie-nota {
    font-size: 11px; color: var(--ink-3); line-height: 1.45; margin-top: 4px;
    /* Alineado con el texto de la casilla, no con el borde. */
    padding-left: 22px;
  }
  /* Org chart */
  .org-wrap { overflow-x: auto; padding: 32px 16px; background: var(--bg-elev); border: 1px solid var(--line); border-radius: 12px; }
  .org-tree { display:flex; flex-direction:column; align-items:center; gap: 32px; min-width: max-content; }
  .org-row { display:flex; gap: 24px; justify-content:center; position: relative; }
  .org-node { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; display:flex; align-items:center; gap:10px; min-width: 180px; box-shadow: var(--sh-1); position: relative; cursor:pointer; }
  .org-node:hover { border-color: var(--teal-500); }
  .org-node .meta { line-height: 1.3; min-width:0; }
  .org-node .nm { font-size: 13px; color: var(--ink); font-weight: 500; }
  .org-node .rl { font-size: 11px; color: var(--ink-3); }
  .org-children { display:flex; gap: 16px; justify-content: center; padding-top: 28px; position: relative; }
  .org-children::before { content:""; position: absolute; top: 0; left: 50%; width: 2px; height: 14px; background: var(--line-2); transform: translateX(-1px); }
  .org-branch { display:flex; flex-direction:column; align-items:center; position: relative; }
  .org-branch::before { content:""; position: absolute; top: -14px; left: 50%; width:2px; height: 14px; background: var(--line-2); transform: translateX(-1px); }
  .org-children > .org-branch + .org-branch::after { content:""; position:absolute; top: -14px; right: 50%; height:2px; left: -50%; background: transparent; }
  .org-children::after { content:""; position: absolute; top: -14px; left: 0; right: 0; height: 2px; background: var(--line-2); }
  .org-children:has(> :only-child)::after { display:none; }

  /* Tabs (used inside employee detail and elsewhere) */
  .tabs { display:flex; gap: 4px; border-bottom: 1px solid var(--line); margin-bottom: 16px; }
  .tab { padding: 10px 14px; border: 0; background: transparent; cursor: pointer; font: inherit; font-size: 13px; color: var(--ink-3); border-bottom: 2px solid transparent; margin-bottom: -1px; }
  .tab.active { color: var(--ink); border-bottom-color: var(--teal-500); }

  /* === Ficha de empleado ===
     La ficha va siempre incrustada en el contenido — «Mi perfil» y la ficha de
     otra persona (#employee/<id>). No es panel: no se superpone, no tiene
     scroll propio ni ancho máximo, así no compite con el layout de debajo. */
  .emp-embed { display: block; }
  .emp-embed .emp-id-row { padding-top: 2px; }

  .emp-id-row {
    display: flex; align-items: flex-start; gap: 24px;
    padding-bottom: 22px;
  }
  .emp-id-row .emp-avatar {
    width: 96px; height: 96px; border-radius: 999px;
    display: grid; place-items: center;
    font-family: var(--f-display); font-size: 36px; font-weight: 500;
    letter-spacing: -0.02em;
    position: relative; flex-shrink: 0;
  }
  .emp-id-row .emp-avatar::after {
    content: ""; position: absolute; right: -2px; bottom: -2px;
    width: 28px; height: 28px; border-radius: 999px;
    background: var(--surface); border: 1px solid var(--line);
    background-image: radial-gradient(circle at 50% 50%, var(--ink-3) 2px, transparent 2.5px);
    background-size: 4px 4px; background-position: center;
    cursor: pointer;
  }
  /* El nombre de la persona es `h2`: en «Mi perfil» convive con el `h1` de la
     página y dos encabezados de primer nivel dejan el documento sin una cabeza
     clara. El tamaño no cambia, solo el nivel. */
  .emp-id-row h2 {
    font-family: var(--f-display); font-size: 38px; font-weight: 500;
    line-height: 1.05; letter-spacing: -0.02em; margin: 0;
  }
  .emp-id-meta { display: flex; align-items: center; gap: 10px; margin-top: 8px; flex-wrap: wrap; }
  .emp-id-meta .since { font-size: 13px; color: var(--ink-3); }
  .emp-pill-row { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
  .emp-pill {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 12px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--surface);
    font-size: 13px; color: var(--ink-2);
  }
  .emp-pill .lucide { width: 13px; height: 13px; color: var(--ink-3); }
  .emp-pill b { font-weight: 500; color: var(--ink); }

  /* Primary tab bar */
  .emp-tabs {
    display: flex; gap: 4px; border-bottom: 1px solid var(--line);
    margin-bottom: 24px; overflow-x: auto;
  }
  .emp-tabs::-webkit-scrollbar { display: none; }
  .emp-tab {
    padding: 12px 16px; border: 0; background: transparent;
    cursor: pointer; font: inherit; font-size: 14px;
    color: var(--ink-3); border-bottom: 2px solid transparent;
    margin-bottom: -1px; white-space: nowrap; font-weight: 500;
  }
  .emp-tab:hover { color: var(--ink-2); }
  .emp-tab.active { color: var(--teal-500); border-bottom-color: var(--teal-500); }

  /* Two-column subnav layout for "Datos personales" */
  .emp-subgrid {
    display: grid; grid-template-columns: 240px 1fr; gap: 32px;
    align-items: flex-start;
  }
  .emp-subnav {
    display: flex; flex-direction: column; gap: 2px;
    position: sticky; top: 16px;
  }
  .emp-subnav button {
    text-align: left; padding: 10px 14px; border-radius: 8px;
    border: 0; background: transparent; cursor: pointer;
    font: inherit; font-size: 13px; color: var(--ink-2);
  }
  .emp-subnav button:hover { background: var(--bg-2); }
  .emp-subnav button.active {
    background: var(--teal-50); color: var(--teal-600); font-weight: 500;
  }

  /* Field rows inside a "datos" panel */
  .field-card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 12px; overflow: hidden;
  }
  .field-row {
    display: grid; grid-template-columns: 220px 1fr auto;
    gap: 16px; align-items: center;
    padding: 14px 18px; border-bottom: 1px solid var(--line);
  }
  .field-row:last-child { border-bottom: 0; }
  .field-row .lbl { font-size: 13px; color: var(--ink-2); font-weight: 500; }
  .field-row .val { font-size: 13px; color: var(--ink); }
  .field-row .val.muted { color: var(--ink-4); }
  .field-row .edit-icon { width: 28px; height: 28px; border-radius: 6px; border: 0; background: transparent; color: var(--ink-3); cursor: pointer; display:grid; place-items:center; }
  .field-row:hover .edit-icon { color: var(--ink-2); background: var(--bg-2); }

  .panel-title {
    font-family: var(--f-display); font-size: 22px; font-weight: 500;
    letter-spacing: -0.01em; margin: 0 0 16px;
  }
  .panel-sub {
    font-family: var(--f-mono); font-size: 11px; color: var(--ink-3);
    letter-spacing: 0.06em; text-transform: uppercase; margin: 24px 0 10px;
  }
  .panel-sub:first-child { margin-top: 0; }

  /* Side cards (used in "Acerca de" right column) */
  .side-card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 12px; padding: 18px;
  }
  .side-card + .side-card { margin-top: 14px; }
  .side-card .title {
    font-size: 13px; color: var(--ink-2); font-weight: 500;
    margin-bottom: 8px;
  }
  .side-card .big {
    font-family: var(--f-mono); font-size: 30px; font-weight: 500;
    letter-spacing: -0.02em; color: var(--ink);
  }
  .side-card .big .unit { font-size: 13px; color: var(--ink-3); font-family: var(--f-sans); margin-left: 4px; }

  /* Map / hero block */
  .hero-tile {
    border-radius: 12px; height: 160px; position: relative; overflow: hidden;
    background:
      radial-gradient(circle at 30% 30%, rgba(31,119,102,0.35), transparent 60%),
      radial-gradient(circle at 70% 70%, rgba(15,68,57,0.65), transparent 60%),
      linear-gradient(180deg, #0a1d1a 0%, #112e29 100%);
    color: #fff;
    display: grid; place-items: center;
  }
  .hero-tile .clock-time {
    font-family: var(--f-display); font-size: 56px; font-weight: 500;
    letter-spacing: -0.02em; line-height: 1;
  }
  .hero-tile .clock-label {
    position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%);
    padding: 4px 12px; border-radius: 999px;
    background: rgba(255,255,255,0.18); backdrop-filter: blur(8px);
    font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
    color: rgba(255,255,255,0.9);
  }

  /* Mini-grid actions row inside side card */
  .mini-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); border-radius: 10px; overflow: hidden; margin-top: 14px; border: 1px solid var(--line); }
  .mini-actions button {
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 14px; background: var(--surface); border: 0; cursor: pointer;
    font: inherit; font-size: 12px; color: var(--ink-2);
  }
  .mini-actions button:hover { background: var(--bg-2); }
  .mini-actions .lucide { width: 16px; height: 16px; color: var(--ink-3); }

  /* Departamento/Equipo block in About */
  .relation-card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 12px; padding: 20px; margin-bottom: 14px;
  }
  .relation-card .label {
    font-size: 12px; color: var(--ink-3); text-transform: uppercase;
    letter-spacing: 0.06em; font-weight: 500; margin-bottom: 8px;
  }
  .relation-card .value {
    font-family: var(--f-display); font-size: 26px; font-weight: 500;
    letter-spacing: -0.01em; line-height: 1.1;
  }
  .relation-card .link {
    margin-top: 10px; display:inline-flex; align-items:center; gap: 4px;
    font-size: 12px; color: var(--teal-500); cursor: pointer;
  }
  .relation-card .link:hover { color: var(--teal-txt); }

  .reports-block {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 12px; padding: 16px 20px;
  }
  .reports-block .head {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 12px;
  }
  .reports-block .head .title { font-size: 13px; font-weight: 500; color: var(--ink); display: inline-flex; align-items: center; gap: 6px; }
  .reports-block .head .count { font-family: var(--f-mono); font-size: 12px; color: var(--ink-3); }
  .reports-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .report-chip {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 12px; border-radius: 8px; cursor: pointer;
    border: 1px solid transparent;
  }
  .report-chip:hover { background: var(--bg-2); border-color: var(--line); }
  .report-chip .nm { font-size: 13px; color: var(--ink); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .report-chip .rl { font-size: 11px; color: var(--ink-3); }

  /* Working hours mini week */
  .week-row { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; margin-top: 12px; }
  .week-row .d {
    background: var(--bg-2); border-radius: 8px; padding: 10px 6px;
    text-align: center;
  }
  .week-row .d.off { background: transparent; border: 1px dashed var(--line); }
  .week-row .d .dn { font-size: 11px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; }
  .week-row .d .dh { font-family: var(--f-mono); font-size: 12px; color: var(--ink); margin-top: 4px; }
  .week-row .d.off .dh { color: var(--ink-4); }

  /* Notes editor */
  .note-area {
    width: 100%; min-height: 240px; resize: vertical;
    padding: 16px; border-radius: 12px;
    border: 1px solid var(--line); background: var(--surface);
    font: inherit; font-size: 14px; color: var(--ink);
  }
  .note-area:focus { outline: none; border-color: var(--teal-200); box-shadow: 0 0 0 3px var(--teal-50); }

  .note-compose { display: flex; flex-direction: column; gap: 10px; margin-bottom: 28px; }
  .note-compose .note-area { min-height: 120px; }
  .note-compose-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
  .note-hint { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-4); }
  .note-hint .lucide { width: 13px; height: 13px; }

  .note-history-hd { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--ink-2); margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
  .note-history-hd .lucide { width: 15px; height: 15px; color: var(--ink-3); }
  .note-list { display: flex; flex-direction: column; gap: 12px; }

  .note-item { display: flex; gap: 12px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
  .note-avatar { flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; }
  .note-body { flex: 1; min-width: 0; }
  .note-meta { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
  .note-author { font-size: 13px; font-weight: 600; color: var(--ink); }
  .note-dot { color: var(--ink-4); }
  .note-time { font-size: 12px; color: var(--ink-3); }
  .note-edited { font-size: 10px; color: var(--ink-4); font-style: italic; }
  .note-del { margin-left: auto; flex-shrink: 0; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; color: var(--ink-4); border-radius: 6px; cursor: pointer; opacity: 0; transition: opacity .12s, color .12s, background .12s; }
  .note-item:hover .note-del { opacity: 1; }
  .note-del:hover { color: var(--rose-fg); background: var(--rose-bg); }
  .note-del .lucide { width: 14px; height: 14px; }
  .note-text { font-size: 14px; line-height: 1.55; color: var(--ink); white-space: pre-wrap; word-break: break-word; }

  .note-empty { text-align: center; padding: 40px 20px; color: var(--ink-3); border: 1px dashed var(--line-2); border-radius: 12px; }
  .note-empty .lucide { width: 26px; height: 26px; color: var(--ink-4); margin-bottom: 10px; }
  .note-empty-sub { font-size: 12px; color: var(--ink-4); margin-top: 4px; }

  @media (max-width: 900px) {
    .emp-subgrid { grid-template-columns: 1fr; gap: 16px; }
    .emp-subnav { position: static; flex-direction: row; overflow-x: auto; }
    .field-row { grid-template-columns: 1fr; gap: 4px; }
  }

  .cal-cell.holiday { background: rgba(182,83,90,0.05); }
  .cal-cell.holiday .day-num { color: var(--rose-fg); }
  .holiday-tag { font-size: 9px; padding: 1px 4px; background: var(--rose-bg); color: var(--rose-fg); border-radius: 3px; font-weight: 500; align-self: flex-start; margin-bottom: 2px; line-height: 1.3; }

  .kan-card[draggable="true"] { user-select: none; }
  .kan-card.dragging { opacity: 0.4; }
  .kan-col.drop-target { background: var(--teal-50); border-color: var(--teal-200); }

  /* ===== Roles y permisos ===== */
  .perm-table { width: 100%; }
  .perm-table th { font-size: 11px; }
  .perm-table td { vertical-align: middle; }
  .perm-col { text-align: center; width: 130px; }
  .perm-col-hd { display: flex; flex-direction: column; gap: 2px; align-items: center; }
  .perm-col-hd span { font-weight: 600; color: var(--ink); font-size: 12.5px; }
  .perm-col-hd small { font-family: var(--f-mono); font-size: 9.5px; color: var(--ink-3); font-weight: 400; letter-spacing: 0.02em; }
  .perm-col.perm-admin small, .perm-cell.perm-admin { color: var(--teal-txt); }
  .perm-group-row td { background: var(--bg-2); padding: 8px 16px !important; border-top: 1px solid var(--line); }
  .perm-group-hd { display: inline-flex; align-items: center; gap: 8px; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); }
  .perm-group-hd .lucide { width: 14px; height: 14px; color: var(--teal-txt); }
  .perm-cap { padding-left: 16px !important; }
  .perm-cap-lbl { display: block; color: var(--ink); font-size: 13px; }
  .perm-cap-key { display: block; font-family: var(--f-mono); font-size: 10px; color: var(--ink-4); margin-bottom: 2px; }
  .perm-cell { text-align: center; }

  /* Checkbox cuadrado custom (matriz + modal) */
  .perm-check { position: relative; display: inline-flex; cursor: pointer; }
  .perm-check input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
  .perm-box {
    width: 20px; height: 20px; border-radius: 6px; border: 1.5px solid var(--line-2);
    background: var(--surface); display: grid; place-items: center; transition: all .14s ease;
  }
  .perm-box::after {
    content: ""; width: 10px; height: 6px; border-left: 2px solid #fff; border-bottom: 2px solid #fff;
    transform: rotate(-45deg) scale(0); transform-origin: center; margin-top: -2px; transition: transform .14s ease;
  }
  .perm-check input:checked + .perm-box { background: var(--teal-600); border-color: var(--teal-600); }
  .perm-check input:checked + .perm-box::after { transform: rotate(-45deg) scale(1); }
  .perm-check input:focus-visible + .perm-box { box-shadow: 0 0 0 3px var(--teal-50); }

  /* Modal de permisos individuales */
  .uperm-list { display: flex; flex-direction: column; gap: 16px; max-height: 52vh; overflow-y: auto; padding-right: 4px; }
  .uperm-group-hd { display: flex; align-items: center; gap: 8px; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px; }
  .uperm-group-hd .lucide { width: 14px; height: 14px; color: var(--teal-txt); }
  .uperm-row {
    display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: var(--r-1);
    border: 1px solid var(--line); margin-bottom: 6px; cursor: pointer; background: var(--surface);
  }
  .uperm-row:hover { border-color: var(--teal-200); }
  .uperm-row.is-exception { border-color: var(--teal-400); background: var(--teal-50); }
  /* La fila de excepcion se tiñe de `--teal-50`, que es claro en los dos temas:
     sobre ese tinte el acento suelto no vale y hay que volver al texto oscuro. */
  .uperm-row.is-exception .uperm-flag { color: var(--teal-700); }
  .uperm-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .uperm-lbl { font-size: 13px; color: var(--ink); }
  .uperm-default { font-family: var(--f-mono); font-size: 10px; color: var(--ink-4); }
  .uperm-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
  .uperm-flag { font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--teal-txt); min-width: 56px; text-align: right; }


/* =============================================================================
   Atajos de navegación (punto 2 y 3 del backlog)
   ========================================================================== */

/* Tarjetas KPI que llevan a su vista. Solo llevan data-go cuando el usuario
   tiene permiso, así que el estilo de "clicable" nunca miente. */
.kpi[data-go] { cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; }
.kpi[data-go]:hover { border-color: var(--teal-200); box-shadow: var(--sh-2); transform: translateY(-1px); }
.kpi[data-go]:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.kpi[data-go]::after {
  content: ""; position: absolute; top: 14px; right: 14px; width: 14px; height: 14px;
  opacity: 0; transition: opacity .15s ease;
  background: currentColor; color: var(--ink-3);
  -webkit-mask: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M7 7h10v10'/%3E%3C/svg%3E");
          mask: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M7 7h10v10'/%3E%3C/svg%3E");
}
.kpi[data-go]:hover::after, .kpi[data-go]:focus-visible::after { opacity: 1; }

/* Chevron del menú de cuenta: el avatar ya navega a la ficha propia. */
.user-caret { width: 20px; height: 28px; border: 0; background: transparent; padding: 0; }
.user-caret .lucide { width: 13px; height: 13px; }

/* =============================================================================
   Calendario de ausencias · barras continuas y selección por arrastre
   (puntos 4 y 5 del backlog)
   ========================================================================== */

/* Cada ausencia ocupa un carril fijo durante toda la semana. Los días sin
   ausencia en ese carril dejan un hueco de la misma altura para que las barras
   de los demás carriles queden alineadas de lunes a domingo. */
.cal-slot { height: 18px; flex-shrink: 0; }

/* La barra se derrama hasta el borde de la celda (y 1px más, para tapar la
   línea divisoria) salvo en el primer y último día del rango. Así un rango de
   varios días se lee como un único bloque en lugar de etiquetas sueltas. */
.cal-bar {
  height: 18px; display: flex; align-items: center;
  margin-left: -8px; margin-right: -9px; padding-left: 8px; padding-right: 4px;
  border-radius: 0; position: relative; z-index: 1;
}
.cal-bar.bar-start { margin-left: 0; border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.cal-bar.bar-end { margin-right: 0; border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
.cal-cell:nth-child(7n) .cal-bar { margin-right: 0; }

.cal-hint {
  display: flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--ink-3); margin: 10px 2px 0;
}
.cal-hint .lucide { width: 13px; height: 13px; }

/* Rango que se está arrastrando. */
.cal-cell.selecting { background: var(--teal-50); box-shadow: inset 0 0 0 1px var(--teal-200); }
.cal-cell[data-day] { user-select: none; }

/* =============================================================================
   Límites de ausencias y detalle de saldo (puntos 6 y 7)
   ========================================================================== */

.abs-hint {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 12px; line-height: 1.45; color: var(--ink-2);
  background: var(--bg-2); border-radius: 8px; padding: 10px 12px;
}
.abs-hint .lucide { width: 14px; height: 14px; flex-shrink: 0; margin-top: 1px; color: var(--ink-3); }
.abs-hint.err { background: var(--rose-bg); color: var(--rose-fg); }
.abs-hint.err .lucide { color: var(--rose-fg); }

.lim-row {
  display: grid; grid-template-columns: 1.6fr 1fr 1fr 0.8fr 0.9fr 40px;
  gap: 10px; align-items: center; padding: 10px 14px;
  border-bottom: 1px solid var(--line); font-size: 13px;
}
.lim-row:last-child { border-bottom: 0; }
.lim-head {
  background: var(--bg-elev); font-size: 10px; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.06em; font-weight: 500;
}
.lim-row .input { height: 30px; padding: 0 8px; font-size: 12.5px; }
.lim-check { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-2); }

.bal-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin-bottom: 12px;
}
.bal-cell {
  display: flex; flex-direction: column; gap: 4px;
  background: var(--bg-2); border-radius: 10px; padding: 12px 14px;
}
.bal-cell .lbl { font-size: 10px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; }
.bal-cell .val { font-family: var(--f-display); font-size: 22px; font-weight: 500; line-height: 1; }
.bal-cell .val.pos { color: var(--teal-txt); }
.bal-cell .val.neg { color: var(--rose-fg); }
.bal-cell .sub { font-size: 11px; color: var(--ink-3); }
.bal-cell.strong { background: var(--teal-50); }
.bal-cell.strong .val { color: var(--teal-700); }

.bal-bar {
  display: flex; height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--bg-elev); margin-bottom: 4px;
}
.bal-bar .used { background: var(--teal-500); }
.bal-bar .reserved { background: var(--amber); }

.bal-h {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3); font-weight: 500; margin: 22px 0 8px;
}
.bal-pos { color: var(--teal-txt); }
.bal-neg { color: var(--rose-fg); }

/* Filas de saldo de la ficha: se nota que son clicables. */
.bal-row { cursor: pointer; border-radius: 8px; padding: 4px 6px; margin: 0 -6px; transition: background .15s ease; }
.bal-row:hover { background: var(--bg-2); }
.bal-row:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 1px; }

@media (max-width: 900px) {
  .bal-grid { grid-template-columns: repeat(2, 1fr); }
  .lim-row { grid-template-columns: 1fr 1fr; row-gap: 6px; }
  .lim-head { display: none; }
}

/* =============================================================================
   Visor de documentos en línea (punto 8)
   ========================================================================== */

.modal.doc-viewer { max-width: 1000px; width: 94vw; }
.doc-viewer-bd { padding: 0; background: var(--bg-elev); min-height: 60vh; display: flex; }
.doc-view-frame { width: 100%; height: 70vh; border: 0; background: var(--surface); }
.doc-view-img { max-width: 100%; max-height: 70vh; margin: auto; object-fit: contain; display: block; }
.doc-viewer-loading {
  margin: auto; display: flex; align-items: center; gap: 10px;
  color: var(--ink-3); font-size: 13px; padding: 60px 0;
}
.doc-viewer-loading .lucide { width: 16px; height: 16px; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.doc-card[role="button"] { cursor: pointer; }
.doc-card[role="button"]:focus-visible,
.doc-line:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.doc-line:hover { background: var(--bg-2); }

/* Nombre de documento como enlace dentro de una fila (portal). */
.doc-inline-link {
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; background: transparent; padding: 0; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--ink-2); text-align: left;
}
.doc-inline-link:hover { color: var(--teal-txt); text-decoration: underline; }

/* =============================================================================
   Reclutamiento · pestañas, puestos de trabajo y métricas (puntos 9 y 10)
   ========================================================================== */

.rec-tabs {
  display: flex; gap: 2px; margin-bottom: 18px;
  border-bottom: 1px solid var(--line); overflow-x: auto;
}
.rec-tab {
  display: inline-flex; align-items: center; gap: 7px;
  border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: 13.5px; color: var(--ink-3);
  padding: 10px 14px; margin-bottom: -1px;
  border-bottom: 2px solid transparent; white-space: nowrap;
}
.rec-tab:hover { color: var(--ink-2); }
.rec-tab.active { color: var(--teal-txt); border-bottom-color: var(--teal-600); font-weight: 500; }
.rec-tab-badge {
  font-family: var(--f-mono); font-size: 10px; padding: 1px 6px;
  border-radius: 999px; background: var(--bg-elev); color: var(--ink-3);
}
.rec-tab.active .rec-tab-badge { background: var(--teal-50); color: var(--teal-700); }

/* --- Resumen --- */
.rec-overview { display: grid; grid-template-columns: 1fr 1.1fr 0.95fr; gap: 16px; align-items: start; }

.rec-counters { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.rec-counter {
  display: flex; flex-direction: column; gap: 3px;
  background: var(--bg-2); border-radius: 10px; padding: 12px 14px;
  border: 1px solid transparent;
}
.rec-counter .n { font-family: var(--f-display); font-size: 26px; font-weight: 500; line-height: 1; }
.rec-counter .l { font-size: 11.5px; color: var(--ink-3); line-height: 1.35; }
.rec-counter.ok .n { color: var(--teal-txt); }
.rec-counter.warn .n { color: var(--amber-fg); }
.rec-counter.is-link { cursor: pointer; }
.rec-counter.is-link:hover { border-color: var(--teal-200); background: var(--teal-50); }
/* Con el raton encima el contador coge fondo `--teal-50`: ahi el numero vuelve
   al texto oscuro, porque el acento suelto se lavaria sobre el tinte claro. */
.rec-counter.ok.is-link:hover .n { color: var(--teal-700); }
.rec-counter.is-link:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

.rec-updates { display: flex; flex-direction: column; max-height: 420px; overflow-y: auto; }
.rec-update { display: flex; gap: 10px; padding: 10px 0; border-bottom: 1px dashed var(--line); }
.rec-update:last-child { border-bottom: 0; }
.rec-update .ic {
  width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
  display: grid; place-items: center; background: var(--bg-2); color: var(--ink-3);
}
.rec-update .ic .lucide { width: 13px; height: 13px; }
.rec-update .tx { font-size: 13px; color: var(--ink-2); line-height: 1.4; }
.rec-update .when { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); margin-top: 2px; }

.rec-cal-title { text-transform: capitalize; font-size: 13px; font-weight: 500; text-align: center; margin-bottom: 8px; }
.rec-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.rec-dow { text-align: center; font-size: 10px; color: var(--ink-3); padding: 4px 0; text-transform: lowercase; }
.rec-day {
  position: relative; aspect-ratio: 1; border: 0; background: transparent;
  border-radius: 999px; cursor: pointer; font: inherit; font-size: 12px; color: var(--ink-2);
  display: grid; place-items: center;
}
.rec-day:hover { background: var(--bg-2); }
.rec-day.muted { color: var(--ink-4); }
.rec-day.today { font-weight: 600; color: var(--teal-txt); }
.rec-day.sel { background: var(--teal-600); color: #fff; }
.rec-day .dot {
  position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: var(--amber);
}
.rec-day.sel .dot { background: #fff; }

.rec-cal-list { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
.rec-slot { display: flex; align-items: center; gap: 10px; padding: 8px 0; cursor: pointer; }
.rec-slot:hover .nm { color: var(--teal-txt); }
.rec-slot .hh { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); width: 40px; flex-shrink: 0; }
.rec-slot .nm { font-size: 13px; color: var(--ink); font-weight: 500; }
.rec-slot .mt { font-size: 11px; color: var(--ink-3); }

/* --- Puestos de trabajo --- */
.rec-jobs { display: grid; grid-template-columns: 260px 1fr; gap: 16px; align-items: start; }

.job-view {
  display: flex; align-items: center; gap: 8px; width: 100%;
  border: 0; background: transparent; cursor: pointer; font: inherit;
  font-size: 13px; color: var(--ink-2); text-align: left;
  padding: 8px 10px; border-radius: 8px;
}
.job-view:hover { background: var(--bg-2); }
.job-view.active { background: var(--teal-50); color: var(--teal-700); font-weight: 500; }
.job-view span:first-child { flex: 1; min-width: 0; }
.job-view .count { font-family: var(--f-mono); font-size: 10.5px; color: var(--ink-3); }
.job-view.active .count { color: var(--teal-600); }

.job-table { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); overflow: hidden; }
.job-row {
  display: grid; grid-template-columns: minmax(260px, 1fr) 1.4fr; gap: 20px;
  padding: 18px 20px; border-bottom: 1px solid var(--line);
}
.job-row:last-child { border-bottom: 0; }
.job-head {
  padding: 10px 20px; background: var(--bg-elev);
  font-size: 10px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 500;
}
.job-title { font-size: 15px; font-weight: 600; color: var(--ink); line-height: 1.3; }
.job-meta {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 6px; font-size: 12px; color: var(--ink-3);
}
.job-actions { display: flex; gap: 6px; margin-top: 12px; }
.job-actions .btn.is-on { background: var(--teal-50); color: var(--teal-700); border-color: var(--teal-200); }

.job-funnel { display: flex; gap: 8px; flex-wrap: wrap; align-content: flex-start; }
.funnel-cell {
  min-width: 92px; border: 1px solid var(--line); background: var(--bg-2);
  border-radius: 10px; padding: 10px 12px; cursor: pointer; font: inherit;
  display: flex; flex-direction: column; gap: 4px; text-align: left;
}
.funnel-cell:hover { border-color: var(--teal-200); background: var(--teal-50); }
.funnel-cell .n {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--f-display); font-size: 19px; font-weight: 500; line-height: 1; color: var(--ink);
}
.funnel-cell .l { font-size: 10.5px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.funnel-cell .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.funnel-empty { font-size: 12px; color: var(--ink-4); align-self: center; }

.dot.s-all       { background: var(--teal-700); }
.dot.s-applied   { background: var(--amber); }
.dot.s-interview { background: var(--blue-fg); }
.dot.s-offer     { background: var(--plum-fg); }
.dot.s-hired     { background: var(--teal-500); }
.dot.s-rejected  { background: var(--coral); }

/* --- Candidaturas --- */
.kan-card.is-stale { border-left: 3px solid var(--amber); }
.cand-rating {
  display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--f-mono); font-size: 11px; color: var(--amber-fg);
}
.cand-rating .lucide { width: 11px; height: 11px; }
.is-stale-text { color: var(--amber-fg); font-weight: 600; }

.cand-section { border-top: 1px solid var(--line); padding-top: 14px; margin-top: 4px; }
.cand-section-hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cand-section-hd h4 {
  margin: 0; font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3); font-weight: 500;
}
.cand-iv { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px dashed var(--line); }
.cand-iv:last-child { border-bottom: 0; }
.cand-iv .ic { width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center; background: var(--bg-2); color: var(--ink-3); flex-shrink: 0; }
.cand-iv .ic .lucide { width: 13px; height: 13px; }
.cand-iv .wh { font-size: 12.5px; color: var(--ink); }
.cand-iv .mt { font-size: 11px; color: var(--ink-3); }
.cand-iv.needs-outcome .ic { background: var(--amber-bg); color: var(--amber-fg); }

/* --- Métricas --- */
.fun-row { display: grid; grid-template-columns: 92px 1fr 76px; gap: 10px; align-items: center; padding: 7px 0; }
.fun-row .fl { font-size: 12.5px; color: var(--ink-2); }
.fun-row .fb { height: 10px; background: var(--bg-elev); border-radius: 999px; overflow: hidden; }
.fun-row .fill { height: 100%; border-radius: 999px; }
.fun-row .fill.s-applied   { background: var(--amber); }
.fun-row .fill.s-interview { background: var(--blue-fg); }
.fun-row .fill.s-offer     { background: var(--plum-fg); }
.fun-row .fill.s-hired     { background: var(--teal-500); }
.fun-row .fv { font-family: var(--f-mono); font-size: 12px; color: var(--ink); text-align: right; }
.fun-row .pc { color: var(--ink-3); font-size: 10.5px; margin-left: 5px; }

.rec-bars { display: flex; gap: 10px; align-items: flex-end; height: 180px; padding-top: 10px; }
.rec-bar { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px; height: 100%; }
.rec-bar .col { flex: 1; width: 100%; display: flex; align-items: flex-end; }
.rec-bar .fill { width: 100%; background: var(--teal-500); border-radius: 6px 6px 0 0; min-height: 0; }
.rec-bar .n { font-family: var(--f-mono); font-size: 12px; color: var(--ink-2); }
.rec-bar .m { font-size: 10.5px; color: var(--ink-3); text-transform: capitalize; }

@media (max-width: 1200px) {
  .rec-overview { grid-template-columns: 1fr 1fr; }
  .rec-jobs { grid-template-columns: 220px 1fr; }
}
@media (max-width: 900px) {
  .rec-overview { grid-template-columns: 1fr; }
  .rec-jobs { grid-template-columns: 1fr; }
  .job-row { grid-template-columns: 1fr; gap: 14px; }
}

/* =============================================================================
   Onboarding · responsable, fechas y vista del empleado (punto 13)
   ========================================================================== */

/* Fecha límite vencida en una tarea sin completar. */
.task .meta.is-late { color: var(--rose-fg); font-weight: 500; }

/* Botón de responsable/fecha, visible al pasar por encima de la tarea. */
.ob-edit {
  border: 0; background: transparent; cursor: pointer; padding: 4px;
  color: var(--ink-3); border-radius: 6px; opacity: 0; transition: opacity .15s ease;
  flex-shrink: 0;
}
.task:hover .ob-edit, .ob-edit:focus-visible { opacity: 1; }
.ob-edit:hover { background: var(--bg-2); color: var(--ink); }

.ob-tpl {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; margin-bottom: 8px;
}
.ob-tpl .nm { font-size: 13.5px; font-weight: 500; color: var(--ink); }
.ob-tpl .mt { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }

.ob-portal { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 18px; }
.ob-cat {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3); font-weight: 500; margin-bottom: 6px;
}

/* Gestión de empresas (punto 12). */
.co-row {
  display: grid; grid-template-columns: 1.6fr 0.7fr 1.1fr 0.8fr 40px;
  gap: 10px; align-items: center; padding: 10px 4px;
  border-bottom: 1px solid var(--line); font-size: 13px;
}
.co-row:last-child { border-bottom: 0; }
.co-head {
  font-size: 10px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: 0.06em; font-weight: 500; border-bottom: 1px solid var(--line);
}
.co-row .input, .co-row .select { height: 32px; padding: 0 8px; font-size: 12.5px; }
@media (max-width: 760px) {
  .co-row { grid-template-columns: 1fr 1fr; row-gap: 6px; }
  .co-head { display: none; }
}

/* Restablecer contraseña desde el panel de admin. */
.rp-option {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px;
  margin-bottom: 8px; cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.rp-option:hover { border-color: var(--teal-200); }
.rp-option:has(input:checked) { border-color: var(--teal-500); background: var(--teal-50); }
.rp-option input { margin-top: 3px; flex-shrink: 0; }
.rp-option b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }
.rp-option small { display: block; font-size: 11.5px; color: var(--ink-3); line-height: 1.45; margin-top: 3px; }
/* EL FONDO VA CON SU TEXTO — ver «CUIDADO CON LOS TOKENS DE ACENTO» más abajo.
 * La opción marcada se pintaba con fondo `--teal-50` dejando el texto en `--ink`
 * y `--ink-3`. Como `applyAccent()` (js/shell.js) escribe la rampa CLARA del
 * acento en línea sobre `:root`, en tema oscuro ese fondo es casi blanco y
 * `--ink` también: contraste 1.01. Al elegir una de las dos opciones dejaba de
 * poder leerse cuál se había elegido. */
.rp-option:has(input:checked) b { color: var(--teal-700); }
.rp-option:has(input:checked) small { color: var(--teal-600); }

/* ===========================================
 * GESTIÓN DEL TIEMPO
 * Barra de herramientas común a las tres pestañas.
 * =========================================== */
.time-toolbar .time-month {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--ink); text-transform: capitalize;
}
.time-toolbar .time-month .lucide { width: 14px; height: 14px; color: var(--ink-3); }
.select-sm { height: 30px; padding: 0 26px 0 10px; font-size: 12px; width: auto; min-width: 120px; }
.mono { font-family: var(--f-mono); }

/* Conmutador de dos posiciones (Todo el equipo / Solo yo) */
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--surface); }
.seg button {
  border: 0; background: transparent; cursor: pointer; font: inherit;
  font-size: 12px; color: var(--ink-3); padding: 6px 12px; height: 30px;
}
.seg button + button { border-left: 1px solid var(--line); }
.seg button.on { background: var(--teal-50); color: var(--teal-700); font-weight: 500; }

/* Enlace «Mostrar desglose ›» dentro de una tarjeta */
.link-more {
  margin-left: auto; display: inline-flex; align-items: center; gap: 3px;
  border: 0; background: transparent; cursor: pointer; font: inherit;
  font-size: 12px; color: var(--ink-3);
}
.link-more:hover { color: var(--teal-txt); }
.link-more .lucide { width: 13px; height: 13px; }

/* Casilla de los desplegables de filtro */
.row-menu-item .ck {
  width: 15px; height: 15px; border-radius: 4px; flex-shrink: 0;
  border: 1.5px solid var(--line-2); background: transparent; position: relative;
}
.row-menu-item .ck.on { background: var(--teal-500); border-color: var(--teal-500); }
.row-menu-item .ck.on::after {
  content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}

/* Pestaña «Ausencia»: calendario + panel de saldos */
.abs-split { display: grid; grid-template-columns: 1fr 288px; gap: 16px; align-items: start; }
.bal-panel .bal-row { display: flex; align-items: center; gap: 10px; padding: 8px 6px; }
.bal-panel .bal-row + .bal-row { border-top: 1px solid var(--line); }
.bal-dot { width: 14px; height: 14px; border: 2px solid var(--teal-500); border-radius: 999px; flex-shrink: 0; }
.bal-name { font-size: 13px; color: var(--ink); font-weight: 500; }
.bal-sub { font-size: 11px; color: var(--ink-3); margin-top: 1px; }
@media (max-width: 1100px) { .abs-split { grid-template-columns: 1fr; } }

/* ===========================================
 * CALENDARIO DEL EQUIPO · línea de tiempo
 * -------------------------------------------
 * Scroll horizontal con la columna de personas fija (sticky). Cada fila es una
 * rejilla de un día por columna; las barras de ausencia ocupan varias columnas
 * y se reparten en carriles cuando se solapan.
 * =========================================== */
.tl-scroll { overflow-x: auto; overflow-y: hidden; }
.tl-inner {
  --tl-dw: 34px;            /* ancho de un día */
  --tl-name-w: 208px;       /* ancho de la columna de personas */
  position: relative;
  width: calc(var(--tl-name-w) + var(--tl-n) * var(--tl-dw));
  min-width: 100%;
}
.tl-head { display: flex; border-bottom: 1px solid var(--line); background: var(--surface); }
.tl-headcols { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.tl-hrow { display: grid; grid-template-columns: repeat(var(--tl-n), var(--tl-dw)); }
.tl-month {
  font-size: 12px; color: var(--ink-2); font-weight: 500; text-transform: capitalize;
  padding: 8px 8px 4px; white-space: nowrap; overflow: hidden;
}
.tl-week {
  font-family: var(--f-mono); font-size: 10px; color: var(--ink-4);
  padding: 0 6px 4px; border-left: 1px solid var(--line);
}
.tl-dcell {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 4px 0 6px; font-size: 11px; color: var(--ink-3);
  border-left: 1px solid var(--line);
}
.tl-dcell .dw { font-size: 9px; text-transform: lowercase; color: var(--ink-4); }
.tl-dcell .dn { font-family: var(--f-mono); font-size: 11px; color: var(--ink-2); }
.tl-dcell.wknd { background: var(--bg-2); }
.tl-dcell.hol .dn { color: var(--rose-fg); }
.tl-dcell.today .dn {
  background: var(--teal-500); color: #fff; border-radius: 999px;
  width: 20px; height: 20px; display: grid; place-items: center;
}

.tl-row { display: flex; border-bottom: 1px solid var(--line); }
.tl-row:last-child { border-bottom: 0; }
.tl-name {
  position: sticky; left: 0; z-index: 2;
  flex: 0 0 var(--tl-name-w); width: var(--tl-name-w);
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; background: var(--surface);
  border-right: 1px solid var(--line); cursor: pointer;
}
.tl-row:hover .tl-name { background: var(--bg-2); }
.tl-name .who { min-width: 0; }
.tl-name .nm { font-size: 13px; color: var(--ink); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-name .rl { font-size: 11px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-corner { cursor: default; align-items: flex-end; padding-bottom: 8px; }
.tl-corner .nm { font-size: 13px; }

.tl-track {
  flex: 1; display: grid;
  grid-template-columns: repeat(var(--tl-n), var(--tl-dw));
  grid-auto-rows: 22px; padding: 5px 0; position: relative; min-height: 44px;
}
.tl-day { border-left: 1px solid var(--line); }
.tl-day.wknd { background: var(--bg-2); }
.tl-day.hol { background: rgba(182,83,90,0.06); }

.tl-bar {
  margin: 1px 2px; border-radius: 5px; padding: 0 7px;
  font-size: 11px; font-weight: 500; line-height: 20px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer; z-index: 1; border-left: 3px solid currentColor;
}
.tl-bar.t-vac { background: var(--amber-bg); color: var(--amber-fg); }
.tl-bar.t-sick { background: var(--blue-bg); color: var(--blue-fg); }
.tl-bar.t-mat { background: var(--plum-bg); color: var(--plum-fg); }
.tl-bar.t-perm { background: var(--teal-50); color: var(--teal-700); }
.tl-bar.is-pending { border-left-style: dashed; opacity: 0.75; }

.tl-now { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--teal-500); z-index: 3; pointer-events: none; }

/* ===========================================
 * REGISTRO DE JORNADA
 * =========================================== */
.ts-cards { display: grid; grid-template-columns: 1.4fr 1.4fr 1fr; gap: 14px; margin-bottom: 18px; }
.ts-cards .side-card { padding: 16px 18px; }
.ts-inner { margin-top: 6px; }
.ts-bar { margin-top: 12px; height: 6px; background: var(--bg-2); border-radius: 999px; overflow: hidden; }
.ts-bar div { height: 100%; background: linear-gradient(90deg, var(--teal-400), var(--teal-500)); }
.ts-warn {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--f-mono); font-size: 11px; color: var(--amber-fg);
}
.ts-warn .lucide { width: 13px; height: 13px; }
@media (max-width: 1100px) { .ts-cards { grid-template-columns: 1fr; } }

.ts-table { overflow: hidden; }
.ts-hd, .ts-row {
  display: grid;
  /* La penúltima (22px) es la marca de «corregido»: un icono, no una columna
     de datos. Sale del Estado, que es donde estaba compitiendo con el estado
     de verdad. Ver tsTableHtml() en js/views.js. */
  grid-template-columns: 34px 1.7fr 0.7fr 24px 0.7fr 1.2fr 1.5fr 0.6fr 0.7fr 0.9fr 22px 36px;
  gap: 8px; align-items: center; padding: 11px 16px;
  border-bottom: 1px solid var(--line);
}
.ts-hd {
  font-size: 11px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: 0.06em; background: var(--bg-2);
}
.ts-row { font-size: 13px; }
.ts-row:last-child { border-bottom: 0; }
.ts-row:hover { background: var(--bg); }
.ts-row.wknd { background: var(--bg-2); }
.ts-row.hol { background: rgba(31,119,102,0.05); }
.ts-row.sel { background: var(--teal-50); }
.ts-row.today .ts-date { font-weight: 500; color: var(--ink); }
.ts-row .mono, .ts-hd .mono { font-family: var(--f-mono); color: var(--ink); }
.ts-row .mono:empty::before { content: "—"; color: var(--ink-4); }
.ts-date { color: var(--ink-2); display: flex; align-items: center; gap: 8px; min-width: 0; }
.ts-arrow { display: grid; place-items: center; color: var(--ink-4); }
.ts-arrow .lucide { width: 13px; height: 13px; }
.ts-progress { height: 8px; background: var(--bg-2); border-radius: 999px; overflow: hidden; }
.ts-progress div { height: 100%; background: var(--teal-500); }
.ts-tag { font-size: 10px; padding: 1px 6px; border-radius: 4px; white-space: nowrap; }
.ts-tag.hol { background: var(--rose-bg); color: var(--rose-fg); }
.ts-tag.abs { background: var(--amber-bg); color: var(--amber-fg); }
.ts-badge { display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 500; white-space: nowrap; }
.ts-badge.ok { background: var(--teal-50); color: var(--teal-600); }
.ts-badge.abs { background: var(--amber-bg); color: var(--amber-fg); }
.ts-badge.hol { background: var(--rose-bg); color: var(--rose-fg); }
.ts-badge.miss { background: var(--bg-2); color: var(--ink-4); }
.ts-check {
  width: 18px; height: 18px; border: 1.5px solid var(--line-2); border-radius: 5px;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background .12s, border-color .12s;
}
.ts-check.on { background: var(--teal-500); border-color: var(--teal-500); }
.ts-selbar {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px;
  background: var(--teal-50); border-bottom: 1px solid var(--line);
  font-size: 13px; color: var(--teal-700);
}
/* En pantallas estrechas la tabla se reordena en tarjeta. Antes se escondían
   cinco de las once columnas (descansos, ausencia y estado incluidos): en el
   móvil el registro de jornada dejaba de contar lo que había pasado ese día. */
@media (max-width: 900px) {
  .ts-hd { display: none; }
  .ts-row {
    grid-template-columns: 28px minmax(0, 1fr) auto auto;
    grid-template-areas:
      "chk date flag badge"
      "chk tin  arr  tout"
      "chk hrs  brk  abs"
      "chk prg  prg  act";
    row-gap: 6px; align-items: center; padding: 12px 14px;
  }
  .ts-row > div:nth-child(1)  { grid-area: chk; align-self: start; }
  .ts-row > div:nth-child(2)  { grid-area: date; font-weight: 500; }
  .ts-row > div:nth-child(3)  { grid-area: tin; }
  .ts-row > div:nth-child(4)  { grid-area: arr; }
  .ts-row > div:nth-child(5)  { grid-area: tout; }
  .ts-row > div:nth-child(6)  { grid-area: hrs; }
  .ts-row > div:nth-child(7)  { grid-area: prg; }
  .ts-row > div:nth-child(8)  { grid-area: brk; }
  .ts-row > div:nth-child(9)  { grid-area: abs; }
  .ts-row > div:nth-child(10) { grid-area: badge; justify-self: end; }
  .ts-row > div:nth-child(11) { grid-area: flag; justify-self: end; }
  .ts-row > div:nth-child(12) { grid-area: act; justify-self: end; }
  /* Sin cabecera visible, cada dato lleva su etiqueta. */
  .ts-row > div:nth-child(8):not(:empty)::before { content: "Descanso "; color: var(--ink-4); }
  .ts-row > div:nth-child(9):not(:empty)::before { content: "Ausencia "; color: var(--ink-4); }
}

/* Respeta a quien ha pedido al sistema que reduzca el movimiento: la app anima
   modales, toasts, olas del login y barras de progreso. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Foco visible y coherente para navegación por teclado. */
:focus-visible {
  outline: 2px solid var(--teal-500);
  outline-offset: 2px;
  border-radius: 4px;
}
.modal:focus { outline: none; }

/* Controles que son <div> con rol asignado: sin esto no se pueden usar sin ratón. */
[role="checkbox"], [role="button"], [role="link"] { cursor: pointer; }
[role="checkbox"]:focus-visible, [role="button"]:focus-visible {
  outline: 2px solid var(--teal-500); outline-offset: 2px;
}

/* Aviso de fichaje sin cerrar / día por validar. */
.ts-badge.warn { background: var(--amber-bg); color: var(--amber-fg); }
/* Jornada validada por el responsable (o por un admin). Es el estado bueno, y
   se lee más fuerte que «Registrado» porque dice algo más: que alguien la ha
   mirado. Ver la migración `…_aprobacion_de_jornadas.sql`. */
.ts-badge.appr { background: var(--teal-500); color: #fff; }
/* La marca de «corregido»: un icono en su propia columna, no una etiqueta en la
   de Estado. Conserva el color ciruela que tenía la etiqueta, que es lo que la
   hacía reconocible de un vistazo. */
.ts-flag { display: inline-flex; color: var(--plum-fg); }
.ts-flag .lucide { width: 13px; height: 13px; }

/* Densidad: el control de Tweaks existía pero no cambiaba nada. */
:root[data-density="comfortable"] .table td,
:root[data-density="comfortable"] .table th { padding-top: 15px; padding-bottom: 15px; }
:root[data-density="comfortable"] .ts-row,
:root[data-density="comfortable"] .ts-hd { padding-top: 16px; padding-bottom: 16px; }
:root[data-density="comfortable"] .field-row { padding-top: 14px; padding-bottom: 14px; }
:root[data-density="comfortable"] .req-row { padding-top: 16px; padding-bottom: 16px; }

/* Aviso de privacidad del login. */
.legal-foot { margin-top: 10px; text-align: center; font-size: 11px; color: var(--ink-4); }
.legal-foot button {
  border: 0; background: transparent; color: var(--ink-3); cursor: pointer;
  font: inherit; text-decoration: underline; padding: 0 2px;
}

/* Las tres colas de «Aprobaciones» se cambian por el desplegable del menú
   lateral, que se abre al pasar el ratón por encima. Esta fila de pestañas es el
   plan B para cuando no hay ratón: en un teléfono el desplegable no se abre
   nunca y sin ella dos de las tres colas serían inalcanzables.

   La condición es la CAPACIDAD DEL PUNTERO y no el ancho de la ventana, porque
   el problema es el puntero: una tableta apaisada tiene sitio de sobra y sigue
   sin poder abrir el desplegable. Ver la cabecera de js/views-approvals.js. */
@media (hover: hover) and (pointer: fine) {
  .appr-tabs { display: none; }
}

/* Fila de jornada pulsable: abre el fichaje de ese día (o de la selección). */
.ts-row.clickable { cursor: pointer; }
.ts-row.clickable:hover { background: var(--teal-50); }
.ts-row.clickable.sel:hover { background: var(--teal-100); }
.ts-multi-days {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px;
  max-height: 96px; overflow-y: auto;
}
.ts-multi-days span {
  font-family: var(--f-mono); font-size: 11px; padding: 2px 8px;
  border-radius: 999px; background: var(--bg-2); color: var(--ink-2);
}

/* ===== Widget de fichaje rápido (pie de la barra lateral) ===== */
.clock-widget {
  margin: 0 12px 10px; padding: 12px; border-radius: 10px;
  background: var(--surface); border: 1px solid var(--line);
  flex-shrink: 0;
}
.cw-hd { display: flex; align-items: center; gap: 6px; }
.cw-dot {
  width: 7px; height: 7px; border-radius: 999px; flex-shrink: 0;
  background: var(--ink-4);
}
.cw-dot.on { background: var(--teal-500); box-shadow: 0 0 0 3px rgba(31,119,102,0.15); animation: cwPulse 2s ease-in-out infinite; }
@keyframes cwPulse { 50% { box-shadow: 0 0 0 5px rgba(31,119,102,0.05); } }
.cw-title { font-size: 11px; color: var(--ink-2); font-weight: 500; }
.cw-state {
  margin-left: auto; font-family: var(--f-mono); font-size: 9px;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-4);
}
.cw-time {
  font-family: var(--f-mono); font-size: 24px; letter-spacing: -0.02em;
  color: var(--ink); margin-top: 6px; font-variant-numeric: tabular-nums;
}
.cw-time.over { color: var(--teal-txt); }
.cw-sub { font-size: 10px; color: var(--ink-3); margin-top: 2px; }
.cw-bar {
  height: 4px; background: var(--bg-2); border-radius: 999px;
  overflow: hidden; margin-top: 8px;
}
.cw-bar div { height: 100%; background: linear-gradient(90deg, var(--teal-400), var(--teal-500)); transition: width .4s ease; }
.cw-actions { display: flex; gap: 6px; margin-top: 10px; }
.cw-btn {
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--line-2); background: var(--bg-elev); color: var(--ink-2);
  display: grid; place-items: center; transition: background .12s, border-color .12s, color .12s;
}
.cw-btn .lucide { width: 14px; height: 14px; }
.cw-btn:hover:not(:disabled) { background: var(--bg-2); }
.cw-btn:disabled { opacity: 0.4; cursor: default; }
.cw-btn.play { background: var(--teal-500); border-color: var(--teal-500); color: #fff; }
.cw-btn.play:hover:not(:disabled) { background: var(--teal-600); border-color: var(--teal-600); }
.cw-btn.play.stop { background: var(--rose-fg); border-color: var(--rose-fg); }
.cw-btn.play.stop:hover:not(:disabled) { filter: brightness(0.92); background: var(--rose-fg); }
.cw-btn.ghost { margin-left: auto; border-color: transparent; background: transparent; color: var(--ink-3); }
.cw-btn.ghost:hover { background: var(--bg-2); }

/* ===== Ficha del empleado · pestaña de ausencias ===== */
.emp-abs-layout { display: grid; grid-template-columns: 1fr 280px; gap: 20px; align-items: start; }
@media (max-width: 1000px) { .emp-abs-layout { grid-template-columns: 1fr; } }

/* Punto de color de los saldos: comparte las clases t-* de las barras. */
.cal-key {
  width: 14px; height: 14px; border-radius: 999px; flex-shrink: 0;
  background: var(--teal-50); border: 2px solid var(--teal-600);
}
.cal-key.t-vac { background: var(--amber-bg); border-color: var(--amber-fg); }
.cal-key.t-sick { background: var(--blue-bg); border-color: var(--blue-fg); }
.cal-key.t-mat { background: var(--plum-bg); border-color: var(--plum-fg); }
.cal-key.t-perm { background: var(--teal-50); border-color: var(--teal-700); }

/* Solicitud aún sin aprobar: rayada, y con el arranque en línea discontinua.
   Las rayas eran un negro translúcido, así que sobre el tema oscuro no se veían
   y una solicitud pendiente era indistinguible de una aprobada. `currentColor`
   toma el color del propio tipo de ausencia y funciona en los dos temas. */
.cal-bar.pend {
  background-image: repeating-linear-gradient(45deg, currentColor 0 1px, transparent 1px 6px);
  opacity: 0.92;
}
.cal-bar.pend.bar-start { border-left: 2px dashed currentColor; padding-left: 6px; }

/* Enlace de salto al contenido. Fuera de la pantalla hasta que recibe el foco:
   con teclado, entrar en la lista de empleados obligaba a pasar por los quince
   elementos de la navegación lateral en cada cambio de vista. */
.skip-link {
  position: absolute; left: -9999px; top: 8px; z-index: 300;
  background: var(--surface); color: var(--ink-1);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 10px 16px; font-size: 13px; font-weight: 500;
  box-shadow: var(--sh-2); text-decoration: none;
}
.skip-link:focus { left: 8px; }
.content:focus { outline: none; }

/* Esqueleto de carga (UI-08). Antes del panel no había nada: la pantalla de
   acceso se quedaba congelada mientras corrían las doce consultas iniciales. */
.sk {
  display: block;
  background: linear-gradient(90deg, var(--bg-elev) 25%, var(--line) 37%, var(--bg-elev) 63%);
  background-size: 400% 100%;
  border-radius: 6px;
  animation: sk-shimmer 1.4s ease infinite;
}
@keyframes sk-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
.sk-spin { animation: sk-rotate 1s linear infinite; }
@keyframes sk-rotate { to { transform: rotate(360deg); } }
.boot-sk .side, .boot-sk .header { pointer-events: none; }

/* Texto solo para lectores de pantalla: lo que la animación no puede decir. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* =============================================================================
   Guía y políticas (base de conocimiento)
   -----------------------------------------------------------------------------
   El cuerpo de un artículo se genera desde texto plano (kbRender en core.js) y
   solo produce estas cinco clases. Van aquí y no como atributos `style=` porque
   son texto largo, que es donde el interlineado y los márgenes de verdad
   importan — y porque los `style=` inline son justo lo que SEC-21 quiere reducir.
   ========================================================================== */

/* Entrada del índice lateral. */
.kb-item { border-radius: 8px; transition: background .12s ease; }
.kb-item:hover { background: var(--bg-elev); }
.kb-item.on { font-weight: 500; }
.kb-item:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }

/* Cuerpo del artículo. */
.kb-body { color: var(--ink-2); max-width: 68ch; }
.kb-body .kb-p { margin: 0 0 12px; }
.kb-body .kb-h { font-family: var(--f-display); font-weight: 500; letter-spacing: -0.01em;
                 color: var(--ink); margin: 22px 0 8px; }
.kb-body h3.kb-h { font-size: 17px; }
.kb-body h4.kb-h { font-size: 15px; }
.kb-body h5.kb-h { font-size: 13.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.kb-body .kb-h:first-child { margin-top: 0; }
.kb-body .kb-list { margin: 0 0 12px; padding-left: 20px; }
.kb-body .kb-list li { margin: 0 0 5px; }

/* =============================================================================
   Herramientas · Tableros (js/tools-boards.js)
   -----------------------------------------------------------------------------
   El prefijo `tb-` es de «tool board», y NO se toca el `kan-` de al lado: aquel
   es el pipeline de reclutamiento, con sus cinco columnas fijas en una rejilla.
   Este tablero tiene tantas columnas como quiera quien lo monte, así que es una
   fila que se desplaza en horizontal y no una rejilla que se reparte el ancho.

   Lo importante de aquí abajo, y la razón de que no valga el kanban existente:
   la CABECERA DE CADA COLUMNA ES PERMANENTE. La columna es un contenedor en
   columna con altura máxima; lo que hace scroll es `.tb-col-body`, no la
   sección entera, así que `.tb-col-hd` se queda donde está por construcción. El
   `position: sticky` que lleva encima es para el otro caso: cuando la ventana
   es tan baja que quien hace scroll es la página.
   ========================================================================== */

/* ----- Lista de tableros ----- */
.tb-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
}

.tb-tile {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-3); padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
  cursor: pointer; transition: border-color .12s ease, box-shadow .12s ease;
}
.tb-tile:hover { border-color: var(--teal-200); box-shadow: var(--sh-2); }
.tb-tile:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.tb-tile.archived { opacity: .72; }

.tb-tile-hd { display: flex; align-items: flex-start; gap: 8px; }
.tb-tile-hd h3 {
  margin: 0; flex: 1; font-family: var(--f-display); font-weight: 500;
  font-size: 16px; letter-spacing: -0.01em; color: var(--ink); line-height: 1.25;
}
.tb-tile-desc {
  margin: 0; font-size: 12.5px; color: var(--ink-3); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.tb-tile-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tb-tile-ft {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line);
}
.tb-tile-meta { font-family: var(--f-mono); font-size: 10.5px; color: var(--ink-3); }

/* Los avatares se solapan un poco: dicen «estas personas» sin ocupar una fila. */
.tb-avatars { display: inline-flex; align-items: center; }
.tb-avatars .avatar { margin-left: -6px; border: 2px solid var(--surface); }
.tb-avatars .avatar:first-child { margin-left: 0; }

/* ----- El tablero ----- */
.tb-board {
  display: flex; align-items: flex-start; gap: 12px;
  overflow-x: auto; padding-bottom: 10px;
  /* Sin esto, arrastrar una tarjeta cerca del borde arrastra la página. */
  scroll-snap-type: x proximity;
}
.tb-board > * { scroll-snap-align: start; }

.tb-col {
  flex: 0 0 288px; max-width: 288px;
  display: flex; flex-direction: column;
  max-height: calc(100vh - 250px);
  background: var(--bg-elev); border: 1px solid var(--line);
  border-radius: var(--r-3);
}

.tb-col-hd {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  padding: 12px 10px 10px 14px;
  border-bottom: 1px solid var(--line);
  border-radius: var(--r-3) var(--r-3) 0 0;
  background: var(--bg-elev);
}
.tb-col-hd h4 {
  margin: 0; flex: 1; min-width: 0;
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-2); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-col-count {
  font-family: var(--f-mono); font-size: 10px; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 8px;
}

.tb-col-body {
  flex: 1; min-height: 60px; overflow-y: auto;
  padding: 10px; display: flex; flex-direction: column; gap: 8px;
}
.tb-col-body.drop-target { background: var(--teal-50); }
.tb-col-empty {
  padding: 14px 8px; text-align: center; color: var(--ink-4); font-size: 11px;
  border: 1px dashed var(--line-2); border-radius: 8px;
}

.tb-col-new {
  display: flex; align-items: center; gap: 6px;
  margin: 0 10px 10px; padding: 8px 10px;
  background: transparent; border: 0; border-radius: 8px;
  color: var(--ink-3); font-family: inherit; font-size: 12px; cursor: pointer;
}
.tb-col-new:hover { background: var(--bg-2); color: var(--ink); }
.tb-col-new .lucide { width: 14px; height: 14px; }

/* La columna fantasma del final: añadir una más sin salir del tablero. */
.tb-col-add {
  flex: 0 0 216px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 56px; padding: 16px;
  background: transparent; border: 1px dashed var(--line-2); border-radius: var(--r-3);
  color: var(--ink-3); font-family: inherit; font-size: 12.5px; cursor: pointer;
}
.tb-col-add:hover { border-color: var(--teal-200); color: var(--teal-700); background: var(--teal-50); }
.tb-col-add .lucide { width: 15px; height: 15px; }

/* ----- Tarjetas ----- */
.tb-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  padding: 10px; cursor: pointer; text-align: left;
  box-shadow: var(--sh-1); transition: border-color .12s ease, transform .08s ease;
}
.tb-card:hover { border-color: var(--teal-200); }
.tb-card:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 1px; }
.tb-card[draggable="true"] { user-select: none; }
.tb-card.dragging { opacity: .4; }
/* La raya que marca dónde va a caer la tarjeta que se arrastra. */
.tb-card.drop-before { box-shadow: 0 -3px 0 -1px var(--teal-500), var(--sh-1); }

.tb-card-cover {
  display: block; width: 100%; max-height: 132px; object-fit: cover;
  border-radius: 5px; margin-bottom: 8px; background: var(--bg-2);
}
.tb-card-title { font-size: 13px; color: var(--ink); line-height: 1.35; }
.tb-card-body {
  font-size: 11.5px; color: var(--ink-3); line-height: 1.45; margin-top: 5px;
  white-space: pre-wrap;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.tb-card-ft { display: flex; align-items: center; gap: 6px; margin-top: 9px; }
.tb-card-meta {
  display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--f-mono); font-size: 10px; color: var(--ink-3);
}
.tb-card-meta .lucide { width: 12px; height: 12px; }

/* ----- Compositor de tarjeta ----- */
.tb-compose {
  background: var(--surface); border: 1px solid var(--teal-200);
  border-radius: 8px; padding: 8px;
}
.tb-compose .textarea { font-size: 13px; resize: vertical; }
.tb-compose-ft { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
.tb-hint { font-family: var(--f-mono); font-size: 10px; color: var(--ink-4); }

/* ----- Colores de columna y de tarjeta -----
   La columna se colorea por la cabecera —que es lo que se ve siempre— y la
   tarjeta por una banda a la izquierda: pintarle el fondo entero le quitaría
   contraste al texto en el modo oscuro. */
/* La clave que se guarda en la base de datos sigue siendo `teal` y NO se
   renombra: hay columnas y tarjetas escritas con ese valor. Lo que cambia es lo
   que PINTA — un verde fijo, no el acento. Ver BOARD_COLORS en
   js/tools-boards.js. */
.tb-col.tb-c-teal  .tb-col-hd { background: var(--green-bg); border-bottom-color: var(--green-bg); }
.tb-col.tb-c-amber .tb-col-hd { background: var(--amber-bg); border-bottom-color: var(--amber-bg); }
.tb-col.tb-c-blue  .tb-col-hd { background: var(--blue-bg);  border-bottom-color: var(--blue-bg); }
.tb-col.tb-c-plum  .tb-col-hd { background: var(--plum-bg);  border-bottom-color: var(--plum-bg); }
.tb-col.tb-c-rose  .tb-col-hd { background: var(--rose-bg);  border-bottom-color: var(--rose-bg); }
.tb-col.tb-c-teal  .tb-col-hd h4 { color: var(--green-fg); }
.tb-col.tb-c-amber .tb-col-hd h4 { color: var(--amber-fg); }
.tb-col.tb-c-blue  .tb-col-hd h4 { color: var(--blue-fg); }
.tb-col.tb-c-plum  .tb-col-hd h4 { color: var(--plum-fg); }
.tb-col.tb-c-rose  .tb-col-hd h4 { color: var(--rose-fg); }
/* El color libre no tiene tinta propia en la hoja: la escribe boardColorEstilo()
   sobre la cabecera. El `h4` tiene `color` propio —`--ink-2`— asi que no la
   heredaria; esta linea es lo que se lo permite. */
.tb-col.tb-c-libre .tb-col-hd h4 { color: inherit; }

.tb-card.tb-c-teal  { border-left: 3px solid var(--green); }
.tb-card.tb-c-amber { border-left: 3px solid var(--amber); }

/* Los puntos del selector: el mismo par fondo/tinta que pinta la cabecera de la
   columna, para que lo que se elige sea exactamente lo que se ve despues. */
.tb-sw-teal  .ff-sw-dot { background: var(--green-bg); box-shadow: inset 0 0 0 1px var(--green-fg); }
.tb-sw-amber .ff-sw-dot { background: var(--amber-bg); box-shadow: inset 0 0 0 1px var(--amber-fg); }
.tb-sw-blue  .ff-sw-dot { background: var(--blue-bg);  box-shadow: inset 0 0 0 1px var(--blue-fg); }
.tb-sw-plum  .ff-sw-dot { background: var(--plum-bg);  box-shadow: inset 0 0 0 1px var(--plum-fg); }
.tb-sw-rose  .ff-sw-dot { background: var(--rose-bg);  box-shadow: inset 0 0 0 1px var(--rose-fg); }
.tb-card.tb-c-blue  { border-left: 3px solid var(--blue-fg); }
.tb-card.tb-c-plum  { border-left: 3px solid var(--plum-fg); }
.tb-card.tb-c-rose  { border-left: 3px solid var(--rose); }
/* El color libre no tiene clase de color: el tono lo pone un `style=` en linea
   (ver boardColorEstilo en js/tools-boards.js). Lo unico que hace falta aqui es
   que exista el filete al que ese estilo le cambia el color. */
.tb-card.tb-c-libre { border-left: 3px solid currentColor; }

/* ----- Adjuntos (dentro del modal de la tarjeta) ----- */
.tb-drop {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 18px 14px; text-align: center;
  border: 1px dashed var(--line-2); border-radius: var(--r-2);
  background: var(--bg-elev); color: var(--ink-3); font-size: 12.5px;
}
.tb-drop .lucide { width: 20px; height: 20px; color: var(--ink-4); }
.tb-drop.over { border-color: var(--teal-500); background: var(--teal-50); color: var(--teal-700); }
.tb-drop.busy { opacity: .6; pointer-events: none; }
.tb-drop:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

.tb-att-grid { display: grid; gap: 8px; }
.tb-att {
  display: flex; align-items: center; gap: 10px;
  padding: 8px; border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--surface);
}
.tb-att img {
  width: 52px; height: 52px; object-fit: cover; border-radius: 6px;
  background: var(--bg-2); flex: 0 0 auto;
}
.tb-att-ic {
  width: 52px; height: 52px; flex: 0 0 auto; display: grid; place-items: center;
  border-radius: 6px; background: var(--teal-50); color: var(--teal-700);
  border: 1px solid var(--teal-100);
}
.tb-att-ic .lucide { width: 18px; height: 18px; }
.tb-att-info { flex: 1; min-width: 0; }
.tb-att-name {
  font-size: 12.5px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-att-meta { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); margin-top: 3px; }
.tb-att-acts { display: flex; gap: 4px; }
.tb-att-acts .icon-btn { width: 28px; height: 28px; }

/* ----- Responsables de una tarjeta -----
   Una tarjeta la puede llevar más de una persona. La lista se limita en alto y
   hace scroll: un tablero compartido con veinte empujaría el resto del
   formulario —los adjuntos, el botón de guardar— fuera de la pantalla. */
.tb-asig {
  max-height: 168px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--surface);
}
.tb-asig-row {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 9px; cursor: pointer;
  border-bottom: 1px solid var(--line);
}
.tb-asig-row:last-child { border-bottom: 0; }
.tb-asig-row:hover { background: var(--bg-2); }
/* La hoja no da estilo global a las casillas —cada pantalla usa la del
   navegador—, pero aquí van seis o veinte seguidas y el rojo de serie del tema
   oscuro se lee como un error. Solo el color; la casilla sigue siendo la
   nativa, que es la que sabe teclearse. */
.tb-asig-row input {
  width: 16px; height: 16px; flex: 0 0 auto; cursor: pointer;
  accent-color: var(--teal-600);
}
.tb-asig-row input:disabled { cursor: default; }
.tb-asig-name {
  flex: 1; min-width: 0; font-size: 13px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Alguien que lleva la tarjeta pero ya no ve el tablero: se le retiró el acceso
   después de asignársela. Sale para poder quitarlo, no para poder ponerlo. */
.tb-asig-row.sin-acceso .tb-asig-name { color: var(--ink-3); }

/* El «+2» de la tarjeta. Es un avatar más de la pila, así que hereda su tamaño
   y su borde; solo cambia el relleno. */
.tb-avatar-mas {
  background: var(--bg-2); color: var(--ink-3);
  font-family: var(--f-mono); font-size: 9.5px;
}

/* ----- Reparto de accesos ----- */
.tb-share-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 4px; border-bottom: 1px solid var(--line);
}
.tb-share-row:last-child { border-bottom: 0; }
.tb-share-name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tb-share-name strong {
  font-weight: 500; font-size: 13px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-share-name span { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); }
.tb-share-empty { padding: 14px 4px; font-size: 12px; color: var(--ink-4); }

/* ----- Cabecera de la sección -----
   `.h-actions` dentro de `.page-head` NO lleva regla en la hoja general (solo
   la llevan las de `.header` y `.card-hd`), así que es un div de bloque y sus
   botones fluyen en línea: con cuatro elementos se partía en dos renglones y el
   de opciones caía solo, debajo, pegado al subtítulo.

   Se arregla aquí y no allí a propósito: `.page-head .h-actions` lo usan otras
   diez pantallas y darle un flex global cambiaría la maqueta de todas de golpe.
   Esto solo toca a Tableros. */
.tb-head { align-items: flex-start; flex-wrap: wrap; }
.tb-head-main { flex: 1 1 280px; min-width: 0; }
.tb-head-actions {
  display: flex; align-items: center; gap: 8px;
  margin-left: auto; flex-wrap: nowrap;
}
/* En pantallas estrechas el grupo entero baja de línea, pero no se despieza. */
@media (max-width: 720px) {
  .tb-head-actions { margin-left: 0; flex-wrap: wrap; }
}

/* ----- Peso de cada tablero -----
   La barra mide contra los MISMOS 50 MB que el marcador de arriba, no contra sí
   misma: así las de todos los tableros suman la de arriba y se ve de un vistazo
   cuál se está llevando el sitio. Una barra medida contra su propio máximo no
   diría nada. */
.tb-tile-peso {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--f-mono); font-size: 10px; color: var(--ink-3);
}
.tb-tile-peso-bar {
  flex: 1; height: 4px; border-radius: 999px;
  background: var(--bg-2); overflow: hidden;
}
.tb-tile-peso-bar > div { height: 100%; border-radius: 999px; background: var(--teal-400); }
.tb-tile-peso-otros { color: var(--ink-4); }
.tb-tile-peso.warn   .tb-tile-peso-bar > div { background: var(--amber); }
.tb-tile-peso.warn   { color: var(--amber-fg); }
.tb-tile-peso.danger .tb-tile-peso-bar > div { background: var(--rose); }
.tb-tile-peso.danger { color: var(--rose-fg); }

/* Cuando no se puede leer el uso. Discreto, pero no invisible: un hueco en
   blanco es justo lo que impide darse cuenta de que falta algo. */
.tb-quota-off {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px; margin-bottom: 16px;
  border: 1px dashed var(--line-2); border-radius: var(--r-2);
  background: var(--bg-elev); color: var(--ink-4); font-size: 12px;
}
.tb-quota-off .lucide { width: 15px; height: 15px; flex: 0 0 auto; }

/* ----- Marcador de topes -----
   Se pinta SIEMPRE, no solo cerca del límite: quien está en 2 de 10 tampoco
   sabía que hubiera un tope, y enterarse el día que choca con él es la peor
   manera. Verde hasta el 75 %, ámbar hasta el 90 %, rojo a partir de ahí. */
.tb-quota {
  display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  padding: 14px 16px; margin-bottom: 16px;
}
.tb-quota-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.tb-quota-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-3);
}
.tb-quota-num { font-family: var(--f-mono); font-size: 11.5px; color: var(--ink-2); }
.tb-quota-bar {
  height: 5px; margin: 7px 0 6px; border-radius: 999px;
  background: var(--bg-2); overflow: hidden;
}
.tb-quota-bar > div { height: 100%; border-radius: 999px; background: var(--teal-500); transition: width .2s ease; }
.tb-quota-note { font-size: 11px; color: var(--ink-4); line-height: 1.45; }
.tb-quota-item.warn   .tb-quota-bar > div { background: var(--amber); }
.tb-quota-item.warn   .tb-quota-num { color: var(--amber-fg); }
.tb-quota-item.danger .tb-quota-bar > div { background: var(--rose); }
.tb-quota-item.danger .tb-quota-num { color: var(--rose-fg); }
.tb-quota-item.danger .tb-quota-note { color: var(--ink-3); }

/* ----- Móvil -----
   Una columna casi entera de ancho: en un teléfono, media columna a cada lado
   no deja leer ninguna de las dos. */
@media (max-width: 720px) {
  .tb-col { flex-basis: 84vw; max-width: 84vw; max-height: calc(100vh - 210px); }
  .tb-col-add { flex-basis: 84vw; }
}

/* ===========================================================================
   AJUSTES DE PLATAFORMA · PANEL DE HERRAMIENTAS (prefijo ts-)
   ---------------------------------------------------------------------------
   El panel desde el que se administran los topes de la sección Herramientas y
   se barre el almacén. Prefijo propio y no reutilización de `tb-`: aquello es
   la pantalla de tableros, que la ve todo el mundo, y esto es administración —
   si un día cambia el marcador de tableros, esto no se tiene que enterar.
   =========================================================================== */

/* Aviso de migración sin aplicar. Ocupa el sitio de los campos: lo que hay que
   entender es que los topes existen y se aplican, y que lo único que falta es
   poder editarlos. */
.ts-aviso {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--r-2);
  background: var(--amber-bg); border: 1px solid var(--amber-bg);
  font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
}
.ts-aviso .lucide { width: 16px; height: 16px; flex: 0 0 auto; color: var(--amber-fg); margin-top: 2px; }
.ts-aviso code { font-family: var(--f-mono); font-size: 11px; }

/* Uso agregado de toda la instalación. Son recuentos, nunca una lista: ver la
   cabecera del panel en js/admin.js. */
.ts-uso {
  display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  padding: 12px 14px; margin-bottom: 16px;
  background: var(--bg-elev); border-radius: var(--r-2);
}
.ts-uso-item { min-width: 0; }
.ts-uso-num { font-family: var(--f-mono); font-size: 17px; color: var(--ink); line-height: 1.2; }
.ts-uso-lbl { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.ts-uso-nota { font-size: 10.5px; color: var(--ink-4); font-family: var(--f-mono); margin-top: 2px; }
.ts-uso-cargando, .ts-uso-off {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--ink-4);
}
.ts-uso-off .lucide { width: 15px; height: 15px; flex: 0 0 auto; }

/* La cuenta del peor caso, debajo de los dos campos y recalculada al teclear. */
.ts-peor {
  margin-top: 12px; padding: 10px 12px;
  border-left: 2px solid var(--line-2); background: var(--bg-elev);
  border-radius: 0 var(--r-2) var(--r-2) 0;
  font-size: 12px; line-height: 1.55; color: var(--ink-3);
}

/* ----- Huérfanos ----- */
.ts-huerfanos { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line); }
.ts-huerfanos-hd { display: flex; gap: 16px; align-items: flex-start; justify-content: space-between; }
.ts-huerfanos-hd > button { flex: 0 0 auto; }

.ts-orph-msg {
  display: flex; gap: 14px; align-items: center; justify-content: space-between;
  margin-top: 14px; padding: 11px 13px; border-radius: var(--r-2);
  background: var(--bg-elev); font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
}
.ts-orph-msg > button { flex: 0 0 auto; }
.ts-orph-hay { background: var(--amber-bg); }
.ts-orph-err { background: var(--rose-bg); color: var(--ink-2); }

/* La lista es informativa: dice CUÁNTOS y CUÁNTO ocupan, no invita a auditar
   fichero a fichero. La ruta empieza por el id del tablero y no dice nada de su
   contenido — que es justo lo que tiene que seguir pasando. */
.ts-orph-list {
  margin-top: 8px; max-height: 220px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--r-2);
}
.ts-orph-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center;
  padding: 7px 12px; border-bottom: 1px solid var(--line);
  font-family: var(--f-mono); font-size: 11px; color: var(--ink-3);
}
.ts-orph-row:last-child { border-bottom: 0; }
.ts-orph-path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-4); }

@media (max-width: 720px) {
  .ts-huerfanos-hd { flex-direction: column; }
  .ts-orph-msg { flex-direction: column; align-items: flex-start; }
}

/* ===========================================================================
   Herramientas · Conversores (js/tools-converters.js) · prefijo cn-
   ---------------------------------------------------------------------------
   Prefijo propio y no reutilización de `tb-` (Tableros) ni de `ts-` (el panel
   de administración de Herramientas): son tres pantallas distintas del mismo
   área, y compartir clases significaría que retocar una mueve las otras dos.

   Casi todo de aquí abajo son DOS PIEZAS que se repiten en los nueve paneles:

     .cn-fx     la fila de controles de arriba. Es un flex que envuelve, no una
                rejilla: cada conversor tiene un número distinto de campos y de
                anchos distintos, y una rejilla de columnas fijas dejaría huecos
                en unos y apretaría otros.
     .cn-tabla  la lista de resultados de abajo, siempre igual: rótulo a la
                izquierda, valor en monoespaciada a la derecha. Que las nueve
                pantallas den el resultado en el mismo sitio y con la misma
                forma es lo que hace que se puedan usar sin leer nada.
   =========================================================================== */

/* ----- La barra de conversores -----
   Se desplaza en horizontal en pantallas estrechas en lugar de partirse en
   cuatro renglones: nueve pastillas apiladas empujarían el panel fuera de la
   pantalla y habría que hacer scroll antes de ver un solo campo. */
.cn-rail {
  display: flex; gap: 6px; margin-bottom: 16px;
  overflow-x: auto; padding-bottom: 4px;
  scrollbar-width: thin;
}
.cn-chip {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  padding: 7px 13px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font: inherit; font-size: 12.5px; color: var(--ink-2);
  cursor: pointer; white-space: nowrap;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.cn-chip .lucide { width: 14px; height: 14px; }
.cn-chip:hover { border-color: var(--teal-200); color: var(--ink); }
.cn-chip.on {
  background: var(--teal-50); border-color: var(--teal-200);
  color: var(--teal-700); font-weight: 500;
}
.cn-chip:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

/* La descripción del conversor activo, a la derecha del rótulo de la tarjeta.
   Se esconde en móvil: ahí el rótulo ya ocupa la línea entera. */
.cn-sub { font-size: 11.5px; color: var(--ink-4); text-align: right; }

/* ----- Controles ----- */
.cn-fx { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.cn-fx-fin { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }

.cn-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1 1 150px; }
.cn-field-ancho { flex: 2 1 260px; }
.cn-field-pleno { flex-basis: 100%; }
.cn-lbl {
  font-size: 11px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.05em;
  display: flex; align-items: center; gap: 6px;
}
.cn-lbl-b { text-transform: none; letter-spacing: 0; color: var(--ink-4); font-family: var(--f-mono); }
/* La casilla de la base libre vive dentro de su propia etiqueta. */
.cn-mini {
  width: 52px; height: 22px; padding: 0 4px; font: inherit; font-size: 11px;
  font-family: var(--f-mono); border: 1px solid var(--line); border-radius: var(--r-1);
  background: var(--surface); color: var(--ink);
}

/* El botón de intercambiar: se alinea con la base de los campos, no con su
   rótulo, para que quede a la altura de las cajas y no flotando encima. */
.cn-swap { flex: 0 0 auto; margin-bottom: 1px; }

.cn-check {
  display: flex; align-items: center; gap: 7px; flex: 0 0 auto;
  font-size: 12.5px; color: var(--ink-2); cursor: pointer; margin-bottom: 9px;
}

/* ----- El resultado grande (moneda) ----- */
.cn-result {
  display: flex; align-items: center; gap: 14px;
  margin-top: 18px; padding: 16px 18px;
  background: var(--bg-elev); border-radius: var(--r-3);
}
.cn-result-num {
  font-family: var(--f-mono); font-size: 26px; line-height: 1.2; color: var(--ink);
  min-width: 0; overflow-wrap: anywhere;
}
.cn-result-sub {
  font-family: var(--f-mono); font-size: 11px; color: var(--ink-4);
  margin-left: auto; text-align: right; white-space: pre-wrap;
}
.cn-result .cn-copy { flex: 0 0 auto; }

/* ----- La tabla de resultados -----
   Rótulo y valor en dos columnas, con el valor pegado a la derecha y en
   monoespaciada: así las cifras de todas las filas quedan alineadas y se
   comparan de un vistazo, que es para lo que está la tabla. */
.cn-tabla { margin-top: 18px; }
.cn-tabla-hd {
  font-size: 11.5px; color: var(--ink-3); padding: 0 2px 8px;
  border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
.cn-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 8px 2px; border-bottom: 1px solid var(--line); min-width: 0;
}
.cn-fila:last-child { border-bottom: 0; }
.cn-fila-k {
  font-size: 12.5px; color: var(--ink-2); display: flex; flex-direction: column; gap: 1px;
  flex: 0 1 auto; min-width: 0;
}
.cn-fila-n { font-size: 10.5px; color: var(--ink-4); font-weight: 400; }
.cn-fila-v {
  display: flex; align-items: center; gap: 8px; justify-content: flex-end;
  font-family: var(--f-mono); font-size: 13px; color: var(--ink);
  text-align: right; min-width: 0; overflow-wrap: anywhere;
}
/* Una forma de texto puede ser un párrafo entero: se corta en una línea para
   que la tabla siga siendo una tabla. El valor completo se copia igual. */
.cn-recorta {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch;
}
.cn-dia {
  font-family: var(--f-sans); font-size: 10.5px; color: var(--amber-fg);
  background: var(--amber-bg); border-radius: 999px; padding: 1px 7px; flex: 0 0 auto;
}
.cn-quitar { opacity: 0; transition: opacity .12s ease; }
.cn-fila:hover .cn-quitar, .cn-quitar:focus-visible { opacity: 1; }

.cn-vacio { padding: 24px 4px; font-size: 12.5px; color: var(--ink-4); text-align: center; }

/* ----- Entrada y salida en dos cuadros ----- */
.cn-io { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 18px; }
.cn-io-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cn-io-hd { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 26px; }
.cn-code { font-family: var(--f-mono); font-size: 12px; line-height: 1.55; }
textarea.cn-code { white-space: pre; overflow-wrap: normal; overflow-x: auto; }

/* Un input con su botón de copiar dentro del mismo renglón. */
.cn-con-copia { display: flex; align-items: center; gap: 4px; }
.cn-con-copia .input { flex: 1 1 auto; min-width: 0; }

/* ----- Bases numéricas ----- */
.cn-bases { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* ----- Color ----- */
.cn-color { display: flex; gap: 18px; align-items: stretch; flex-wrap: wrap; }
.cn-muestra {
  flex: 0 0 120px; min-height: 120px; border-radius: var(--r-3);
  border: 1px solid var(--line); box-shadow: var(--sh-1) inset;
}
.cn-color-campos { flex: 1 1 320px; display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.cn-picker { padding: 3px; cursor: pointer; }

/* ----- Avisos -----
   Mismo patrón que `.ts-aviso` del panel de administración, pero con los tres
   tonos que esta pantalla necesita: neutro mientras se cargan las tasas, ámbar
   cuando se están usando las de ayer y rojo cuando la entrada no se entiende. */
.cn-aviso {
  display: flex; gap: 10px; align-items: flex-start;
  margin-top: 14px; padding: 11px 13px; border-radius: var(--r-2);
  background: var(--bg-elev); font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
}
.cn-aviso .lucide { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 1px; color: var(--ink-4); }
.cn-aviso code { font-family: var(--f-mono); font-size: 11px; }
.cn-aviso.warn { background: var(--amber-bg); }
.cn-aviso.warn .lucide { color: var(--amber-fg); }
.cn-aviso.err { background: var(--rose-bg); }
.cn-aviso.err .lucide { color: var(--rose-fg); }

/* El pie de cada panel: la letra pequeña de lo que hace y de lo que no. No es
   decorativa —dice qué subconjunto de YAML se entiende, o de qué día son las
   tasas—, así que se lee, pero no compite con el resultado. */
.cn-pie {
  margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 11.5px; line-height: 1.6; color: var(--ink-4);
}
.cn-pie code { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); }
.cn-pie .btn { margin-left: 6px; vertical-align: -1px; }

/* ----- Móvil -----
   Los dos cuadros de texto se apilan: media pantalla de ancho para un JSON no
   deja leer ni una línea entera. */
@media (max-width: 860px) {
  .cn-io { grid-template-columns: 1fr; }
  .cn-sub { display: none; }
  .cn-result { flex-wrap: wrap; }
  .cn-result-sub { margin-left: 0; text-align: left; flex-basis: 100%; }
  .cn-recorta { max-width: 24ch; }
  /* Sin ratón no hay «pasar por encima»: el botón de quitar una zona horaria
     tiene que estar siempre visible o no se puede quitar ninguna. */
  .cn-quitar { opacity: 1; }
}

/* ===========================================================================
   Herramientas · Code Lab (js/tools-codelab.js) · prefijo lab-
   ---------------------------------------------------------------------------
   Cuarto prefijo del área, por el mismo motivo que los otros tres: `tb-`
   (Tableros), `ts-` (administración de Herramientas), `cn-` (Conversores) y
   `lab-` son pantallas distintas, y compartir clases significaría que retocar
   una mueve las demás.

   LA BARRA, LOS CAMPOS, LA TABLA DE RESULTADOS Y LOS AVISOS SON UN CALCO
   DELIBERADO de los de Conversores. No se reutilizan las clases, pero sí las
   medidas y los colores: son las dos pantallas vecinas del mismo grupo del
   menú, y que un panel se vea igual en las dos es lo que hace que la segunda
   no haya que aprenderla.

   LO QUE SÍ ES PROPIO DE AQUÍ son las piezas que Conversores no necesitaba:
   el cuadro de resultado con desplazamiento (.lab-pre), las filas de clave y
   valor editables (.lab-kv), el diff a dos colores, la zona de soltar ficheros
   y la ficha de cada uno.
   =========================================================================== */

/* ----- La barra de herramientas ----- */
.lab-rail {
  display: flex; gap: 6px; margin-bottom: 16px;
  overflow-x: auto; padding-bottom: 4px;
  scrollbar-width: thin;
}
.lab-chip {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  padding: 7px 13px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font: inherit; font-size: 12.5px; color: var(--ink-2);
  cursor: pointer; white-space: nowrap;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.lab-chip .lucide { width: 14px; height: 14px; }
.lab-chip:hover { border-color: var(--teal-200); color: var(--ink); }
.lab-chip.on {
  background: var(--teal-50); border-color: var(--teal-200);
  color: var(--teal-700); font-weight: 500;
}
.lab-chip:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

.lab-sub { font-size: 11.5px; color: var(--ink-4); text-align: right; }

/* ----- La explicación de la herramienta -----
   Va dentro de la tarjeta, entre la cabecera y el panel, y SIEMPRE VISIBLE: un
   «¿qué es esto?» plegado solo lo abre quien ya sospecha que lo necesita, y aquí
   el problema es justo el contrario. Se pinta en letra pequeña y en tono apagado
   para que no compita con los campos, con el filete de color como única señal de
   que es una nota y no una parte del formulario. */
.lab-intro {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 0 0 18px; padding: 12px 14px;
  border-radius: var(--r-2); background: var(--bg-elev);
  border-left: 3px solid var(--teal-200);
}
.lab-intro .lucide { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 2px; color: var(--teal-txt); }
.lab-intro-que { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--ink-2); }
/* Los ejemplos. En pantalla ancha llegan abiertos y en móvil plegados: ver el
   marcado que genera labIntroHtml(). */
.lab-intro-mas { margin-top: 6px; }
.lab-intro-mas > summary {
  font-size: 11.5px; color: var(--ink-3); cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 5px; font-weight: 500;
}
.lab-intro-mas > summary::before { content: "▸"; font-size: 9px; }
.lab-intro-mas[open] > summary::before { content: "▾"; }
.lab-intro-mas > summary::-webkit-details-marker { display: none; }
.lab-intro-casos { margin: 4px 0 0; font-size: 11.5px; line-height: 1.55; color: var(--ink-4); }
/* Cada caso en su renglón y no todos seguidos separados por puntos: son frases
   largas, y de corrido no se distingue dónde acaba una y empieza la siguiente. */
.lab-intro-casos span { display: block; position: relative; padding-left: 13px; }
.lab-intro-casos span::before {
  content: "·"; position: absolute; left: 4px; font-weight: 700; color: var(--teal-200);
}

/* ----- Controles ----- */
.lab-fx { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.lab-fx-fin { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
/* Las casillas de las banderas de una expresión regular van pegadas a su campo,
   no separadas por un filete: son parte de la misma pregunta. */
.lab-fx-apretado { margin-top: 10px; gap: 10px; align-items: center; }

.lab-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1 1 150px; }
.lab-field-ancho { flex: 2 1 260px; }
.lab-field-pleno { flex-basis: 100%; }
.lab-lbl {
  font-size: 11px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.05em;
  display: flex; align-items: center; gap: 8px;
}
.lab-lbl code { font-family: var(--f-mono); text-transform: none; letter-spacing: 0; font-size: 10.5px; }
.lab-lbl .lab-fila-n { text-transform: none; letter-spacing: 0; }

/* Un desplegable pequeño dentro de la propia etiqueta: los ejemplos de regex y
   de cron, y el tipo de cuerpo de una petición. No es un campo más del
   formulario, es un atajo para rellenar el que tiene al lado. */
.lab-mini-sel {
  height: 22px; padding: 0 4px; font: inherit; font-size: 11px;
  text-transform: none; letter-spacing: 0;
  border: 1px solid var(--line); border-radius: var(--r-1);
  background: var(--surface); color: var(--ink-2); cursor: pointer;
}

/* La expresión regular entre barras, como se escribe en cualquier lenguaje.
   Las barras son parte del campo, no adorno: dejan claro dónde empieza y acaba
   el patrón y dónde van las banderas. */
.lab-slash { display: flex; align-items: center; gap: 6px; }
.lab-slash .input { flex: 1 1 auto; min-width: 0; }
.lab-slash-b { font-family: var(--f-mono); font-size: 15px; color: var(--ink-4); flex: 0 0 auto; }

.lab-check {
  display: flex; align-items: center; gap: 7px; flex: 0 0 auto;
  font-size: 12.5px; color: var(--ink-2); cursor: pointer;
}
.lab-check .lab-code { font-size: 13px; }

/* Grupo de botones excluyentes (buscar/reemplazar, curl/fetch/PowerShell…).
   Se usa un grupo pegado y no un desplegable porque son dos o cuatro opciones y
   verlas todas a la vez ahorra un clic cada vez. */
.lab-seg { display: inline-flex; flex: 0 0 auto; border: 1px solid var(--line); border-radius: var(--r-2); overflow: hidden; }
.lab-seg-b {
  padding: 6px 12px; border: 0; background: var(--surface);
  font: inherit; font-size: 12.5px; color: var(--ink-3); cursor: pointer;
  border-right: 1px solid var(--line);
  transition: background .12s ease, color .12s ease;
}
.lab-seg-b:last-child { border-right: 0; }
.lab-seg-b:hover { color: var(--ink); }
.lab-seg-b.on { background: var(--teal-50); color: var(--teal-700); font-weight: 500; }
.lab-seg-b:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }

/* ----- Entrada y salida en dos cuadros ----- */
.lab-io { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.lab-io-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.lab-io-hd { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 26px; }
.lab-code { font-family: var(--f-mono); font-size: 12px; line-height: 1.55; }
textarea.lab-code { white-space: pre; overflow-wrap: normal; overflow-x: auto; }

/* El cuadro de resultado. No es un <textarea>: lleva dentro marcas de color
   —las coincidencias de una regex, los tres trozos de un JWT— y un textarea no
   admite HTML. Se le pone el mismo aspecto para que la pareja se vea simétrica.

   `white-space: pre-wrap` y no `pre`: aquí lo que se enseña es el resultado, y
   una línea larga cortada a la vista se lee; una que se sale por la derecha,
   no. En los campos de entrada es al revés y por eso allí sí es `pre`. */
.lab-pre {
  border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--bg-elev); padding: 10px 12px;
  min-height: 120px; max-height: 460px; overflow: auto;
  white-space: pre-wrap; overflow-wrap: anywhere;
  color: var(--ink);
}
.lab-pre-corto { min-height: 60px; max-height: 260px; }

/* El espejo invisible del resultado. El botón de copiar va por selector y lee
   el `value` de un campo; los cuadros de resultado no lo tienen, así que cada
   panel mantiene al lado un textarea con el texto plano. Se saca del flujo sin
   `display:none` para que siga siendo un nodo con valor. */
.lab-oculto {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; border: 0; overflow: hidden; clip: rect(0 0 0 0);
}

/* ----- La tabla de resultados ----- */
.lab-tabla { margin-top: 18px; }
.lab-tabla-hd {
  font-size: 11.5px; color: var(--ink-3); padding: 0 2px 8px;
  border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
.lab-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 8px 2px; border-bottom: 1px solid var(--line); min-width: 0;
}
.lab-fila:last-child { border-bottom: 0; }
.lab-fila-alta { align-items: flex-start; }
.lab-fila-k {
  font-size: 12.5px; color: var(--ink-2); display: flex; flex-direction: column; gap: 1px;
  flex: 0 1 auto; min-width: 0;
}
.lab-fila-n { font-size: 10.5px; color: var(--ink-4); font-weight: 400; }
.lab-fila-v {
  display: flex; align-items: center; gap: 8px; justify-content: flex-end;
  font-family: var(--f-mono); font-size: 13px; color: var(--ink);
  text-align: right; min-width: 0; overflow-wrap: anywhere;
}
/* Una coincidencia con grupos necesita dos renglones: el texto encontrado
   arriba y sus grupos debajo. */
.lab-fila-v-col { flex-direction: column; align-items: flex-end; gap: 4px; }
.lab-recorta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }

.lab-grupos { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
.lab-grupo {
  font-family: var(--f-mono); font-size: 11px; color: var(--ink-2);
  background: var(--bg-elev); border-radius: var(--r-1); padding: 1px 6px;
}
.lab-grupo b { color: var(--teal-txt); font-weight: 500; }

.lab-vacio { padding: 24px 4px; font-size: 12.5px; color: var(--ink-4); text-align: center; }

/* ----- Avisos ----- */
.lab-aviso {
  display: flex; gap: 10px; align-items: flex-start;
  margin-top: 14px; padding: 11px 13px; border-radius: var(--r-2);
  background: var(--bg-elev); font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
}
.lab-aviso .lucide { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 1px; color: var(--ink-4); }
.lab-aviso code { font-family: var(--f-mono); font-size: 11px; }
.lab-aviso.warn { background: var(--amber-bg); }
.lab-aviso.warn .lucide { color: var(--amber-fg); }
.lab-aviso.err { background: var(--rose-bg); }
.lab-aviso.err .lucide { color: var(--rose-fg); }
.lab-aviso.ok { background: var(--teal-50); }
.lab-aviso.ok .lucide { color: var(--teal-700); }

/* ----- Marcas sobre el texto ----- */
.lab-mark { background: var(--teal-50); color: var(--ink); border-radius: 2px; padding: 0 1px; }
/* Una coincidencia vacía no tiene texto que pintar: se marca con una rayita, o
   `\b` y `a*` parecerían no encontrar nada. */
.lab-mark-vacia { display: inline-block; width: 2px; height: 1.1em; vertical-align: -2px; background: var(--teal-400); padding: 0; }
.lab-mark-mas { background: var(--teal-50); color: var(--ink); border-radius: 2px; }
.lab-mark-menos { background: var(--rose-bg); color: var(--ink); border-radius: 2px; }

/* ----- Chuleta y desplegables de detalle ----- */
.lab-chuleta { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 12px; }
.lab-chuleta > summary {
  font-size: 12px; color: var(--ink-3); cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 6px;
}
.lab-chuleta > summary::before { content: "▸"; font-size: 10px; }
.lab-chuleta[open] > summary::before { content: "▾"; }
.lab-chuleta > summary::-webkit-details-marker { display: none; }
.lab-chuleta-cuerpo {
  margin-top: 10px; display: grid; gap: 4px 16px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.lab-chuleta-fila { display: flex; gap: 10px; align-items: baseline; font-size: 11.5px; color: var(--ink-3); }
.lab-chuleta-fila code {
  font-family: var(--f-mono); font-size: 11px; color: var(--ink);
  background: var(--bg-elev); border-radius: var(--r-1); padding: 1px 5px;
  flex: 0 0 auto; min-width: 96px;
}

/* ----- El pie de cada panel ----- */
.lab-pie {
  margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 11.5px; line-height: 1.6; color: var(--ink-4);
}
.lab-pie code { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); }
.lab-pie b { color: var(--ink-3); font-weight: 500; }

/* ----- JWT · los tres trozos ----- */
.lab-jwt-color {
  margin-top: 12px; padding: 10px 12px; border-radius: var(--r-2);
  background: var(--bg-elev); overflow-wrap: anywhere; line-height: 1.6;
}
.lab-jwt-h { color: var(--rose-fg); }
.lab-jwt-p { color: var(--teal-txt); }
.lab-jwt-s { color: var(--blue-fg); }

/* ----- Filas de clave y valor editables ----- */
.lab-kv {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 4px 0;
}
.lab-kv .input { flex: 1 1 160px; min-width: 0; }
.lab-kv-n { font-size: 11px; color: var(--ink-4); font-family: var(--f-mono); flex: 0 0 18px; }
.lab-kv-pie { flex-basis: 100%; font-size: 10.5px; color: var(--ink-4); padding-left: 26px; }
.lab-kv-pie code { font-family: var(--f-mono); }

/* ----- Vista previa de Markdown -----
   Estilos mínimos y contenidos: es una previsualización dentro de una tarjeta,
   no una página. Todo se acota al cuadro para que un título de nivel 1 no
   descoloque el panel entero. */
.lab-md-vista { font-family: var(--f-sans); font-size: 13.5px; line-height: 1.65; white-space: normal; }
.lab-md-vista > :first-child { margin-top: 0; }
.lab-md-vista > :last-child { margin-bottom: 0; }
.lab-md-vista h1 { font-size: 20px; margin: 18px 0 8px; }
.lab-md-vista h2 { font-size: 17px; margin: 16px 0 8px; }
.lab-md-vista h3 { font-size: 15px; margin: 14px 0 6px; }
.lab-md-vista h4, .lab-md-vista h5, .lab-md-vista h6 { font-size: 13.5px; margin: 12px 0 6px; }
.lab-md-vista p { margin: 0 0 10px; }
.lab-md-vista ul, .lab-md-vista ol { margin: 0 0 10px; padding-left: 22px; }
.lab-md-vista li { margin: 2px 0; }
.lab-md-vista code { font-family: var(--f-mono); font-size: 11.5px; background: var(--surface); border-radius: var(--r-1); padding: 1px 5px; }
.lab-md-vista pre {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-2);
  padding: 10px 12px; overflow-x: auto; margin: 0 0 10px;
}
.lab-md-vista pre code { background: none; padding: 0; font-size: 11.5px; }
.lab-md-vista blockquote {
  margin: 0 0 10px; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--teal-200); color: var(--ink-2);
}
.lab-md-vista hr { border: 0; border-top: 1px solid var(--line); margin: 14px 0; }
.lab-md-vista a { color: var(--teal-txt); }
.lab-md-vista img { max-width: 100%; height: auto; border-radius: var(--r-1); }
.lab-md-vista table { border-collapse: collapse; width: 100%; margin: 0 0 10px; font-size: 12.5px; }
.lab-md-vista th, .lab-md-vista td { border: 1px solid var(--line); padding: 5px 9px; text-align: left; }
.lab-md-vista th { background: var(--surface); font-weight: 500; }

/* ----- Diff -----
   Cuatro columnas: número de línea del antes, del después, el signo y el texto.
   Los dos números y no uno solo porque lo que uno busca en un diff es «en qué
   línea de MI fichero está esto», y las dos numeraciones se separan en cuanto
   hay un cambio. */
.lab-diff {
  margin-top: 18px; border: 1px solid var(--line); border-radius: var(--r-2);
  overflow: auto; max-height: 560px; background: var(--bg-elev);
}
.lab-diff-f {
  display: grid; grid-template-columns: 48px 48px 18px 1fr;
  font-family: var(--f-mono); font-size: 12px; line-height: 1.6;
  border-bottom: 1px solid var(--line);
}
.lab-diff-f:last-child { border-bottom: 0; }
.lab-diff-f.mas { background: var(--teal-50); }
.lab-diff-f.menos { background: var(--rose-bg); }
.lab-diff-n {
  text-align: right; padding: 1px 8px 1px 0; color: var(--ink-4); font-size: 11px;
  user-select: none; border-right: 1px solid var(--line);
}
.lab-diff-s { text-align: center; color: var(--ink-3); user-select: none; }
.lab-diff-t { padding: 1px 10px 1px 4px; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ink); }

/* ----- Cron ----- */
.lab-cron-in { font-size: 15px; letter-spacing: 0.08em; }
.lab-cron-frase {
  margin-top: 16px; padding: 14px 16px; border-radius: var(--r-3);
  background: var(--bg-elev); font-size: 15px; line-height: 1.5; color: var(--ink);
}

/* ----- Zona de soltar ficheros ----- */
.lab-suelta {
  display: flex; align-items: center; gap: 14px;
  padding: 22px 20px; border-radius: var(--r-3);
  border: 1.5px dashed var(--line-2); background: var(--bg-elev);
  cursor: pointer; font-size: 13px; color: var(--ink-2);
  transition: border-color .12s ease, background .12s ease;
}
.lab-suelta:hover, .lab-suelta:focus-visible { border-color: var(--teal-200); }
.lab-suelta.on { border-color: var(--teal-500); background: var(--teal-50); }
.lab-suelta .lucide { width: 22px; height: 22px; color: var(--ink-4); flex: 0 0 auto; }

/* ----- Ficha de un fichero ----- */
.lab-ficha {
  margin-top: 16px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--r-3); background: var(--bg-elev);
}
.lab-ficha-hd {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-family: var(--f-mono); font-size: 12.5px; color: var(--ink);
  overflow-wrap: anywhere;
}
.lab-ficha-cuerpo { display: flex; gap: 16px; align-items: flex-start; }
.lab-ficha-datos { flex: 1 1 auto; min-width: 0; }
/* La miniatura se acota por los dos lados: un PNG de 4.000 píxeles de alto
   dejaría la ficha de datos fuera de la pantalla. */
.lab-ficha-img {
  flex: 0 0 auto; width: 120px; max-height: 160px; object-fit: contain;
  border: 1px solid var(--line); border-radius: var(--r-2); background: var(--surface);
  margin-top: 14px;
}

/* ----- Identificadores ----- */
.lab-ids-in { font-size: 15px; letter-spacing: 0.06em; }

/* ----- Móvil -----
   Mismo criterio que en Conversores: los cuadros de texto se apilan, porque
   media pantalla de ancho para un JSON no deja leer ni una línea entera. */
@media (max-width: 860px) {
  .lab-io { grid-template-columns: 1fr; }
  .lab-sub { display: none; }
  .lab-recorta { max-width: 24ch; }
  .lab-fila { flex-wrap: wrap; gap: 6px; }
  .lab-fila-v { justify-content: flex-start; text-align: left; }
  .lab-fila-v-col { align-items: flex-start; }
  .lab-grupos { justify-content: flex-start; }
  /* En una pantalla estrecha, dos numeraciones y el signo se comen el texto. */
  .lab-diff-f { grid-template-columns: 34px 34px 16px 1fr; }
  .lab-cron-frase { font-size: 13.5px; }
  .lab-cron-in, .lab-ids-in { font-size: 14px; }
  .lab-ficha-cuerpo { flex-direction: column; }
  .lab-ficha-img { margin-top: 0; width: 100%; max-height: 200px; }
  .lab-chuleta-fila code { min-width: 72px; }
}


/* ===========================================================================
   Herramientas · Portapapeles (js/tools-clipboard.js) · prefijo sc-
   ---------------------------------------------------------------------------
   Quinto prefijo del área, por el mismo motivo que los otros cuatro: `tb-`
   (Tableros), `ts-` (administración de Herramientas), `cn-` (Conversores),
   `lab-` (Code Lab) y `sc-` son pantallas distintas, y compartir clases
   significaría que retocar una mueve las demás.

   LAS MEDIDAS Y LOS COLORES SON LOS DE CONVERSORES Y CODE LAB, deliberadamente:
   son las tres pantallas vecinas del mismo grupo del menú. Se reutiliza incluso
   `.lab-intro` y `.lab-check` tal cual —son la nota de cabecera y la casilla,
   idénticas en las tres— porque duplicarlas con otro prefijo sería copiar
   veinte líneas para que digan lo mismo.

   LO QUE SÍ ES PROPIO DE AQUÍ son las tres piezas que ninguna de las otras
   necesitaba: la zona de pegado, la insignia del tipo detectado —con su
   variante de duda, que es la que evita afirmar lo que no se sabe— y la ficha
   de cada entrada del historial.
   =========================================================================== */

.sc-sub { font-size: 11.5px; color: var(--ink-4); text-align: right; }

/* ----- La zona de pegado -----
   Es lo primero y lo más grande de la pantalla porque es lo único que hay que
   hacer para usarla. El borde discontinuo dice «suelta algo aquí» sin tener que
   escribirlo. */
.sc-zona {
  border: 1.5px dashed var(--line); border-radius: var(--r-3);
  padding: 10px; background: var(--bg-elev);
  transition: border-color .12s ease, background .12s ease;
}
.sc-zona:focus-within { border-color: var(--teal-200); border-style: solid; }
/* Mientras se arrastra algo por encima. Sin esta señal no se sabe si la página
   va a aceptar lo que traes o a abrirlo en una pestaña. */
.sc-zona.sobre { border-color: var(--teal-500); border-style: solid; background: var(--teal-50); }

.sc-in {
  width: 100%; background: var(--surface);
  font-family: var(--f-mono); font-size: 12.5px; line-height: 1.55;
  resize: vertical; min-height: 96px;
}

.sc-zona-pie {
  display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center;
  justify-content: space-between; margin-top: 8px; padding: 0 2px;
  font-size: 11.5px; color: var(--ink-4);
}
.sc-atajos { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.sc-atajos kbd {
  font-family: var(--f-mono); font-size: 10.5px; padding: 1px 5px;
  border: 1px solid var(--line); border-bottom-width: 2px; border-radius: var(--r-1);
  background: var(--surface); color: var(--ink-3);
}

.sc-ejemplos { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sc-ej {
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-3);
  border-radius: 999px; padding: 2px 9px; font: inherit; font-size: 11px;
  cursor: pointer; transition: border-color .12s ease, color .12s ease;
}
.sc-ej:hover { border-color: var(--teal-200); color: var(--teal-txt); }
.sc-ej:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

/* ----- El análisis ----- */
.sc-analisis { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }

/* La insignia del tipo. Con el color de la casa cuando la detección es firme y
   en gris cuando no llega al umbral: el color es la señal de «esto lo sé», y
   gastarlo también en las sospechas lo dejaría sin significado. */
.sc-insignia {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px; border-radius: 999px;
  background: var(--teal-50); border: 1px solid var(--teal-200); color: var(--teal-700);
  font-size: 13px; font-weight: 500;
}
.sc-insignia .lucide { width: 15px; height: 15px; }
.sc-insignia.duda { background: var(--bg-elev); border-color: var(--line); color: var(--ink-3); font-weight: 400; }
.sc-insignia.mini { padding: 3px 9px; font-size: 11.5px; }
.sc-insignia.mini .lucide { width: 13px; height: 13px; }
/* El porcentaje de confianza, en monoespaciada y apagado: es un dato de apoyo,
   no el rótulo. */
.sc-insignia-c {
  font-family: var(--f-mono); font-size: 10.5px; opacity: 0.65;
  padding-left: 7px; border-left: 1px solid currentColor;
}

/* El distintivo de «esto no se guarda». En ámbar y no en rojo: no es un error
   ni algo que hayas hecho mal, es una decisión de la herramienta que te
   conviene ver. */
.sc-sensible {
  display: inline-flex; align-items: center; gap: 5px; margin-left: 8px;
  padding: 3px 9px; border-radius: 999px;
  background: var(--amber-bg); color: var(--amber-fg);
  font-size: 11px; font-weight: 500;
}
.sc-sensible .lucide { width: 12px; height: 12px; }
.sc-sensible.mini { margin-left: 0; font-size: 10.5px; padding: 2px 7px; }

.sc-analisis-resumen { margin-top: 8px; font-size: 12.5px; color: var(--ink-3); }

.sc-alt { margin-top: 6px; font-size: 11.5px; color: var(--ink-4); }
.sc-alt-b {
  border: 0; background: transparent; padding: 0; font: inherit; font-size: 11.5px;
  color: var(--teal-txt); text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}

/* El aviso de contenido que no se deja leer. Mismo filete lateral que
   `.lab-intro`: en esta aplicación, un bloque con filete a la izquierda es una
   nota sobre lo que estás mirando. */
.sc-error {
  display: flex; gap: 10px; align-items: flex-start;
  margin-top: 12px; padding: 11px 13px;
  border-radius: var(--r-2); background: var(--amber-bg);
  border-left: 3px solid var(--amber);
}
.sc-error .lucide { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 1px; color: var(--amber-fg); }
.sc-error p { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--ink-2); }
.sc-error summary { margin-top: 5px; font-size: 11.5px; color: var(--ink-4); cursor: pointer; }
.sc-error code { display: block; margin-top: 5px; font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); }

/* ----- Los botones de acción -----
   Cada uno lleva debajo A DÓNDE LLEVA en letra pequeña, y no es adorno: todos
   sacan de esta pantalla, y salir de una pantalla sin haberlo pedido es lo más
   molesto que puede hacer una herramienta. Con el destino escrito, pulsar es
   una decisión. */
.sc-acciones { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; align-items: stretch; }

.sc-accion {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 13px; border-radius: var(--r-2);
  border: 1px solid var(--line); background: var(--surface);
  font: inherit; font-size: 12.5px; color: var(--ink); cursor: pointer; text-align: left;
  transition: border-color .12s ease, background .12s ease;
}
.sc-accion:hover { border-color: var(--teal-200); background: var(--teal-50); }
.sc-accion:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.sc-accion .lucide { width: 15px; height: 15px; flex: 0 0 auto; color: var(--ink-3); }
.sc-accion:hover .lucide { color: var(--teal-700); }
.sc-accion small { display: block; font-size: 10.5px; color: var(--ink-4); line-height: 1.3; }
.sc-accion span { display: block; line-height: 1.3; }

/* Las que no caben. Plegadas para que no haya quince botones a la vez: con
   quince delante no se elige ninguno. */
.sc-mas { position: relative; }
.sc-mas > summary {
  display: flex; align-items: center; gap: 6px; list-style: none;
  padding: 8px 13px; border-radius: var(--r-2);
  border: 1px dashed var(--line); background: transparent;
  font-size: 12.5px; color: var(--ink-3); cursor: pointer; height: 100%; box-sizing: border-box;
}
.sc-mas > summary::-webkit-details-marker { display: none; }
.sc-mas > summary:hover { border-color: var(--teal-200); color: var(--teal-txt); }
.sc-mas > summary .lucide { width: 15px; height: 15px; }
.sc-mas-caja { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }

.sc-analisis-pie {
  display: flex; gap: 8px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line);
}

/* ----- La barra del historial ----- */
.sc-barra { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 12px; }
.sc-buscar { position: relative; flex: 1 1 240px; min-width: 0; }
.sc-buscar .lucide {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--ink-4); pointer-events: none;
}
.sc-buscar .input { padding-left: 32px; }
/* El filtro de favoritos es un interruptor, no un botón de una sola pulsación:
   tiene que verse si está puesto. */
.sc-barra .btn.on { background: var(--teal-50); color: var(--teal-700); }
.sc-barra .btn.on .lucide { fill: currentColor; }

.sc-ajustes { margin-bottom: 14px; }
.sc-ajustes > summary {
  font-size: 11.5px; color: var(--ink-3); cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 5px; font-weight: 500;
}
.sc-ajustes > summary::before { content: "▸"; font-size: 9px; }
.sc-ajustes[open] > summary::before { content: "▾"; }
.sc-ajustes > summary::-webkit-details-marker { display: none; }
.sc-ajustes-cuerpo {
  display: flex; flex-direction: column; gap: 12px;
  margin-top: 10px; padding: 13px 15px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
}
.sc-tope { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ink-2); }

/* La nota de privacidad. Va SIEMPRE VISIBLE dentro de los ajustes y no en un
   pie de página en gris claro: es la respuesta a la única pregunta que importa
   de una herramienta que guarda lo que pegas, y quien la busca la busca aquí. */
.sc-privacidad {
  display: flex; gap: 10px; align-items: flex-start;
  padding-top: 11px; border-top: 1px solid var(--line);
}
.sc-privacidad .lucide { width: 15px; height: 15px; flex: 0 0 auto; margin-top: 2px; color: var(--teal-txt); }
.sc-privacidad p { margin: 0 0 6px; font-size: 11.5px; line-height: 1.6; color: var(--ink-3); }
.sc-privacidad p:last-child { margin-bottom: 0; }
.sc-privacidad strong { color: var(--ink-2); font-weight: 500; }

/* ----- Una entrada del historial ----- */
.sc-entrada {
  border: 1px solid var(--line); border-radius: var(--r-2);
  padding: 12px 14px; margin-bottom: 10px; background: var(--surface);
}
.sc-entrada:last-child { margin-bottom: 0; }
/* Un favorito se marca con un filete a la izquierda y no con un fondo distinto:
   el fondo compite con la vista previa, que es lo que hay que leer. */
.sc-entrada.fav { border-left: 3px solid var(--teal-500); }

.sc-entrada-hd { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sc-entrada-hora { margin-left: auto; font-size: 11px; color: var(--ink-4); }

.sc-icono {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: 0 0 auto;
  border: 0; background: transparent; border-radius: var(--r-1);
  color: var(--ink-4); cursor: pointer;
}
.sc-icono:hover { background: var(--bg-elev); color: var(--ink-2); }
.sc-icono .lucide { width: 14px; height: 14px; }
.sc-icono.on { color: var(--teal-txt); }
.sc-icono.on .lucide { fill: currentColor; }

/* La vista previa.
   `pre` con `white-space: pre-wrap` para que un JSON conserve su sangrado y una
   línea larga no saque una barra horizontal por toda la tarjeta. El alto máximo
   es el corte de verdad: sin él, pegar un fichero entero deja el historial
   inservible. */
.sc-vista {
  margin: 9px 0 0; padding: 9px 11px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
  font-family: var(--f-mono); font-size: 11.5px; line-height: 1.5; color: var(--ink-2);
  white-space: pre-wrap; word-break: break-word; overflow: hidden; max-height: 120px;
}
.sc-entrada-resumen { margin-top: 7px; font-size: 11.5px; color: var(--ink-4); }
.sc-entrada .sc-acciones { margin-top: 10px; gap: 6px; }
.sc-entrada .sc-accion { padding: 6px 11px; font-size: 12px; }
.sc-entrada .sc-mas > summary { padding: 6px 11px; font-size: 12px; }

.sc-vacio {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 34px 20px; color: var(--ink-4); text-align: center;
}
.sc-vacio .lucide { width: 26px; height: 26px; }
.sc-vacio p { margin: 0; font-size: 12.5px; }

/* ----- Móvil -----
   Mismo criterio que en Conversores y Code Lab. Aquí lo que se cae es el pie de
   la zona de pegado: los atajos de teclado no le sirven a nadie sin teclado, y
   ocupan dos renglones que hacen falta para el contenido. */
@media (max-width: 860px) {
  .sc-atajos { display: none; }
  .sc-sub { display: none; }
  .sc-acciones { gap: 6px; }
  .sc-accion { flex: 1 1 auto; }
  .sc-accion small { display: none; }
  .sc-entrada-hora { margin-left: 0; width: 100%; }
  .sc-vista { max-height: 92px; }
}

/* ----- La fila de limpieza -----
   Va entre el resumen y las acciones: se limpia ANTES de mandar el texto a otro
   sitio. Los botones son más pequeños y en tono apagado que los de acción,
   porque no sacan de la pantalla — retocan lo que ya tienes delante, y esa
   diferencia tiene que verse sin leer los rótulos. */
.sc-limpieza {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 12px; padding: 9px 11px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
}
.sc-limpieza-t {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 500; color: var(--ink-3); margin-right: 2px;
}
.sc-limpieza-t .lucide { width: 14px; height: 14px; }
.sc-limpia {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
  font: inherit; font-size: 11.5px; cursor: pointer;
  transition: border-color .12s ease, color .12s ease;
}
.sc-limpia .lucide { width: 13px; height: 13px; color: var(--ink-4); }
.sc-limpia:hover { border-color: var(--teal-200); color: var(--teal-txt); }
.sc-limpia:hover .lucide { color: var(--teal-txt); }
.sc-limpia:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.sc-limpia.todo { border-style: dashed; }
/* La nota de «esto no toca el historial». En letra pequeña y al final: es la
   respuesta a una duda que aparece la primera vez y ya no vuelve. */
.sc-limpieza-nota { flex: 1 1 100%; font-size: 10.5px; color: var(--ink-4); }

@media (max-width: 860px) {
  .sc-limpieza-nota { display: none; }
}

/* ----- Lo que hay dentro -----
   La segunda pregunta de la pantalla: no «qué es esto» sino «qué lleva dentro».
   Va en su propio bloque con fondo, entre el resumen y la limpieza, porque es
   una respuesta distinta de la de la insignia y mezclarlas confunde: arriba se
   dice qué ES el texto, aquí qué SE HA ENCONTRADO en él. */
.sc-dentro {
  margin-top: 12px; padding: 11px 13px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
}
.sc-dentro-hd {
  display: flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 500; color: var(--ink-3); margin-bottom: 9px;
}
.sc-dentro-hd .lucide { width: 14px; height: 14px; }

/* El aviso de datos personales. En ámbar y ARRIBA DEL TODO: es lo único de este
   bloque que dice una consecuencia y no una posibilidad. */
.sc-dentro-aviso {
  display: flex; gap: 8px; align-items: flex-start;
  margin-bottom: 10px; padding: 8px 10px;
  border-radius: var(--r-2); background: var(--amber-bg);
  border-left: 3px solid var(--amber);
  font-size: 11.5px; line-height: 1.5; color: var(--ink-2);
}
.sc-dentro-aviso .lucide { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 1px; color: var(--amber-fg); }

.sc-dentro-grupos { display: flex; flex-direction: column; gap: 6px; }
.sc-grupo {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 6px 9px; border-radius: var(--r-2);
  background: var(--surface); border: 1px solid var(--line);
}
/* La cuenta en grande y en monoespaciada: es el dato que se lee de un vistazo
   («¿cuántos correos hay?») y tiene que ganarle al rótulo. */
.sc-grupo-n {
  font-family: var(--f-mono); font-size: 15px; font-weight: 600; color: var(--teal-txt);
  min-width: 26px; text-align: right;
}
.sc-grupo.sens .sc-grupo-n { color: var(--amber-fg); }
.sc-grupo-q { flex: 1 1 auto; font-size: 12.5px; color: var(--ink-2); min-width: 0; }
.sc-grupo-q small { display: block; font-size: 10.5px; color: var(--ink-4); }
.sc-grupo-b {
  display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--line); background: transparent; color: var(--ink-2);
  font: inherit; font-size: 11.5px; cursor: pointer;
  transition: border-color .12s ease, color .12s ease;
}
.sc-grupo-b .lucide { width: 13px; height: 13px; color: var(--ink-4); }
.sc-grupo-b:hover { border-color: var(--teal-200); color: var(--teal-txt); }
.sc-grupo-b:hover .lucide { color: var(--teal-txt); }
.sc-grupo-b:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

/* En la tarjeta del historial no caben los grupos enteros: solo la cuenta,
   detrás del resumen y en el mismo renglón. */
.sc-entrada-dentro { color: var(--ink-3); }

@media (max-width: 860px) {
  .sc-grupo { gap: 6px; }
  .sc-grupo-q { flex-basis: 100%; }
}

/* ----- La tabla y su gráfico -----
   Las dos caras de lo mismo bajo un grupo de botones. No se enseñan a la vez
   porque cada una necesita el ancho entero para servir de algo: media tabla no
   deja leer una fila y medio gráfico no deja comparar nada. */
.sc-tabla-caja {
  margin-top: 12px; padding: 11px 13px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
}
.sc-tabla-barra {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px;
}
.sc-tabla-info { font-size: 11.5px; color: var(--ink-4); margin-left: auto; }

/* El desplazamiento vive AQUÍ y no en la página: una tabla de quince columnas
   no puede sacar una barra horizontal por debajo de toda la aplicación. */
.sc-tabla-scroll {
  overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--surface); scrollbar-width: thin;
}
.sc-tabla { border-collapse: collapse; width: 100%; font-size: 12px; }
.sc-tabla th, .sc-tabla td {
  padding: 6px 10px; text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.sc-tabla th {
  position: sticky; top: 0; background: var(--bg-elev);
  font-weight: 500; color: var(--ink-2); font-size: 11.5px;
}
/* El tipo de la columna debajo del rótulo, en minúscula y apagado: dice por qué
   una columna puede ir en el eje Y y otra no, sin tener que explicarlo aparte. */
.sc-tabla th small { display: block; font-size: 10px; color: var(--ink-4); font-weight: 400; }
.sc-tabla td { color: var(--ink-2); }
.sc-tabla tbody tr:last-child td { border-bottom: 0; }
.sc-tabla tbody tr:hover td { background: var(--bg-elev); }
/* Los números a la derecha y en monoespaciada, que es como se comparan de un
   vistazo: alineados por la unidad y con todas las cifras del mismo ancho. */
.sc-tabla .num { text-align: right; font-family: var(--f-mono); font-size: 11.5px; }
.sc-tabla-pie { margin-top: 7px; font-size: 11px; color: var(--ink-4); }

.sc-graf-mandos { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-bottom: 12px; }
.sc-graf-campo { display: flex; flex-direction: column; gap: 4px; }
.sc-graf-campo > span { font-size: 11px; color: var(--ink-3); }
.sc-tabla-caja .chart-wrap { height: 260px; }

@media (max-width: 860px) {
  .sc-tabla-info { margin-left: 0; flex-basis: 100%; }
  .sc-graf-campo { flex: 1 1 120px; }
  .sc-tabla-caja .chart-wrap { height: 200px; }
}

/* ----- Imágenes -----
   La vista previa se limita en ALTO y no en ancho: una captura de pantalla es
   apaisada, y limitando el ancho una captura alta se sale por abajo igual. */
.sc-img-vista {
  margin-top: 12px; padding: 10px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
  display: flex; justify-content: center;
}
.sc-img-vista img {
  max-width: 100%; max-height: 320px; object-fit: contain;
  border-radius: var(--r-1); background: var(--surface);
}

/* La miniatura de la tarjeta del historial. Mismo hueco que ocupa el `<pre>` de
   una entrada de texto, para que la lista no dé saltos de alto al mezclarse
   imágenes y texto. */
.sc-vista-img {
  margin-top: 9px; padding: 8px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
  max-height: 120px; overflow: hidden; display: flex; justify-content: center;
}
.sc-vista-img img { max-width: 100%; max-height: 104px; object-fit: contain; border-radius: var(--r-1); }

@media (max-width: 860px) {
  .sc-img-vista img { max-height: 220px; }
}

/* ----- Etiquetas, marcas y comparación ----- */
.sc-etifila { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 12px; }
.sc-eti {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-3);
  font: inherit; font-size: 11px; cursor: pointer;
  transition: border-color .12s ease, color .12s ease;
}
.sc-eti .lucide { width: 11px; height: 11px; }
.sc-eti:hover { border-color: var(--teal-200); color: var(--teal-txt); }
.sc-eti.on { background: var(--teal-50); border-color: var(--teal-200); color: var(--teal-700); font-weight: 500; }
/* Puesta en una entrada: no es un filtro, no se pulsa. Solo se quita. */
.sc-eti.puesta { cursor: default; background: var(--bg-elev); }
.sc-eti.puesta:hover { border-color: var(--line); color: var(--ink-3); }
.sc-eti.puesta button {
  border: 0; background: transparent; padding: 0 0 0 2px; margin: 0;
  font: inherit; font-size: 13px; line-height: 1; color: var(--ink-4); cursor: pointer;
}
.sc-eti.puesta button:hover { color: var(--coral); }
.sc-eti.nueva { border-style: dashed; }
.sc-eti-in {
  height: 22px; padding: 0 8px; font-size: 11px; width: 140px;
  border-radius: 999px;
}
.sc-etiquetas { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
/* Sin etiquetas, el botón de añadir se apaga hasta que se pasa por la tarjeta:
   una fila de «+ etiqueta» repetida en veinte tarjetas es ruido puro. */
.sc-etiquetas:not(:hover) .sc-eti.nueva { opacity: 0.35; }
.sc-entrada:hover .sc-eti.nueva { opacity: 1; }

.sc-marca { display: inline-flex; align-items: center; flex: 0 0 auto; cursor: pointer; }
.sc-marca input { cursor: pointer; margin: 0; }

/* El contador de veces. En monoespaciada, junto a la hora: es un dato de la
   entrada, no una etiqueta que se pueda pulsar. */
.sc-veces {
  font-family: var(--f-mono); font-size: 10.5px; color: var(--ink-4);
  padding: 1px 6px; border-radius: 999px; background: var(--bg-elev);
}

/* La barra de comparar. Va debajo de los filtros y por encima de la lista,
   donde estorba menos y se ve al marcar la primera casilla. */
.sc-comparar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px; padding: 8px 12px;
  border-radius: var(--r-2); background: var(--teal-50); border: 1px solid var(--teal-200);
  font-size: 12.5px; color: var(--teal-700);
}
.sc-comparar .lucide { width: 15px; height: 15px; }
.sc-comparar > span { flex: 1 1 auto; }
.sc-comparar-no { flex: 1 1 100%; font-size: 11.5px; color: var(--ink-3); }

.sc-tope small { font-size: 10.5px; color: var(--ink-4); }

@media (max-width: 860px) {
  .sc-etiquetas:not(:hover) .sc-eti.nueva { opacity: 1; }
  .sc-comparar { gap: 8px; }
}


/* ===========================================================================
   Herramientas · Tratar imagen (js/tools-images.js) · prefijo im-
   ---------------------------------------------------------------------------
   Sexto prefijo del área. Reutiliza tal cual `.lab-chip`, `.lab-intro`,
   `.lab-seg` y `.lab-aviso` de Code Lab —la barra de paneles, la nota de
   cabecera, los grupos de botones y los avisos son idénticos en las dos, y
   duplicarlas con otro prefijo sería copiar sesenta líneas para que digan lo
   mismo— y define solo lo suyo: la escena con los dos lienzos, la zona de
   soltar, los mandos y la comparación de pesos.
   =========================================================================== */

.im-rail {
  display: flex; gap: 6px; margin-bottom: 16px;
  overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin;
}

/* ----- La zona de soltar ----- */
.im-soltar {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 46px 24px; text-align: center; cursor: pointer;
  border: 1.5px dashed var(--line); border-radius: var(--r-3); background: var(--bg-elev);
  transition: border-color .12s ease, background .12s ease;
}
.im-soltar:hover, .im-soltar:focus-visible { border-color: var(--teal-200); }
.im-soltar:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.im-soltar.sobre { border-color: var(--teal-500); border-style: solid; background: var(--teal-50); }
.im-soltar .lucide { width: 30px; height: 30px; color: var(--ink-4); }
.im-soltar-t { margin: 0; font-size: 13.5px; color: var(--ink-2); }
.im-soltar-s { margin: 0; font-size: 11.5px; color: var(--ink-4); display: flex; align-items: center; gap: 4px; }
.im-soltar-s kbd {
  font-family: var(--f-mono); font-size: 10.5px; padding: 1px 5px;
  border: 1px solid var(--line); border-bottom-width: 2px; border-radius: var(--r-1);
  background: var(--surface); color: var(--ink-3);
}

.im-privacidad {
  display: flex; gap: 9px; align-items: flex-start;
  margin-top: 14px; padding: 10px 12px;
  border-radius: var(--r-2); background: var(--bg-elev); border-left: 3px solid var(--teal-200);
  font-size: 11.5px; line-height: 1.55; color: var(--ink-3);
}
.im-privacidad .lucide { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 2px; color: var(--teal-txt); }

/* ----- La escena -----
   Los dos lienzos van uno encima del otro: el de abajo da el tamaño y el de
   arriba se posiciona sobre él. El fondo a cuadros es el convenio de siempre
   para «aquí hay transparencia», y hace falta: un PNG con fondo transparente
   sobre un fondo liso parece que tiene ese fondo. */
.im-escena-caja { margin-bottom: 18px; }
.im-escena {
  position: relative; display: flex; align-items: center; justify-content: center;
  padding: 12px; border-radius: var(--r-2); border: 1px solid var(--line);
  background-color: var(--bg-elev);
  background-image:
    linear-gradient(45deg, rgba(128,128,128,0.09) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.09) 75%),
    linear-gradient(45deg, rgba(128,128,128,0.09) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.09) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
  min-height: 120px; overflow: hidden;
}
.im-escena canvas { display: block; max-width: 100%; border-radius: var(--r-1); }
/* El de arriba recibe el ratón: es donde se arrastran los recuadros de recorte
   y de tapado. `touch-action:none` es lo que hace que en una tableta se pueda
   arrastrar sin que el gesto lo robe el desplazamiento de la página. */
.im-escena #imCapa {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  cursor: crosshair; touch-action: none;
}

.im-barra { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.im-medidas { display: flex; flex-direction: column; gap: 1px; margin-right: auto; }
.im-medidas strong { font-family: var(--f-mono); font-size: 13px; color: var(--ink); }
.im-medidas span { font-size: 11.5px; color: var(--ink-3); }
/* De dónde venía. En letra pequeña y tachada: dice de un vistazo que la imagen
   ya no es la que entró. */
.im-medidas small { font-size: 10.5px; color: var(--ink-4); text-decoration: line-through; }

/* ----- Controles ----- */
.im-fx { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.im-fx-fin { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
.im-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1 1 150px; }
.im-field-ancho { flex: 2 1 260px; }
.im-field-pleno { flex-basis: 100%; }
.im-apagado { opacity: 0.45; }
.im-lbl { font-size: 11.5px; color: var(--ink-3); }
.im-lbl b { font-family: var(--f-mono); color: var(--ink-2); font-weight: 500; }
.im-fijo {
  height: 36px; display: flex; align-items: center; padding: 0 12px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
  font-family: var(--f-mono); font-size: 13px; color: var(--ink-3);
}
.im-atajos { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.im-nota { font-size: 11px; color: var(--ink-4); flex: 1 1 220px; }
.im-hint {
  font-size: 12px; color: var(--ink-3); padding: 8px 11px; margin-top: 4px;
  border-radius: var(--r-2); background: var(--bg-elev); border: 1px solid var(--line);
}

.im-mandos { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px 18px; margin-top: 14px; }
.im-mando { display: flex; flex-direction: column; gap: 5px; }
.im-mando input[type="range"] { width: 100%; accent-color: var(--teal-600); }
.im-fx input[type="range"] { accent-color: var(--teal-600); }

/* ----- La comparación de pesos -----
   Es lo que hace útil el panel de tamaño, así que se pinta como el resultado
   principal y no como una nota al pie. */
.im-resultado { margin-top: 16px; }
.im-calculando { font-size: 12px; color: var(--ink-4); padding: 14px 0; }
.im-pesos {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 14px; border-radius: var(--r-2);
  background: var(--bg-elev); border: 1px solid var(--line);
}
.im-pesos > .lucide { width: 16px; height: 16px; color: var(--ink-4); flex: 0 0 auto; }
.im-peso { display: flex; flex-direction: column; gap: 1px; }
.im-peso-k { font-size: 10.5px; color: var(--ink-4); text-transform: uppercase; letter-spacing: 0.04em; }
.im-peso-v { font-family: var(--f-mono); font-size: 16px; color: var(--ink); }
.im-peso small { font-size: 10.5px; color: var(--ink-4); }
.im-peso.destacado .im-peso-v { color: var(--teal-txt); font-weight: 600; }
.im-ahorro {
  margin-left: auto; padding: 3px 10px; border-radius: 999px;
  font-size: 11.5px; font-weight: 500; background: var(--bg-elev); color: var(--ink-3);
}
.im-ahorro.bien { background: var(--teal-50); color: var(--teal-700); }
.im-ahorro.mal { background: var(--amber-bg); color: var(--amber-fg); }

/* ----- La paleta ----- */
.im-paleta { display: flex; flex-direction: column; gap: 7px; }
.im-color {
  display: flex; align-items: center; gap: 12px;
  padding: 7px 10px; border-radius: var(--r-2);
  background: var(--surface); border: 1px solid var(--line);
}
.im-color-m { width: 40px; height: 32px; flex: 0 0 auto; border-radius: var(--r-1); border: 1px solid var(--line); }
.im-color-d { flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.im-color-d b { font-family: var(--f-mono); font-size: 13px; color: var(--ink); font-weight: 500; }
.im-color-d small { font-size: 10.5px; color: var(--ink-4); }
.im-color-b { display: flex; gap: 5px; flex: 0 0 auto; }

/* ----- La tabla de datos ----- */
.im-tabla { border: 1px solid var(--line); border-radius: var(--r-2); overflow: hidden; }
.im-fila { display: flex; align-items: center; gap: 12px; padding: 7px 12px; border-bottom: 1px solid var(--line); }
.im-fila:last-child { border-bottom: 0; }
.im-fila-k { flex: 0 0 190px; font-size: 12px; color: var(--ink-3); }
.im-fila-v { flex: 1 1 auto; font-family: var(--f-mono); font-size: 12.5px; color: var(--ink); word-break: break-word; }
.im-sub { margin: 20px 0 10px; font-size: 12.5px; color: var(--ink-2); font-weight: 500; }
.im-limpio {
  display: flex; gap: 9px; align-items: flex-start; padding: 11px 13px;
  border-radius: var(--r-2); background: var(--teal-50); border: 1px solid var(--teal-200);
  font-size: 12px; line-height: 1.55; color: var(--teal-700);
}
.im-limpio .lucide { width: 15px; height: 15px; flex: 0 0 auto; margin-top: 1px; }

/* ----- El OCR ----- */
.im-ocr-curro { padding: 18px 0; }
.im-ocr-barra { height: 6px; border-radius: 999px; background: var(--bg-elev); overflow: hidden; }
.im-ocr-barra span {
  display: block; height: 100%; background: var(--teal-500); border-radius: 999px;
  transition: width .2s ease;
}
.im-ocr-paso { margin: 9px 0 4px; font-size: 12.5px; color: var(--ink-2); }
.im-ocr-out { width: 100%; font-family: var(--f-mono); font-size: 12.5px; line-height: 1.55; margin-top: 10px; }

@media (max-width: 860px) {
  .im-fila-k { flex-basis: 130px; }
  .im-medidas { margin-right: 0; flex-basis: 100%; }
  .im-mandos { grid-template-columns: 1fr; }
  .im-soltar { padding: 32px 16px; }
  .im-pesos { gap: 10px; }
  .im-ahorro { margin-left: 0; }
}


/* ===========================================================================
   Herramientas · PDF Global (js/tools-pdf.js) · prefijo pd-
   ---------------------------------------------------------------------------
   Séptimo prefijo del área, y el que menos define de los siete. Reutiliza tal
   cual casi todo lo que ya existía —`.lab-chip`, `.lab-intro`, `.lab-seg`,
   `.lab-aviso` y `.lab-check` de Code Lab; y de Tratar imagen la barra de
   paneles `.im-rail`, la zona de soltar `.im-soltar`, los campos `.im-field`,
   la fila de mandos `.im-fx`, la comparación de pesos `.im-pesos` y la barra
   del OCR— porque las dos pantallas hacen lo mismo con cosas distintas: coges
   un fichero, tocas unos mandos y sacas otro fichero.

   LO QUE SÍ ES PROPIO son las seis piezas que ninguna de las otras necesitaba,
   y casi todas salen de que aquí un documento tiene MUCHAS páginas:

     · la barra de utilidades en cuatro grupos, que es lo primero que se ve;
     · la rejilla de miniaturas, con sus botones por página y su arrastre;
     · la lista ordenable, para lo que se une y para lo que se convierte;
     · la barra de faena, porque ochenta páginas tardan y hay que decirlo;
     · la hoja de «Tapar», con los recuadros colocados en tanto por ciento;
     · la tabla de datos y la ficha del documento abierto.
   =========================================================================== */

/* ----- La barra de utilidades: cuatro chips y tres desplegables -----
   Ya no es `.im-rail` de Tratar imagen —una fila de chips y a correr— porque
   catorce chips seguidos son una tira que hay que leer entera para encontrar
   «Numerar». Son cuatro: tres que despliegan su familia al pasar por encima y
   «Datos», que es una sola cosa y va directo.

   El menú se posiciona contra su chip (`position: absolute` sobre un padre
   `relative`) y no contra la ventana como el del menú lateral: aquel se abre
   desde una barra con `overflow` propio y tenía que escaparse al `<body>`; este
   vive en el cuerpo de la página, donde nada lo recorta. */
.pd-rail { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 16px; }
.pd-grupo { position: relative; }

/* El nombre de la utilidad abierta, dentro de su chip de grupo: sin él, con el
   menú cerrado la barra dice en qué familia estás pero no en qué pantalla. */
.pd-grupo-b small { font-size: 11px; opacity: .75; }
.pd-grupo-b small::before { content: "· "; }
.pd-grupo-v { display: inline-flex; margin-left: -2px; opacity: .55; }
.pd-grupo-v .lucide { width: 12px; height: 12px; }
.pd-grupo-b[aria-expanded="true"] .pd-grupo-v { transform: rotate(180deg); }

.pd-menu {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 30;
  min-width: 248px; padding: 5px;
  border: 1px solid var(--line); border-radius: var(--r-3);
  background: var(--surface); box-shadow: var(--sh-2);
  display: flex; flex-direction: column; gap: 1px;
}
.pd-menu[hidden] { display: none; }
.pd-menu-b {
  display: flex; align-items: flex-start; gap: 9px; width: 100%;
  padding: 7px 9px; border: 0; border-radius: var(--r-2);
  background: transparent; text-align: left; cursor: pointer;
  color: var(--ink-2);
}
.pd-menu-b:hover { background: var(--bg-elev); color: var(--ink); }
.pd-menu-b:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.pd-menu-b.on { background: var(--teal-50); color: var(--teal-700); }
.pd-menu-b > .lucide { width: 15px; height: 15px; flex: 0 0 auto; margin-top: 2px; }
.pd-menu-b span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pd-menu-b strong { font-size: 12.5px; font-weight: 500; }
.pd-menu-b small { font-size: 11px; color: var(--ink-4); line-height: 1.4; }
.pd-menu-b.on small { color: var(--teal-600); }

/* Aire entre las dos tarjetas. Son dos cosas distintas —QUÉ tienes abierto y
   QUÉ le estás haciendo— y pegadas con el margen normal de la casa se leían
   como una sola tarjeta partida por una raya. */
.pd-trabajo { margin-top: 26px; }

/* ----- La ficha del documento abierto -----
   Es la gemela de `.im-escena` de Tratar imagen: lo que hay cargado ahora mismo
   y lo que se puede hacer con ello pase lo que pase. Ahí se enseña la imagen; un
   PDF no se puede enseñar de un vistazo, así que aquí se enseña su nombre, sus
   páginas y su peso. */
.pd-ficha-caja { margin-bottom: 18px; }
.pd-ficha {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 12px 14px; border-radius: var(--r-2);
  border: 1px solid var(--line); background: var(--bg-elev);
}
.pd-ficha > .lucide { width: 20px; height: 20px; flex: 0 0 auto; margin-top: 1px; color: var(--teal-txt); }
.pd-ficha-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pd-ficha-txt strong {
  font-size: 13.5px; color: var(--ink); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pd-ficha-txt span { font-size: 11.5px; color: var(--ink-3); }
/* De dónde venía, tachado y pequeño: dice de un vistazo que el documento ya no
   es el que entró. Mismo recurso que `.im-medidas small`. */
.pd-ficha-txt small { font-size: 10.5px; color: var(--ink-4); text-decoration: line-through; }

/* ----- La barra de faena -----
   Ochenta páginas a 200 ppp tardan medio minuto largo, y una pantalla quieta
   parece rota. Va DENTRO del cuerpo del panel y encima de todo, que es donde
   está mirando quien acaba de pulsar el botón. */
.pd-faena {
  margin: 0 0 16px; padding: 12px 14px;
  border-radius: var(--r-2); background: var(--bg-elev); border-left: 3px solid var(--teal-500);
}
.pd-faena-t { font-size: 12.5px; color: var(--ink-2); margin-bottom: 8px; }
.pd-faena-t span { color: var(--ink-4); }
.pd-faena-barra { height: 6px; border-radius: 999px; background: var(--surface); overflow: hidden; }
.pd-faena-barra span {
  display: block; height: 100%; background: var(--teal-500); border-radius: 999px;
  transition: width .2s ease;
}
/* Cuando no se sabe cuánto queda —abrir un documento, guardar unos metadatos— la
   barra se mueve sola en vez de mentir con un porcentaje inventado. */
.pd-faena-barra.indeterminada span { animation: pd-vaiven 1.1s ease-in-out infinite; transform-origin: left; }
@keyframes pd-vaiven {
  0%   { transform: scaleX(0.15) translateX(0); }
  50%  { transform: scaleX(0.45) translateX(120%); }
  100% { transform: scaleX(0.15) translateX(560%); }
}
.pd-faena-n { margin-top: 6px; font-family: var(--f-mono); font-size: 11px; color: var(--ink-4); }
@media (prefers-reduced-motion: reduce) {
  .pd-faena-barra.indeterminada span { animation: none; transform: scaleX(1); opacity: .5; }
}

/* ----- La rejilla de páginas -----
   `auto-fill` y no `auto-fit`: con `auto-fit`, un documento de dos páginas las
   estiraba hasta ocupar la fila entera y salían dos folios gigantes. Con
   `auto-fill` la columna mide siempre lo mismo y dos páginas son dos páginas. */
.pd-rejilla {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(124px, 1fr));
  gap: 14px; margin: 16px 0;
}
.pd-pag {
  margin: 0; display: flex; flex-direction: column; gap: 6px;
  padding: 8px; border-radius: var(--r-2);
  border: 1px solid var(--line); background: var(--surface);
  transition: border-color .12s ease, background .12s ease;
}
.pd-pag:hover { border-color: var(--teal-200); }
.pd-pag.marcada { border-color: var(--teal-500); background: var(--teal-50); }

/* El fondo a cuadros es el convenio de siempre para «aquí hay transparencia», y
   hace falta por lo mismo que en Tratar imagen: una página con fondo
   transparente sobre un fondo liso parece que tiene ese fondo. */
.pd-pag-lienzo {
  position: relative; border-radius: var(--r-1); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background-color: var(--bg-elev);
  background-image:
    linear-gradient(45deg, rgba(128,128,128,0.08) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.08) 75%),
    linear-gradient(45deg, rgba(128,128,128,0.08) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.08) 75%);
  background-size: 12px 12px;
  background-position: 0 0, 6px 6px;
  border: 1px solid var(--line);
}
/* La miniatura va de fondo en una capa PROPIA, y no en la caja: así el giro la
   mueve a ella sola y los cuadros de la transparencia se quedan quietos detrás,
   que es lo que hace que una página girada siga leyéndose como una página sobre
   un fondo y no como un fondo torcido.

   Y por eso girar es instantáneo: no se vuelve a pintar la página, se gira una
   imagen que ya estaba. */
.pd-pag-giro {
  position: absolute; inset: 0;
  background-size: contain; background-repeat: no-repeat; background-position: center;
  transition: transform .18s ease;
}
.pd-pag-lienzo .sk { position: absolute; }

.pd-pag figcaption { display: flex; flex-direction: column; gap: 5px; }
/* El asa va en la fila del número y no con los otros botones: con ella dentro
   eran cinco iconos de 24 píxeles en una tarjeta de 124, que se partían en dos
   filas y dejaban el aspa sola debajo. */
.pd-pag-fila { display: flex; align-items: center; justify-content: space-between; gap: 4px; }
.pd-pag-fila .pd-asa { width: 22px; height: 22px; border: 0; background: transparent; }
.pd-pag-fila .pd-asa .lucide { width: 13px; height: 13px; }
.pd-pag-n {
  display: flex; align-items: center; gap: 6px; cursor: pointer;
  font-family: var(--f-mono); font-size: 12px; color: var(--ink-2);
}
.pd-pag-n input { width: 14px; height: 14px; accent-color: var(--teal-600); }
.pd-pag-n small { font-size: 9.5px; color: var(--ink-4); font-family: var(--f-sans, inherit); }
/* Los seis botones por página no caben con su tamaño normal: son 28 píxeles en
   una tarjeta de 124. Se aprietan aquí y no en `.icon-btn`, que la usa media
   aplicación. */
.pd-pag-btns { display: flex; gap: 1px; flex-wrap: wrap; }
.pd-pag-btns .icon-btn { width: 24px; height: 24px; border: 0; background: transparent; }
.pd-pag-btns .icon-btn .lucide { width: 13px; height: 13px; }
.pd-pag-btns .icon-btn:disabled { opacity: .3; cursor: default; }

/* ----- Arrastrar páginas -----
   `touch-action: none` va SOLO en el asa, y es la regla que decide todo lo
   demás. Puesto en la tarjeta entera, el dedo que baja por una rejilla de
   cuarenta páginas movería una página en vez de desplazar la pantalla; puesto
   solo aquí, con el dedo se arrastra desde el asa y por el resto se navega
   normal. Con el ratón vale la tarjeta entera, que eso lo decide el JS. */
.pd-asa { cursor: grab; touch-action: none; }
.pd-asa:active { cursor: grabbing; }

/* La que se lleva en la mano. `pointer-events: none` no es cosmético: es lo que
   permite preguntarle al navegador qué tarjeta hay DEBAJO del puntero. Los
   eventos siguen llegando igual porque el puntero está capturado. */
.pd-pag.arrastrando {
  pointer-events: none; position: relative; z-index: 3;
  border-color: var(--teal-500); box-shadow: var(--sh-2); opacity: .95;
}
/* Sin esto, arrastrar por encima de las miniaturas iba seleccionando los
   números de página de todas las que cruzaba. */
.pd-rejilla.arrastrando { user-select: none; }
.pd-pag:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

.pd-nota-orden { display: flex; align-items: flex-start; gap: 7px; margin: 0 0 4px; line-height: 1.55; }
.pd-nota-orden .lucide { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 2px; color: var(--ink-4); }
.pd-nota-orden kbd {
  font-family: var(--f-mono); font-size: 10.5px; padding: 1px 5px;
  border: 1px solid var(--line); border-bottom-width: 2px; border-radius: var(--r-1);
  background: var(--surface); color: var(--ink-3);
}

/* ----- Las listas ordenables ----- */
.pd-lista { list-style: none; margin: 16px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.pd-lista-fila {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: var(--r-2);
  border: 1px solid var(--line); background: var(--surface);
}
/* El documento que ya estaba abierto va el primero y no se puede mover ni
   quitar: se sale de la lista cerrándolo, no reordenándola. */
.pd-lista-fila.fijo { background: var(--bg-elev); }
.pd-lista-n {
  flex: 0 0 auto; width: 22px; text-align: center;
  font-family: var(--f-mono); font-size: 11.5px; color: var(--ink-4);
}
.pd-lista-fila > .lucide { width: 16px; height: 16px; flex: 0 0 auto; color: var(--ink-4); }
.pd-lista-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.pd-lista-txt strong {
  font-size: 12.5px; font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pd-lista-txt span { font-size: 11px; color: var(--ink-4); }

/* La zona de soltar de «Unir» y «Desde imágenes» convive con la lista de lo ya
   soltado, así que ocupa menos que la de la pantalla vacía. */
.pd-soltar-chico { padding: 26px 20px; }

/* ----- Mandos en fila ----- */
.pd-mandos { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }

/* ----- Datos y metadatos ----- */
.pd-sub {
  margin: 26px 0 12px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 12px; font-weight: 600; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.05em;
}
.pd-datos {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-2);
  overflow: hidden; margin-bottom: 4px;
}
.pd-dato { display: flex; flex-direction: column; gap: 2px; padding: 9px 12px; background: var(--surface); }
.pd-dato-k { font-size: 10.5px; color: var(--ink-4); text-transform: uppercase; letter-spacing: 0.04em; }
.pd-dato-v { font-size: 12.5px; color: var(--ink-2); overflow-wrap: anywhere; }
.pd-dato-v small { font-size: 10.5px; color: var(--ink-4); }

.pd-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--bg-elev);
  font-family: var(--f-mono); font-size: 11px; color: var(--ink-2);
}
.pd-pill small { color: var(--ink-4); }

/* ----- Formularios del PDF ----- */
.pd-campos { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin: 4px 0 6px; }
.pd-campo { display: flex; flex-direction: column; gap: 5px; min-width: 0; }

.pd-mono { font-family: var(--f-mono); font-size: 12.5px; line-height: 1.55; }

/* ----- La hoja de «Tapar» -----
   Los recuadros van en TANTO POR CIENTO sobre la hoja, no en píxeles, y por eso
   la hoja se reserva su hueco con `aspect-ratio` en vez de esperar a que llegue
   la imagen: así el recuadro que se dibuja cae exactamente donde caerá al
   aplicarlo, mida lo que mida la ventana, y la página no pega el salto de
   siempre cuando termina de pintarse el fondo.

   `touch-action: none` para poder dibujar con el dedo. Es el motivo del
   `max-width`: alrededor de la hoja tiene que quedar sitio por donde bajar. */
.pd-tap-hoja {
  /* LA HOJA TIENE QUE CABER EN LA PANTALLA, y esta es la línea que lo consigue.
     Con `max-width: 620px` a secas, un A4 salía de 620 × 877 y no cabía en
     ninguna ventana: se veía el margen de arriba de la página —un folio en
     blanco— y los mandos quedaban dos pantallazos más abajo. Tapar es señalar
     con el dedo dónde está el dato: si hay que buscar la página desplazando, no
     se puede señalar nada.

     El ancho es el MENOR de tres: lo que da la tarjeta, 620 píxeles, y el que
     deja el alto de la ventana. `--pd-ratio` lo escribe pdfTapHtml() con la
     proporción de la página, así que la tercera opción es «el ancho que hace
     que la altura sea 62vh». Y como `aspect-ratio` deriva la altura del ancho,
     la proporción sale EXACTA en los tres casos: si se hubiera limitado el alto
     con `max-height`, el navegador recorta la altura y deja el ancho, y los
     recuadros —que van en tanto por ciento— dejarían de caer donde se
     dibujaron. */
  position: relative; margin: 4px 0 8px;
  width: min(100%, 620px, calc(62vh * var(--pd-ratio, 0.707)));
  border: 1px solid var(--line); border-radius: var(--r-2); overflow: hidden;
  background: var(--bg-elev); cursor: crosshair; touch-action: none; user-select: none;
}
/* Mientras se pinta. Sin esto, una hoja en blanco parece la hoja y no la
   espera. */
.pd-tap-espera {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 12px; color: var(--ink-4);
}
.pd-tap-fondo {
  position: absolute; inset: 0;
  background-size: 100% 100%; background-repeat: no-repeat; background-position: center;
}
.pd-tap-caja { position: absolute; background: #000; }
/* El aspa va DENTRO del recuadro: fuera se la comía el `overflow: hidden` de la
   hoja en cuanto el recuadro tocaba un borde, que es justo donde se tapan las
   cosas. */
.pd-tap-caja .icon-btn {
  position: absolute; top: 1px; right: 1px; width: 18px; height: 18px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--surface);
}
.pd-tap-caja .icon-btn .lucide { width: 11px; height: 11px; }
/* El recuadro que se está dibujando: translúcido y con filete, para que se vea
   qué queda debajo mientras se decide dónde soltar. */
.pd-tap-fantasma {
  position: absolute; background: rgba(0, 0, 0, .38);
  border: 1px dashed #fff; pointer-events: none;
}

@media (max-width: 860px) {
  .pd-rejilla { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
  .pd-datos { grid-template-columns: 1fr; }
  .pd-lista-fila { flex-wrap: wrap; }
  .pd-soltar-chico { padding: 20px 14px; }
  /* Cada grupo ocupa su propia fila, y su menú va a lo ancho de ella. Es lo que
     evita el problema de siempre en un móvil: un menú de 248 píxeles colgando
     de un chip que empieza a media pantalla se sale por la derecha. Empezando
     todos en el margen izquierdo, eso no puede pasar. */
  .pd-grupo { width: 100%; }
  .pd-grupo-b { width: 100%; justify-content: flex-start; }
  .pd-menu { left: 0; right: 0; min-width: 0; }
  .pd-trabajo { margin-top: 18px; }
}

/* ===========================================================================
   Herramientas · Sala de descanso (js/tools-arcade.js) · prefijo sd-
   ---------------------------------------------------------------------------
   Octavo prefijo del área, y el que más se separa de los otros siete: aquí no
   hay campos, ni resultados, ni ficheros que soltar. Hay tarjetas de juego, un
   marcador y una cabina a pantalla completa.

   TRES COSAS QUE NO SE PARECEN A NADA DE LA HOJA:

     · LA PUERTA. El aviso de «estás en jornada» es lo primero de la pantalla y
       tiene aspecto de norma, no de error. Por eso NO usa el rojo de los avisos
       (`--rose-*`): no has hecho nada mal. Usa el arena de la casa.

     · LA CABINA es fija, ocupa la ventana entera y va SIEMPRE OSCURA, con el
       tema claro y con el oscuro. Es una pantalla de juego: los seis canvas
       pintan de noche en el mar, y un marco claro alrededor se lee como un
       fallo de maquetación. Sus colores son literales por eso — no salen de las
       variables del tema.

     · EL MARCADOR es una tabla sin ser una <table>: filas de rejilla con el
       puesto, el avatar, el nombre, las partidas y la marca. Se hace con
       `grid-template-columns` para que las cifras queden alineadas aunque los
       nombres midan lo que midan.
   =========================================================================== */

/* ----- La puerta -----
   Lo primero que se lee al entrar cuando no se puede jugar. */
.sd-puerta {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 18px 20px; margin-bottom: 20px;
  border: 1px solid var(--line); border-left: 3px solid var(--amber);
  border-radius: var(--r-3); background: var(--bg-elev);
}
.sd-puerta-ico {
  width: 42px; height: 42px; flex: 0 0 auto; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  background: var(--amber-bg); color: var(--amber-fg);
}
.sd-puerta-ico .lucide { width: 20px; height: 20px; }
.sd-puerta-txt { flex: 1 1 320px; min-width: 0; }
.sd-puerta-txt h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.sd-puerta-txt p { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--ink-3); }
/* La jornada de ayer sin cerrar es un dato que hay que arreglar, no una espera:
   se marca con el rojo de la casa para que no se confunda con «vuelve luego». */
.sd-puerta-olvido { border-left-color: var(--rose); }
.sd-puerta-olvido .sd-puerta-ico { background: var(--rose-bg); color: var(--rose-fg); }

/* ----- El vestíbulo ----- */
.sd-rejilla {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
  gap: 14px; margin-bottom: 20px;
}
.sd-card {
  display: flex; flex-direction: column; justify-content: space-between; gap: 14px;
  padding: 16px; border: 1px solid var(--line); border-radius: var(--r-3);
  background: var(--surface); box-shadow: var(--sh-1);
  cursor: pointer; transition: border-color .15s, transform .15s, box-shadow .15s;
}
.sd-card:hover { border-color: var(--teal-200); transform: translateY(-2px); box-shadow: var(--sh-2); }
.sd-card:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.sd-card-hd { display: flex; gap: 12px; align-items: flex-start; }
.sd-card-ico {
  width: 38px; height: 38px; flex: 0 0 auto; border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
  background: var(--teal-50); color: var(--teal-600);
}
.sd-card-ico .lucide { width: 19px; height: 19px; }
.sd-card-hd h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.sd-card-hd p { margin: 0; font-size: 12px; line-height: 1.55; color: var(--ink-3); }
.sd-card-pie {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-top: 12px; border-top: 1px solid var(--line);
}
.sd-record {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--ink-2); min-width: 0;
}
.sd-record .lucide { width: 13px; height: 13px; color: var(--amber); flex: 0 0 auto; }
.sd-record b { font-family: var(--f-mono); font-weight: 500; }
.sd-record-vacio { color: var(--ink-4); }
.sd-mia { font-size: 11px; color: var(--ink-4); white-space: nowrap; }
.sd-mia b { font-family: var(--f-mono); color: var(--ink-2); }

/* CUIDADO CON LOS TOKENS DE ACENTO EN ESTA HOJA, que aquí costó una pasada
   entera descubrirlo: `applyAccent()` (js/shell.js) escribe la paleta CLARA del
   acento en `:root` con `style.setProperty`, y eso pisa los valores que el
   bloque `[data-theme="dark"]` da a `--teal-50` … `--teal-700`. O sea:

     · en tema oscuro, `--teal-50` sigue siendo un pastel CLARO, y
     · `--teal-600` / `--teal-700` siguen siendo colores OSCUROS.

   Por eso el resto de la hoja los usa siempre EN PAREJA —fondo `--teal-50` con
   texto `--teal-700`— y nunca sueltos. Un `color: var(--teal-600)` sobre el
   fondo de la página se ve en claro y desaparece en oscuro; un `background:
   var(--teal-50)` sin fijar el texto deja una fila clara con letra clara.

   Y cuando hace falta acento SUELTO —texto o icono sobre el fondo de la página,
   sin tinte detrás— el token es `--teal-txt`, que es el único de la rampa que sí
   cambia con el tema (applyAccent() lo escribe claro en oscuro y oscuro en
   claro). Es lo que usan `.rec-tab.active`, `.bal-pos`, `.call-ring-txt` y las
   otras treinta y tantas de ese grupo. NO uses `--teal-600` ni `--teal-700`
   sueltos: en oscuro dan 2.28 y 1.64 de contraste.

   Aquí la fila propia se marca con `--bg-2` (que sí es del tema) y una barra de
   acento a la izquierda, que funciona en los dos y con los cuatro acentos. Las
   parejas fondo+texto de `.sd-card-ico` y `.sd-h1-ico` sí siguen el patrón de la
   casa, porque ahí el texto va sobre el fondo claro.

   ----- La tabla de la casa ----- */
.sd-general-lista { display: flex; flex-direction: column; gap: 1px; }
.sd-general-fila {
  display: grid; grid-template-columns: 26px 24px 1fr auto auto;
  align-items: center; gap: 10px; padding: 7px 8px; border-radius: var(--r-2);
}
.sd-general-fila.yo { background: var(--bg-2); box-shadow: inset 2px 0 0 var(--teal-500); }
.sd-general-nom { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-general-pts { font-family: var(--f-mono); font-size: 14px; color: var(--ink); }
.sd-oros {
  display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--f-mono); font-size: 11px; color: var(--amber-fg);
}
.sd-oros .lucide { width: 12px; height: 12px; }

/* ----- Ficha de un juego ----- */
.sd-head { align-items: flex-start; flex-wrap: wrap; }
.sd-head-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.sd-head h1 { display: flex; align-items: center; gap: 10px; }
.sd-h1-ico {
  width: 34px; height: 34px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--teal-50); color: var(--teal-600);
}
.sd-h1-ico .lucide { width: 18px; height: 18px; }

.sd-dos { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
.sd-largo p { margin: 0 0 12px; font-size: 13px; line-height: 1.65; color: var(--ink-2); }
.sd-como { margin: 0 0 12px; padding-left: 20px; }
.sd-como li { font-size: 12.5px; line-height: 1.6; color: var(--ink-3); margin-bottom: 6px; }
.sd-largo p.sd-teclas {
  display: flex; align-items: center; gap: 7px; margin-bottom: 0;
  font-family: var(--f-mono); font-size: 11.5px; color: var(--ink-3);
  padding: 8px 11px; border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--bg-2);
}
.sd-teclas .lucide { width: 14px; height: 14px; flex: 0 0 auto; }

/* ----- El marcador -----
   Cinco columnas fijas: puesto, avatar, nombre, partidas y marca. La marca va
   la última y a la derecha porque es lo que se compara de un vistazo bajando
   con la mirada por la columna. */
.sd-tabla { display: flex; flex-direction: column; gap: 1px; }
.sd-fila {
  display: grid; grid-template-columns: 28px 24px 1fr auto auto;
  align-items: center; gap: 10px; padding: 7px 8px; border-radius: var(--r-2);
}
.sd-fila.yo { background: var(--bg-2); box-shadow: inset 2px 0 0 var(--teal-500); }
.sd-fila-nom { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-fila-nom small { color: var(--ink-4); }
.sd-fila-p { font-family: var(--f-mono); font-size: 11px; color: var(--ink-4); }
.sd-fila-b { font-family: var(--f-mono); font-size: 14px; color: var(--ink); }
.sd-fila-b small { font-size: 10px; color: var(--ink-4); }
.sd-corte { text-align: center; color: var(--ink-4); font-size: 12px; letter-spacing: 3px; padding: 2px 0; }

.sd-pos {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 7px;
  font-family: var(--f-mono); font-size: 11px; color: var(--ink-4);
  background: var(--bg-2);
}
/* Oro, plata y bronce. No llevan icono a propósito: tres coronas seguidas en
   una lista de diez son ruido; el color ya lo dice. */
.sd-pos.oro    { background: var(--amber-bg); color: var(--amber-fg); }
.sd-pos.plata  { background: var(--bg-2); color: var(--ink-2); }
.sd-pos.bronce { background: rgba(198,106,74,0.16); color: var(--coral); }

.sd-resumen {
  display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--line); font-size: 11.5px; color: var(--ink-3);
}
.sd-resumen b { font-family: var(--f-mono); font-size: 14px; color: var(--ink); }

/* ----- Mis últimas partidas ----- */
.sd-partidas { display: flex; flex-direction: column; gap: 1px; }
.sd-partida {
  display: grid; grid-template-columns: 130px 1fr auto 32px;
  align-items: center; gap: 10px; padding: 6px 8px; border-radius: var(--r-2);
}
.sd-partida:hover { background: var(--bg-2); }
.sd-partida-n { font-family: var(--f-mono); font-size: 14px; }
.sd-partida-n small { font-size: 10px; color: var(--ink-4); }
.sd-partida-t { font-size: 12px; color: var(--ink-3); }
.sd-partida-d { font-family: var(--f-mono); font-size: 11px; color: var(--ink-4); }

.sd-sub { font-size: 11.5px; color: var(--ink-4); text-align: right; }
.sd-cargando, .sd-vacio {
  padding: 22px 4px; text-align: center; font-size: 12.5px; color: var(--ink-4);
}
.sd-aviso {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 13px 15px; margin-bottom: 16px;
  border: 1px solid var(--line); border-radius: var(--r-3); background: var(--bg-elev);
  font-size: 12.5px; line-height: 1.6; color: var(--ink-3);
}
.sd-aviso .lucide { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 2px; }
.sd-aviso code { font-family: var(--f-mono); font-size: 11px; }
.sd-aviso-warn { border-left: 3px solid var(--amber); }
.sd-aviso-warn .lucide { color: var(--amber-fg); }

@media (max-width: 900px) {
  .sd-dos { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  .sd-partida { grid-template-columns: 110px 1fr 32px; }
  .sd-partida-d { display: none; }
  .sd-sub { display: none; }
}

/* ===========================================================================
   LA CABINA
   ---------------------------------------------------------------------------
   Capa fija a pantalla completa, colgada de <body> y no de #content: la vista
   de debajo se repinta entera con cualquier cambio de estado y se llevaría el
   canvas por delante. Ver la cabecera de js/tools-arcade.js.

   `body.sd-jugando` no hace falta para bloquear el scroll —el body de esta
   aplicación ya es `overflow: hidden`— pero sí para poder apagar cualquier cosa
   que quiera ponerse por encima mientras se juega.
   =========================================================================== */
.sd-cabina {
  position: fixed; inset: 0; z-index: 120;
  display: flex; flex-direction: column;
  background: #04141c;
  color: #e8efec;
}
/* Los toasts salen por encima de todo lo demás y aquí también deben: si la
   partida no se ha guardado, eso hay que verlo. Solo se sube la cabina por
   encima del resto de la aplicación. */
body.sd-jugando .side, body.sd-jugando .header { pointer-events: none; }

.sd-cab-hd {
  display: flex; align-items: center; gap: 16px;
  padding: 10px 16px; flex: 0 0 auto;
  border-bottom: 1px solid rgba(232,239,236,0.10);
  background: #071e28;
}
.sd-cab-nom {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600; letter-spacing: -0.01em;
}
.sd-cab-nom .lucide { width: 17px; height: 17px; color: #7ddcc6; }
.sd-cab-marca {
  font-family: var(--f-mono); font-size: 20px; color: #4fd6b4;
  padding-left: 16px; border-left: 1px solid rgba(232,239,236,0.12);
}
.sd-cab-marca small { font-size: 11px; color: rgba(232,239,236,0.55); }
.sd-cab-teclas {
  font-size: 11.5px; color: rgba(232,239,236,0.45); margin-left: auto;
}
.sd-cab-acc { display: flex; gap: 4px; }
.sd-cab-acc .icon-btn {
  border-color: rgba(232,239,236,0.16); background: transparent; color: #e8efec;
}
.sd-cab-acc .icon-btn:hover { background: rgba(232,239,236,0.10); }

.sd-cab-lienzo { position: relative; flex: 1 1 auto; min-height: 0; }
#sdLienzo { display: block; width: 100%; height: 100%; touch-action: none; }

/* «Gira el móvil». El mundo de los seis juegos es apaisado (960×540): en un
   teléfono de pie la partida entera cabe en una franja de tres dedos de alto y
   no se juega, se adivina. Se tapa y se pide girar — y en js/tools-arcade.js la
   misma condición pausa la partida, para que detrás no siga pasando nada.

   Va por encima de `.sd-capa` a propósito: girar el teléfono es lo primero que
   hay que hacer, antes que seguir o volver a jugar. */
.sd-girar {
  position: absolute; inset: 0; z-index: 2;
  display: none; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; padding: 30px; text-align: center;
  background: #04141c;
}
.sd-girar .lucide { width: 40px; height: 40px; color: #7ddcc6; }
.sd-girar p { margin: 0; font-family: var(--f-display); font-size: 22px; }
.sd-girar span { font-size: 12.5px; color: rgba(232,239,236,0.55); max-width: 30ch; line-height: 1.6; }
@media (max-width: 820px) and (orientation: portrait) {
  .sd-girar { display: flex; }
}

/* La capa de mensajes (inicio, pausa, final). Va ENCIMA del canvas y no dentro
   de él porque son botones de verdad: se pueden tabular y leer con un lector de
   pantalla, cosa que un rectángulo dibujado no. */
.sd-capa {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(4,20,28,0.78);
  backdrop-filter: blur(2px);
}
.sd-capa[hidden] { display: none; }
.sd-panel {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 28px 34px; text-align: center;
  border: 1px solid rgba(232,239,236,0.14); border-radius: 18px;
  background: rgba(7,30,40,0.94); box-shadow: 0 30px 60px -20px rgba(0,0,0,0.6);
  max-width: 420px;
}
.sd-panel h3 { margin: 0; font-family: var(--f-display); font-size: 24px; font-weight: 500; }
.sd-panel p { margin: 0; font-size: 13px; color: rgba(232,239,236,0.62); line-height: 1.6; }
.sd-panel-ico {
  width: 48px; height: 48px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(232,239,236,0.10); color: #e8efec;
}
.sd-panel-ico.oro { background: rgba(232,180,74,0.18); color: #e8b44a; }
.sd-panel-ico .lucide { width: 24px; height: 24px; }
.sd-panel p.sd-panel-n { font-family: var(--f-mono); font-size: 40px; color: #4fd6b4; }
.sd-panel-n small { font-size: 14px; color: rgba(232,239,236,0.55); }
.sd-panel p.sd-panel-sub { font-size: 12px; }
.sd-panel p.sd-panel-estado { font-size: 11.5px; min-height: 17px; }
.sd-panel p.sd-panel-estado.ok { color: #4fd6b4; }
.sd-panel p.sd-panel-estado.warn { color: #e8b44a; }
.sd-panel p.sd-panel-estado.tenue { color: rgba(232,239,236,0.42); }
.sd-panel-acc { display: flex; gap: 8px; margin-top: 6px; }
.sd-panel-acc .btn-ghost { color: rgba(232,239,236,0.72); }
.sd-panel-acc .btn-ghost:hover { background: rgba(232,239,236,0.10); }

@media (max-width: 620px) {
  .sd-cab-teclas { display: none; }
  .sd-cab-marca { font-size: 17px; padding-left: 10px; }
  .sd-panel { padding: 22px; max-width: 88vw; }
  .sd-panel p.sd-panel-n { font-size: 32px; }
}

/* =============================================================================
   Herramientas · Descargas (js/tools-downloads.js) · prefijo dl-
   -----------------------------------------------------------------------------
   Una rejilla de fichas, y dentro de cada ficha una fila de botones de sistema.
   Lo único que tiene truco es el ICONO, y merece las cuatro líneas:

   `.dl-icono` lleva SIEMPRE la inicial debajo, y la <img> encima en absoluto.
   No es un adorno: la imagen no llega con la plantilla —hay que bajarla del
   bucket y envolverla en un blob, ver appIconUrl en data.js— así que entre que
   se pinta la ficha y llega el icono hay un rato. Con la inicial debajo ese
   rato no se ve; con un hueco vacío, la rejilla parpadea entera.

   La <img> se pinta transparente hasta que tiene contenido (`.ok`, que pone
   hidrataIconosApps al asignar el src). Sin eso, el borde del alt roto asoma
   por encima de la inicial durante la descarga.
   ========================================================================== */

.dl-filtros { display: flex; flex-wrap: wrap; gap: 6px; }
.dl-chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 26px; padding: 0 10px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-3); font-size: 11.5px; font-family: inherit; cursor: pointer;
  transition: border-color .12s ease, color .12s ease, background .12s ease;
}
.dl-chip:hover { border-color: var(--teal-200); color: var(--ink); }
.dl-chip.on { border-color: var(--teal-500); background: var(--teal-50); color: var(--teal-700); }
.dl-chip svg { flex-shrink: 0; }

.dl-grid {
  display: grid; gap: 14px; padding: 6px 16px 18px;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.dl-app {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-3); padding: 16px;
  display: flex; flex-direction: column; gap: 12px;
}
/* Un borrador se ve DISTINTO, no más flojo: bajarle la opacidad lo haría
   parecer deshabilitado, y está perfectamente vivo para quien lo publica. */
.dl-app.borrador { border-style: dashed; border-color: var(--line-2); }

.dl-app-hd { display: flex; align-items: flex-start; gap: 12px; }
.dl-app-txt { flex: 1; min-width: 0; }
.dl-app-txt h3 {
  margin: 0 0 4px; font-family: var(--f-display); font-weight: 500;
  font-size: 16px; letter-spacing: -0.01em; color: var(--ink); line-height: 1.25;
  overflow-wrap: anywhere;
}
.dl-app-txt p {
  margin: 0; font-size: 12.5px; color: var(--ink-3); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.dl-app-txt p.vacio { color: var(--ink-4); font-style: italic; }
.dl-menu { flex-shrink: 0; }

.dl-icono {
  position: relative; flex-shrink: 0;
  width: 44px; height: 44px; border-radius: var(--r-3);
  border: 1px solid var(--line); background: var(--bg-elev);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.dl-icono.grande { width: 56px; height: 56px; }
.dl-icono img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; padding: 4px; opacity: 0; transition: opacity .15s ease;
}
.dl-icono img.ok { opacity: 1; }
.dl-mono {
  font-family: var(--f-mono); font-size: 14px; font-weight: 500;
  color: var(--ink-3); letter-spacing: .04em;
}

.dl-marcas { display: flex; flex-wrap: wrap; gap: 6px; margin-top: -4px; }

.dl-sin {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px; border-radius: var(--r-2);
  background: var(--amber-bg); color: var(--amber-fg);
  font-size: 11.5px; line-height: 1.5;
}
.dl-sin .lucide { width: 14px; height: 14px; flex-shrink: 0; margin-top: 2px; }

/* ----- Los botones de sistema -----
   Se reparten el ancho en dos columnas y no en una fila que se desborda: con
   seis sistemas, una fila obligaría a leer en horizontal para encontrar el
   propio. En dos columnas, Windows está siempre arriba a la izquierda. */
.dl-os {
  display: grid; gap: 8px; margin-top: auto;
  grid-template-columns: repeat(auto-fit, minmax(146px, 1fr));
}
.dl-btn {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: var(--r-2);
  border: 1px solid var(--line); background: var(--bg-elev);
  color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.dl-btn:hover { border-color: var(--teal-400); background: var(--teal-50); color: var(--teal-700); }
.dl-btn:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.dl-btn-ico { display: flex; color: var(--ink-2); flex-shrink: 0; }
.dl-btn:hover .dl-btn-ico { color: var(--teal-700); }
.dl-btn-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.dl-btn-txt b { font-size: 12px; font-weight: 500; line-height: 1.2; }
/* La segunda línea ENVUELVE en vez de cortarse: «v7.2.1 · 48.0 MB» no cabe en
   una columna estrecha, y un tamaño cortado por la mitad no informa de nada.
   Que ocupe dos líneas sale más barato que esconder el dato. */
.dl-btn-txt em {
  font-family: var(--f-mono); font-size: 9.5px; font-style: normal;
  color: var(--ink-4); line-height: 1.25; overflow-wrap: anywhere;
}
.dl-btn:hover .dl-btn-txt em { color: var(--teal-700); }
.dl-btn-flecha { width: 14px; height: 14px; color: var(--ink-4); flex-shrink: 0; }
.dl-btn:hover .dl-btn-flecha { color: var(--teal-700); }

/* ----- Los formularios de quien publica ----- */
.dl-form-icono { display: flex; align-items: center; gap: 16px; }

.dl-origen { display: grid; gap: 8px; margin: 16px 0 14px; }
.dl-origen-op {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-2);
  cursor: pointer; transition: border-color .12s ease, background .12s ease;
}
.dl-origen-op:hover { border-color: var(--teal-200); }
/* El fondo de la opción elegida es `--teal-50`, que en ESTA casa es claro en
   los dos temas (mírese en el bloque de tokens). Así que el texto de dentro hay
   que teñirlo también: heredando `--ink` en tema oscuro salía blanco sobre
   celeste, y el título de la opción marcada desaparecía. */
.dl-origen-op:has(input:checked) { border-color: var(--teal-500); background: var(--teal-50); }
.dl-origen-op:has(input:checked) b { color: var(--teal-700); }
.dl-origen-op:has(input:checked) em { color: var(--teal-700); opacity: .85; }
.dl-origen-op input { margin-top: 2px; width: 16px; height: 16px; flex-shrink: 0; }
.dl-origen-op span { display: flex; flex-direction: column; gap: 2px; }
.dl-origen-op b { font-size: 12.5px; font-weight: 500; }
.dl-origen-op em {
  font-style: normal; font-size: 11px; color: var(--ink-3); line-height: 1.45;
}

.dl-drop {
  border: 2px dashed var(--line-2); border-radius: var(--r-3);
  padding: 24px 16px; text-align: center; background: var(--bg-elev); cursor: pointer;
  transition: border-color .12s ease;
}
.dl-drop:hover, .dl-drop.on { border-color: var(--teal-400); }
.dl-drop .lucide { width: 26px; height: 26px; color: var(--ink-3); margin-bottom: 6px; }
.dl-drop > div { font-size: 13px; color: var(--ink); overflow-wrap: anywhere; }
.dl-drop .dl-drop-sub { font-family: var(--f-mono); font-size: 10.5px; color: var(--ink-4); margin-top: 4px; }

@media (max-width: 640px) {
  .dl-grid { grid-template-columns: 1fr; padding: 6px 12px 14px; }
}

/* =============================================================================
 * CHAT FLOTANTE
 * -----------------------------------------------------------------------------
 * El widget vive FUERA de #root (ver la cabecera de js/chat.js), así que nada de
 * esto cuelga de `.app`: son reglas de primer nivel, ancladas a la ventana.
 *
 * LA ESQUINA INFERIOR DERECHA YA ESTABA OCUPADA por dos cosas —los toasts y el
 * panel de Tweaks—, y las tres juntas se pisaban. Se resuelve al final de este
 * bloque, con `body.has-chat`: la clase la pone chatMontar() y la quita
 * chatApagar(), así que en la pantalla de acceso —donde no hay chat— los toasts
 * siguen exactamente donde estaban.
 *
 * Z-INDEX. Por encima de Tweaks (90) y por debajo del fondo de los modales
 * (100): un modal tiene que tapar el chat, y el chat tiene que tapar el panel de
 * preferencias. Los toasts (1000) quedan por encima de todo a propósito — un
 * aviso de mensaje nuevo que el propio chat tapara no serviría de nada.
 * ========================================================================== */

.chat-dock { position: fixed; bottom: 24px; right: 24px; z-index: 95; }

/* ----- El botón cuando está minimizado ----- */
.chat-launcher {
  position: relative; width: 52px; height: 52px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--teal-500); color: #fff;
  box-shadow: var(--sh-3); cursor: pointer;
  display: grid; place-items: center;
  transition: transform .15s ease, background .15s ease;
}
.chat-launcher:hover { transform: translateY(-2px); background: var(--teal-400); }
.chat-launcher:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.chat-launcher .lucide { width: 22px; height: 22px; }

/* El contador de no leídos. `min-width` y no `width` para que un 99+ no se salga. */
.chat-dot {
  position: absolute; top: -4px; right: -4px;
  min-width: 20px; height: 20px; padding: 0 5px; border-radius: 999px;
  background: var(--rose); color: #fff;
  font-family: var(--f-mono); font-size: 10.5px; font-weight: 500;
  display: grid; place-items: center; border: 2px solid var(--bg);
}

/* ----- El panel ----- */
.chat-panel {
  width: 380px; height: min(600px, calc(100vh - 96px));
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-4); box-shadow: var(--sh-3);
  /* El botón de bajar y los paneles de emoticonos y GIFs se colocan contra este
   * borde: son de los pocos elementos del widget que no van en el flujo. */
  position: relative;
}

.chat-head {
  display: flex; align-items: center; gap: 8px; flex-shrink: 0;
  padding: 12px 12px 12px 14px; border-bottom: 1px solid var(--line);
  background: var(--bg-elev);
}
/* Nombre y estado, uno debajo del otro. El contenedor es quien se estira; el
 * título ya no, o en una columna se estiraría a lo alto. */
.chat-head-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.chat-head-txt .chat-title { flex: none; }
.chat-title {
  margin: 0; flex: 1; min-width: 0;
  font-family: var(--f-display); font-size: 16px; font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chat-head-ico { display: grid; place-items: center; color: var(--teal-500); }
.chat-head-ico .lucide { width: 18px; height: 18px; }
.chat-head-acts { display: flex; gap: 2px; flex-shrink: 0; }

.chat-ico {
  width: 30px; height: 30px; border: 0; background: transparent; border-radius: var(--r-2);
  color: var(--ink-3); cursor: pointer; display: grid; place-items: center;
}
.chat-ico:hover { background: var(--bg-2); color: var(--ink); }
.chat-ico:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.chat-ico .lucide { width: 17px; height: 17px; }
/* El botón cuyo panel está abierto ahora mismo: el buscador del hilo, los
 * emoticonos, los GIFs. NO se usa `.live` para esto —aquella parpadea, y algo
 * que parpadea mientras escribes es una lucecita de aviso, no un estado—. */
.chat-ico.on { background: var(--teal-50); color: var(--teal-700); }
/* Hay una llamada en curso en esta sala: el botón deja de ser un icono neutro. */
.chat-ico.live { color: var(--teal-500); animation: chat-late 1.6s ease-in-out infinite; }
@keyframes chat-late { 50% { opacity: .45; } }
/* Actualizando los estados. Dura lo que dure la consulta —normalmente un
 * pestañeo—, y por eso gira en vez de desaparecer: sin señal ninguna, pulsar
 * un botón que no cambia nada visible parece que no ha hecho nada. */
.chat-ico.girando { color: var(--teal-500); pointer-events: none; }
.chat-ico.girando .lucide { animation: chat-gira .8s linear infinite; }
@keyframes chat-gira { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .chat-ico.girando .lucide { animation: none; }
  .chat-ico.live { animation: none; }
}

/* ----- Buscador ----- */
.chat-search { position: relative; flex-shrink: 0; padding: 10px 12px; border-bottom: 1px solid var(--line); }
.chat-search .lucide {
  position: absolute; left: 22px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--ink-4); pointer-events: none;
}
.chat-search input {
  width: 100%; height: 34px; padding: 0 10px 0 32px;
  border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--bg); color: var(--ink); font: inherit; font-size: 13px;
}
.chat-search input:focus { outline: 0; border-color: var(--teal-400); }

.chat-hint { margin: 0; padding: 10px 14px 6px; font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }

/* ----- La bandeja ----- */
.chat-list { flex: 1; overflow-y: auto; padding: 6px; }

.chat-row {
  width: 100%; display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 9px 10px; border: 0; border-radius: var(--r-2);
  background: transparent; cursor: pointer; font: inherit; color: var(--ink);
}
.chat-row:hover { background: var(--bg-2); }
.chat-row:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
/* Fila marcada en «Nueva conversación». EL FONDO VA CON SU TEXTO, que es la
 * regla de esta hoja — ver «CUIDADO CON LOS TOKENS DE ACENTO», unas líneas más
 * arriba, justo antes de este bloque. `applyAccent()` (js/shell.js) escribe la rampa CLARA del acento en
 * `:root` con `style.setProperty`, y un estilo en línea le gana al bloque
 * `[data-theme="dark"]`, así que `--teal-50` es un pastel CLARO también en tema
 * oscuro.
 *
 * Esta regla se escribió sin fijar el color del texto, y el nombre se quedaba en
 * `--ink`, que en oscuro es casi blanco: pastel claro con letra clara, contraste
 * 1.01 sobre el 4.5 que pide WCAG AA. Al marcar a alguien dejaba de poder leerse
 * a quién se había marcado.
 *
 * Los tres colores de dentro se fijan a mano y ninguno se hereda del tema. Es lo
 * mismo que hacen `.palette-item.active`, `.doc-folder.active` y las otras
 * treinta reglas de la casa que resaltan algo. */
.chat-row.on { background: var(--teal-50); }
.chat-row.on .chat-row-name,
.chat-row.on .chat-check { color: var(--teal-700); }
.chat-row.on .chat-row-prev { color: var(--teal-700); opacity: .72; }
/* El anillo del punto de presencia imita el fondo que tiene detrás, que en una
 * fila marcada ya no es `--surface`. */
.chat-row.on .chat-pres { border-color: var(--teal-50); }
/* Y el avatar, otra pareja fondo+texto. Los colores de av() se quitan en el HTML
 * (ver chatNuevoHtml en js/chat.js): un estilo en línea le gana a esta regla, así
 * que la única forma de mandar aquí es que allí no se escriban. */
.chat-row.on .chat-av { background: var(--teal-100); color: var(--teal-700); }

.chat-av {
  position: relative; flex-shrink: 0;
  width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
}
.chat-av .lucide { width: 17px; height: 17px; }

/* El pilotito. Cuatro estados y cuatro colores, y ninguno de ellos dice una
 * hora: verde y ámbar salen de la vista
 * `chat_online` (un booleano contra un umbral de 90 segundos) y azul de
 * `presence_today` (dos booleanos por día). Ver PRESENCIA en js/chat.js.
 *
 * Los colores NO son decorativos y por eso los cuatro llevan `title`: el color
 * solo no basta para quien no lo distingue. */
.chat-pres {
  position: absolute; right: -1px; bottom: -1px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--pres-off); border: 2px solid var(--surface);
}
/* Verde: en el HUB y mirándolo. */
.chat-pres.online  { background: var(--pres-online); }
/* Ámbar: el HUB abierto en una pestaña de atrás. */
.chat-pres.away    { background: var(--pres-away); }
/* Azul: fichado y trabajando, pero sin el HUB delante. Es el estado que no
 * existía antes y el que más se consulta: contesta, pero no ahora mismo. */
.chat-pres.fichado { background: var(--pres-fichado); }
/* Y desconectado, en gris apagado. Se pinta SIEMPRE, también en la bandeja: sin
 * punto no se distingue «está desconectado» de «el pilotito no funciona». */
.chat-pres.off     { background: var(--pres-off); }

/* Fuera de un avatar —en la cabecera del hilo y en la columna «Conexión» de la
 * lista de empleados— el punto no está colocado en una esquina: va en línea con
 * el texto y sin el anillo, que ahí no separa de nada. */
.chat-sub .chat-pres,
.pres-cell .chat-pres {
  position: static; width: 7px; height: 7px; border: 0; flex-shrink: 0;
}
.chat-sub {
  display: flex; align-items: center; gap: 5px;
  font-size: 11px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* La celda de la columna «Conexión»: el punto y su palabra. Lleva texto y no
 * solo el color porque una columna entera de puntos de colores no la lee quien
 * no los distingue, y `white-space: nowrap` porque «Desconectado» partido en
 * dos líneas descuadra la fila entera. */
.pres-cell {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--ink-2); white-space: nowrap;
}

.chat-row-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.chat-row-top { display: flex; align-items: baseline; gap: 8px; }
.chat-row-name {
  flex: 1; min-width: 0; font-size: 13.5px; font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chat-row-when { flex-shrink: 0; font-size: 10.5px; color: var(--ink-4); font-family: var(--f-mono); }
.chat-row-prev {
  font-size: 12px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chat-row.unread .chat-row-name { font-weight: 600; }
.chat-row.unread .chat-row-prev { color: var(--ink-2); }

.chat-badge {
  flex-shrink: 0; min-width: 19px; height: 19px; padding: 0 5px; border-radius: 999px;
  background: var(--teal-500); color: #fff;
  font-family: var(--f-mono); font-size: 10.5px;
  display: grid; place-items: center;
}
.chat-live { flex-shrink: 0; color: var(--teal-500); display: grid; place-items: center; }
.chat-live .lucide { width: 15px; height: 15px; }
.chat-check { flex-shrink: 0; width: 18px; color: var(--teal-500); }
.chat-check .lucide { width: 16px; height: 16px; }

.chat-empty { padding: 40px 24px; text-align: center; color: var(--ink-3); }
.chat-empty .lucide { width: 28px; height: 28px; color: var(--ink-4); margin-bottom: 8px; }
.chat-empty p { margin: 0 0 12px; font-size: 13px; line-height: 1.5; }

.chat-foot {
  flex-shrink: 0; padding: 10px 12px; border-top: 1px solid var(--line);
  display: flex; gap: 8px; align-items: center; background: var(--bg-elev);
}
.chat-foot .input { flex: 1; height: 32px; font-size: 13px; }

/* ----- El hilo ----- */
.chat-thread {
  flex: 1; overflow-y: auto; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 2px;
}

/* La conversación se lee de abajo arriba: lo último tiene que quedar pegado al
 * composer aunque haya cuatro mensajes y sobre sitio.
 *
 * ESTO ERA `justify-content: flex-end` EN EL PROPIO CONTENEDOR, Y ESO ROMPÍA EL
 * SCROLL. No «lo hacía incómodo»: lo rompía del todo. Cuando el contenido de un
 * contenedor con `overflow` no cabe, `flex-end` lo empuja hacia ARRIBA más allá
 * del borde de inicio, y el desbordamiento por ese lado no entra en la región
 * desplazable — el navegador ni siquiera lo cuenta—. Medido en el navegador con
 * una conversación de 246 mensajes:
 *
 *     justify-content: flex-end     scrollHeight 474 = clientHeight 474
 *     con margin-top: auto          scrollHeight 14302, clientHeight 474
 *
 * Con lo primero, catorce mil píxeles de conversación no existían: la barra no
 * aparecía, la rueda del ratón no hacía nada y no había forma de leer nada de
 * hace media hora.
 *
 * `margin-top: auto` en el PRIMER hijo hace lo mismo cuando sobra sitio y NO
 * hace nada cuando falta: un margen automático se resuelve a cero si no hay
 * espacio libre que repartir. Que es exactamente la diferencia que hacía falta.
 */
.chat-thread > :first-child { margin-top: auto; }

.chat-day { text-align: center; margin: 14px 0 8px; }
.chat-day span {
  font-size: 10.5px; color: var(--ink-4); background: var(--bg-2);
  padding: 3px 10px; border-radius: 999px;
}

.chat-sys {
  align-self: center; margin: 6px 0;
  display: flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--ink-4); font-style: italic;
}
.chat-sys .lucide { width: 13px; height: 13px; }

.chat-msg { display: flex; flex-direction: column; align-items: flex-start; margin-top: 8px; max-width: 100%; }
.chat-msg.mine { align-items: flex-end; }
/* Mensajes seguidos de la misma persona: se pegan y no repiten el nombre. */
.chat-msg.cont { margin-top: 2px; }

.chat-msg-who { font-size: 11px; color: var(--ink-3); margin: 0 0 3px 10px; font-weight: 500; }

.chat-bubble {
  max-width: 84%; padding: 7px 11px 5px;
  border-radius: 14px 14px 14px 4px;
  background: var(--bg-2); color: var(--ink);
  font-size: 13.5px; line-height: 1.45;
  word-break: break-word; overflow-wrap: anywhere;
}
.chat-msg.mine .chat-bubble {
  background: var(--teal-500); color: #fff;
  border-radius: 14px 14px 4px 14px;
}
.chat-msg.cont .chat-bubble { border-radius: 14px; }

.chat-bubble a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.chat-body { display: block; }

/* La lápida de un mensaje retirado. Va con `.chat-msg` delante a propósito: sin
 * ese selector, `.chat-msg.mine .chat-bubble` —que tiene tres clases— le ganaba
 * en especificidad y los mensajes retirados PROPIOS salían con la burbuja verde
 * de un mensaje normal, que es justo lo contrario de lo que dicen. */
.chat-msg .chat-bubble.gone {
  background: transparent; color: var(--ink-4); border: 1px dashed var(--line-2);
  font-style: italic; font-size: 12.5px;
  display: flex; align-items: center; gap: 6px;
}
.chat-bubble.gone .lucide { width: 13px; height: 13px; }

.chat-meta {
  display: flex; align-items: center; justify-content: flex-end; gap: 4px;
  margin-top: 2px; font-family: var(--f-mono); font-size: 9.5px; opacity: .6;
}

/* =============================================================================
 * LA CONVERSACIÓN · reacciones, citas, reenvíos, acciones y GIFs
 * -----------------------------------------------------------------------------
 * Todo lo que cuelga de una burbuja. Ver js/chat-plus.js.
 *
 * La regla que gobierna esta sección: NADA de lo que aparece al pasar el ratón
 * puede cambiar el tamaño de la burbuja. En un hilo, un elemento que se mete en
 * el flujo al acercar el cursor mueve los cinco mensajes de abajo, y entonces se
 * pulsa el que no era. Por eso los botones de acción van en `position:absolute`
 * y por eso su hueco no lo reserva nadie.
 * ========================================================================== */

.chat-msg { position: relative; }

/* ----- Los botones que salen al pasar por encima ----- */
.chat-acts {
  position: absolute; top: -10px; z-index: 2;
  display: flex; gap: 1px; padding: 2px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); box-shadow: var(--sh-1);
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
/* En los míos a la izquierda y en los ajenos a la derecha: siempre por el lado
 * donde hay hueco, para no taparle el texto a nadie. */
.chat-msg .chat-acts { right: 6px; }
.chat-msg.mine .chat-acts { right: auto; left: 6px; }

.chat-msg:hover .chat-acts,
.chat-msg:focus-within .chat-acts { opacity: 1; pointer-events: auto; }
/* En una pantalla táctil no hay «pasar por encima», así que la barra se queda
 * puesta… pero reducida al botón de «más opciones», que abre un menú con las
 * tres acciones dentro. Con los tres botones visibles en cada mensaje, un hilo
 * de veinte líneas son sesenta botones flotando sobre la conversación en un
 * ancho de 380 píxeles: se ve más barra que texto.
 *
 * `hover: none` acierta en el móvil y no en un portátil con pantalla táctil, que
 * sigue teniendo ratón — que es exactamente lo que se quiere. */
@media (hover: none) {
  .chat-acts { opacity: .5; pointer-events: auto; }
  .chat-acts .chat-act:not([data-chat="msg-menu"]) { display: none; }
}

.chat-act {
  width: 24px; height: 24px; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink-3); cursor: pointer;
  display: grid; place-items: center;
}
.chat-act:hover { background: var(--bg-2); color: var(--ink); }
.chat-act:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.chat-act .lucide { width: 13px; height: 13px; }

/* ----- La cita de una respuesta ----- */
.chat-quote {
  display: block; width: 100%; text-align: left; cursor: pointer;
  margin: 0 0 5px; padding: 4px 8px;
  border: 0; border-left: 3px solid currentColor;
  border-radius: var(--r-1);
  background: rgba(127, 127, 127, .13);
  color: inherit; font: inherit; opacity: .85;
}
.chat-quote:hover { opacity: 1; background: rgba(127, 127, 127, .2); }
.chat-quote.ida { display: flex; align-items: center; gap: 5px; cursor: default; font-style: italic; }
.chat-quote.ida .lucide { width: 12px; height: 12px; }
.chat-quote-who { display: block; font-size: 11px; font-weight: 600; }
.chat-quote-txt {
  display: block; font-size: 12px; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* El mensaje al que se acaba de saltar desde una cita o desde la búsqueda. Dos
 * segundos y medio de tinte: sin esto, el hilo salta y no queda claro a cuál de
 * los mensajes de la pantalla se ha venido. */
.chat-msg.saltado .chat-bubble { animation: chat-salto 2.6s ease-out; }
@keyframes chat-salto {
  0%, 55% { box-shadow: 0 0 0 3px var(--teal-400); }
  100%    { box-shadow: 0 0 0 3px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-msg.saltado .chat-bubble { animation: none; box-shadow: 0 0 0 3px var(--teal-400); }
}

/* ----- «Reenviado de Fulano» ----- */
.chat-fwd {
  display: flex; align-items: center; gap: 4px;
  margin-bottom: 3px; font-size: 10.5px; font-style: italic; opacity: .75;
}
.chat-fwd .lucide { width: 11px; height: 11px; }

/* ----- Las reacciones ----- */
.chat-reacts { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 3px; max-width: 84%; }
.chat-msg.mine .chat-reacts { justify-content: flex-end; }

.chat-react {
  display: flex; align-items: center; gap: 3px;
  padding: 1px 7px 2px; border: 1px solid var(--line);
  border-radius: 999px; background: var(--surface);
  color: var(--ink-2); cursor: pointer; font: inherit; line-height: 1.3;
}
.chat-react:hover { border-color: var(--teal-400); }
.chat-react:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 1px; }
/* La propia. El borde de acento importa más que el fondo: en tema oscuro un
 * tinte tan pequeño no se distingue del fondo de la burbuja. */
.chat-react.on { border-color: var(--teal-500); background: var(--teal-50); color: var(--teal-700); }
.chat-react-e { font-size: 12.5px; }
.chat-react-n { font-size: 10.5px; font-family: var(--f-mono); }

/* ----- Un GIF dentro de la burbuja ----- */
/* El mínimo no es estética: la fila no guarda el tamaño del GIF, así que
 * mientras la imagen baja —o si no llega nunca— el <img> mide cero y la burbuja
 * se queda en una pastilla con la hora dentro. Con el mínimo puesto hay un hueco
 * del tamaño aproximado de lo que va a aparecer, y el hilo no pega un salto
 * cuando termina de cargar. */
.chat-gif {
  display: block; max-width: 100%; max-height: 220px;
  min-width: 120px; min-height: 90px;
  border-radius: var(--r-2); background: var(--bg-2);
}
.chat-gif-roto { display: flex; align-items: center; gap: 6px; font-style: italic; opacity: .8; }
.chat-gif-roto .lucide { width: 14px; height: 14px; }

/* ----- «Ver mensajes anteriores» ----- */
.chat-mas { display: flex; justify-content: center; padding: 4px 0 8px; }

/* ----- El botón de bajar ----- */
.chat-bajar {
  position: absolute; right: 16px; bottom: 68px; z-index: 3;
  width: 32px; height: 32px; border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface); color: var(--ink-2); cursor: pointer;
  display: grid; place-items: center; box-shadow: var(--sh-2);
  opacity: 0; pointer-events: none; transform: translateY(6px);
  transition: opacity .15s ease, transform .15s ease;
}
.chat-bajar.on { opacity: 1; pointer-events: auto; transform: none; }
.chat-bajar:hover { color: var(--ink); border-color: var(--teal-400); }
.chat-bajar .lucide { width: 15px; height: 15px; }

/* ----- Respondiendo a… ----- */
.chat-respondiendo {
  flex-shrink: 0; display: flex; align-items: center; gap: 8px;
  padding: 7px 10px 7px 12px;
  border-top: 1px solid var(--line); background: var(--bg-2);
  border-left: 3px solid var(--teal-500);
}
.chat-respondiendo > .lucide { width: 15px; height: 15px; color: var(--teal-600); flex-shrink: 0; }
.chat-resp-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.chat-resp-txt b { font-size: 11px; color: var(--teal-700); }
.chat-resp-txt span {
  font-size: 12px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ----- La búsqueda dentro del hilo ----- */
.chat-search-hilo { display: flex; align-items: center; gap: 6px; }
.chat-search-hilo input { flex: 1; }
/* La lupa de `.chat-search` va en `position:absolute` dentro del cuadro, y esa
 * regla alcanzaba también al icono del botón de cerrar —que es el único botón
 * que hay dentro de un `.chat-search`—: la X se pintaba encima de la lupa y el
 * botón quedaba invisible. Se devuelve a su sitio. */
.chat-search-hilo .chat-ico .lucide {
  position: static; transform: none; width: 16px; height: 16px;
  color: inherit; pointer-events: auto;
}
.chat-resultados { flex: 1; overflow-y: auto; padding: 6px; }
.chat-resultados mark {
  background: var(--teal-100); color: var(--teal-700);
  padding: 0 1px; border-radius: 2px;
}

/* ----- Los paneles flotantes: emoticonos y GIFs ----- */
.chat-flot {
  flex-shrink: 0; display: flex; flex-direction: column;
  max-height: 300px; border-top: 1px solid var(--line);
  background: var(--bg-elev);
}
.chat-flot-hd {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 8px 6px 12px;
}
.chat-flot-tit { font-size: 11.5px; font-weight: 600; color: var(--ink-2); }

.chat-emoji-rapidos {
  display: flex; gap: 4px; padding: 0 10px 8px;
  border-bottom: 1px solid var(--line); margin-bottom: 6px;
}
.chat-emoji-tabs { display: flex; gap: 2px; padding: 0 8px 6px; }
.chat-emoji-tab {
  flex: 1; height: 28px; border: 0; border-radius: var(--r-2);
  background: transparent; color: var(--ink-4); cursor: pointer;
  display: grid; place-items: center;
}
.chat-emoji-tab:hover { background: var(--bg-2); color: var(--ink-2); }
.chat-emoji-tab.on { background: var(--teal-50); color: var(--teal-700); }
.chat-emoji-tab .lucide { width: 15px; height: 15px; }

.chat-emoji-q { position: relative; padding: 0 10px 8px; }
.chat-emoji-q .lucide {
  position: absolute; left: 19px; top: 8px;
  width: 13px; height: 13px; color: var(--ink-4); pointer-events: none;
}
.chat-emoji-q input {
  width: 100%; height: 29px; padding: 0 10px 0 28px;
  border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--surface); color: var(--ink); font: inherit; font-size: 12.5px;
}
.chat-emoji-q input:focus { outline: 0; border-color: var(--teal-400); }

.chat-emoji-grid {
  flex: 1; overflow-y: auto; padding: 0 8px 8px;
  display: flex; flex-wrap: wrap; gap: 1px; align-content: flex-start;
}
.chat-emoji-b {
  width: 32px; height: 32px; border: 0; border-radius: var(--r-2);
  background: transparent; cursor: pointer; font-size: 19px; line-height: 1;
  display: grid; place-items: center;
}
.chat-emoji-b:hover { background: var(--bg-2); }
.chat-emoji-b:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.chat-emoji-nada { margin: 12px auto; font-size: 12px; color: var(--ink-3); }

/* La rejilla de GIFs va en dos columnas y con altura fija por fila: sin altura
 * fija, cada imagen que termina de cargar mueve las de abajo y la que ibas a
 * pulsar ya no está donde estaba. */
.chat-gif-grid {
  flex: 1; overflow-y: auto; padding: 0 8px 8px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px; align-content: flex-start;
}
.chat-gif-grid .chat-empty,
.chat-gif-mas { grid-column: 1 / -1; }
.chat-gif-b {
  height: 92px; padding: 0; border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--bg-2); cursor: pointer; overflow: hidden;
}
.chat-gif-b:hover { border-color: var(--teal-400); }
.chat-gif-b:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 1px; }
.chat-gif-b img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chat-gif-pie {
  margin: 0; padding: 6px 12px 8px; font-size: 10.5px; line-height: 1.4;
  color: var(--ink-4); border-top: 1px solid var(--line);
}

.chat-reenv-prev {
  margin: 0 0 12px; padding: 8px 12px;
  border: 0; border-left: 3px solid var(--teal-500); border-radius: var(--r-1);
  background: var(--bg-2); color: var(--ink-2); font-size: 12.5px; line-height: 1.45;
}

/* ----- El cuadro de escribir ----- */
.chat-composer {
  flex-shrink: 0; display: flex; align-items: flex-end; gap: 8px;
  padding: 10px 12px; border-top: 1px solid var(--line); background: var(--bg-elev);
}
.chat-composer textarea {
  flex: 1; resize: none; max-height: 120px; min-height: 34px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-3);
  background: var(--surface); color: var(--ink);
  font: inherit; font-size: 13.5px; line-height: 1.4;
}
.chat-composer textarea:focus { outline: 0; border-color: var(--teal-400); }
/* Los botones de emoticonos y GIF, a la altura del cuadro de escribir: sin esto
 * quedan cuatro píxeles por encima cuando la caja está en su altura mínima. */
.chat-composer .chat-ico { height: 34px; flex-shrink: 0; }
.chat-send {
  flex-shrink: 0; width: 34px; height: 34px; border: 0; border-radius: 50%;
  background: var(--teal-500); color: #fff; cursor: pointer; display: grid; place-items: center;
}
.chat-send:disabled { background: var(--bg-2); color: var(--ink-4); cursor: default; }
.chat-send .lucide { width: 16px; height: 16px; }

/* ----- El asistente de navegación ----- */
.chat-gos { display: flex; flex-direction: column; gap: 5px; margin-top: 9px; }
.chat-go {
  display: flex; align-items: center; gap: 7px; width: 100%; text-align: left;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--surface); color: var(--ink); cursor: pointer;
  font: inherit; font-size: 12.5px;
}
/* Mismo caso que `.chat-row.on`: si se pone el tinte de acento de fondo, hay que
 * poner también el texto. Sin el `color`, al pasar el ratón por encima el botón
 * se volvía ilegible en tema oscuro. */
.chat-go:hover { border-color: var(--teal-400); background: var(--teal-50); color: var(--teal-700); }
/* `currentColor` y no `--teal-500`: un token de acento SUELTO sobre el fondo del
 * tema se ve en claro y se apaga en oscuro (2.0 de contraste). Heredando, la
 * flecha va con `--ink` en reposo y con `--teal-700` sobre el tinte al pasar por
 * encima — legible en los dos temas y con los cuatro acentos. */
.chat-go .lucide { width: 13px; height: 13px; color: currentColor; flex-shrink: 0; }

.chat-sugs { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; }
.chat-sug {
  padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink-2); cursor: pointer;
  font: inherit; font-size: 11.5px;
}
.chat-sug:hover { border-color: var(--teal-400); color: var(--ink); }

/* =============================================================================
 * LA VIDEOLLAMADA
 * -----------------------------------------------------------------------------
 * Se pinta dentro de #chatCallSlot, un hueco que el panel deja entre la cabecera
 * y el cuerpo. Así la llamada se ve sin salir de la conversación y —lo que de
 * verdad importa— los <video> no se destruyen cuando el hilo se repinta.
 * ========================================================================== */

.chat-call-slot:empty { display: none; }
.chat-call-slot { flex-shrink: 0; border-bottom: 1px solid var(--line); }

/* La llamada entrante. Va arriba del todo porque es lo único del widget que
 * tiene prisa. */
.call-ring {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; background: var(--teal-50);
}
.call-ring .lucide { width: 17px; height: 17px; color: var(--teal-600); flex-shrink: 0; }
.call-ring-txt { flex: 1; min-width: 120px; font-size: 12.5px; color: var(--teal-700); }

.call-box { background: #0d1614; }
.call-grid { display: grid; gap: 2px; padding: 2px; }

.call-tile {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  border-radius: var(--r-1); background: #16211f;
}
.call-tile video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* El vídeo propio va en espejo, que es como la gente espera verse. El de los
 * demás NO: a los demás se les ve como son. */
.call-tile video[data-call-me] { transform: scaleX(-1); }
.call-tile video[data-call-me].sharing { transform: none; object-fit: contain; }

.call-name {
  position: absolute; left: 5px; bottom: 5px;
  padding: 2px 7px; border-radius: 999px;
  background: rgba(0,0,0,0.55); color: #fff; font-size: 10.5px;
}
.call-off, .call-muted {
  position: absolute; top: 5px; right: 5px;
  width: 22px; height: 22px; border-radius: 50%;
  background: rgba(0,0,0,0.55); color: #fff; display: grid; place-items: center;
}
.call-muted { right: 31px; }
.call-off .lucide, .call-muted .lucide { width: 12px; height: 12px; }

.call-wait {
  margin: 0; padding: 8px 12px; display: flex; align-items: center; gap: 7px;
  font-size: 12px; color: rgba(255,255,255,0.65);
}
.call-wait .lucide { width: 14px; height: 14px; }

.call-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 10px; background: #0d1614;
}
.call-time { flex: 1; font-family: var(--f-mono); font-size: 11px; color: rgba(255,255,255,0.55); }

.call-btn {
  width: 32px; height: 32px; border: 0; border-radius: 50%;
  background: rgba(255,255,255,0.12); color: #fff; cursor: pointer;
  display: grid; place-items: center;
}
.call-btn:hover { background: rgba(255,255,255,0.20); }
.call-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.call-btn .lucide { width: 15px; height: 15px; }
/* Apagado = rojo. Es la convención de todas las aplicaciones de vídeo y no hay
 * ningún motivo para inventar otra: el micrófono tachado en rojo se entiende sin
 * leer nada. */
.call-btn.off { background: var(--rose); }
.call-btn.on { background: var(--teal-500); }
.call-btn.hang { background: var(--rose); }
.call-btn.hang:hover { background: #a04249; }

/* =============================================================================
 * EL ESCENARIO DE LA LLAMADA
 * -----------------------------------------------------------------------------
 * La llamada se pintaba dentro del panel del chat, que mide 380 píxeles: cuatro
 * personas salían en cuatro sellos de correo. Ahora ocupa la pantalla, y el
 * panel se aparta mientras dura. Ver EL ESCENARIO en js/chat-call.js.
 *
 * `z-index: 96` está elegido y no es un número al azar: uno por encima del dock
 * del chat (95), para taparlo cuando la llamada está entera, y por debajo de los
 * modales (100), para que el diálogo del fondo salga por encima de la llamada y
 * no debajo.
 * ========================================================================== */

.call-stage {
  position: fixed; inset: 0; z-index: 96;
  display: flex; flex-direction: column;
  background: #0d1614; color: #e8efec;
}

/* Reducida. Es lo que permite escribir en el chat sin colgar, que es la mitad de
 * lo que pasa en una llamada de trabajo. Sube por encima del dock para que el
 * panel del chat quede accesible debajo. */
.call-stage.mini {
  inset: auto 24px 96px auto;
  width: min(320px, calc(100vw - 32px));
  border-radius: var(--r-4); overflow: hidden;
  border: 1px solid rgba(255,255,255,0.12); box-shadow: var(--sh-3);
}

.call-stage-hd {
  display: flex; align-items: center; gap: 10px; flex-shrink: 0;
  padding: 12px 16px; border-bottom: 1px solid rgba(255,255,255,0.08);
}
.call-stage.mini .call-stage-hd { padding: 8px 10px; }
/* El punto rojo que late: «esto está en directo». Reutiliza el mismo latido que
 * el botón de llamar de la cabecera del chat. */
.call-stage-dot {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
  background: var(--rose); animation: chat-late 1.6s ease-in-out infinite;
}
.call-stage-t {
  margin: 0; flex: 1; min-width: 0;
  font-family: var(--f-display); font-size: 15px; font-weight: 500; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.call-stage.mini .call-stage-t { font-size: 12.5px; }
/* El cronómetro vivía en la barra de botones y allí se estiraba. Aquí no. */
.call-stage-hd .call-time { flex: none; }

/* La rejilla llena lo que queda. `grid-auto-rows: 1fr` reparte el alto entre las
 * filas que haya: sin él, los recuadros se quedarían en su altura mínima y la
 * llamada saldría pegada arriba con medio metro de negro debajo. */
.call-stage .call-grid {
  flex: 1; min-height: 0; padding: 12px; gap: 10px;
  grid-auto-rows: 1fr;
}
.call-stage .call-tile { aspect-ratio: auto; min-height: 0; border-radius: var(--r-3); }
.call-stage.mini .call-grid { padding: 4px; gap: 4px; grid-auto-rows: auto; }
.call-stage.mini .call-tile { aspect-ratio: 4 / 3; border-radius: var(--r-1); }

.call-stage .call-bar {
  justify-content: center; gap: 10px; padding: 14px;
  border-top: 1px solid rgba(255,255,255,0.08);
}
.call-stage .call-btn { width: 44px; height: 44px; }
.call-stage .call-btn .lucide { width: 19px; height: 19px; }
.call-stage.mini .call-bar { padding: 7px; gap: 5px; }
.call-stage.mini .call-btn { width: 30px; height: 30px; }
.call-stage.mini .call-btn .lucide { width: 14px; height: 14px; }
.call-btn.ghost { background: transparent; }
.call-btn.ghost:hover { background: rgba(255,255,255,0.14); }

/* Con la llamada entera no tiene sentido dejar el botón del chat asomando por
 * debajo: se llega al chat desde la propia barra de la llamada. */
body.has-call .chat-dock { display: none; }

/* =============================================================================
 * EL FONDO DE LA VIDEOLLAMADA
 * -----------------------------------------------------------------------------
 * El diálogo que elige entre nada, desenfoque e imagen. Ver EL FONDO en
 * js/chat-call.js (el mando) y js/chat-fx.js (la maquinaria).
 * ========================================================================== */

.fondo-ops { display: flex; flex-direction: column; gap: 8px; }
.fondo-op {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 11px 12px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-3);
  background: var(--bg); color: var(--ink); font: inherit;
}
.fondo-op:hover:not([disabled]) { border-color: var(--teal-400); }
.fondo-op[disabled] { opacity: .5; cursor: default; }
.fondo-op:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
/* Elegida. Ojo, que el comentario que hubo aquí decía justo lo contrario de lo
   que pasa: `--teal-600` NO se invierte con el tema —applyAccent() lo fija en
   #155b4f en los dos— y el tinte de esta tarjeta NO se oscurece —`--teal-50`
   sigue siendo el pastel claro—. O sea que esto es una pareja fondo+texto
   normal, de las de la casa, y se lee en los dos temas: 7.06 de contraste.
   `--teal-700` daría 9.78 y sería lo coherente con las reglas vecinas; se deja
   `--teal-600` porque ya cumple de sobra y el cambio sería solo estético.
   Para texto de acento SIN tinte detrás el token es `--teal-txt`. */
.fondo-op.on { background: var(--teal-50); border-color: var(--teal-400); }
.fondo-op.on .fondo-op-txt b { color: var(--teal-600); }
.fondo-op-ico {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--bg-2); color: var(--ink-3);
}
.fondo-op.on .fondo-op-ico { background: var(--teal-100); color: var(--teal-600); }
.fondo-op-ico .lucide { width: 17px; height: 17px; }
.fondo-op-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.fondo-op-txt b { font-size: 13.5px; font-weight: 500; }
.fondo-op-txt span { font-size: 11.5px; color: var(--ink-3); }
.fondo-op-ok { width: 16px; height: 16px; color: var(--teal-500); flex-shrink: 0; }

.fondo-ajuste {
  display: flex; align-items: center; gap: 10px; margin-top: 12px;
  padding: 10px 12px; border-radius: var(--r-3); background: var(--bg-2);
}
.fondo-ajuste label { font-size: 12px; color: var(--ink-2); flex-shrink: 0; }
.fondo-ajuste input[type="range"] { flex: 1; min-width: 0; accent-color: var(--teal-500); }
.fondo-eco { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); width: 42px; text-align: right; }

.fondo-imagen { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.fondo-mini {
  width: 84px; height: 52px; object-fit: cover;
  border-radius: var(--r-2); border: 1px solid var(--line);
}
/* El botón de subir es un <label> con el <input file> dentro: así se puede
 * repintar el cuerpo del diálogo sin que el navegador cierre su ventana de
 * archivos. Necesita el mismo aspecto que un botón de verdad. */
.fondo-imagen label.btn { cursor: pointer; }

.fondo-nota {
  display: flex; align-items: flex-start; gap: 8px; margin: 14px 0 0;
  font-size: 11.5px; line-height: 1.5; color: var(--ink-3);
}
.fondo-nota .lucide { width: 14px; height: 14px; flex-shrink: 0; margin-top: 1px; color: var(--teal-500); }

/* =============================================================================
 * EL AVISO DE «NO VA A COGER LA LLAMADA»
 * -----------------------------------------------------------------------------
 * Ver chatAvisoNadieModal() en js/chat.js.
 * ========================================================================== */

.chat-aviso p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.55; color: var(--ink-2); }
.chat-aviso p:last-child { margin-bottom: 0; }
.chat-aviso .aviso-bien,
.chat-aviso .aviso-mal {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px; border-radius: var(--r-3); font-size: 12.5px;
}
.chat-aviso .aviso-bien { background: var(--teal-50); color: var(--teal-600); }
.chat-aviso .aviso-mal  { background: var(--bg-2);    color: var(--ink-3); }
.chat-aviso .aviso-bien .lucide,
.chat-aviso .aviso-mal .lucide { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; }
.chat-aviso .aviso-pie { font-size: 11.5px; color: var(--ink-3); }

/* =============================================================================
 * LA ESQUINA COMPARTIDA
 * -----------------------------------------------------------------------------
 * Los toasts vivían en `bottom: 24px; right: 24px`, que es exactamente donde se
 * pone ahora el botón del chat. Se suben los 68px que ocupa el botón más su
 * margen, y SOLO cuando el chat está montado: la clase la pone chatMontar() y la
 * quita chatApagar(), así que en la pantalla de acceso todo sigue donde estaba.
 *
 * El panel de personalización compartía esta esquina y ya no: desde que es un
 * cajón de altura completa pegado al borde derecho no se pisa con nada. Ver su
 * bloque, más arriba.
 * ========================================================================== */
body.has-chat .toast-wrap { bottom: 92px; }

/* ----- Móvil -----
 * El panel pasa a ocupar la pantalla: 380px de ancho en un teléfono dejarían el
 * hilo en una columna de treinta caracteres. Y el botón sube por encima de la
 * barra de navegación inferior, que en móvil ocupa la franja de abajo. */
@media (max-width: 640px) {
  .chat-dock { bottom: 84px; right: 16px; }
  /* Abajo a la derecha, en un teléfono, están la barra de navegación y el botón
     del chat. La ventanita de la llamada se va arriba, donde no tapa nada. */
  .call-stage.mini { inset: 12px 12px auto auto; width: 168px; }
  .call-stage .call-grid { padding: 8px; gap: 8px; }
  .call-stage .call-bar { padding: 10px; gap: 8px; }
  .call-stage .call-btn { width: 40px; height: 40px; }
  .chat-panel {
    position: fixed; inset: 0;
    width: 100vw; height: 100dvh; max-height: none;
    border: 0; border-radius: 0;
  }
  body.has-chat .toast-wrap { bottom: 152px; }
}

/* =============================================================================
 * PANELES DE LAS DOS PANTALLAS DE INICIO
 * -----------------------------------------------------------------------------
 * El armazon esta en js/panels.js; esto es solo como se colocan.
 *
 * Una rejilla de TRES columnas con tres tamanos —narrow (1), wide (2) y full
 * (3)— en vez de las filas escritas a mano que habia antes («2fr 1fr», una por
 * pareja de tarjetas). El motivo es justo lo que hace esta version: si cada
 * persona puede esconder los paneles que no usa, una fila fija se queda a medias
 * en cuanto falta uno de sus dos huecos. Asi se recolocan solos.
 *
 * `align-items: start` y no `stretch`: un panel de tres lineas al lado de una
 * grafica de 280px no deberia estirarse hasta igualarla — quedaria una tarjeta
 * casi vacia con el contenido pegado arriba.
 * ========================================================================== */
/* `dense` es lo que hace que esto aguante que cada persona esconda y mueva lo
   que quiera: sin el, dos paneles anchos seguidos dejan una columna vacia a la
   derecha del primero, y con paneles colocables esos huecos aparecen solos y en
   sitios distintos para cada cual. Con `dense`, el siguiente panel estrecho que
   quepa sube a taparlo. */
/* La separacion VERTICAL no es `row-gap`: es el `padding-bottom` de cada panel,
   y lo lleva siempre —masoneada la rejilla o no— para que el alto de un panel
   sea el mismo en los dos estados. Si dependiera de la clase, la primera medida
   se quedaria 16px corta por panel. Ver _panelMedir() en js/panels.js. */
.dash-grid { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 16px; row-gap: 0; align-items: start; grid-auto-flow: row dense; }
.dash-panel { min-width: 0; padding-bottom: 16px; }
.dash-panel.is-narrow { grid-column: span 1; }
.dash-panel.is-wide   { grid-column: span 2; }
.dash-panel.is-full   { grid-column: 1 / -1; }

/* ----- Sin huecos verticales -----
 * `dense` tapa los huecos de COLUMNA, pero no los de altura: una rejilla coloca
 * por filas, asi que un panel corto al lado de uno largo deja un agujero debajo
 * hasta que empieza la fila siguiente. Con filas de ocho pixeles y un `span`
 * calculado por JS a partir del alto real de cada panel (panelsLayout, en
 * js/panels.js), cada tarjeta ocupa exactamente lo que mide y la de abajo sube
 * a pegarse. La separacion, que antes era `row-gap`, pasa a ser el hueco de
 * abajo de cada panel: el `gap` de una fila de ocho pixeles no serviria.
 *
 * `is-masoned` la pone el JS, no el CSS, y a proposito: si el calculo fallara,
 * la rejilla se queda en la de filas normales —con huecos, pero entera— en vez
 * de en un monton de tarjetas de ocho pixeles encaramadas unas sobre otras. */
.dash-grid.is-masoned { grid-auto-rows: 8px; }

/* ----- Modo «colocar paneles» ----- */
.panel-edit-toolbar {
  position: sticky; top: 0; z-index: 6;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 16px; padding: 12px 16px;
  background: var(--teal-50); border: 1px solid var(--teal-200);
  border-radius: var(--r-3); box-shadow: var(--sh-2);
}
.panel-edit-toolbar .pet-ico {
  width: 34px; height: 34px; border-radius: 10px; flex-shrink: 0;
  display: grid; place-items: center; background: var(--teal-500); color: #fff;
}
.panel-edit-toolbar .pet-ico .lucide { width: 17px; height: 17px; }
.panel-edit-toolbar .pet-txt { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 2px; }
.panel-edit-toolbar .pet-txt b { font-size: 13px; color: var(--teal-700); }
.panel-edit-toolbar .pet-txt span { font-size: 11.5px; color: var(--teal-600); line-height: 1.4; }
/* La banda es clara en los dos temas (la rampa `--teal-*` no se invierte), asi
   que un `btn-ghost` —que tira de `--ink-2`— se vuelve gris claro sobre claro en
   modo oscuro y desaparece. Sus botones llevan color propio. */
.panel-edit-toolbar .btn-ghost { color: var(--teal-600); }
.panel-edit-toolbar .btn-ghost:hover { background: rgba(255,255,255,0.55); color: var(--teal-700); }

.dash-grid.is-editing .dash-panel { cursor: grab; }
.dash-grid.is-editing .dash-panel:active { cursor: grabbing; }
/* Lo de dentro deja de responder: en modo colocar, pulsar una tarjeta la coge,
   no navega a ninguna parte. La barra de arriba sigue viva. */
.dash-grid.is-editing .dash-panel-body { pointer-events: none; user-select: none; }
.dash-grid.is-editing .dash-panel-body > .card,
.dash-grid.is-editing .dash-panel-body > .kpi-strip { outline: 1px dashed var(--teal-200); outline-offset: 3px; border-radius: var(--r-3); }
.dash-panel.is-dragging { opacity: .4; }
/* La pista de donde va a caer: una barra del color de la casa en el lado por el
   que entra. Un recuadro alrededor del panel de destino diria «lo sueltas
   DENTRO», que es justo lo que no pasa. */
.dash-panel.is-drop-before .dash-panel-body { box-shadow: -4px 0 0 var(--teal-500); }
.dash-panel.is-drop-after  .dash-panel-body { box-shadow:  4px 0 0 var(--teal-500); }
/* Un panel apagado sigue en la rejilla mientras se coloca —hay que poder verlo
   para devolverlo a su sitio— pero se distingue de un golpe de vista. */
.dash-panel.is-off .dash-panel-body { opacity: .35; filter: saturate(.4); }

.panel-edit-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 8px; margin-bottom: 6px;
  background: var(--bg-elev); border: 1px solid var(--line);
  border-radius: var(--r-2);
}
.panel-edit-bar .peb-grip { color: var(--ink-4); display: grid; place-items: center; cursor: grab; }
.panel-edit-bar .peb-grip .lucide { width: 14px; height: 14px; }
.panel-edit-bar .peb-name {
  flex: 1; min-width: 0; font-size: 11.5px; font-weight: 500; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.panel-edit-bar .peb-btn {
  display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0;
  height: 24px; padding: 0 7px; border: 1px solid var(--line);
  background: var(--surface); border-radius: 6px; cursor: pointer;
  font: inherit; font-size: 10.5px; color: var(--ink-3);
  transition: border-color .12s ease, color .12s ease;
}
.panel-edit-bar .peb-btn:hover { border-color: var(--teal-200); color: var(--teal-600); }
.panel-edit-bar .peb-btn:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 1px; }
.panel-edit-bar .peb-btn.is-on { background: var(--teal-50); border-color: var(--teal-200); color: var(--teal-600); }
.panel-edit-bar .peb-btn .lucide { width: 13px; height: 13px; }
/* En un panel estrecho el rotulo del ancho no cabe al lado de cuatro botones:
   se queda el icono, que ya dice cual de los tres es, y el texto vive en el
   `title` y en el `aria-label`. */
.panel-edit-bar .peb-btn.is-size span { display: none; }
.dash-panel.is-full .panel-edit-bar .peb-btn.is-size span,
.dash-panel.is-wide .panel-edit-bar .peb-btn.is-size span { display: inline; }
.panel-edit-bar .peb-caret { width: 11px; height: 11px; opacity: .55; }

/* ----- El desplegable del ancho -----
 * Vive en <body> con posicion fija, como el del menu lateral: dentro de la
 * barra del panel lo recortaria el borde de la tarjeta. */
.panel-size-pop {
  position: fixed; z-index: 95; min-width: 176px; padding: 5px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-2); box-shadow: var(--sh-3);
  display: flex; flex-direction: column; gap: 2px;
}
.panel-size-pop .psp-opt {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border: 0; background: transparent; border-radius: var(--r-1);
  font: inherit; font-size: 12.5px; color: var(--ink); text-align: left; cursor: pointer;
}
.panel-size-pop .psp-opt:hover { background: var(--bg-elev); }
.panel-size-pop .psp-opt:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.panel-size-pop .psp-opt .lucide { width: 15px; height: 15px; color: var(--ink-3); flex-shrink: 0; }
.panel-size-pop .psp-opt span { flex: 1; min-width: 0; }
.panel-size-pop .psp-opt.is-current { color: var(--teal-600); font-weight: 500; }
.panel-size-pop .psp-opt.is-current .lucide { color: var(--teal-500); }
.panel-size-pop .psp-tick { margin-left: auto; }

/* La fila de cifras ya no es de cuatro fijas: se arma con lo que cada persona
   puede ver, asi que el numero cambia de una sesion a otra. `auto-fit` con un
   minimo es lo unico que aguanta eso sin dejar huecos ni tarjetas apretadas. */
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(208px, 1fr)); gap: 16px; }

@media (max-width: 1280px) {
  .dash-grid { grid-template-columns: repeat(2, 1fr); }
  .dash-panel.is-wide { grid-column: 1 / -1; }
}
@media (max-width: 1024px) {
  .dash-grid { grid-template-columns: 1fr; }
  .dash-panel.is-narrow, .dash-panel.is-wide { grid-column: 1 / -1; }
  .kpi-strip { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
}
/* En un telefono, un titulo de tarjeta y dos botones al lado no caben en la
   misma linea: el titulo se parte en tres y los botones se estrujan. Los
   botones bajan a su propia fila, que es lo que hacen de todos modos en cuanto
   se les da la vuelta al orden de lectura. */
@media (max-width: 640px) {
  .dash-panel .card-hd { flex-wrap: wrap; }
  .dash-panel .card-hd h3 { flex: 1 1 100%; }
}

/* ----- La banda de saludo -----
 * Llevaba dos botones y ahora puede llevar cuatro: fichar, solicitar ausencia,
 * elegir que paneles ves y colocarlos. En cuanto la pantalla se estrecha eso ya
 * no cabe al lado del texto, y no habia ni una regla responsive para la banda.
 * Los botones envuelven; por debajo de 640 se apila entera. */
.dash-greeting { flex-wrap: wrap; }
.dash-greeting .actions { flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 640px) {
  .dash-greeting { flex-direction: column; align-items: stretch; gap: 16px; }
  /* Cuatro pastillas envueltas en tres filas desiguales se leen mal. En un
     telefono se reparten en dos columnas iguales y ocupan el ancho: es una
     botonera, y como botonera se comporta. */
  .dash-greeting .actions { display: grid; grid-template-columns: 1fr 1fr; }
  .dash-greeting .actions .pill { justify-content: center; }
}

/* ----- Fila de «esto te espera» -----
 * La usan la bandeja del Dashboard, los pendientes del inicio y media docena de
 * paneles mas para listar cosas sobre las que se puede pulsar. Es un <button>
 * cuando lleva a alguna parte y un <div> cuando no, y se ve igual en los dos
 * casos salvo por el subrayado del foco. */
.todo-list { display: flex; flex-direction: column; gap: 2px; }
.todo-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 9px 8px; border: 0; background: transparent; border-radius: var(--r-2);
  font: inherit; text-align: left; cursor: pointer; color: var(--ink-2);
  transition: background .12s ease;
}
.todo-row:hover { background: var(--bg-elev); }
.todo-row.is-flat { cursor: default; }
.todo-row.is-flat:hover { background: transparent; }
.todo-row:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }
.todo-ico {
  width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--teal-50); color: var(--teal-600);
}
.todo-ico .lucide { width: 15px; height: 15px; }
.todo-ico.amber { background: var(--amber-bg); color: var(--amber-fg); }
.todo-ico.blue  { background: var(--blue-bg);  color: var(--blue-fg); }
.todo-ico.plum  { background: var(--plum-bg);  color: var(--plum-fg); }
.todo-ico.rose  { background: var(--rose-bg);  color: var(--rose-fg); }
.todo-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.todo-txt b { font-size: 13px; font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.todo-txt span { font-family: var(--f-mono); font-size: 10px; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.todo-n {
  font-family: var(--f-mono); font-size: 13px; font-weight: 500; color: var(--ink-3);
  flex-shrink: 0; min-width: 22px; text-align: right;
}
/* El vacio de una bandeja no es un error ni una pantalla vacia: es la buena
   noticia del dia, asi que se dice en verde y en una linea. */
.todo-clear {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 10px; border-radius: var(--r-2);
  background: var(--teal-50); color: var(--teal-600); font-size: 12.5px;
}
.todo-clear .lucide { width: 16px; height: 16px; flex-shrink: 0; }

/* ----- Tres o cuatro cifras dentro de una tarjeta ----- */
.mini-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: 12px; }
.mini-stats > div { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mini-stats b { font-family: var(--f-display); font-size: 22px; font-weight: 500; color: var(--ink); line-height: 1; }
.mini-stats span { font-size: 10.5px; color: var(--ink-3); line-height: 1.3; }

/* ----- Atajo a las herramientas, en el inicio ----- */
.tool-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 8px; }
.tool-tile {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 14px 8px; border: 1px solid var(--line); border-radius: var(--r-2);
  background: var(--surface); cursor: pointer; font: inherit;
  color: var(--ink-2); font-size: 11.5px; text-align: center;
  transition: border-color .15s ease, background .15s ease, transform .15s ease;
}
.tool-tile:hover { border-color: var(--teal-200); background: var(--bg-elev); transform: translateY(-1px); }
.tool-tile:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }
.tool-tile .lucide { width: 18px; height: 18px; color: var(--teal-500); }
.tool-tile span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }

/* ----- El selector de paneles ----- */
.panel-pick { display: flex; flex-direction: column; gap: 2px; max-height: 52vh; overflow-y: auto; }
.panel-pick-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 8px; border-radius: var(--r-2); cursor: pointer;
  transition: background .12s ease;
}
.panel-pick-row:hover { background: var(--bg-elev); }
.panel-pick-row input { width: 17px; height: 17px; flex-shrink: 0; cursor: pointer; }
.panel-pick-ico {
  width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
  display: grid; place-items: center; background: var(--bg-2); color: var(--ink-3);
}
.panel-pick-ico .lucide { width: 15px; height: 15px; }
/* Marcado = a la vista: el icono se enciende con el acento de la casa, para que
   de un vistazo se vea cuantos quedan encendidos sin leer las casillas. */
.panel-pick-row input:checked ~ .panel-pick-ico { background: var(--teal-50); color: var(--teal-600); }
.panel-pick-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.panel-pick-txt b { font-size: 13px; font-weight: 500; color: var(--ink); }
.panel-pick-txt span { font-size: 11.5px; color: var(--ink-3); line-height: 1.35; }

/* ===========================================
 * OBJETIVOS TÁCTILES EN PANTALLA TÁCTIL
 * -------------------------------------------
 * Medidos en la revisión: avatar 24×24, cerrar sesión 28×28, fichar 30×30 y las
 * acciones de fila 36×28. Con el ratón se aciertan; con el dedo no, y esta
 * aplicación tiene barra de pestañas inferior y layout móvil propio, así que se
 * usa con el dedo.
 *
 * Va detrás de `pointer: coarse` a propósito. Agrandar el área de pulsación con
 * un pseudoelemento invisible habría valido para todos los tamaños, pero estos
 * botones van pegados unos a otros —tres seguidos en cada fila de una tabla— y
 * las áreas se solaparían: pulsarías «revocar acceso» creyendo pulsar
 * «restablecer contraseña». Aquí crece el botón de verdad, y solo donde hay
 * sitio porque el layout ya es el ancho.
 * =========================================== */
@media (pointer: coarse) {
  .icon-btn { min-width: 44px; min-height: 44px; }
  .btn-sm { min-height: 40px; padding: 0 12px; }
  /* Las acciones de fila son iconos sueltos dentro de una celda estrecha: se
     les da alto completo y ancho suficiente sin ensanchar la columna. */
  td.acts .btn-sm, .acts .btn.btn-ghost { min-width: 44px; min-height: 44px; }
  /* El avatar de cabecera abre el menú de usuario; el de una fila es adorno. */
  .header .avatar, button > .avatar { min-width: 40px; min-height: 40px; }
}

/* ===========================================
 * IMPRESIÓN
 * -------------------------------------------
 * No había ni una regla `@media print` en toda la hoja, y esta es una
 * aplicación de la que se imprime: una nómina, el calendario del equipo del mes
 * que viene, el informe de jornada que pide la Inspección. Lo que salía por la
 * impresora era la pantalla entera —barra lateral, cabecera, widget de fichaje,
 * chat flotante— sobre fondo crema, con el contenido encajonado en su columna y
 * cortado por la mitad, porque `.content` es un panel con scroll propio dentro
 * de una rejilla de `height: 100vh`.
 *
 * Tres cosas hace este bloque, y ninguna más:
 *   1. quitar de en medio todo lo que es NAVEGAR, no leer;
 *   2. deshacer el armazón de altura fija para que el contenido fluya y pagine;
 *   3. pasar a tinta sobre papel, que es más legible y no se come un cartucho.
 *
 * Es además lo que convierte el informe de jornada (§7.5 de PENDIENTE.md) en un
 * PDF sin meter ni una librería nueva en el bundle: «imprimir → guardar como
 * PDF» ya lo hace el navegador.
 *
 * Dos utilidades para el resto del código: `.no-print` esconde algo al
 * imprimir, `.print-only` lo enseña SOLO al imprimir.
 * =========================================== */
@media print {
  /* ----- 1. Lo que no se imprime ----- */
  /* Navegación y armazón. */
  .skip-link, .side, .header, .mobile-nav, .tweaks, .pz-bd,
  /* Piezas flotantes que viven en <body>, fuera de #root. */
  .chat-dock, .toast-wrap, .palette-bd, .row-menu, .nav-fly, #anchoredMenu,
  /* El widget de fichaje corre un temporizador: en papel es un número muerto. */
  .clock-widget,
  /* Controles. Una barra de herramientas impresa son botones que no se pulsan. */
  .toolbar, .btn, .icon-btn, .link-more, .page-head .h-actions,
  .no-print {
    display: none !important;
  }

  /* Con un modal abierto se imprime EL MODAL, y no la pantalla de detrás: quien
     pulsa imprimir con el informe de jornada delante quiere el informe. El
     modal cuelga de <body>, no de `.app`, así que basta con esconder `.app`
     cuando hay uno. `:has()` es lo que permite decirlo aquí en vez de tener que
     tocar el JavaScript de cada modal. */
  body:has(.modal-backdrop) .app { display: none !important; }

  /* Y el propio modal deja de ser una capa flotante con scroll para ser el
     documento: sin fondo, sin sombra y sin tope de altura, que es lo que lo
     cortaba por la mitad al pasar de una hoja. */
  .modal-backdrop {
    position: static; background: none; padding: 0; display: block;
  }
  .modal-backdrop .modal {
    max-width: none; width: auto; box-shadow: none; border: 0; max-height: none;
  }
  .modal-backdrop .modal-hd .icon-btn, .modal-backdrop .modal-ft { display: none !important; }

  .print-only { display: revert !important; }

  /* ----- 2. El armazón, deshecho ----- */
  /* `.app` es una rejilla de dos columnas y 100vh, y `.content` el único trozo
     con scroll. En papel no hay scroll: lo que no cabe pasa a la hoja
     siguiente, y para eso el alto tiene que ser automático en toda la cadena. */
  html, body { height: auto !important; overflow: visible !important; }
  .app { display: block !important; height: auto !important; }
  .main { display: block !important; height: auto !important; }
  .content {
    overflow: visible !important; height: auto !important;
    padding: 0 !important; flex: none !important;
  }

  /* ----- 3. Tinta sobre papel ----- */
  /* Los tokens, redefinidos en vez de sobrescribir regla a regla: toda la hoja
     pinta con variables, así que cambiarlas aquí basta. El tema oscuro no
     sobrevive a la impresora —blanco sobre negro se come el tóner y se lee
     peor—, y por eso esto va también con `[data-theme="dark"]`. */
  :root, :root[data-theme="dark"] {
    --bg: #ffffff; --bg-elev: #ffffff; --bg-2: #f4f4f4; --surface: #ffffff;
    --ink: #000000; --ink-2: #1a1a1a; --ink-3: #444444; --ink-4: #666666;
    --line: #cccccc; --line-2: #999999;
    --sh-1: none; --sh-2: none; --sh-3: none;
  }
  body { background: #fff !important; color: #000 !important; }
  .card, .side-card {
    box-shadow: none !important; border: 1px solid #ccc !important;
    background: #fff !important; break-inside: avoid;
  }

  /* Los chips llevan el significado en el color de fondo (aprobada / pendiente
     / rechazada). En una impresora en blanco y negro son todos el mismo gris,
     así que aquí el significado pasa al borde y al texto. */
  .chip {
    background: none !important; border: 1px solid #666 !important;
    color: #000 !important;
  }

  /* ----- Paginación ----- */
  @page { margin: 14mm; }
  /* Una tabla larga reparte sus filas entre hojas, pero la cabecera tiene que
     repetirse arriba de cada una: sin esto, la página 2 son números sin
     columnas. `thead` como grupo de cabecera es lo que lo consigue. */
  thead { display: table-header-group; }
  tfoot { display: table-footer-group; }
  tr, img { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
  /* Un título al final de la hoja con su contenido en la siguiente es la forma
     más fácil de que un informe parezca dos. */
  .page-head { break-after: avoid; }

  /* Los gráficos son <canvas>: se imprimen como imagen, pero hay que decirle al
     navegador que no los recorte ni los escale a un tamaño de pantalla. */
  canvas { max-width: 100% !important; height: auto !important; }

  /* El enlace de una app interna no sirve de nada en papel —nadie teclea un
     hash a mano—, así que NO se imprime el href detrás del texto, que es lo que
     recomiendan casi todas las guías y aquí solo ensucia. */
  a[href]::after { content: none !important; }
}

/* ===========================================
 * VENTANA DE FICHAJE (fichaje.html)
 * -------------------------------------------
 * La mini-aplicación instalable. Ver §7.8 de PENDIENTE.md y la cabecera de
 * js/fichaje.js.
 *
 * Va aquí y no en una hoja aparte porque comparte TODO lo de arriba: los
 * tokens, la tipografía, los dos temas. Una segunda hoja para cuatro pantallas
 * de una sola vista sería otro sitio donde el acento se queda desincronizado.
 *
 * Se diseña para una ventana ESTRECHA —Windows abre las aplicaciones instaladas
 * en unos 400 px de ancho, y en el móvil es la pantalla entera— así que aquí no
 * hay rejilla ni puntos de ruptura: una columna que se centra y ya.
 * =========================================== */
.fx-body { background: var(--bg); }

.fx-app {
  min-height: 100vh;
  min-height: 100dvh;   /* el móvil, con la barra del navegador dentro y fuera */
  display: flex; flex-direction: column;
  max-width: 460px; margin: 0 auto;
  padding: 0 20px;
}

.fx-hd {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 18px 2px 0;
}
.fx-marca {
  font-family: var(--f-display); font-size: 15px; letter-spacing: -0.01em;
  color: var(--ink-2);
}
.fx-hoy {
  font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.05em;
  color: var(--ink-4); text-transform: uppercase; text-align: right;
}

/* El cuerpo se come el espacio sobrante y centra su contenido: en una ventana
   de 700 px de alto y en una de 400, el reloj queda en el mismo sitio. */
.fx-cuerpo {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 24px 0;
}

.fx-estado {
  display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; color: var(--ink-3); margin-bottom: 10px;
}
.fx-punto {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ink-4); flex-shrink: 0;
}
/* Encendido = jornada en curso. El latido es lento a propósito: esto se mira de
   reojo durante horas, y una animación rápida en una ventana siempre visible
   cansa. Se respeta quien haya pedido que no se mueva nada. */
.fx-punto.on { background: var(--teal-500); animation: fx-latido 2.4s ease-in-out infinite; }
@keyframes fx-latido { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) {
  .fx-punto.on { animation: none; }
}

/* El número. `tabular-nums` no es un adorno: sin él, cada segundo cambia el
   ancho de las cifras y el reloj entero baila. */
.fx-reloj {
  font-family: var(--f-mono);
  font-size: clamp(40px, 15vw, 58px);
  font-weight: 500; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink); line-height: 1.05;
}
.fx-reloj.over { color: var(--teal-txt); }

.fx-sub { font-size: 12.5px; color: var(--ink-3); margin-top: 6px; text-align: center; }

.fx-barra {
  width: 100%; max-width: 260px; height: 4px; margin-top: 16px;
  background: var(--bg-2); border-radius: 999px; overflow: hidden;
}
.fx-barra > div {
  height: 100%; background: var(--teal-500); border-radius: 999px;
  transition: width .4s ease;
}

.fx-acciones {
  display: flex; align-items: center; gap: 10px;
  margin-top: 28px; width: 100%; max-width: 300px;
}

/* Botones grandes: esto se usa con el dedo en el móvil y con prisa en el
   escritorio. 52 px de alto, muy por encima del mínimo táctil. */
.fx-btn {
  height: 52px; min-width: 52px;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  border: 1px solid var(--line-2); border-radius: var(--r-2);
  background: var(--surface); color: var(--ink);
  font-family: inherit; font-size: 14px; font-weight: 500;
  cursor: pointer; padding: 0 16px;
  transition: background .15s ease, border-color .15s ease, opacity .15s ease;
}
.fx-btn i { width: 18px; height: 18px; }
.fx-btn:hover:not(:disabled) { background: var(--bg-2); }
.fx-btn:disabled { opacity: .4; cursor: default; }
.fx-btn.primaria {
  flex: 1;
  background: var(--teal-600); border-color: var(--teal-600); color: #fff;
}
.fx-btn.primaria:hover:not(:disabled) { background: var(--teal-700); border-color: var(--teal-700); }
/* Fichar la salida es la acción de la que uno se arrepiente si la pulsa sin
   querer, así que deja de ser el botón verde y se pone en coral. */
.fx-btn.primaria.parar { background: var(--coral); border-color: var(--coral); }
.fx-btn.primaria.parar:hover:not(:disabled) { filter: brightness(0.93); }

.fx-pie {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 2px calc(18px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  font-size: 12px;
}
.fx-quien { color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fx-enlace {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--teal-txt); text-decoration: none; font-weight: 500; flex-shrink: 0;
}
.fx-enlace:hover { text-decoration: underline; }
.fx-enlace i { width: 13px; height: 13px; }

/* =============================================================================
   COMPAÑÍA · DIRECTORIO DE PROYECTOS  (js/views-projects.js)
   -----------------------------------------------------------------------------
   Una rejilla de tarjetas, no una tabla. Es deliberado: lo que se busca aquí no
   es comparar filas por una columna —qué proyecto empezó antes— sino reconocer
   uno de un vistazo, y lo que lo hace reconocible son las caras del equipo y el
   nombre, que en una tabla quedan aplastados en dos celdas estrechas.

   La tarjeta ENTERA es el disparador de la ficha, y por eso lleva `tabindex` y
   `role="button"`: con teclado se llega a ella igual que con el ratón. Los
   controles de dentro (el enlace al repositorio, los botones de gestión) paran
   la propagación — ver `data-stop-prop` en el JS.
   ========================================================================== */

.pj-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}

.pj-card {
  display: flex; flex-direction: column; gap: 10px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-3); padding: 16px 18px;
  cursor: pointer; text-align: left;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.pj-card:hover { border-color: var(--teal-200); box-shadow: var(--sh-2); transform: translateY(-1px); }
.pj-card:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

.pj-hd { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pj-code {
  font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.04em;
  color: var(--ink-3); background: var(--bg-2);
  padding: 2px 7px; border-radius: 999px;
}
.pj-name {
  font-family: var(--f-display); font-weight: 500; font-size: 18px;
  letter-spacing: -0.01em; line-height: 1.25; margin: 0; color: var(--ink);
}
.pj-meta {
  display: flex; flex-wrap: wrap; gap: 4px 14px;
  font-size: 11.5px; color: var(--ink-3);
}
.pj-meta span { display: inline-flex; align-items: center; gap: 5px; }
.pj-meta .lucide { width: 12px; height: 12px; color: var(--ink-4); flex-shrink: 0; }

/* La descripción se recorta a tres líneas: la ficha la enseña entera, y una
   tarjeta que crece con el texto rompe la alineación de toda la rejilla. */
.pj-sum {
  margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* El equipo va separado por una línea porque es la mitad de la tarjeta: quien
   mira esto casi siempre viene buscando a una persona, no una fecha. */
.pj-team {
  display: flex; align-items: center; gap: 10px;
  border-top: 1px dashed var(--line); padding-top: 12px; margin-top: auto;
}
.pj-lead { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.pj-lead strong {
  font-size: 13px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pj-lead span {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pj-lead .lucide { width: 11px; height: 11px; color: var(--amber); flex-shrink: 0; }
/* Sin líder no es un dato más: es un hueco que alguien tiene que rellenar. */
.pj-sinlider strong { color: var(--ink-3); font-weight: 500; font-style: italic; }

.pj-avs { display: flex; align-items: center; flex-shrink: 0; }
.pj-avs .avatar { margin-left: -7px; border: 2px solid var(--surface); }
.pj-avs .avatar:first-child { margin-left: 0; }
.pj-mas {
  margin-left: -7px; width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center; border: 2px solid var(--surface);
  background: var(--bg-2); color: var(--ink-3);
  font-family: var(--f-mono); font-size: 10.5px;
}

.pj-ft { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pj-ft .btn { height: 30px; }
.pj-norepo {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--ink-4);
}
.pj-norepo .lucide { width: 13px; height: 13px; }

/* ----- La ficha (modal) ----- */

.pj-datos {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin-bottom: 4px;
}
.pj-dato {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--bg-elev); border: 1px solid var(--line);
  border-radius: var(--r-2); padding: 9px 12px;
}
.pj-dato span {
  font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ink-4);
}
.pj-dato strong { font-size: 13px; font-weight: 500; color: var(--ink); }

.pj-h4 {
  display: flex; align-items: center; gap: 7px;
  font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink-3); margin: 22px 0 10px;
}
.pj-h4 .lucide { width: 14px; height: 14px; color: var(--ink-4); }

.pj-lista { display: flex; flex-direction: column; }
.pj-fila {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px dashed var(--line);
}
.pj-fila:last-child { border-bottom: 0; }
.pj-fila-nm { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.pj-fila-nm strong {
  font-size: 13px; font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pj-fila-nm span {
  font-size: 11px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* La corona de quien YA es líder se queda encendida y sin pulsar: dice quién lo
   es, no ofrece nombrarlo otra vez. */
.pj-lead-on { color: var(--amber); opacity: 1; }
.pj-lead-on:disabled { opacity: 1; cursor: default; }

/* El enlace al repositorio es lo tercero que se viene a buscar aquí, así que en
   la ficha no es un botón más: es una fila entera que enseña la URL de destino
   antes de pulsarla. */
.pj-repo {
  display: flex; align-items: center; gap: 12px;
  border: 1px solid var(--line-2); border-radius: var(--r-2);
  padding: 12px 14px; text-decoration: none; color: var(--ink);
  transition: border-color .15s ease, background .15s ease;
}
.pj-repo:hover { border-color: var(--teal-200); background: var(--teal-50); }
.pj-repo .lucide { width: 18px; height: 18px; color: var(--teal-txt); flex-shrink: 0; }
.pj-repo div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pj-repo strong { font-size: 13px; font-weight: 600; }
.pj-repo span {
  font-family: var(--f-mono); font-size: 10.5px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.pj-vacio-mini {
  font-size: 12.5px; color: var(--ink-4);
  border: 1px dashed var(--line-2); border-radius: var(--r-2);
  padding: 14px; text-align: center;
}

/* El semáforo, arriba a la derecha de la tarjeta. Es un BOTÓN que contiene un
   chip, y no un chip con un `onclick`: se llega con el tabulador, se anuncia
   como pulsable y hereda el foco visible del resto de la aplicación.

   El botón se despoja de todo (fondo, borde, relleno) porque lo que se ve es el
   chip de dentro; lo único que aporta es el `margin-left:auto` que lo manda al
   otro extremo de la cabecera y el resalte al pasar por encima. */
.pj-estado {
  margin-left: auto; background: none; border: 0; padding: 0;
  cursor: pointer; line-height: 0; border-radius: 999px;
  transition: transform .12s ease, box-shadow .12s ease;
}
.pj-estado:hover { transform: translateY(-1px); box-shadow: var(--sh-1); }
.pj-estado:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 2px; }

/* =============================================================================
   PANTALLA · EL ESTADO DE UN PROYECTO  (#project/<id>)
   -----------------------------------------------------------------------------
   Ver la cabecera de js/views-projects.js. El color aquí no decora: es el dato.
   Por eso el nivel se dice tres veces —la franja de la tarjeta, el chip y el
   punto del historial— y ninguna de las tres es el único sitio donde se dice,
   que es lo que exige no depender del color para leer la pantalla.
   ========================================================================== */

.ps-resumen {
  display: flex; align-items: stretch; gap: 14px; flex-wrap: wrap;
  margin-top: 4px;
}

/* El semáforo en grande. Lleva el fondo del nivel, y es lo único de la pantalla
   que se pinta entero de color: es UNO, y su trabajo es verse desde la puerta. */
.ps-sem {
  display: flex; align-items: center; gap: 14px;
  border-radius: var(--r-3); padding: 16px 20px; min-width: 260px; flex: 1;
  border: 1px solid var(--line);
}
.ps-sem .lucide { width: 26px; height: 26px; flex-shrink: 0; }
.ps-sem div { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ps-sem strong {
  font-family: var(--f-display); font-weight: 500; font-size: 20px;
  letter-spacing: -0.01em; line-height: 1.15;
}
.ps-sem span { font-size: 12px; opacity: .85; }

.ps-sem-rose  { background: var(--rose-bg);  color: var(--rose-fg); }
.ps-sem-amber { background: var(--amber-bg); color: var(--amber-fg); }
.ps-sem-teal  { background: var(--teal-50);  color: var(--teal-700); }
.ps-sem-azul  { background: var(--blue-bg);  color: var(--blue-fg); }
.ps-sem-gris  { background: var(--bg-2);     color: var(--ink-2); }

/* El recuento por nivel. Los que están a cero se quedan puestos y en gris: «0
   bloqueados» es una afirmación, y esconderla deja la duda de si es que no hay o
   es que nadie ha mirado. */
.ps-cuenta {
  display: flex; gap: 8px; flex-wrap: wrap; align-items: stretch;
}
.ps-cuenta-i {
  display: flex; flex-direction: column; gap: 2px; justify-content: center;
  background: var(--bg-elev); border: 1px solid var(--line);
  border-radius: var(--r-2); padding: 10px 14px; min-width: 76px;
}
.ps-cuenta-i strong {
  font-family: var(--f-display); font-size: 20px; font-weight: 500; line-height: 1;
  color: var(--ink);
}
.ps-cuenta-i span {
  font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-4); white-space: nowrap;
}
.ps-cuenta-i.ps-cero strong { color: var(--ink-4); font-weight: 400; }
.ps-cuenta-tarde { border-color: var(--rose-fg); }
.ps-cuenta-tarde strong, .ps-cuenta-tarde span { color: var(--rose-fg); }

/* ----- Las tarjetas de cada frente ----- */

.ps-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}

/* La franja de color va a la IZQUIERDA y no de fondo: una rejilla con tres
   tarjetas rojas enteras no se lee, se sufre. */
.ps-card {
  display: flex; flex-direction: column; gap: 9px;
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--line-2);
  border-radius: var(--r-3); padding: 15px 17px;
}
.ps-b-rose  { border-left-color: var(--rose-fg); }
.ps-b-amber { border-left-color: var(--amber-fg); }
.ps-b-teal  { border-left-color: var(--teal-500); }
.ps-b-azul  { border-left-color: var(--blue-fg); }
.ps-b-gris  { border-left-color: var(--line-2); }

.ps-card-hd { display: flex; align-items: center; gap: 10px; }
.ps-card-hd h3 {
  font-family: var(--f-display); font-weight: 500; font-size: 15.5px;
  letter-spacing: -0.005em; line-height: 1.3; margin: 0; flex: 1; min-width: 0;
  color: var(--ink);
}
/* El desplegable de nivel es estrecho a propósito: es un control, no el título
   de la tarjeta. */
.ps-nivel { width: 132px; height: 30px; font-size: 12.5px; flex-shrink: 0; }

.ps-detalle {
  margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
  white-space: pre-wrap;
}
/* El siguiente paso lleva sangría y flecha: se lee como consecuencia de lo de
   arriba, que es lo que es. */
.ps-paso {
  display: flex; gap: 7px; margin: 0;
  font-size: 12px; line-height: 1.5; color: var(--ink-3);
  border-left: 2px solid var(--line); padding-left: 9px;
}
.ps-paso .lucide { width: 13px; height: 13px; color: var(--ink-4); flex-shrink: 0; margin-top: 2px; }

.ps-card-ft {
  display: flex; align-items: center; gap: 6px 14px; flex-wrap: wrap;
  border-top: 1px dashed var(--line); padding-top: 10px; margin-top: auto;
}
.ps-meta {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--ink-2);
}
.ps-meta .lucide { width: 12px; height: 12px; color: var(--ink-4); flex-shrink: 0; }
.ps-meta .avatar { width: 22px; height: 22px; font-size: 9.5px; }
.ps-flojo { color: var(--ink-4); }
/* Vencido y todavía abierto. Una tarjeta cerrada fuera de plazo no se marca: ya
   no es un problema, es historia. */
.ps-tarde { color: var(--rose-fg); font-weight: 500; }
.ps-tarde .lucide { color: var(--rose-fg); }
.ps-acciones { display: inline-flex; gap: 2px; margin-left: auto; }
.ps-acciones .icon-btn { width: 28px; height: 28px; }

/* ----- El historial ----- */

.ps-hist {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-3); padding: 4px 16px;
}
.ps-hist-i {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 11px 0; border-bottom: 1px dashed var(--line);
}
.ps-hist-i:last-child { border-bottom: 0; }

.ps-punto {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; margin-top: 5px;
  background: var(--line-2);
}
.ps-p-rose  { background: var(--rose-fg); }
.ps-p-amber { background: var(--amber-fg); }
.ps-p-teal  { background: var(--teal-500); }
.ps-p-azul  { background: var(--blue-fg); }
.ps-p-gris  { background: var(--line-2); }

.ps-hist-tx { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.ps-hist-tx strong { font-size: 12.5px; font-weight: 600; color: var(--ink); }
.ps-hist-tx span { font-size: 12px; color: var(--ink-3); }
.ps-hist-tx em {
  font-style: normal; font-size: 12px; color: var(--ink-2);
  border-left: 2px solid var(--line); padding-left: 8px; margin-top: 3px;
}
.ps-hist-meta {
  display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  flex-shrink: 0; font-size: 11px; color: var(--ink-4); text-align: right;
}
/* Cómo quedó el proyecto ENTERO tras ese cambio. Es el dato que contesta
   «¿desde cuándo estábamos en rojo?», y por eso va en la línea y no escondido. */
.ps-hist-proy {
  font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--ink-3);
  background: var(--bg-2); border-radius: 999px; padding: 1px 7px;
}

@media (max-width: 640px) {
  .pj-grid { grid-template-columns: 1fr; }
  .ps-grid { grid-template-columns: 1fr; }
  /* En pantalla estrecha el recuento se lleva la fila entera: partido en dos
     columnas con el semáforo al lado, cada casilla se quedaba en 40px y el
     número se salía. */
  .ps-resumen { flex-direction: column; }
  .ps-hist-meta { align-items: flex-start; text-align: left; }
}

/* =============================================================================
 * IMPORTACIÓN EN LOTE DE DOCUMENTOS (js/docs-import.js)
 * -----------------------------------------------------------------------------
 * El modal es más ancho que el resto porque la tabla de revisión lleva cinco
 * columnas y una de ellas es un nombre de fichero largo. Se usa `doc-viewer`
 * como referencia de anchura: son las dos pantallas de esta casa donde el
 * contenido manda sobre el diálogo.
 * ============================================================================= */
.modal.imp-modal { max-width: 1080px; width: 94vw; }
.imp-modal .modal-bd { max-height: 62vh; overflow-y: auto; }

.imp-drop {
  border: 2px dashed var(--line-2); border-radius: 12px; padding: 40px;
  text-align: center; background: var(--bg-elev); cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.imp-drop.activo { border-color: var(--teal-400); background: var(--teal-50); }

.imp-nota {
  display: flex; gap: 10px; align-items: flex-start; margin-top: 14px;
  padding: 12px 14px; border-radius: 10px; font-size: 12.5px; line-height: 1.5;
  background: var(--teal-50); border: 1px solid var(--teal-100); color: var(--ink-2);
}
.imp-nota.warn { background: var(--amber-bg); border-color: transparent; color: var(--amber-fg); }
.imp-nota i { width: 16px; height: 16px; flex-shrink: 0; margin-top: 2px; }

.imp-aviso {
  margin-top: 14px; padding: 10px 12px; border-radius: 8px; font-size: 12.5px;
  background: var(--amber-bg); color: var(--amber-fg);
}

/* ----- Los tres recuentos de la cabecera ----- */
.imp-cuentas { display: flex; gap: 10px; margin-bottom: 14px; }
.imp-cuenta {
  flex: 1; padding: 12px 14px; border-radius: 10px; background: var(--bg-2);
  border: 1px solid var(--line); display: flex; align-items: baseline; gap: 8px;
}
.imp-cuenta strong { font-family: var(--f-display); font-size: 24px; font-weight: 500; }
.imp-cuenta span { font-size: 12px; color: var(--ink-3); }
.imp-cuenta.ok strong   { color: var(--teal-600); }
.imp-cuenta.warn strong { color: var(--amber-fg); }
.imp-cuenta.dup strong  { color: var(--ink-3); }

/* ----- La tabla de revisión ----- */
.imp-tabla { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; margin-top: 14px; }
.imp-cab, .imp-fila {
  display: grid; grid-template-columns: 36px minmax(0, 2fr) minmax(0, 1.6fr) 130px 150px;
  gap: 10px; align-items: center; padding: 10px 14px;
}
.imp-cab {
  background: var(--bg-2); border-bottom: 1px solid var(--line);
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3);
}
.imp-fila { border-bottom: 1px solid var(--line); font-size: 13px; }
.imp-fila:last-child { border-bottom: 0; }

/* La banda de color va a la IZQUIERDA y no de fondo: con doscientas filas, dos
   fondos alternándose convierten la tabla en un tablero de ajedrez y deja de
   leerse cuál es cuál. */
.imp-fila.ok   { box-shadow: inset 3px 0 0 var(--teal-600); }
.imp-fila.warn { box-shadow: inset 3px 0 0 var(--amber-fg); }
.imp-fila.dup  { box-shadow: inset 3px 0 0 var(--line-2); opacity: .72; }

.imp-doc { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.imp-nombre { color: var(--ink); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.imp-meta { font-size: 11px; color: var(--ink-3); }
.imp-etiqueta {
  align-self: flex-start; margin-top: 2px; font-size: 10px; padding: 1px 7px;
  border-radius: 999px; background: var(--bg-2); color: var(--ink-3);
}
.imp-ambiguo { font-size: 11px; color: var(--amber-fg); margin-top: 3px; }

.imp-via { font-size: 11.5px; color: var(--ink-2); }
.imp-via.ok   { color: var(--teal-600); }
.imp-via.warn { color: var(--amber-fg); }
.imp-via.no   { color: var(--rose-fg); }

/* ----- Trabajando, y el resumen ----- */
.imp-trabajando { padding: 48px 24px; text-align: center; }
.imp-barra { height: 6px; border-radius: 999px; background: var(--bg-2); overflow: hidden; margin-top: 14px; }
.imp-barra-in { height: 100%; background: var(--teal-600); transition: width .2s ease; }

.imp-resumen { padding: 44px 24px; text-align: center; }
.imp-errores {
  margin-top: 16px; text-align: left; font-size: 12px; color: var(--rose-fg);
  background: var(--rose-bg); border-radius: 8px; padding: 10px 12px;
  max-height: 160px; overflow-y: auto;
}

@media (max-width: 860px) {
  /* En estrecho la tabla no cabe en cinco columnas: el nombre del fichero y la
     persona se llevan la fila entera y el resto se apila debajo. */
  .imp-cab { display: none; }
  .imp-fila { grid-template-columns: 28px 1fr; row-gap: 8px; }
  .imp-fila > div:nth-child(3),
  .imp-fila > div:nth-child(4),
  .imp-fila > div:nth-child(5) { grid-column: 2; }
}

/* La casilla, con el verde de la casa en vez del azul (o el rojo, según el
   sistema) que pinta el navegador por su cuenta. No se reutiliza `.perm-check`
   —la casilla dibujada a mano de la matriz de permisos— porque aquí hay una por
   fila y pueden ser quinientas: `accent-color` es una línea y no añade tres
   elementos por casilla. */
.imp-cab input[type="checkbox"],
.imp-fila input[type="checkbox"] { accent-color: var(--teal-600); width: 15px; height: 15px; }

@media (max-width: 860px) {
  /* Los tres recuentos no caben en fila en un teléfono: el tercero se salía por
     la derecha del diálogo. Se parten en dos líneas. */
  .imp-cuentas { flex-wrap: wrap; }
  .imp-cuenta { flex: 1 1 calc(50% - 5px); }
}

/* =============================================================================
   ADMINISTRACIÓN → ESTADÍSTICAS  ·  js/views-stats.js
   -----------------------------------------------------------------------------
   Dos pantallas de solo mirar. Todo lo de aquí son TABLAS CON UNA BARRA AL
   FONDO y ni un gráfico más: quince filas ordenadas de mayor a menor con una
   barra de proporción se leen de un vistazo, y sin leyenda que descifrar. La
   única gráfica de verdad es la serie temporal, que es la única pregunta que
   una tabla contesta peor.
   ========================================================================== */

/* ----- La tabla con barra ----- */
.st-tabla { display: flex; flex-direction: column; }

.st-cab, .st-fila {
  display: grid; grid-template-columns: 1fr 90px 90px 120px;
  gap: 10px; align-items: center; padding: 9px 4px;
}
/* La de uso lleva dos columnas más (personas y gestos). Se declara aparte en
   vez de con variables porque son dos tablas, no una parametrizable: la de
   tamaños habla de bytes y la de uso, de gente. */
.st-uso .st-cab, .st-uso .st-fila { grid-template-columns: 1fr 90px 80px 80px 70px 110px; }

.st-cab {
  font-family: var(--f-mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-3); border-bottom: 1px solid var(--line);
}
.st-cab span:not(:first-child), .st-fila .st-num { text-align: right; }

.st-fila { border-bottom: 1px solid var(--line); font-size: 13px; }
.st-fila:last-child { border-bottom: 0; }

.st-nombre {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  color: var(--ink); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.st-num { font-family: var(--f-mono); font-size: 12px; color: var(--ink-2); }

/* El id técnico de la sección, al lado del nombre humano. Está a propósito: el
   nombre sale del menú y el id es lo que hay en la base, y quien lee esta
   pantalla es quien va a mirar la tabla después. */
.st-id {
  font-family: var(--f-mono); font-size: 10px; color: var(--ink-4);
  background: var(--bg-2); border-radius: 4px; padding: 1px 5px; flex: none;
}
.st-caret { width: 13px; height: 13px; color: var(--ink-3); flex: none; }

.st-barra {
  display: block; height: 6px; border-radius: 999px;
  background: var(--bg-2); overflow: hidden;
}
.st-barra > span { display: block; height: 100%; background: var(--teal-500); border-radius: 999px; }

.st-clic { cursor: pointer; }
.st-clic:hover { background: var(--bg-elev); }
.st-clic:focus-visible { outline: 2px solid var(--teal-500); outline-offset: -2px; }

/* ----- El desglose desplegado ----- */
.st-sub {
  padding: 8px 4px 12px 26px; font-size: 12.5px; color: var(--ink-3);
  border-bottom: 1px solid var(--line); background: var(--bg-elev);
}
.st-sub-fila {
  display: grid; grid-template-columns: 1fr 130px 110px 90px;
  gap: 10px; padding: 4px 0; align-items: center;
}
.st-sub-fila > span:not(:first-child) { text-align: right; }
.st-sub-cargando { padding: 12px 4px 12px 26px; }

/* ----- Las notas al pie de cada tarjeta ----- */
.st-nota {
  margin: 12px 0 0; font-size: 12px; line-height: 1.55; color: var(--ink-3);
}
.st-nota code { font-family: var(--f-mono); font-size: 11px; }

/* ----- Dos tarjetas en fila, una debajo de otra en estrecho ----- */
.st-cols {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  align-items: flex-start; margin-top: 16px;
}

/* ----- «De dónde sale cada cifra» ----- */
.st-explica { margin-top: 16px; }
.st-explica dl { margin: 0; display: grid; gap: 12px; }
.st-explica dt {
  font-family: var(--f-mono); font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-3);
}
.st-explica dd {
  margin: 3px 0 0; font-size: 13px; line-height: 1.6; color: var(--ink-2);
}

/* ----- El aviso de «no se ha podido consultar» ----- */
.st-aviso {
  display: flex; gap: 12px; align-items: flex-start;
  border-color: var(--amber-bg);
}
.st-aviso > .lucide { width: 20px; height: 20px; color: var(--amber-fg); flex: none; }
.st-aviso div div { font-size: 13px; color: var(--ink-3); line-height: 1.6; margin-top: 4px; }

/* ----- El selector de periodo ----- */
.st-periodo {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px;
  margin-bottom: 16px;
}
.st-chips, .st-grano { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.st-grano > span {
  font-family: var(--f-mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-3); margin-right: 2px;
}
.st-chip {
  padding: 5px 11px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-2); cursor: pointer;
  font: inherit; font-size: 12.5px;
}
.st-chip:hover { border-color: var(--line-2); color: var(--ink); }
.st-chip.active { background: var(--teal-600); border-color: var(--teal-600); color: #fff; }

.st-fechas { display: flex; gap: 10px; flex-wrap: wrap; }
.st-fechas label {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--ink-3);
}
.st-fechas input { width: 150px; }

.st-rango {
  margin-left: auto; font-family: var(--f-mono); font-size: 11px; color: var(--ink-3);
}

/* ----- La retención ----- */
.st-retencion {
  display: flex; gap: 16px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; margin-top: 16px;
}
.st-retencion div div {
  font-size: 12.5px; color: var(--ink-3); line-height: 1.6;
  margin-top: 4px; max-width: 68ch;
}

@media (max-width: 900px) {
  .st-cols { grid-template-columns: 1fr; }
  .st-rango { margin-left: 0; width: 100%; }
  /* En estrecho la barra de proporción es lo primero que sobra: el número ya
     está escrito al lado y la barra solo servía para comparar de un vistazo. */
  .st-cab > span:last-child, .st-fila > .st-barra { display: none; }
  /* Y el id técnico: en estrecho no cabe entero y se quedaba cortado a media
     palabra («dashbo», «app…»), que es peor que no estar. El nombre humano
     basta para saber de qué sección se habla. */
  .st-id { display: none; }
  .st-cab, .st-fila { grid-template-columns: 1fr 80px 90px; }
  .st-uso .st-cab, .st-uso .st-fila { grid-template-columns: 1fr 70px 70px; }
  .st-uso .st-cab > span:nth-child(4), .st-uso .st-cab > span:nth-child(5),
  .st-uso .st-fila > span:nth-child(4), .st-uso .st-fila > span:nth-child(5) { display: none; }
  .st-sub-fila { grid-template-columns: 1fr 100px; }
  .st-sub-fila > span:nth-child(3), .st-sub-fila > span:nth-child(4) { display: none; }
}

/* =============================================================================
   DOCUMENTOS · BORRAR DEL EXPEDIENTE  ·  js/views.js
   -----------------------------------------------------------------------------
   La papelera por tarjeta y el modo de selección para borrar varios de golpe.
   ========================================================================== */

/* La papelera solo se pinta al pasar por encima de la tarjeta, y siempre en
   táctil (donde no hay «encima»). Que un botón de borrar esté visible en veinte
   tarjetas a la vez invita al accidente; que no exista, obliga a entrar en la
   ficha de la persona para quitar una nómina mal subida. */
.doc-card .doc-del { color: var(--ink-4); opacity: 0; transition: opacity .12s ease, color .12s ease; }
.doc-card:hover .doc-del, .doc-card:focus-within .doc-del { opacity: 1; }
.doc-card .doc-del:hover { color: var(--rose-fg); }
@media (hover: none) {
  .doc-card .doc-del { opacity: 1; }
}

/* La casilla del modo selección ocupa el sitio del icono del tipo de documento:
   así la tarjeta no cambia de tamaño al entrar y salir del modo, y la rejilla no
   da un salto. */
.doc-check {
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-2); background: var(--surface); color: transparent;
}
.doc-check .lucide { width: 16px; height: 16px; }
.doc-check.on { background: var(--teal-600); border-color: var(--teal-600); color: #fff; }

.doc-card.picked { border-color: var(--teal-500); box-shadow: 0 0 0 1px var(--teal-500) inset; }

.doc-selcount { font-family: var(--f-mono); font-size: 11px; color: var(--ink-3); margin-right: 4px; }
