/**
 * Media Hub NewsAI — tokens del sistema de diseño
 *
 * Fuente de verdad: MEDIA-HUB-NEWSAI-DESIGN-SYSTEM.md
 *
 * ESTE ES EL UNICO ARCHIVO DEL PRODUCTO DONDE PUEDE APARECER UN VALOR LITERAL.
 * Cualquier otro CSS que escriba un color, un tamaño o un radio a mano está mal,
 * sin excepciones más allá de las tres documentadas en la sección 14 del sistema.
 *
 * Sobre el ámbito, que es la decisión de arquitectura más importante de este
 * archivo: los tokens NO se declaran en :root. El widget de chat lo hacía, es
 * decir escribía variables en el ámbito global del sitio del cliente. Un widget
 * incrustado en una plantilla ajena no tiene ese derecho: sus nombres podrían
 * chocar con los del medio y, peor, sus valores se filtran a elementos que no le
 * pertenecen.
 *
 * En su lugar se declaran sobre la raíz de cada superficie del producto. Cuesta
 * una lista de selectores y cierra la puerta a una clase entera de conflictos.
 *
 * Los contrastes de cada color fueron calculados, no estimados. Cuatro valores de
 * la propuesta original reprobaron AA y se sustituyeron; el detalle está en la
 * sección 0 del documento del sistema.
 *
 * @copyright  (C) 2026 Bruno Invernizzi. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

/* ==========================================================================
   MODO CLARO
   ========================================================================== */

/*
 * Las clases de esta lista son las que EXISTEN en el marcado.
 *
 * Hasta 1.13.7 decía `.nm-chat, .nm-widget, .nm-pwa`, y ninguna de las tres
 * aparecía en una sola plantilla del producto: las reales siempre fueron
 * `.newsmind-chat`, `.newsmind-widget` y `.newsmind-pwa`. Las setenta y dos
 * variables se declaraban y no las recibía nadie.
 *
 * No se notó durante doce fases porque el widget del lector traía su propia
 * paleta en :root, así que se veía bien — con los colores viejos, sin verificar,
 * y contaminando el ámbito global del sitio del cliente. El sistema de diseño
 * estaba escrito, verificado, documentado, y entregado a una dirección
 * inexistente.
 *
 * Es la novena vez que este proyecto tropieza con lo mismo: verificar el origen
 * no verifica el destino. Antes de dar por buena una hoja de estilos, comprobar
 * que sus selectores le pegan a algo.
 */
.newsmind-wrapper,
.newsmind-assistant,
.newsmind-chat,
.newsmind-widget,
.newsmind-pwa {
    /* ---------- Texto ---------- */
    --nm-text: #0F172A;             /* 17,85:1 sobre superficie */
    --nm-text-secondary: #334155;   /* 10,35:1 */
    --nm-text-muted: #64748B;       /*  4,76:1 — el mínimo aceptable */

    /* ---------- Acento ---------- */
    --nm-accent: #3B5BDB;           /*  5,67:1 */
    --nm-accent-contrast: #FFFFFF;  /*  5,67:1 sobre el acento */
    --nm-accent-soft: #EEF2FF;

    /* ---------- Cita: identidad propia, no negociable ----------
       Petróleo. Es el único color fuera de la paleta de acento y existe por un
       hallazgo de producción: las citas del chat salían naranjas porque las
       pintaba la plantilla del cliente. La cita es la promesa del producto
       —responde sólo con lo publicado y lo demuestra— así que su aspecto no
       puede quedar a merced del CSS ajeno. */
    --nm-cite: #1F5F5B;             /*  7,38:1 */
    --nm-cite-soft: #E7EFEE;      /* --nm-cite encima: 6,32:1 */

    /* ---------- Color de marca del medio ----------
       Capa opcional. Por defecto no existe: hereda el acento del sistema, así
       que un medio que no configure nada obtiene la paleta verificada.

       Se separa del acento por una razón medible. El color primario de Radio
       Gabal es #ffa621, un ámbar que sobre blanco da 1,96:1 — el mínimo para
       texto es 4,5. Como color de texto o como fondo de botón con letra blanca
       es ilegible, y de hecho ES la causa del hallazgo V-06: las citas del chat
       salían de ese ámbar porque las pintaba la plantilla, es decir a 2:1.

       Pero el mismo ámbar con tinta oscura encima da 9,12:1. El color no está
       mal; está mal el papel que se le pedía.

       De ahí los tres tokens: uno para rellenar, uno para el texto QUE VA
       ENCIMA del relleno, y uno oscurecido del mismo tono para cuando la marca
       tiene que ser texto. */
    --nm-brand: var(--nm-accent);            /* rellenos, barras, cabeceras */
    --nm-brand-contrast: var(--nm-accent-contrast); /* texto sobre el relleno */
    --nm-brand-text: var(--nm-accent);       /* cuando la marca es el texto */

    /* ---------- Superficies ---------- */
    --nm-bg: #F8FAFC;
    --nm-surface: #FFFFFF;

    /* ---------- Bordes ----------
       Dos tokens que NO son intercambiables. WCAG 1.4.11 exige 3:1 al límite
       visual de un control, y ningún gris claro lo cumple: #E2E8F0 da 1,23:1 y
       hasta #94A3B8 se queda en 2,56:1. El claro separa; el oscuro delimita algo
       con lo que se interactúa. */
    --nm-border: #E2E8F0;           /*  1,23:1 — decorativo, sin requisito */
    --nm-border-control: #64748B;   /*  4,76:1 — campos e inputs */

    /* ---------- Semánticos ----------
       Cada uno lleva su fondo suave. El texto pasa AA sobre blanco Y sobre su
       propio fondo: es donde reprobó el rojo original, que daba 4,83 sobre
       blanco y 4,41 sobre su insignia. */
    --nm-success: #047857;          /*  5,48:1 */
    --nm-success-soft: #ECFDF5;
    --nm-warning: #B45309;          /*  5,02:1 */
    --nm-warning-soft: #FFFBEB;
    --nm-error: #B91C1C;            /*  6,47:1 */
    --nm-error-soft: #FEF2F2;
    --nm-info: #0369A1;             /*  5,93:1 */
    --nm-info-soft: #F0F9FF;

    /* Tinta sobre el fondo suave.
       Acá coincide con el color pleno, porque estos cuatro ya pasan AA sobre su
       propia insignia. Existe como token aparte porque en la piel de
       administración NO coincide: la paleta de Joomla está pensada para rellenos
       con letra blanca encima, y su verde y su ámbar sobre un fondo pálido se
       quedan cortos. Sin este token, adoptar esa paleta habría bajado el
       contraste de todas las insignias sin que se notara. */
    --nm-success-on-soft: var(--nm-success);
    --nm-warning-on-soft: var(--nm-warning);
    --nm-error-on-soft: var(--nm-error);
    --nm-info-on-soft: var(--nm-info);

    /* Ámbar de relleno: fondo lleno con tinta oscura encima. Ver --nm-brand. */
    --nm-warning-fill: var(--nm-warning);
    --nm-warning-fill-contrast: #FFFFFF;

    /* Enlaces. Separado del acento porque un acento puede ser muy oscuro para
       leerse como enlace, que es justo lo que pasa con el azul marino de Joomla. */
    --nm-link: var(--nm-accent);
    --nm-link-hover: var(--nm-accent);

    /* Estados del botón primario. Aparte del acento para poder tomarlos tal cual
       de la plantilla que los define, en vez de calcularlos con color-mix. */
    --nm-accent-hover: color-mix(in srgb, var(--nm-accent) 88%, black);
    --nm-accent-active: color-mix(in srgb, var(--nm-accent) 80%, black);

    /* ---------- Tipografía ----------
       Sin fuentes web. El widget se carga en el sitio de un medio local sobre
       teléfonos modestos: dos archivos de fuente son un coste que paga el lector.
       Las tres pilas existen en todos los sistemas. */
    --nm-font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, sans-serif;
    --nm-font-data: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                    monospace;

    /* ---------- Serif de display ----------
       UN SITIO, Y SÓLO UNO. Regla escrita: si aparece en un tercero, se
       revierte, y `check-display-serif.py` lo hace fallar antes.

       Coste cero: las tres familias están en todos los sistemas desde hace
       veinte años, así que no se descarga nada. Es lo contrario de lo que hace
       AMA, que baja 96 KB de tipografía para UN elemento —y ni siquiera llega
       a aplicarla: `document.fonts.check()` da falso y 192 de sus 193 elementos
       se dibujan con la sans del sistema—.

       Y es lo único que separa esta portada de cualquier caja de chat: un
       saludo en serif dice «esto lo escribe un medio», que es exactamente lo
       que el producto es. */
    --nm-font-display: Georgia, "Times New Roman", serif;

    /* Había una tercera pila, Georgia, para el bloque de citas. Se eliminó tras
       verla en pantalla: dentro de una misma respuesta el cambio de tipografía se
       leía como un error de renderizado, no como una decisión. Dos familias
       bastan, y una de ellas sólo para cifras. */

    /* Siete tamaños. Antes había dieciocho, siete de ellos apretados entre
       .78 y .88rem. Si algo "necesita 15px", la respuesta es 14 o 16. */
    --nm-text-xs: .75rem;      /* 12 */
    --nm-text-sm: .8125rem;    /* 13 */
    --nm-text-base: .875rem;   /* 14 */
    --nm-text-md: 1rem;        /* 16 */
    --nm-text-lg: 1.25rem;     /* 20 */
    --nm-text-xl: 1.5rem;      /* 24 */
    --nm-text-2xl: 1.875rem;   /* 30 */

    /* La jerarquía sale del tamaño y del color; el grosor la refuerza donde el
       tamaño no puede.
       Esta nota decía que el 700 tenía un único uso legítimo —el marcador de
       cita— y la práctica la desmintió: los textos que nombran algo se quedaron
       cortos en 600 justamente donde son más chicos. A 11 y 12 px el contraste
       entre 400 y 600 casi no se ve, y son esos rótulos minúsculos los que hay
       que poder recorrer sin leer el resto.
       El 700 queda reservado a etiquetas que nombran: título de tarjeta, rótulo
       de métrica, nombre de fila, insignia y marcador de cita. Nunca para texto
       corrido ni para cifras — una cifra grande ya pesa por tamaño, y en negrita
       además se vuelve un grito. */
    --nm-weight-normal: 400;
    --nm-weight-medium: 500;
    --nm-weight-semibold: 600;
    --nm-weight-bold: 700;

    --nm-leading-tight: 1.2;
    --nm-leading-normal: 1.5;
    --nm-leading-relaxed: 1.6;

    /* ---------- Espaciado (base 4) ---------- */
    --nm-space-1: .25rem;
    --nm-space-2: .5rem;
    --nm-space-3: .75rem;
    --nm-space-4: 1rem;
    --nm-space-6: 1.5rem;
    --nm-space-8: 2rem;
    --nm-space-12: 3rem;
    --nm-space-16: 4rem;

    /* ---------- Radios ---------- */
    --nm-radius-sm: 6px;
    --nm-radius-md: 8px;
    --nm-radius-lg: 12px;
    --nm-radius-full: 9999px;

    /* Radio del botón, aparte de la escala general.
       En el widget del lector sigue siendo el nuestro; en el panel pasa a ser el
       de Joomla, porque ahí el botón convive con los del CMS y dos radios
       distintos en la misma pantalla se notan aunque nadie sepa nombrarlos. */
    --nm-radius-btn: var(--nm-radius-md);

    /* ---------- Foco ----------
       Borde que cambia más halo alrededor, en vez de un contorno seco. Es el
       gesto que usa Joomla en sus campos, y copiarlo evita que el panel se
       sienta de otro programa justo en el momento en que el usuario está
       escribiendo. */
    --nm-focus-border: var(--nm-accent);
    --nm-focus-ring: 0 0 0 .2rem rgb(59 91 219 / .25);

    /* ---------- Sombras ----------
       La elevación indica cuán temporal es un elemento, no cuán importante. */
    --nm-shadow-sm: 0 1px 2px rgb(15 23 42 / .05);
    --nm-shadow-md: 0 4px 12px rgb(15 23 42 / .08);
    --nm-shadow-lg: 0 12px 32px rgb(15 23 42 / .12);

    /* ---------- Bordes ---------- */
    --nm-border-width: 1px;
    --nm-border-width-accent: 3px;

    /* ---------- Transiciones ---------- */
    --nm-transition-fast: 120ms ease-out;
    --nm-transition-base: 200ms ease-out;

    /* ---------- Movimiento ----------
       CAMBIAR DE ESTADO NO ES LO MISMO QUE APARECER.

       Los dos tokens de arriba son para algo que YA ESTÁ y cambia: un botón que
       se ilumina, un borde que toma color. Los cuatro de acá son para algo que
       NO ESTABA y llega, que es un problema distinto —tiene origen, tiene
       dirección, y el ojo lo sigue.

       Se escriben ahora, antes del streaming, por una razón concreta: el día
       que el asistente transmita van a aparecer al mismo tiempo el texto que
       llega de a pedazos, los chips de herramienta, el panel de fuentes y las
       repreguntas. Cuatro cosas que entran. Si cada una elige su duración, el
       resultado no se lee como cuatro animaciones distintas: se lee como que la
       pantalla está inestable. Unificar cinco animaciones ya escritas cuesta
       más que definir cuatro tokens hoy.

       Las duraciones no son arbitrarias. Entrar desde un borde recorre
       distancia y necesita menos tiempo del que parece; aparecer en el sitio no
       recorre nada y necesita un poco más para no parpadear; desplegar mueve
       todo lo que está debajo y por eso lleva la curva con rebote contenido, que
       frena al final; y el texto que llega token a token va lineal, porque
       cualquier curva sobre algo que se repite cincuenta veces por segundo se
       percibe como tirón.

       El respeto por `prefers-reduced-motion` NO se escribe acá ni en cada
       hoja: está una sola vez, al final de este archivo, y alcanza a las cinco
       superficies. Repetirlo por archivo sería la cuarta copia de una regla que
       después se separa. */
    --nm-motion-enter: 180ms ease-out;                  /* entra por un borde  */
    --nm-motion-appear: 240ms ease-out;                 /* aparece en sitio    */
    --nm-motion-expand: 280ms cubic-bezier(.22, 1, .36, 1); /* se despliega    */
    --nm-motion-stream: 120ms linear;                   /* token de texto      */

    /* Y los tres ritmos de espera, que son otra cosa: no entran ni salen, se
       repiten mientras algo tarda. Sus periodos son distintos a proposito y no
       se unifican — una rueda a 1,6s parece colgada y un latido a 0,7s parece
       nervioso—, pero tener nombre es lo que impide que aparezca un cuarto
       ritmo inventado la proxima vez que alguien necesite indicar espera. */
    --nm-motion-spin: .7s linear;                       /* rueda que gira      */
    --nm-motion-typing: 1.2s ease-in-out;               /* puntos de escritura */
    --nm-motion-pulse: 1.6s ease-in-out;                /* latido de carga     */

    /* ---------- Z-index ----------
       Fin de los números mágicos: el chat usaba 1040 sin escala ninguna.

       DOS PROBLEMAS DISTINTOS QUE SE PARECEN, Y SÓLO UNO ES DE APILAMIENTO.

       El primero es CONVIVIR: el botón flotante y el widget de accesibilidad del
       medio quieren la misma esquina. Ahí subir el número no arregla nada —sólo
       decide cuál de los dos queda inutilizable— y la solución son los
       desplazamientos configurables de Apariencia, que corren el chat lo justo.

       El segundo es TAPAR: el menú fijo del sitio se dibujaba ENCIMA del panel
       abierto, partiendo el mensaje de bienvenida al medio. Eso sí es
       apilamiento, y ahí el número es la herramienta correcta: el lector no
       puede mover el menú del diario, y el panel abierto es la superficie que
       está usando.

       De 900 a 1035, y el valor está elegido, no tanteado. En la escala de
       Bootstrap —que es la que usan casi todas las plantillas de Joomla— la
       barra fija vale 1030 y el fondo de un modal 1040. 1035 cae justo en el
       medio: por encima del menú del sitio, por debajo de cualquier diálogo que
       el medio abra. Un lightbox de fotos o un aviso de cookies siguen ganando,
       que es lo correcto: son cosas que el lector tiene que poder cerrar.

       Y para las plantillas que no siguen esa escala —las hay con 9999— el guión
       mide el apilamiento real de la página y sube el chat lo necesario, sin que
       nadie tenga que configurar un número. Ver `raiseAboveChrome()`. */
    --nm-z-base: 1;
    --nm-z-dropdown: 100;
    --nm-z-sticky: 200;
    --nm-z-widget: 1035;
    --nm-z-modal: 1000;
    --nm-z-toast: 1100;

    /* ---------- Área táctil mínima ---------- */
    --nm-touch: 44px;

    /* Ritmo vertical entre secciones. El mismo valor que el hueco horizontal de
       las rejillas: cuando difieren, la retícula se lee torcida aunque nadie
       sepa decir por qué. */
    --nm-section-gap: var(--nm-space-4);
}

/* ==========================================================================
   PIEL DE ADMINISTRACIÓN — paleta de Joomla (plantilla Atum)

   El panel es una pantalla DENTRO de Joomla, no un producto aparte. Con paleta
   propia, el botón primario del componente no era del mismo azul que el botón
   primario que el usuario tenía tres centímetros más arriba, en la barra de
   herramientas del CMS. Esa diferencia no se percibe como personalidad: se
   percibe como que algo no terminó de cargar.

   Los valores salen de `media/templates/administrator/atum/css/template.css` del
   propio sitio, que es la única fuente que no puede quedar desactualizada.

   Los contrastes están calculados sobre esos valores, no estimados, y ahí
   apareció el matiz que gobierna todo este bloque: **la paleta de Joomla está
   hecha para rellenos**. Sus doce pares de botón pasan AA con holgura porque el
   color va de fondo y la letra encima es blanca o negra. Pero dos de esos
   colores NO sirven para lo que este panel también necesita —ser texto, línea de
   gráfico o filo de tarjeta sobre blanco—:

     · #ffb514 (warning) da 1,77:1 sobre blanco. Ni como texto (4,5) ni como
       borde de un control (3,0). Es un relleno, y sólo eso.
     · Los bordes «subtle» de Bootstrap rondan 1,3–2,0:1. Separan bloques, no
       delimitan controles.

   Así que el ámbar se parte en dos tokens, como ya se había hecho con el color
   de marca del medio por exactamente la misma razón: uno para rellenar y otro,
   oscurecido del mismo tono, para cuando tiene que ser tinta.

   Va ANTES del bloque de modo oscuro a propósito: si fuera después, ganaría por
   orden de aparición y dejaría el panel en claro dentro de un Joomla oscuro.
   ========================================================================== */

.newsmind-wrapper {
    /* ---------- Texto: escala de grises de Joomla ---------- */
    --nm-text: #212529;             /* gray-900 · 15,43:1 */
    --nm-text-secondary: #495057;   /* gray-700 ·  8,18:1 */
    --nm-text-muted: #666e76;       /* gray-600 ·  5,18:1 */

    /*
     * Los títulos NO son del color del texto.
     *
     * Atum pinta todos los `h1`…`h6` de #1f3047 —un azul muy oscuro, casi
     * negro— y nuestros `h2` de sección lo venían tomando prestado sin que
     * ninguna regla nuestra lo dijera. Los desplegables, en cambio, usan un
     * `<span>` con `--nm-text` (#212529), y por eso se veían de otro color:
     * dos títulos hermanos tomando el color de dos sitios distintos.
     *
     * Se declara acá y se aplica a los dos. Deja de ser una herencia accidental
     * —que desaparecería el día que Atum cambie esa regla o el componente se
     * monte en otra plantilla— y pasa a ser una decisión con nombre.
     *
     * 15,08:1 sobre blanco: por encima de AAA, como el resto de la escala.
     */
    --nm-heading: #1f3047;

    /* ---------- Acento: el azul marino de la barra de Joomla ---------- */
    --nm-accent: #132f53;           /* --primary        · 13,47:1 con blanco */
    --nm-accent-hover: #102847;     /* --btn-hover-bg   · 14,82:1 */
    --nm-accent-active: #0f2642;    /* --btn-active-bg  · 15,26:1 */
    --nm-accent-contrast: #FFFFFF;
    --nm-accent-soft: #d4e1f1;      /* --primary-bg-subtle */

    /* El marino no se lee como enlace: para eso Joomla usa su propio azul. */
    --nm-link: #2a69b8;             /* --link-color       · 5,51:1 */
    --nm-link-hover: #173a65;       /* --link-hover-color */

    /* ---------- Superficies y bordes ---------- */
    --nm-bg: #f8f9fa;               /* gray-100 */
    --nm-surface: #ffffff;
    --nm-border: #dee2e6;           /*  1,30:1 — --border-color, decorativo */
    --nm-border-control: #666e76;   /* gray-600 · 5,18:1 — cumple 1.4.11 */

    /* ---------- Semánticos ----------
       Pleno para texto y línea; suave con su tinta propia para insignias. Los
       pares suave+tinta son los de Bootstrap 5.3, que rinden entre 7,8 y 10,9:1
       —bastante mejor que los que había. */
    --nm-success: #457d54;          /* --success · 4,87:1 */
    --nm-success-soft: #dae5dd;
    --nm-success-on-soft: #1c3222;  /* 10,62:1 sobre su fondo */

    --nm-error: #c52827;            /* --danger  · 5,67:1 */
    --nm-error-soft: #f3d4d4;
    --nm-error-on-soft: #4f1010;    /* 10,73:1 */

    --nm-info: #2a69b8;             /* --info    · 5,51:1 */
    --nm-info-soft: #d4e1f1;
    --nm-info-on-soft: #112a4a;     /* 10,89:1 */

    /* El ámbar de Joomla, oscurecido al 50 %. Mismo tono, 6,20:1 sobre blanco:
       sirve de texto, de línea del gráfico y de filo de tarjeta. */
    --nm-warning: #805a0a;          /* 6,20:1 */
    --nm-warning-soft: #fcefdc;
    --nm-warning-on-soft: #60451f;  /* 7,82:1 */

    /* Y el ámbar tal cual de Joomla, para cuando va de fondo. Con tinta negra
       encima da 11,87:1; con blanca daría 1,77 y sería ilegible. */
    --nm-warning-fill: #ffb514;     /* --warning */
    --nm-warning-fill-contrast: #000000;

    /* ---------- Forma y foco de Joomla ---------- */
    --nm-radius-btn: .25rem;        /* --btn-border-radius */
    --nm-focus-border: #39f;        /* --focus */
    --nm-focus-ring: 0 0 0 .25rem rgb(42 105 183 / .25);
}

/* ==========================================================================
   MODO OSCURO

   Los cinco archivos o ninguno. Hoy lo tienen tres, lo que produce mezclas
   incoherentes en la misma página: los widgets se adaptan y el bloque de avisos
   que está al lado, no.

   Los once valores fueron verificados: ninguno de texto baja de 4,5:1 y el borde
   de control llega a 3,21:1.
   ========================================================================== */

/* El panel sale de esta lista a propósito.
 *
 * `prefers-color-scheme` es la preferencia del SISTEMA OPERATIVO, y para el
 * widget del lector es la señal correcta: se incrusta en el sitio de un medio y
 * ahí no hay más información que ésa.
 *
 * En administración no lo es. Joomla tiene su propio interruptor de modo oscuro,
 * que escribe `data-bs-theme` en el <html> y es independiente del sistema. Con
 * el panel atado al sistema operativo, un usuario con Windows en oscuro y Joomla
 * en claro veía el resto del backend blanco y este componente negro. La piel de
 * administración sigue a Joomla; ver el bloque [data-bs-theme="dark"] de abajo.
 *
 * `.nm` y `.newsmind-wrapper` van juntos porque los dos son exclusivos del
 * panel: sólo aparecen en admin/tmpl. */
/* `:not(.nm-scheme--light)` es lo que deja al medio decir «siempre claro».
   Sin eso, la preferencia del sistema del lector ganaba siempre y la opción no
   existía. Y `.nm-scheme--dark` va en la misma lista para no repetir los
   valores: fuerza lo mismo que la consulta, sin consultar nada. */
@media (prefers-color-scheme: dark) {
    .newsmind-assistant:not(.nm-scheme--light),
    .newsmind-chat:not(.nm-scheme--light),
    .newsmind-widget:not(.nm-scheme--light),
    .newsmind-pwa:not(.nm-scheme--light) {
        --nm-text: #E8EDF5;             /* 14,81:1 */
        --nm-text-secondary: #B4C0D3;   /*  9,47:1 */
        --nm-text-muted: #8B98AD;       /*  5,96:1 */

        --nm-accent: #8FA6FF;           /*  7,51:1 */
        --nm-accent-contrast: #0B1220;  /*  8,08:1 */
        --nm-accent-soft: #1B2444;

        --nm-cite: #6FC4BC;             /*  8,54:1 */
        --nm-cite-soft: #13292B;        /* --nm-cite encima: 7,46:1 */

        --nm-bg: #0B1220;
        --nm-surface: #111A2B;

        --nm-border: #1E293B;           /*  1,19:1 — decorativo, sin requisito */
        --nm-border-control: #5A6B85;   /*  3,21:1 */

        --nm-success: #4ADE80;          /*  9,99:1 */
        --nm-success-soft: #0C2A1C;
        --nm-warning: #FBBF24;          /* 10,43:1 */
        --nm-warning-soft: #2A1F08;
        --nm-error: #FCA5A5;            /*  9,17:1 */
        --nm-error-soft: #2A1215;
        --nm-info: #7DD3FC;             /* 10,44:1 */
        --nm-info-soft: #0A2436;

        /* Sobre fondo oscuro la sombra no se ve. La elevación pasa a expresarse
           con borde, así que se anulan en lugar de dejarlas inertes. */
        --nm-shadow-sm: none;
        --nm-shadow-md: none;
        --nm-shadow-lg: none;
    }
}

/* ==========================================================================
   MODO OSCURO FORZADO DEL FRONTEND

   El medio puede decidir por su público en vez de seguir al sistema operativo
   de cada lector. Tres opciones: automático (la consulta de arriba), siempre
   claro (`.nm-scheme--light`, que sale gratis porque desactiva la consulta) y
   siempre oscuro, que es este bloque.

   Los valores NO están escritos a mano: se generan a partir del mismo bloque de
   arriba. Mantener dos copias de una paleta es garantizar que la próxima
   corrección de contraste se aplique a una sola.
   ========================================================================== */

.nm-scheme--dark {

    --nm-text: #E8EDF5;             /* 14,81:1 */
    /* En oscuro el azul casi negro de Atum es ilegible. El título usa el mismo
       blanco que el texto: la jerarquía ya la marcan el cuerpo y el peso. */
    --nm-heading: #E8EDF5;          /* 14,81:1 */
    --nm-text-secondary: #B4C0D3;   /*  9,47:1 */
    --nm-text-muted: #8B98AD;       /*  5,96:1 */

    --nm-accent: #8FA6FF;           /*  7,51:1 */
    --nm-accent-contrast: #0B1220;  /*  8,08:1 */
    --nm-accent-soft: #1B2444;

    --nm-cite: #6FC4BC;             /*  8,54:1 */
    --nm-cite-soft: #13292B;        /* --nm-cite encima: 7,46:1 */

    --nm-bg: #0B1220;
    --nm-surface: #111A2B;

    --nm-border: #1E293B;           /*  1,19:1 — decorativo, sin requisito */
    --nm-border-control: #5A6B85;   /*  3,21:1 */

    --nm-success: #4ADE80;          /*  9,99:1 */
    --nm-success-soft: #0C2A1C;
    --nm-warning: #FBBF24;          /* 10,43:1 */
    --nm-warning-soft: #2A1F08;
    --nm-error: #FCA5A5;            /*  9,17:1 */
    --nm-error-soft: #2A1215;
    --nm-info: #7DD3FC;             /* 10,44:1 */
    --nm-info-soft: #0A2436;

    /* Sobre fondo oscuro la sombra no se ve. La elevación pasa a expresarse
       con borde, así que se anulan en lugar de dejarlas inertes. */
    --nm-shadow-sm: none;
    --nm-shadow-md: none;
    --nm-shadow-lg: none;
}

/* ==========================================================================
   MODO OSCURO DEL PANEL — el interruptor de Joomla, no el del sistema

   Joomla escribe `data-bs-theme="dark"` en el <html> cuando el usuario elige el
   modo oscuro del backend. Ese es el interruptor que el panel tiene que obedecer:
   es una decisión sobre el backend, tomada dentro del backend, y puede no
   coincidir con la del sistema operativo.

   Los valores son los mismos once del bloque de arriba, ya verificados, más los
   que agregó la piel de administración. Se repiten en vez de compartirse porque
   compartirlos exigiría una capa `@layer` o un selector común, y las dos cosas
   costarían más que estas veinte líneas.

   Todos los pares fueron calculados, no estimados. El más ajustado es el borde de
   control, en 3,21:1 — por encima del 3,0 que pide WCAG 1.4.11.
   ========================================================================== */

[data-bs-theme="dark"] .newsmind-wrapper {
    --nm-text: #E8EDF5;             /* 14,81:1 sobre la superficie */
    /*
     * EL TÍTULO, QUE FALTABA.
     *
     * `--nm-heading` es el azul casi negro de Atum, y en claro está bien: 15:1
     * sobre blanco. Pero este bloque no lo redefinía, así que en oscuro los
     * títulos de tarjeta y de desplegable —«Actividad», «Sistema», «Registros»,
     * los diez de Estadísticas— seguían pintándose de azul casi negro sobre
     * fondo casi negro. Ilegibles, y sin que nada fallara: la variable existía,
     * sólo que con el valor del otro modo.
     *
     * Se iguala al texto, como ya hacía el modo oscuro del sitio: en oscuro la
     * jerarquía la marcan el cuerpo y el peso, no el tono.
     */
    --nm-heading: #E8EDF5;          /* 14,81:1 */

    /*
     * LA CITA, QUE FALTABA POR EL MISMO MOTIVO.
     *
     * `cite.css` se carga en las diez pantallas del panel y la vista de una
     * conversación dibuja las fuentes de cada respuesta con este color. En claro
     * es un verde azulado oscuro que da 7,4:1 sobre blanco; sin versión oscura,
     * en modo oscuro quedaba a 2:1 contra su propio fondo.
     *
     * Los mismos valores que el modo oscuro del sitio: es la misma cita, en la
     * misma pantalla, leída por la misma persona.
     */
    --nm-cite: #6FC4BC;             /*  8,54:1 */
    --nm-cite-soft: #13292B;        /* --nm-cite encima: 7,46:1 */
    --nm-text-secondary: #B4C0D3;   /*  9,47:1 */
    --nm-text-muted: #8B98AD;       /*  5,96:1 */

    /* En claro el acento se oscurece al apuntarlo; en oscuro tiene que
       ACLARARSE. Oscurecer sobre fondo negro no es un realce, es un apagado. */
    --nm-accent: #8FA6FF;           /*  7,51:1 */
    --nm-accent-hover: #B0C0FF;
    --nm-accent-active: #C9D4FF;
    --nm-accent-contrast: #0B1220;  /*  8,08:1 sobre el acento */
    --nm-accent-soft: #1B2444;

    --nm-link: #8FA6FF;
    --nm-link-hover: #B0C0FF;

    --nm-bg: #0B1220;
    --nm-surface: #111A2B;

    --nm-border: #1E293B;           /*  1,19:1 — decorativo, sin requisito */
    --nm-border-control: #5A6B85;   /*  3,21:1 */

    /* En oscuro el color pleno YA es claro, así que sirve de tinta sobre su
       propio fondo: no hace falta la variante «on-soft» que en claro existía
       porque la paleta de Joomla está hecha para rellenos. */
    --nm-success: #4ADE80;
    --nm-success-soft: #0C2A1C;
    --nm-success-on-soft: #4ADE80;  /*  8,84:1 */

    --nm-warning: #FBBF24;
    --nm-warning-soft: #2A1F08;
    --nm-warning-on-soft: #FBBF24;  /*  9,69:1 */

    --nm-error: #FCA5A5;
    --nm-error-soft: #2A1215;
    --nm-error-on-soft: #FCA5A5;    /*  9,25:1 */

    --nm-info: #7DD3FC;
    --nm-info-soft: #0A2436;
    --nm-info-on-soft: #7DD3FC;     /*  9,55:1 */

    /* El ámbar de relleno lleva tinta OSCURA también acá: es un fondo claro,
       lo rodee lo que lo rodee. 11,22:1. */
    --nm-warning-fill: #FBBF24;
    --nm-warning-fill-contrast: #0B1220;

    --nm-focus-border: #8FA6FF;
    --nm-focus-ring: 0 0 0 .25rem rgb(143 166 255 / .3);

    /* Una sombra negra sobre fondo negro no existe. Pero anularlas del todo,
       como hace el bloque del widget, dejaba sin respuesta el realce de las
       tarjetas de métrica al apuntarlas: se levantaban dos píxeles y nada más.
       En oscuro la elevación se dibuja con luz, no con sombra — un aro claro
       que crece. Es el mismo gesto con el material invertido. */
    --nm-shadow-sm: 0 0 0 1px rgb(255 255 255 / .04);
    --nm-shadow-md: 0 0 0 1px rgb(143 166 255 / .35), 0 4px 12px rgb(0 0 0 / .45);
    --nm-shadow-lg: 0 0 0 1px rgb(255 255 255 / .12), 0 16px 40px rgb(0 0 0 / .6);
}

/* ==========================================================================
   MOVIMIENTO REDUCIDO

   Global y sin excepciones. Hoy está en tres archivos de cinco.
   ========================================================================== */

/* ==========================================================================
   PRESETS DE APARIENCIA

   Cuatro estilos ya armados. Sólo tocan FORMA y DENSIDAD — nunca color, porque
   el color lo gobierna el campo de marca y sus derivadas verificadas. Es lo que
   hace que ningún preset pueda producir una combinación ilegible: no tienen
   permiso para elegir un color.

   Se aplican como clase en la raíz de cada superficie del frontend, así que
   conviven con el color de marca y con los ajustes finos sin pelearse.

   El cuarto es el que justifica que existan presets. Para una radio local con
   oyentes de sesenta y pico, «alto contraste» no es una decisión estética: es si
   el widget se usa o no. Y es una decisión que el medio SÍ puede tomar, porque
   conoce a su audiencia — a diferencia de «¿qué radio de borde querés?».
   ========================================================================== */

/* Sobrio: el sistema tal como está. No redefine nada a propósito — que el valor
   por omisión no tenga bloque propio es lo que garantiza que sea de verdad el
   valor por omisión y no una copia que se desincroniza. */

.nm-preset--compact {
    --nm-radius-sm: 2px;
    --nm-radius-md: 4px;
    --nm-radius-lg: 6px;
    --nm-space-3: .5rem;
    --nm-space-4: .75rem;
    --nm-space-6: 1rem;
}

.nm-preset--round {
    --nm-radius-sm: 10px;
    --nm-radius-md: 14px;
    --nm-radius-lg: 20px;
    --nm-space-3: 1rem;
    --nm-space-4: 1.25rem;
    --nm-space-6: 1.75rem;
}

/*
 * Alto contraste. Tres cambios, y ninguno es decorativo:
 *
 *  · La tipografía sube un escalón entero. A 14 px un lector de setenta acerca
 *    la cara a la pantalla; a 16 no.
 *  · El borde pasa a 2 px y al gris de control. Un filo de 1 px al 1,3:1 no
 *    existe para quien tiene cataratas.
 *  · Las sombras se apagan. Sobre pantallas malas o con sol de frente, una
 *    sombra suave se lee como suciedad, no como profundidad.
 */
.nm-preset--contrast {
    --nm-text-xs: .8125rem;
    --nm-text-sm: .9375rem;
    --nm-text-base: 1rem;
    --nm-text-md: 1.125rem;

    --nm-text: #0F172A;
    --nm-text-secondary: #1E293B;
    --nm-text-muted: #334155;

    --nm-border: var(--nm-border-control);
    --nm-border-width: 2px;

    --nm-shadow-sm: none;
    --nm-shadow-md: none;
    --nm-shadow-lg: none;
}

/* ---------- Ajustes finos, independientes del preset ---------- */

.nm-density--tight { --nm-space-3: .5rem;  --nm-space-4: .75rem; }
.nm-density--roomy { --nm-space-3: 1rem;   --nm-space-4: 1.5rem; }

.nm-radius--sharp { --nm-radius-sm: 0; --nm-radius-md: 0; --nm-radius-lg: 0; }
.nm-radius--soft  { --nm-radius-sm: 6px;  --nm-radius-md: 8px;  --nm-radius-lg: 12px; }
.nm-radius--round { --nm-radius-sm: 10px; --nm-radius-md: 14px; --nm-radius-lg: 20px; }

/* ==========================================================================
   ACÁ ESTUVO EL PUENTE DE NOMBRES, Y SE FUE EN 1.46.0

   Dieciséis alias —`--newsmind-chat-bg` y sus quince hermanos— que traducían los
   nombres viejos del frontend a los tokens verificados. No queda ninguno.

   POR QUÉ EXISTIÓ. El widget del lector, los módulos y la PWA se escribieron
   antes que el sistema de diseño, con sus propios nombres y su propia paleta sin
   medir. Reescribir a ciegas seiscientas líneas de reglas que funcionaban en
   producción, sólo para renombrar variables, es la clase de trabajo que rompe un
   widget que andaba. El puente cambió el ORIGEN de cada valor sin tocar una sola
   regla: la paleta pasó a ser la verificada, los dos `:root` desaparecieron, y
   nada dejó de funcionar. Fue lo correcto entonces.

   POR QUÉ SE VA. Un puente que se queda es peor que el problema que resolvió.
   Con dos nombres para la misma cosa, leer una hoja obligaba a saltar a ésta para
   averiguar de qué color se estaba hablando — y una hoja que no se puede leer
   sola es una hoja donde el próximo color escrito a mano entra sin que nadie lo
   note. El propio bloque lo dejó escrito: «cuando el último desaparezca, este
   bloque se va».

   CÓMO SE FUE. No de golpe: las etapas 2, 3 y 4 del rediseño del sitio pasaron
   `pwa.css`, `widgets.css` y `chat.css` a `--nm-*` uno por uno, cada uno con su
   versión y sus barridos. Cuando el último terminó, los dieciséis alias se
   quedaron sin un solo consumidor en las siete hojas, y esto pasó de ser una
   migración a ser una supresión.

   QUÉ LO REEMPLAZA. Nada: los tokens `--nm-*` ya estaban ahí. Lo único que hacía
   falta era usarlos.

   Y para que no vuelvan, `check-frontend-tokens.py` falla si aparece un
   `--newsmind-*` en cualquier hoja o plantilla.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    /*
     * Las raíces, no sólo lo que hay dentro.
     *
     * El selector era `.newsmind-widget *` y compañía: alcanzaba a los hijos y
     * dejaba afuera el elemento raíz. Mientras nada animaba la raíz eso no se
     * notaba, pero las clases `.nm-motion-*` se van a poner justo ahí —un panel
     * que entra, un widget que aparece— y ese es el caso que el lector con
     * movimiento reducido menos quiere ver.
     */
    .newsmind-wrapper,
    .newsmind-assistant,
    .newsmind-chat,
    .newsmind-widget,
    .newsmind-pwa,
    .newsmind-wrapper *,
    .newsmind-assistant *,
    .newsmind-chat *,
    .newsmind-widget *,
    .newsmind-pwa * {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}


/* ==========================================================================
   MOVIMIENTO

   Cuatro clases y ni una duración escrita a mano. Cada una usa su token de
   `tokens.css`; el día que una duración tenga que cambiar, cambia en un sitio
   y no en los quince donde alguien la copió.

   `nm-motion-expand` es la única que no es una animación sino una transición,
   y por un motivo: desplegar es ir de una altura a otra, y una animación no
   puede terminar en `auto`. La técnica es `grid-template-rows` de `0fr` a
   `1fr`, que sí anima y respeta la altura real del contenido — lo que sea que
   mida. Va sobre un contenedor con un solo hijo, y el hijo necesita el
   `overflow` y el `min-block-size` de abajo o se desborda mientras crece.

   POR QUÉ ESTÁN EN `tokens.css` Y NO EN `components.css`.

   Estuvieron cinco minutos en `components.css`, que es donde viven el resto de
   las clases `.nm-*`. Está mal: `components.css` lo pide SÓLO el panel de
   administración —diez plantillas, todas de `admin/tmpl/`— y el frontend nunca
   lo carga. O sea que las cuatro clases habrían existido, correctas, en un
   archivo que la pantalla del lector no descarga, justo para la fase en la que
   el asistente empieza a transmitir.

   Es el patrón de siempre otra vez: correcto en el origen, nunca llega al
   destino, sin error y sin síntoma hasta que alguien mira la pantalla.

   `tokens.css` es la única hoja que cargan los dos lados: el panel como
   `com_newsmind.tokens` y el frontend por ruta, en `chat.php` y `pwa.php`.

   El respeto por `prefers-reduced-motion` también vive acá, una sola vez, para
   las cinco superficies.
   ========================================================================== */

@keyframes nm-enter {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
}

@keyframes nm-appear {
    from {
        opacity: 0;
        transform: scale(.98);
    }
}

@keyframes nm-stream {
    from {
        opacity: 0;
    }
}

.nm-motion-enter {
    animation: nm-enter var(--nm-motion-enter) both;
}

.nm-motion-appear {
    animation: nm-appear var(--nm-motion-appear) both;
}

.nm-motion-stream {
    animation: nm-stream var(--nm-motion-stream) both;
}

.nm-motion-expand {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows var(--nm-motion-expand);
}

.nm-motion-expand[data-open="true"] {
    grid-template-rows: 1fr;
}

.nm-motion-expand > * {
    min-block-size: 0;
    overflow: hidden;
}
