/* css/glian-design-system.css — FUENTE ÚNICA de los tokens de diseño de Glian.
 *
 * Referencia y reglas de uso: DESIGN_SYSTEM.md (raíz del monorepo). Léelo antes de añadir o
 * cambiar un token.
 *
 * Qué es este archivo HOY:
 *  · Variables en :root + clases de rol tipográfico (`.glian-*`), iconografía (`.g-icon`),
 *    botones (`.g-btn`, `.g-icon-btn`, `.g-segmented`) y estructura de página (`.g-page`,
 *    `.g-page-header`, `.g-tabs`, `.g-card`, `.g-field`, `.g-check`, `.g-notice`), al final. Ningún selector de elemento →
 *    cargarlo no cambia NADA de lo que se ve: solo cambia lo que use una de esas clases a
 *    propósito. Las pantallas siguen con sus valores escritos a mano hasta que se migren una
 *    por una.
 *  · Bloques de tema (DESIGN_SYSTEM.md §20): oscuro bajo html[data-theme="dark"], ámbito claro
 *    [data-theme="light"] para el papel dentro de una pantalla oscura, e impresión siempre clara. Sin
 *    ese atributo en <html> no aplican.
 *  · Los valores salen de la interfaz profesional ACTUAL (expedix.js `--exp-*`, los `:root` de
 *    index/pacientes/agenda y el `--sh` de Clinimetrix), no de los documentos de marca viejos.
 *  · Inter es la fuente de TODA la interfaz (oct-2026): las pantallas (`.g-page`), el armazón
 *    (menú lateral, barra superior) y el <body> de las páginas profesionales usan --glian-font-ui.
 *    Hanken Grotesk queda solo donde el contenido depende de ella: documentos e impresos y la
 *    vista previa del PDF de Recursos.
 *
 * Cómo llega a las páginas: `js/expedix.js` lo inyecta una sola vez (ensureDesignSystem), así
 * que lo tiene toda página que cargue el armazón — Expedix, Clinimetrix profesional y Recursos
 * profesional. Una página puede además enlazarlo en su <head> (recomendado al migrarla, para
 * que no haya destello); el inyector detecta el <link> y no lo duplica.
 *
 * Vive en la raíz de Expedix (/css/…) a propósito: el armado (scripts/unificar.mjs) deja sin
 * prefijo lo que un módulo anidado NO tiene y la raíz sí, así que /clinimetrix, /formx y
 * /recursos lo piden con la misma ruta. Ningún módulo debe crear su propia carpeta /css/.
 *
 * Nombres: siempre `--glian-<grupo>-<nombre>`. `.g-*` y `--glian-*` son SOLO de este sistema
 * (DESIGN_SYSTEM.md §2.1): un módulo usa su propio prefijo, y scripts/check-namespace.mjs lo revisa.
 */

/* Inter (fuente oficial), SERVIDA POR GLIAN: /fonts/inter/inter-latin-400-700.woff2, un solo archivo
 * variable 400–700 con los pesos de los tokens (css/glian-inter.css, DESIGN_SYSTEM.md §4.6). Ya no
 * se pide a Google: ninguna pantalla de la app depende de un proveedor de fuentes. La hoja pública
 * (scripts/unificar.mjs) quita este @import porque antepone glian-inter.css en línea.
 * Ojo: CSS exige que @import sea la PRIMERA regla del archivo. Un peso pedido de más (800/900,
 * que el CSS viejo usa mucho) se pinta con 700. */
@import url("/css/glian-inter.css");

:root {
  /* ── Marca ───────────────────────────────────────────────────────────────
   * primary  = el cian de Glian: rellenos, iconos, bordes de lo seleccionado, foco.
   * text     = el mismo cian, oscurecido para LETRA (enlaces, navegación activa): #0891b2
   *            sobre blanco da 3.68:1 y no alcanza AA; #0e7490 da 5.36:1.
   * strong   = el navy de Glian (final del degradado, superficies oscuras).
   * gradient = el botón principal tal como se ve hoy en todo Expedix. */
  --glian-brand-primary: #0891b2;
  --glian-brand-primary-hover: #0e7490;
  --glian-brand-primary-active: #155e75;
  --glian-brand-soft: #ecfeff;
  --glian-brand-soft-border: #a5f3fc;
  --glian-brand-text: #0e7490;
  --glian-brand-strong: #123b44;
  --glian-brand-gradient: linear-gradient(135deg, #0891b2, #123b44);

  /* ── Acción ──────────────────────────────────────────────────────────────────
   * brand-primary es IDENTIDAD (color de marca: iconos, bordes de lo seleccionado, foco).
   * action-primary es una SUPERFICIE INTERACTIVA con texto blanco encima: debe cumplir AA (≥4.5:1)
   * con --glian-text-inverse. Son alias de la escala de marca, no colores nuevos. */
  --glian-action-primary: var(--glian-brand-primary-hover);          /* #0e7490 · 5.36:1 con blanco */
  --glian-action-primary-hover: var(--glian-brand-primary-active);   /* #155e75 · 7.27:1 */
  --glian-action-primary-active: var(--glian-brand-strong);          /* #123b44 · 12.1:1 */
  /* Botón destructivo SÓLIDO (.g-btn--danger), con texto blanco encima. En claro es el mismo rojo de
   * siempre; en el tema oscuro --glian-danger se aclara para iconos y letra, y el relleno no puede
   * seguirlo (el blanco dejaría de leerse): por eso es un token propio. */
  --glian-action-danger: var(--glian-danger);                        /* #dc2626 · 4.83:1 con blanco */
  --glian-action-danger-hover: var(--glian-danger-text);             /* #b91c1c · 6.47:1 */

  /* ── Superficies ───────────────────────────────────────────────────────── */
  --glian-bg-page: #f6f8fa;
  --glian-bg-surface: #ffffff;
  --glian-bg-subtle: #f8fafc;
  --glian-bg-backdrop: rgba(15, 23, 42, .55);
  /* elevated = lo que FLOTA (modal, paleta de comandos, menú). En claro es la misma superficie blanca;
   * en el tema oscuro sube un paso para separarse de las tarjetas sin depender solo de la sombra. */
  --glian-bg-elevated: #ffffff;
  /* El aviso de toda la app (Exp.toast): en claro, navy con letra blanca (el de siempre); en el tema
   * oscuro, superficie elevada con letra clara. Nunca un aviso claro invertido sobre lo oscuro. */
  --glian-bg-toast: #123b44;
  /* paper = la hoja de un documento VISTA EN PANTALLA (vista previa dentro de la interfaz): blanca, como
   * sale, también con la pantalla en oscuro. Es igual en los dos temas: el oscuro no la redefine (§20.2).
   * El documento final, el PDF y la impresión son SIEMPRE blanco puro (§20). */
  --glian-bg-paper: #ffffff;

  /* ── Texto ─────────────────────────────────────────────────────────────────
   * tertiary sobre --glian-bg-page queda en 4.47:1: úsalo sobre superficie blanca.
   * muted (2.56:1) es SOLO para placeholder y deshabilitado, nunca para información. */
  --glian-text-primary: #123b44;
  --glian-text-secondary: #475569;
  --glian-text-tertiary: #64748b;
  --glian-text-muted: #94a3b8;
  --glian-text-inverse: #ffffff;
  --glian-text-link: var(--glian-brand-text);
  --glian-text-toast: #ffffff;

  /* ── Bordes ────────────────────────────────────────────────────────────── */
  --glian-border: #e2e8f0;
  --glian-border-strong: #cbd5e1;
  --glian-border-focus: var(--glian-brand-primary);
  /* Borde de un CONTROL (campo, casilla): lo que permite ver que ahí se escribe o se marca. WCAG 1.4.11
   * pide 3:1 contra lo que lo rodea; --glian-border-strong da 1.48:1 sobre blanco y no alcanza. Es el
   * gris neutro más claro que pasa en todas las superficies: 3.37:1 sobre surface, 3.22 sobre subtle,
   * 3.16 sobre bg-page y 3.05 sobre el #f0f4f8 heredado de Configuración. SOLO para controles:
   * tarjetas, tablas y separadores siguen con --glian-border. */
  --glian-control-border: #7f8da2;
  /* Filo de lo que flota (modal): en claro no existe —basta la sombra—; en el tema oscuro, donde la
   * sombra apenas se ve, es lo que separa el diálogo del fondo. */
  --glian-border-overlay: transparent;

  /* ── Estados semánticos ────────────────────────────────────────────────────
   * <estado>       relleno, icono, borde, punto de estado.
   * <estado>-soft  fondo de chip, aviso o fila.
   * <estado>-text  letra sobre <estado>-soft (todas ≥ 4.5:1). Donde el color base ya pasa,
   *                -text es el mismo color. */
  --glian-info: #0369a1;
  --glian-info-soft: #e0f2fe;
  --glian-info-text: #0369a1;

  --glian-success: #059669;
  --glian-success-soft: #ecfdf5;
  --glian-success-text: #047857;

  --glian-warning: #b45309;
  --glian-warning-soft: #fffbeb;
  --glian-warning-text: #b45309;

  --glian-danger: #dc2626;
  --glian-danger-soft: #fef2f2;
  --glian-danger-text: #b91c1c;

  --glian-neutral: #64748b;
  --glian-neutral-soft: #f1f5f9;
  --glian-neutral-text: #475569;

  /* ── Tipografía ────────────────────────────────────────────────────────────
   * --glian-font-ui (Inter) es la fuente OFICIAL; se carga arriba (@import, servida por Glian).
   * Mientras llega se ve el resto de la pila (Hanken solo si la página aún la tiene; si no, la del
   * sistema). */
  --glian-font-ui: "Inter", "Hanken Grotesk", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --glian-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  --glian-text-page-title: 26px;
  --glian-text-section-title: 18px;
  --glian-text-card-title: 15px;
  --glian-text-body: 14px;
  --glian-text-small: 13px;
  --glian-text-caption: 12px;
  --glian-text-button: 14px;
  /* EXCEPCIÓN, no un paso de la escala: SOLO metadata secundaria (hora, contador) dentro de un
   * elemento .glian-dense. Prohibido para texto principal, etiquetas y botones. Existe porque la
   * Agenda semanal lo demostró midiendo (DESIGN_SYSTEM.md §4.4). */
  --glian-text-dense-meta: 11px;

  --glian-weight-regular: 400;
  --glian-weight-medium: 500;
  --glian-weight-semibold: 600;
  --glian-weight-bold: 700;

  --glian-leading-tight: 1.25;
  --glian-leading-normal: 1.5;
  --glian-leading-dense: 1.2;   /* solo para el rol .glian-dense */

  /* Tracking de títulos. Inter está dibujada para texto de ~14px: a 18–26px se ve suelta si no
   * se cierra un poco (es la curva que recomienda su autor: ≈ −0.02em a 26px, −0.01em a 15–18). */
  --glian-tracking-tight: -0.02em;
  --glian-tracking-snug: -0.01em;

  /* ── Espaciado (base 4px) ──────────────────────────────────────────────── */
  --glian-space-1: 4px;
  --glian-space-2: 8px;
  --glian-space-3: 12px;
  --glian-space-4: 16px;
  --glian-space-5: 20px;
  --glian-space-6: 24px;
  --glian-space-8: 32px;
  --glian-space-10: 40px;
  --glian-space-12: 48px;

  /* ── Radios ────────────────────────────────────────────────────────────── */
  --glian-radius-sm: 6px;
  --glian-radius-md: 10px;
  --glian-radius-lg: 14px;
  --glian-radius-xl: 18px;
  --glian-radius-pill: 999px;

  /* ── Sombras ───────────────────────────────────────────────────────────────
   * sm      superficies apenas elevadas (tarjetas). Es el `--sh` de Clinimetrix/index.
   * md      lo que flota sobre el contenido (menú desplegable, popover).
   * overlay modal, panel lateral, paleta de comandos. Es la sombra de modal más repetida. */
  --glian-shadow-none: none;
  --glian-shadow-sm: 0 1px 2px rgba(18, 59, 68, .05), 0 4px 14px rgba(18, 59, 68, .05);
  --glian-shadow-md: 0 8px 24px rgba(18, 59, 68, .14);
  --glian-shadow-overlay: 0 20px 60px rgba(0, 0, 0, .30);

  /* Foco: el halo de los campos va JUNTO con `border-color: var(--glian-border-focus)`
   * (solo el halo, a .12, no se distingue). Botones y enlaces usan el contorno. */
  --glian-focus-ring: 0 0 0 3px rgba(8, 145, 178, .12);
  --glian-focus-outline: 2px solid var(--glian-brand-primary);

  /* ── Barras de desplazamiento ─────────────────────────────────────────────────
   * Las que Glian dibuja a mano (la tabla que se desliza de lado, .exp-tsc): pista, pulgar y pulgar
   * bajo el cursor. Son los valores que hoy escribe el armazón; aún no tienen consumidor en esta hoja. */
  --glian-scroll-track: #f1f5f9;
  --glian-scroll-thumb: #cbd5e1;
  --glian-scroll-thumb-hover: #94a3b8;

  /* ── Gráficas: serie categórica (DESIGN_SYSTEM.md §20) ──────────────────────────
   * Ocho colores en orden de uso. En claro son los que ya usa la ficha (antropometría); el tema oscuro
   * tiene su propia serie, calibrada para la superficie oscura. Con muchas series el color NO basta:
   * el consumidor añade marcador, trazo o patrón. Aún no hay consumidores migrados. */
  --glian-chart-1: #0e7490;
  --glian-chart-2: #b45309;
  --glian-chart-3: #7c3aed;
  --glian-chart-4: #be185d;
  --glian-chart-5: #047857;
  --glian-chart-6: #b91c1c;
  --glian-chart-7: #1d4ed8;
  --glian-chart-8: #a16207;

  /* Acentos de CATEGORÍA (modo oscuro PR 3): índigo, violeta y rosa, para lo que ya se pintaba con
   * esos colores fuera de los estados (distintivos de Pacientes, paleta de etiquetas). Misma forma que
   * los estados y la marca: base · soft · soft-border · text. En claro, los hexadecimales de siempre;
   * en oscuro, candidatos calibrados para la superficie oscura (revisión R3 + check-contraste). */
  --glian-indigo: #4338ca;
  --glian-indigo-soft: #eef2ff;
  --glian-indigo-soft-border: #c7d2fe;
  --glian-indigo-text: #4338ca;
  --glian-violet: #7c3aed;
  --glian-violet-soft: #f5f3ff;
  --glian-violet-soft-border: #ddd6fe;
  --glian-violet-text: #7c3aed;
  --glian-pink: #9d174d;
  --glian-pink-soft: #fdf2f8;
  --glian-pink-soft-border: #fbcfe8;
  --glian-pink-text: #9d174d;

  /* ── Iconografía (DESIGN_SYSTEM.md §10) ──────────────────────────────────────
   * Geometría: css/glian-icons.svg. Tamaño: estos tokens vía .g-icon--*. Color: currentColor.
   * xs (12px) es SOLO para superficies densas (Agenda): no es un tamaño normal de controles. */
  --glian-icon-xs: 12px;
  --glian-icon-sm: 16px;
  --glian-icon-md: 18px;
  --glian-icon-lg: 20px;
  --glian-icon-xl: 24px;
  --glian-icon-stroke: 2;

  /* ── Controles (DESIGN_SYSTEM.md §11) ────────────────────────────────────────
   * Alto de todo control interactivo: botones, botones de icono, controles segmentados (y, más
   * adelante, campos). SOLO dos tamaños. 36px no existe en la escala de espaciado; por eso estos
   * dos tokens propios, que comparten todos los controles para alinearse en una misma fila. */
  --glian-control-sm: 36px;
  --glian-control-md: 40px;
  /* Ancho de la paleta de comandos en escritorio (§15); en un teléfono manda el ancho de la pantalla. */
  --glian-command-width: 600px;

  /* ── Página (DESIGN_SYSTEM.md §19) ───────────────────────────────────────────
   * Ancho máximo de .g-page, con su relleno incluido. Dos medidas y no más: las pantallas usaban 19
   * anchos distintos, pero se agrupan en dos familias. page-width (1400) es el de 28 pantallas de
   * datos (Agenda, Pacientes, Clinimetrix); page-width-narrow (960) es el de formularios y
   * configuración, que hoy viven entre 720 y 980. */
  --glian-page-width: 1400px;
  --glian-page-width-narrow: 960px;

  /* ── Armazón: barra superior global (DESIGN_SYSTEM.md §16) ───────────────────
   * Alto de la barra (48px, el de la escala) y ancho del disparador de búsqueda en escritorio. */
  --glian-topbar-h: var(--glian-space-12);
  --glian-topbar-search-w: 360px;
  /* Desde dónde se pega lo «sticky» de una página. 0 aquí y el alto de la barra solo cuando la
   * página tiene barra (html.has-topbar, abajo): una página de documento o una vista embebida
   * en un marco no tiene barra, y sus sticky deben quedarse arriba del todo. */
  --glian-sticky-top: 0px;

  /* ── Superficies públicas (DESIGN_SYSTEM.md §18) ───────────────────────────────
   * Lo que abre un paciente (agenda pública, recursos). Una sola columna, del mismo ancho en todas;
   * zona táctil mínima de 44px (no es un tercer tamaño de botón profesional: solo existe aquí) y el
   * botón principal a 48px. El texto corrido va a 15px (el paso card-title de la escala): se lee en
   * un teléfono. Los campos a 16px: con menos, Safari en iPhone hace zoom al enfocarlos. */
  --glian-public-width: 480px;
  --glian-public-touch: 44px;
  --glian-public-cta-h: 48px;
  --glian-public-text: var(--glian-text-card-title);
  --glian-public-input: 16px;

  /* Escalas de Clinimetrix (/css/clx-escala.css): la columna de lectura de una escala (un ítem por
   * tarjeta) y el tamaño de la pregunta, que se lee de corrido y a veces en una tablet a un brazo. */
  --glian-scale-width: 720px;
  --glian-scale-question: 20px;

  /* ── Movimiento ────────────────────────────────────────────────────────── */
  --glian-duration-fast: 120ms;
  --glian-duration-normal: 180ms;
  --glian-ease-standard: cubic-bezier(.2, 0, 0, 1);
  --glian-ease-enter: cubic-bezier(0, 0, .2, 1);
  --glian-ease-exit: cubic-bezier(.4, 0, 1, 1);

  /* ── Capas (z-index) ───────────────────────────────────────────────────────
   * Orden: base < sticky < armazón < dropdown < popover < modal < toast < command.
   * Los valores conviven con las capas viejas que siguen en pie (ver DESIGN_SYSTEM.md):
   *  · sticky (30) queda DEBAJO del menú lateral (45–80): en móvil el menú abierto lo cubre.
   *  · modal (1500) queda ENCIMA de los modales viejos (1000–1400): un diálogo nuevo abierto
   *    desde uno viejo no queda escondido.
   *  · toast (6000) iguala a Exp.toast: el aviso se ve aunque haya un modal abierto. */
  --glian-z-base: 0;
  --glian-z-sticky: 30;
  --glian-z-shell: 50;
  --glian-z-dropdown: 300;
  --glian-z-popover: 400;
  --glian-z-modal-backdrop: 1500;
  --glian-z-modal: 1510;
  --glian-z-toast: 6000;
  --glian-z-command: 7000;
  --glian-z-dialogo: 10000;   /* Glian.confirmar / avisar / pedir: encima de todo */
}

/* Con barra superior —expedix.js pone html.has-topbar ANTES del primer pintado (§16)— lo sticky
 * de la página empieza debajo de ella: `top: calc(var(--glian-sticky-top) + Npx)`. */
html.has-topbar { --glian-sticky-top: var(--glian-topbar-h); scroll-padding-top: var(--glian-sticky-top); }
html.has-topbar body { padding-top: var(--glian-topbar-h); }

/* Movimiento reducido: los componentes que usen los tokens de duración se vuelven instantáneos.
 * No afecta a nada que hoy escriba sus duraciones a mano. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --glian-duration-fast: 0ms;
    --glian-duration-normal: 0ms;
  }
}

/* ── Tema oscuro (DESIGN_SYSTEM.md §20) ──────────────────────────────────────────────────────────
 * Solo se aplica con html[data-theme="dark"], que pone el motor del tema (js/glian-tema-arranque.js) en
 * las páginas profesionales que lo cargan. Nunca con una media query prefers-color-scheme: esta hoja
 * también viaja a las caras públicas (glian-publico.css) y la preferencia del profesional no debe
 * llegarle al paciente. Sin el atributo —toda página hoy— nada de esto aplica y el claro es el de
 * siempre. scripts/check-tema.mjs revisa que estos bloques estén completos y que el claro coincida.
 * Lo que no cambia entre temas: --glian-brand-strong, --glian-brand-gradient y --glian-text-inverse
 * (texto sobre rellenos fuertes). Los alias que siguen solos: --glian-text-link, --glian-border-focus
 * y --glian-focus-outline. */
html[data-theme="dark"] {
  color-scheme: dark;
  background-color: var(--glian-bg-page);

  --glian-brand-primary: #22b0cf;
  --glian-brand-primary-hover: #3bbfdb;
  --glian-brand-primary-active: #59cbe3;
  --glian-brand-soft: #10303a;
  --glian-brand-soft-border: #1d5766;
  --glian-brand-text: #67d1e6;

  /* El relleno de acción NO sigue a la marca: se queda en un cian medio que deja leer el blanco. */
  --glian-action-primary: #0e7490;          /* 5.36:1 con blanco */
  --glian-action-primary-hover: #0b6a84;    /* 6.16:1 */
  --glian-action-primary-active: #095a70;   /* 7.75:1 */
  --glian-action-danger: #c62828;           /* 5.62:1 */
  --glian-action-danger-hover: #b71c1c;     /* 6.57:1 */

  --glian-bg-page: #0e1417;
  --glian-bg-surface: #141c20;
  --glian-bg-subtle: #192327;
  --glian-bg-backdrop: rgba(4, 9, 11, .72);
  --glian-bg-elevated: #1c272c;
  --glian-bg-toast: #1c272c;

  --glian-text-primary: #d5e0e4;
  --glian-text-secondary: #b4c2c8;
  --glian-text-tertiary: #91a3aa;
  --glian-text-muted: #6b7e86;
  --glian-text-toast: #d5e0e4;

  --glian-border: #263237;
  --glian-border-strong: #34444b;
  --glian-control-border: #6e838b;
  --glian-border-overlay: rgba(255, 255, 255, .07);

  --glian-info: #5b9cf0;
  --glian-info-soft: #102340;
  --glian-info-text: #a9c8f7;

  --glian-success: #2fbf8a;
  --glian-success-soft: #0c2a20;
  --glian-success-text: #79dcb5;

  --glian-warning: #e0a43a;
  --glian-warning-soft: #2e220c;
  --glian-warning-text: #f2c877;

  --glian-danger: #f0716f;
  --glian-danger-soft: #341416;
  --glian-danger-text: #f6a3a1;

  --glian-neutral: #8a9ba2;
  --glian-neutral-soft: #1e292e;
  --glian-neutral-text: #c3cfd4;

  /* En oscuro la sombra casi no se ve: más densa, y lo que flota lleva además filo y superficie propia. */
  --glian-shadow-sm: 0 1px 2px rgba(0, 0, 0, .40), 0 4px 14px rgba(0, 0, 0, .22);
  --glian-shadow-md: 0 8px 24px rgba(0, 0, 0, .45);
  --glian-shadow-overlay: 0 20px 60px rgba(0, 0, 0, .60);
  --glian-focus-ring: 0 0 0 3px rgba(34, 176, 207, .28);

  --glian-scroll-track: #182226;
  --glian-scroll-thumb: #3b4a51;
  --glian-scroll-thumb-hover: #52646c;

  --glian-chart-1: #22b0cf;
  --glian-chart-2: #f2a03d;
  --glian-chart-3: #a78bfa;
  --glian-chart-4: #e879f9;
  --glian-chart-5: #34d399;
  --glian-chart-6: #f87171;
  --glian-chart-7: #60a5fa;
  --glian-chart-8: #fde047;
  --glian-indigo: #8b93f8;
  --glian-indigo-soft: #1c1f45;
  --glian-indigo-soft-border: #343a7c;
  --glian-indigo-text: #b7bcfb;
  --glian-violet: #a78bfa;
  --glian-violet-soft: #251a40;
  --glian-violet-soft-border: #46317e;
  --glian-violet-text: #cbb7fc;
  --glian-pink: #f17bb5;
  --glian-pink-soft: #33152a;
  --glian-pink-soft-border: #6a2a53;
  --glian-pink-text: #f5a9cf;
}
html[data-theme="light"] { color-scheme: light; }

/* ── Ámbito claro: un subárbol que vuelve a los colores claros (DESIGN_SYSTEM.md §20) ────────────────
 * Para lo que es PAPEL dentro de una pantalla oscura (la vista previa de una receta, un certificado):
 * <div data-theme="light">. Repite EXACTAMENTE los colores claros de :root —también los alias, para que
 * se recalculen en el subárbol— y check-tema.mjs falla si alguno difiere. Lo que no cambia entre temas
 * (el papel incluido) no se repite. Con la página en claro, este bloque no cambia ningún valor. */
[data-theme="light"] {
  --glian-brand-primary: #0891b2;
  --glian-brand-primary-hover: #0e7490;
  --glian-brand-primary-active: #155e75;
  --glian-brand-soft: #ecfeff;
  --glian-brand-soft-border: #a5f3fc;
  --glian-brand-text: #0e7490;
  --glian-action-primary: var(--glian-brand-primary-hover);
  --glian-action-primary-hover: var(--glian-brand-primary-active);
  --glian-action-primary-active: var(--glian-brand-strong);
  --glian-action-danger: var(--glian-danger);
  --glian-action-danger-hover: var(--glian-danger-text);
  --glian-bg-page: #f6f8fa;
  --glian-bg-surface: #ffffff;
  --glian-bg-subtle: #f8fafc;
  --glian-bg-backdrop: rgba(15, 23, 42, .55);
  --glian-bg-elevated: #ffffff;
  --glian-bg-toast: #123b44;
  --glian-text-primary: #123b44;
  --glian-text-secondary: #475569;
  --glian-text-tertiary: #64748b;
  --glian-text-muted: #94a3b8;
  --glian-text-link: var(--glian-brand-text);
  --glian-text-toast: #ffffff;
  --glian-border: #e2e8f0;
  --glian-border-strong: #cbd5e1;
  --glian-border-focus: var(--glian-brand-primary);
  --glian-control-border: #7f8da2;
  --glian-border-overlay: transparent;
  --glian-info: #0369a1;
  --glian-info-soft: #e0f2fe;
  --glian-info-text: #0369a1;
  --glian-success: #059669;
  --glian-success-soft: #ecfdf5;
  --glian-success-text: #047857;
  --glian-warning: #b45309;
  --glian-warning-soft: #fffbeb;
  --glian-warning-text: #b45309;
  --glian-danger: #dc2626;
  --glian-danger-soft: #fef2f2;
  --glian-danger-text: #b91c1c;
  --glian-neutral: #64748b;
  --glian-neutral-soft: #f1f5f9;
  --glian-neutral-text: #475569;
  --glian-shadow-sm: 0 1px 2px rgba(18, 59, 68, .05), 0 4px 14px rgba(18, 59, 68, .05);
  --glian-shadow-md: 0 8px 24px rgba(18, 59, 68, .14);
  --glian-shadow-overlay: 0 20px 60px rgba(0, 0, 0, .30);
  --glian-focus-ring: 0 0 0 3px rgba(8, 145, 178, .12);
  --glian-focus-outline: 2px solid var(--glian-brand-primary);
  --glian-scroll-track: #f1f5f9;
  --glian-scroll-thumb: #cbd5e1;
  --glian-scroll-thumb-hover: #94a3b8;
  --glian-chart-1: #0e7490;
  --glian-chart-2: #b45309;
  --glian-chart-3: #7c3aed;
  --glian-chart-4: #be185d;
  --glian-chart-5: #047857;
  --glian-chart-6: #b91c1c;
  --glian-chart-7: #1d4ed8;
  --glian-chart-8: #a16207;
  --glian-indigo: #4338ca;
  --glian-indigo-soft: #eef2ff;
  --glian-indigo-soft-border: #c7d2fe;
  --glian-indigo-text: #4338ca;
  --glian-violet: #7c3aed;
  --glian-violet-soft: #f5f3ff;
  --glian-violet-soft-border: #ddd6fe;
  --glian-violet-text: #7c3aed;
  --glian-pink: #9d174d;
  --glian-pink-soft: #fdf2f8;
  --glian-pink-soft-border: #fbcfe8;
  --glian-pink-text: #9d174d;
}
html[data-theme="dark"] [data-theme="light"] { color-scheme: light; }

/* ── Impresión: SIEMPRE clara, aunque la pantalla esté en oscuro (DESIGN_SYSTEM.md §20) ──────────────
 * Mismos valores que el ámbito claro, más el papel en blanco puro. Sin data-theme en <html> —toda
 * página hoy— no aplica: la impresión actual no cambia. Los dos `!important` son los ÚNICOS del tema y
 * tienen razón: el arranque escribe `color-scheme` y el fondo EN LÍNEA sobre <html> (para el primer
 * pintado), y una regla sin `!important` no le gana a un estilo en línea. */
@media print {
  html[data-theme],
  html[data-theme] [data-theme] {
    color-scheme: light !important;
    --glian-brand-primary: #0891b2;
    --glian-brand-primary-hover: #0e7490;
    --glian-brand-primary-active: #155e75;
    --glian-brand-soft: #ecfeff;
    --glian-brand-soft-border: #a5f3fc;
    --glian-brand-text: #0e7490;
    --glian-action-primary: var(--glian-brand-primary-hover);
    --glian-action-primary-hover: var(--glian-brand-primary-active);
    --glian-action-primary-active: var(--glian-brand-strong);
    --glian-action-danger: var(--glian-danger);
    --glian-action-danger-hover: var(--glian-danger-text);
    --glian-bg-page: #f6f8fa;
    --glian-bg-surface: #ffffff;
    --glian-bg-subtle: #f8fafc;
    --glian-bg-backdrop: rgba(15, 23, 42, .55);
    --glian-bg-elevated: #ffffff;
    --glian-bg-toast: #123b44;
    --glian-bg-paper: #ffffff;
    --glian-text-primary: #123b44;
    --glian-text-secondary: #475569;
    --glian-text-tertiary: #64748b;
    --glian-text-muted: #94a3b8;
    --glian-text-link: var(--glian-brand-text);
    --glian-text-toast: #ffffff;
    --glian-border: #e2e8f0;
    --glian-border-strong: #cbd5e1;
    --glian-border-focus: var(--glian-brand-primary);
    --glian-control-border: #7f8da2;
    --glian-border-overlay: transparent;
    --glian-info: #0369a1;
    --glian-info-soft: #e0f2fe;
    --glian-info-text: #0369a1;
    --glian-success: #059669;
    --glian-success-soft: #ecfdf5;
    --glian-success-text: #047857;
    --glian-warning: #b45309;
    --glian-warning-soft: #fffbeb;
    --glian-warning-text: #b45309;
    --glian-danger: #dc2626;
    --glian-danger-soft: #fef2f2;
    --glian-danger-text: #b91c1c;
    --glian-neutral: #64748b;
    --glian-neutral-soft: #f1f5f9;
    --glian-neutral-text: #475569;
    --glian-shadow-sm: 0 1px 2px rgba(18, 59, 68, .05), 0 4px 14px rgba(18, 59, 68, .05);
    --glian-shadow-md: 0 8px 24px rgba(18, 59, 68, .14);
    --glian-shadow-overlay: 0 20px 60px rgba(0, 0, 0, .30);
    --glian-focus-ring: 0 0 0 3px rgba(8, 145, 178, .12);
    --glian-focus-outline: 2px solid var(--glian-brand-primary);
    --glian-scroll-track: #f1f5f9;
    --glian-scroll-thumb: #cbd5e1;
    --glian-scroll-thumb-hover: #94a3b8;
    --glian-chart-1: #0e7490;
    --glian-chart-2: #b45309;
    --glian-chart-3: #7c3aed;
    --glian-chart-4: #be185d;
    --glian-chart-5: #047857;
    --glian-chart-6: #b91c1c;
    --glian-chart-7: #1d4ed8;
    --glian-chart-8: #a16207;
    --glian-indigo: #4338ca;
    --glian-indigo-soft: #eef2ff;
    --glian-indigo-soft-border: #c7d2fe;
    --glian-indigo-text: #4338ca;
    --glian-violet: #7c3aed;
    --glian-violet-soft: #f5f3ff;
    --glian-violet-soft-border: #ddd6fe;
    --glian-violet-text: #7c3aed;
    --glian-pink: #9d174d;
    --glian-pink-soft: #fdf2f8;
    --glian-pink-soft-border: #fbcfe8;
    --glian-pink-text: #9d174d;
  }
  html[data-theme] { background-color: var(--glian-bg-paper) !important; }
}

/* ── Tipografía: fuente y roles ───────────────────────────────────────────────────────────────
 * .glian-font-ui pone Inter a un CONTENEDOR; todo lo de adentro la hereda. Desde el pase global
 * (oct-2026) el <body> de las páginas profesionales y el armazón ya usan --glian-font-ui, así que
 * hace falta solo donde un contenedor vive dentro de algo que todavía fija otra fuente.
 *
 * Los roles fijan SOLO tamaño, peso, interlineado y tracking:
 *  · no fijan la fuente (la pone el contenedor), así un rol en una pantalla sin migrar no mezcla
 *    dos fuentes;
 *  · no fijan color (lo pone el componente con --glian-text-*), así aplicar un rol nunca cambia
 *    el color de un texto.
 * Una regla vieja de la página con la misma especificidad gana (este archivo va antes): al
 * aplicar un rol, quita font-size/font-weight de la regla vieja de ese elemento.
 *
 *   rol              tamaño  peso  uso
 *   page-title       26      600   título de la pantalla (uno por pantalla)
 *   page-subtitle    14      400   la línea que lo explica, debajo del título
 *   section-title    18      600   título de sección
 *   card-title       15      600   título de tarjeta, panel o modal
 *   body             14      400   texto de lectura
 *   small            13      400   apoyo, metadatos
 *   caption          12      400   lo más pequeño permitido
 *   label            13      500   etiqueta de campo o de grupo
 *   dense            12      600   SOLO superficies de alta densidad (ver abajo)
 *   dense-meta       11      400   SOLO metadata secundaria dentro de lo denso (token, sin clase)
 *   (botones: 14/600 con --glian-text-button; llegan con el componente de botón) */
.glian-font-ui { font-family: var(--glian-font-ui); }

.glian-page-title,
.g-page-header__title {
  font-size: var(--glian-text-page-title);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  letter-spacing: var(--glian-tracking-tight);
}
.glian-page-subtitle,
.g-page-header__subtitle {
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-regular);
  line-height: var(--glian-leading-normal);
}
.glian-section-title {
  font-size: var(--glian-text-section-title);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  letter-spacing: var(--glian-tracking-snug);
}
.glian-card-title,
.g-card__title {
  font-size: var(--glian-text-card-title);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  letter-spacing: var(--glian-tracking-snug);
}
.glian-body {
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-regular);
  line-height: var(--glian-leading-normal);
}
.glian-small {
  font-size: var(--glian-text-small);
  font-weight: var(--glian-weight-regular);
  line-height: var(--glian-leading-normal);
}
.glian-caption {
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-regular);
  line-height: var(--glian-leading-normal);
}
.glian-label,
.g-field__label {
  font-size: var(--glian-text-small);
  font-weight: var(--glian-weight-medium);
  line-height: var(--glian-leading-tight);
}

/* Rol DENSO: citas de calendario, tablas muy densas, metadata operacional compacta. NO es el
 * estilo normal del sistema: úsalo solo donde el espacio es fijo y cada carácter cuenta.
 * Inter es más ancha que Hanken (≈ 6–10 % en nombres, 12–14 % en dígitos a 12.5/700); con
 * 12/600 y −0.01em recupera casi todo ese ancho sin cambiar el tamaño del contenedor.
 * Dentro de un elemento denso, el dato principal (p. ej. el nombre del paciente) conserva
 * semibold; los símbolos bajan a --glian-weight-medium, y la metadata secundaria (la hora) usa
 * la excepción --glian-text-dense-meta (11px) con --glian-weight-regular. Sin 700 dentro de lo
 * denso. */
.glian-dense {
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-dense);
  letter-spacing: var(--glian-tracking-snug);
}

/* ── Iconografía (DESIGN_SYSTEM.md §10) ───────────────────────────────────────────────────────
 * Un icono = <svg class="g-icon"><use href="/css/glian-icons.svg#nombre"/></svg>, o Glian.icon().
 * El sprite solo trae geometría: trazo, relleno y color se ponen AQUÍ y llegan a la geometría por
 * herencia (el árbol de <use> hereda del <svg>). No lleva color propio: toma el del texto. */
.g-icon {
  width: var(--glian-icon-md);
  height: var(--glian-icon-md);
  fill: none;
  stroke: currentColor;
  stroke-width: var(--glian-icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
  vertical-align: middle;
  overflow: visible;
}
.g-icon--xs { width: var(--glian-icon-xs); height: var(--glian-icon-xs); }   /* solo superficies densas */
.g-icon--sm { width: var(--glian-icon-sm); height: var(--glian-icon-sm); }   /* acciones compactas, tablas */
.g-icon--md { width: var(--glian-icon-md); height: var(--glian-icon-md); }   /* botones (por omisión) */
.g-icon--lg { width: var(--glian-icon-lg); height: var(--glian-icon-lg); }   /* navegación, menú lateral */
.g-icon--xl { width: var(--glian-icon-xl); height: var(--glian-icon-xl); }   /* indicadores destacados */

/* ── Botones (DESIGN_SYSTEM.md §11) ───────────────────────────────────────────────────────────
 * UN solo sistema: .g-btn (con texto), .g-icon-btn (solo icono) y .g-segmented (opción excluyente).
 * Comparten alto (--glian-control-sm/md), radio, transiciones, foco, deshabilitado y la regla de
 * no usar sombras. Variante obligatoria en .g-btn: --primary, --secondary, --ghost, --danger o
 * --danger-secondary.
 * Tamaños: --sm (36px) y --md (40px, por omisión). El icono lo dimensiona el BOTÓN: 16px en sm y
 * 18px en md, aunque Glian.icon() se haya llamado con otro tamaño. */
.g-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--glian-space-2);
  height: var(--glian-control-md);
  padding: 0 var(--glian-space-4);
  margin: 0;
  border: 1px solid transparent;
  border-radius: var(--glian-radius-md);
  background: transparent;
  color: var(--glian-text-primary);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-button);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  white-space: nowrap;
  text-decoration: none;
  vertical-align: middle;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.g-btn .g-icon { width: var(--glian-icon-md); height: var(--glian-icon-md); }
.g-btn--md { height: var(--glian-control-md); padding: 0 var(--glian-space-4); }
.g-btn--sm { height: var(--glian-control-sm); padding: 0 var(--glian-space-3); }
.g-btn--sm .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); }

/* Contador dentro de un botón (icono + texto + contador: «Espera 17») o de una pestaña (§19.3).
 * Solo forma y alineación; los colores con significado llegan con el sistema de contadores. */
.g-btn__badge,
.g-tabs__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--glian-space-5);
  height: var(--glian-space-5);
  padding: 0 var(--glian-space-1);
  border-radius: var(--glian-radius-pill);
  background: var(--glian-neutral-soft);
  color: var(--glian-text-secondary);
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-semibold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* `hidden` gana al display propio de las primitivas (un contador en 0, un aviso o una barra que no
 * aplican todavía): sin esto el atributo no ocultaba nada. */
.g-btn__badge[hidden],
.g-tabs__count[hidden],
.g-notice[hidden],
.g-toolbar[hidden],
.g-toolbar__group[hidden],
.g-state__actions[hidden],
.g-segmented[hidden],
.g-status[hidden],
.g-btn[hidden],
.g-icon-btn[hidden],
.g-field[hidden],
.g-check[hidden],
.g-card[hidden] { display: none; }

/* Primary: la acción dominante del contexto (idealmente UNA por pantalla, modal o tarjeta).
 * Usa --glian-action-primary*, no --glian-brand-primary: el blanco sobre #0891b2 da 3.68:1 (no
 * alcanza AA a 14px); la superficie de acción sí cumple. */
.g-btn--primary { background: var(--glian-action-primary); color: var(--glian-text-inverse); }
.g-btn--primary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-action-primary-hover); }
.g-btn--primary:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-action-primary-active); }

/* Secondary: importante pero subordinada a la primaria. */
.g-btn--secondary { background: var(--glian-bg-surface); border-color: var(--glian-border); color: var(--glian-text-primary); }
.g-btn--secondary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-bg-subtle); border-color: var(--glian-border-strong); }
.g-btn--secondary:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-neutral-soft); border-color: var(--glian-border-strong); }

/* Ghost: acciones frecuentes de menor jerarquía. Sin borde ni fondo hasta el hover. */
.g-btn--ghost { color: var(--glian-text-secondary); }
.g-btn--ghost:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-neutral-soft); color: var(--glian-text-primary); }
.g-btn--ghost:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-border); color: var(--glian-text-primary); }

/* Seleccionado (aria-pressed="true"): un modo, filtro o vista que SIGUE activo («ver canceladas»).
 * No es una variante: es un estado de Ghost y Secondary, con el lenguaje de selección del sistema
 * (el de la opción activa de .g-segmented). Hover y presionado duran lo que el puntero; esto dura
 * hasta que se vuelve a tocar. Encima del seleccionado, el hover solo marca el borde: el botón no
 * deja de verse seleccionado ni salta al gris del hover normal. Nunca Primary, Danger ni sombra. */
.g-btn--ghost[aria-pressed="true"],
.g-btn--secondary[aria-pressed="true"] { background: var(--glian-brand-soft); border-color: var(--glian-brand-soft-border); color: var(--glian-brand-text); }
.g-btn--ghost[aria-pressed="true"]:hover:not(:disabled):not([aria-disabled="true"]),
.g-btn--ghost[aria-pressed="true"]:active:not(:disabled):not([aria-disabled="true"]),
.g-btn--secondary[aria-pressed="true"]:hover:not(:disabled):not([aria-disabled="true"]),
.g-btn--secondary[aria-pressed="true"]:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-brand-soft); border-color: var(--glian-brand-primary); color: var(--glian-brand-text); }

/* Danger: SOLO acciones destructivas o graves (borrar, anular). Nunca para «canceladas»,
 * pendientes, cantidades ni navegación. Solo tokens de peligro: --glian-danger (4.83:1 con blanco)
 * y, en hover y presionado, --glian-danger-text (5.91:1). No hay un tercer tono: comparten. */
.g-btn--danger { background: var(--glian-action-danger); color: var(--glian-text-inverse); }
.g-btn--danger:hover:not(:disabled):not([aria-disabled="true"]),
.g-btn--danger:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-action-danger-hover); }

/* Danger secondary: destructiva de énfasis bajo o medio (pidió la primera: «Quitar consultorio»).
 * Quita algo de un formulario que todavía se guarda o se descarta después, o abre la confirmación
 * de algo destructivo. El botón que borra o confirma YA sigue siendo Danger sólido. Superficie,
 * borde --glian-danger (4.83:1) y texto --glian-danger-text (6.47:1; 5.91:1 sobre el hover). Solo
 * tokens de peligro: el hover pone --glian-danger-soft y el presionado oscurece además el borde. */
.g-btn--danger-secondary { background: var(--glian-bg-surface); border-color: var(--glian-danger); color: var(--glian-danger-text); }
.g-btn--danger-secondary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-danger-soft); }
.g-btn--danger-secondary:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-danger-soft); border-color: var(--glian-danger-text); }

/* Preparado para «cargando» (sin implementar todavía): con aria-busy="true" el botón no admite
 * otro clic y conserva su ancho; el indicador visual llega después. */
.g-btn[aria-busy="true"] { cursor: progress; pointer-events: none; }

/* Botón que SOLO tiene icono. Lleva aria-label (el nombre va en el botón, no en el SVG) y, si la
 * acción no es evidente, title. Mismo alto que .g-btn (36px; --md 40px) para alinearse en una
 * fila, icono de 18px, y los estados de .g-btn--ghost. Su zona táctil llega 4px más allá por
 * cada lado (44/48px) con un ::after invisible que no ocupa sitio en el layout: deja al menos
 * --glian-space-2 entre dos de estos botones. Sin color propio: toma el del texto del contexto.
 * (El botón de contraer del menú lateral fija su propio tamaño de 32px.) */
.g-icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--glian-control-sm);
  height: var(--glian-control-sm);
  padding: 0;
  margin: 0;
  border: 1px solid transparent;
  border-radius: var(--glian-radius-md);
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.g-icon-btn--md { width: var(--glian-control-md); height: var(--glian-control-md); }
.g-icon-btn .g-icon { width: var(--glian-icon-md); height: var(--glian-icon-md); }
.g-icon-btn::after {
  content: "";
  position: absolute;
  inset: calc(var(--glian-space-1) * -1 - 1px);
}
.g-icon-btn:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-neutral-soft); }
.g-icon-btn:active:not(:disabled):not([aria-disabled="true"]) { background: var(--glian-border); }

/* Control segmentado: UNA opción activa entre varias excluyentes («Mes | Semana | Día»). No son
 * botones primarios: pista neutra; la activa con fondo suave de marca, borde de marca y texto de
 * marca (forma Y color, no solo color). Marca la activa con aria-pressed="true" (botones) o
 * aria-checked="true" (role="radio"); .is-active también vale. */
.g-segmented {
  display: inline-flex;
  align-items: stretch;
  height: var(--glian-control-sm);
  padding: var(--glian-space-1);
  border: 1px solid var(--glian-border);
  border-radius: var(--glian-radius-md);
  background: var(--glian-neutral-soft);
  vertical-align: middle;
}
.g-segmented--md { height: var(--glian-control-md); }
.g-segmented__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--glian-space-1);
  padding: 0 var(--glian-space-3);
  margin: 0;
  border: 1px solid transparent;
  border-radius: var(--glian-radius-sm);
  background: transparent;
  color: var(--glian-text-secondary);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-button);
  font-weight: var(--glian-weight-medium);
  line-height: var(--glian-leading-tight);
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.g-segmented__item .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); }
.g-segmented__item:hover:not(:disabled):not([aria-pressed="true"]):not([aria-checked="true"]):not(.is-active) { background: var(--glian-border); color: var(--glian-text-primary); }
.g-segmented__item[aria-pressed="true"],
.g-segmented__item[aria-checked="true"],
.g-segmented__item.is-active { background: var(--glian-brand-soft); border-color: var(--glian-brand-soft-border); color: var(--glian-brand-text); }

/* Comunes a TODO el sistema de botones: medida, transición, foco visible y deshabilitado.
 * border-box explícito: el alto debe ser 36/40px en cualquier página, aunque no fije box-sizing
 * (un <a> o un <div> miden content-box por omisión y el borde y el relleno se sumarían). */
.g-btn, .g-icon-btn, .g-segmented, .g-segmented__item { box-sizing: border-box; }
.g-btn, .g-icon-btn, .g-segmented__item {
  transition: background-color var(--glian-duration-fast) var(--glian-ease-standard),
              border-color var(--glian-duration-fast) var(--glian-ease-standard),
              color var(--glian-duration-fast) var(--glian-ease-standard);
}
.g-btn:focus-visible, .g-icon-btn:focus-visible, .g-segmented__item:focus-visible { outline: var(--glian-focus-outline); outline-offset: 2px; }
.g-btn:disabled, .g-btn[aria-disabled="true"],
.g-icon-btn:disabled, .g-icon-btn[aria-disabled="true"],
.g-segmented__item:disabled { opacity: .45; cursor: not-allowed; }
/* Zona táctil en pantallas táctiles, sin cambiar el tamaño en escritorio (área invisible ::after).
 * Un ::after absoluto se mide desde DENTRO del borde de 1px del control: cada inset suma ese 1px.
 *  · .g-btn (md, 40px) → 44px: 2px más arriba y abajo.
 *  · .g-btn--sm (36px) → 44px: 4px más arriba y abajo.
 *  · .g-segmented__item (26px; 30px en --md) → 44px (48px): cubre el relleno de la pista (4px) y su
 *    borde (1px) y sale 4px más, como .g-btn--sm. Solo en vertical: a los lados las opciones se
 *    tocan entre sí.
 *  · .g-icon-btn → 6px por lado (48px). */
@media (pointer: coarse) {
  .g-btn::after { content: ""; position: absolute; inset: calc(var(--glian-space-1) / -2 - 1px) 0; }
  .g-btn--sm::after { content: ""; position: absolute; inset: calc(var(--glian-space-1) * -1 - 1px) 0; }
  .g-segmented__item::after { content: ""; position: absolute; inset: calc(var(--glian-space-2) * -1 - 2px) 0; }
  .g-icon-btn::after { inset: calc(var(--glian-space-1) * -1.5 - 1px); }
}

/* ── Estructura de página (DESIGN_SYSTEM.md §19) ─────────────────────────────────────────────
 * Cinco primitivas (oleada 0A): .g-page (el contenedor), .g-page-header (título, subtítulo, volver y
 * acciones), .g-tabs (pestañas; --sub para el segundo nivel), .g-card (superficie) y .g-field
 * (etiqueta, control, ayuda y error; .g-field-grid para acomodarlos). Y lo que pidió el primer
 * consumidor real (Mi consultorio → Perfil): .g-check (casilla) y .g-notice (aviso). Puntos de corte: los del
 * armazón, 860px (menú lateral oculto) y 560px (teléfono). Ningún selector de elemento suelto:
 * solo cambia lo que usa una de estas clases a propósito. */

/* Página. Sustituye a .page-body/.container/.wrap y sus anchos locales. La barra superior ya
 * reservó su alto en el body (html.has-topbar): .g-page no la compensa. */
.g-page {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--glian-page-width);
  margin: 0 auto;
  padding: var(--glian-space-6) var(--glian-space-6) var(--glian-space-10);
  font-family: var(--glian-font-ui);
  color: var(--glian-text-primary);
}
.g-page--narrow { max-width: var(--glian-page-width-narrow); }

/* Encabezado de página: lo principal (volver + título y subtítulo) a la izquierda y las acciones a
 * la derecha, alineadas arriba con el título. En el teléfono (≤560px) las acciones bajan. El lugar de
 * las acciones NO depende del largo del subtítulo (el subtítulo se parte en su columna), y el título
 * nunca se achica para caber. */
.g-page-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--glian-space-3) var(--glian-space-4);
  margin: 0 0 var(--glian-space-6);
  font-family: var(--glian-font-ui);
}
.g-page-header__main {
  display: flex;
  align-items: flex-start;
  gap: var(--glian-space-2);
  min-width: 0;
}
/* Volver: un .g-icon-btn (chevron-left + aria-label). Se centra con la PRIMERA línea del título,
 * no con el bloque título+subtítulo. */
.g-page-header__back {
  flex-shrink: 0;
  margin-top: calc((var(--glian-text-page-title) * var(--glian-leading-tight) - var(--glian-control-sm)) / 2);
  color: var(--glian-text-secondary);
}
.g-page-header__titles { min-width: 0; }
.g-page-header__title { margin: 0; color: var(--glian-text-primary); overflow-wrap: anywhere; }
.g-page-header__subtitle { margin: var(--glian-space-1) 0 0; color: var(--glian-text-secondary); }
.g-page-header__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--glian-space-2);
}

/* Pestañas. Primer nivel: subrayado de marca bajo la activa sobre una línea gris. Segundo nivel
 * (--sub): el MISMO dibujo, más bajo y sin la línea gris completa, para que se lea subordinado. Muchas
 * opciones se DESPLAZAN de lado, nunca se parten en varios renglones. Activa: aria-selected="true"
 * (tabs en la misma página), aria-current="page" (enlaces entre páginas) o .is-active. */
.g-tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--glian-space-1);
  margin: 0 0 var(--glian-space-4);
  padding: 0;
  border-bottom: 1px solid var(--glian-border);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  font-family: var(--glian-font-ui);
}
.g-tabs__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: none;
  gap: var(--glian-space-2);
  box-sizing: border-box;
  height: var(--glian-control-md);
  margin: 0 0 -1px;
  padding: 0 var(--glian-space-3);
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: var(--glian-radius-sm) var(--glian-radius-sm) 0 0;
  background: transparent;
  color: var(--glian-text-secondary);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-button);
  font-weight: var(--glian-weight-medium);
  line-height: var(--glian-leading-tight);
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--glian-duration-fast) var(--glian-ease-standard),
              border-color var(--glian-duration-fast) var(--glian-ease-standard),
              background-color var(--glian-duration-fast) var(--glian-ease-standard);
}
.g-tabs__item .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); }
.g-tabs__item:hover { color: var(--glian-text-primary); border-bottom-color: var(--glian-border-strong); }
.g-tabs__item[aria-selected="true"],
.g-tabs__item[aria-current="page"],
.g-tabs__item[aria-current="true"],
.g-tabs__item.is-active { color: var(--glian-brand-text); border-bottom-color: var(--glian-brand-primary); }
/* Dentro del contenedor que se desplaza, el contorno va hacia adentro: afuera lo recortaría. */
.g-tabs__item:focus-visible { outline: var(--glian-focus-outline); outline-offset: -2px; }
.g-tabs__item:disabled, .g-tabs__item[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.g-tabs--sub { border-bottom: 0; }
/* Segundo nivel justo debajo del primero: 8px entre los dos, no los 16 de una sección. */
.g-tabs:not(.g-tabs--sub) + .g-tabs--sub { margin-top: calc(var(--glian-space-2) - var(--glian-space-4)); }
.g-tabs--sub .g-tabs__item { height: var(--glian-control-sm); margin-bottom: 0; padding: 0 var(--glian-space-2); font-size: var(--glian-text-small); }
/* Con ratón la barra de desplazamiento se ve (fina): sin ella, una pestaña escondida a la derecha no
 * se alcanza con la rueda. En pantallas táctiles se desliza con el dedo y se oculta. */
@media (pointer: coarse) {
  .g-tabs { scrollbar-width: none; }
  .g-tabs::-webkit-scrollbar { display: none; }
  .g-tabs__item { min-height: calc(var(--glian-control-md) + var(--glian-space-1)); }
}

/* Tarjeta: UNA familia (sustituye a .card, .panel, .rc-panel, .fx-card, .scale-card…). Plana: borde
 * y la sombra discreta de tarjeta, nada más. Las diferencias las pone el contenido, no variantes. */
.g-card {
  box-sizing: border-box;
  min-width: 0;
  background: var(--glian-bg-surface);
  border: 1px solid var(--glian-border);
  border-radius: var(--glian-radius-lg);
  box-shadow: var(--glian-shadow-sm);
  color: var(--glian-text-primary);
  font-family: var(--glian-font-ui);
}
.g-card__header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--glian-space-2) var(--glian-space-3);
  padding: var(--glian-space-4) var(--glian-space-4) 0;
}
.g-card__title { margin: 0; color: var(--glian-text-primary); }
.g-card__body { padding: var(--glian-space-4); }
.g-card__header + .g-card__body { padding-top: var(--glian-space-3); }
.g-card__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--glian-space-2);
  padding: var(--glian-space-3) var(--glian-space-4);
  border-top: 1px solid var(--glian-border);
}

/* Campo: etiqueta (rol label), control, ayuda y error. El control lleva .g-field__control
 * (input, select, textarea o uno propio): los <input> sueltos de la página no cambian. 40px de alto;
 * 44px en pantallas táctiles. Foco oficial: borde de marca + halo. Error: aria-invalid="true" en el
 * control y el texto en .g-field__error, ligado con aria-describedby. */
.g-field {
  display: flex;
  flex-direction: column;
  gap: var(--glian-space-2);
  min-width: 0;
  font-family: var(--glian-font-ui);
}
.g-field__label { color: var(--glian-text-primary); }
.g-field__control {
  box-sizing: border-box;
  width: 100%;
  min-height: var(--glian-control-md);
  margin: 0;
  padding: var(--glian-space-2) var(--glian-space-3);
  border: 1px solid var(--glian-control-border);
  border-radius: var(--glian-radius-md);
  background: var(--glian-bg-surface);
  color: var(--glian-text-primary);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-regular);
  line-height: var(--glian-leading-normal);
  transition: border-color var(--glian-duration-fast) var(--glian-ease-standard),
              box-shadow var(--glian-duration-fast) var(--glian-ease-standard);
}
.g-field__control::placeholder { color: var(--glian-text-muted); opacity: 1; }
.g-field__control:focus { outline: none; border-color: var(--glian-border-focus); box-shadow: var(--glian-focus-ring); }
.g-field__control[aria-invalid="true"] { border-color: var(--glian-danger); }
.g-field__control:disabled { background: var(--glian-bg-subtle); color: var(--glian-text-muted); cursor: not-allowed; }
textarea.g-field__control { min-height: calc(var(--glian-control-md) * 2); resize: vertical; }
/* El select se dibuja igual en todos los navegadores (Safari ignora alto y relleno del nativo): sin
 * apariencia propia y con el chevrón en el color del texto. */
select.g-field__control {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--glian-space-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - var(--glian-space-4)) 50%, calc(100% - var(--glian-space-3)) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.g-field__help { margin: 0; color: var(--glian-text-tertiary); font-size: var(--glian-text-small); line-height: var(--glian-leading-normal); }
.g-field__error { margin: 0; color: var(--glian-danger-text); font-size: var(--glian-text-small); font-weight: var(--glian-weight-medium); line-height: var(--glian-leading-normal); }
.g-field__error:empty { display: none; }
@media (pointer: coarse) { .g-field__control { min-height: calc(var(--glian-control-md) + var(--glian-space-1)); } }
/* En un teléfono el campo va a 16px: con menos, Safari en iPhone hace zoom al enfocarlo (el mismo
 * motivo y el mismo token que en las superficies públicas, §18). */
@media (pointer: coarse) and (max-width: 560px) { .g-field__control { font-size: var(--glian-public-input); } }

/* Casilla (§19.6): una elección o confirmación DENTRO de un formulario, que se guarda con él. El
 * <input type="checkbox"> nativo va a la vista (accesible tal cual) con el color de marca al marcarse;
 * toda la fila es la etiqueta, así que se marca tocando el texto. Ayuda opcional debajo del texto. */
.g-check {
  display: flex;
  align-items: flex-start;
  gap: var(--glian-space-2);
  color: var(--glian-text-primary);
  font-family: var(--glian-font-ui);
  cursor: pointer;
}
.g-check__input {
  flex-shrink: 0;
  width: var(--glian-icon-md);
  height: var(--glian-icon-md);
  margin: calc((var(--glian-text-body) * var(--glian-leading-normal) - var(--glian-icon-md)) / 2) 0 0;
  accent-color: var(--glian-brand-primary);
  cursor: pointer;
}
.g-check__input:focus-visible { outline: var(--glian-focus-outline); outline-offset: 2px; }
.g-check__text { display: flex; flex-direction: column; gap: var(--glian-space-1); min-width: 0; }
.g-check__label { font-size: var(--glian-text-body); line-height: var(--glian-leading-normal); }
.g-check__help { color: var(--glian-text-tertiary); font-size: var(--glian-text-small); line-height: var(--glian-leading-normal); }
.g-check:has(.g-check__input:disabled) { color: var(--glian-text-muted); cursor: not-allowed; }
@media (pointer: coarse) { .g-check { min-height: calc(var(--glian-control-md) + var(--glian-space-1)); } }

/* Aviso en la página (§19.7): algo que la persona tiene que saber o decidir AHÍ, que se queda a la
 * vista (no es un toast). Cuatro tonos, cada uno con sus consumidores: --warning (revisar o decidir),
 * --danger (algo falló), --info (cómo funciona algo, sin nada que decidir: Mensajes, Políticas,
 * Importar, Duplicados, Glian-AI) y --success (algo quedó bien o es seguro: Mensajes, Importar,
 * Duplicados, Nombres). Fondo y letra del estado semántico; el borde es el mismo color, rebajado.
 * Icono del sprite opcional al principio; acciones opcionales (botones .g-btn) en __actions. */
.g-notice {
  display: flex;
  align-items: flex-start;
  gap: var(--glian-space-2);
  padding: var(--glian-space-3) var(--glian-space-4);
  border: 1px solid transparent;
  border-radius: var(--glian-radius-md);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-small);
  line-height: var(--glian-leading-normal);
}
.g-notice > .g-icon {
  width: var(--glian-icon-md);
  height: var(--glian-icon-md);
  margin-top: calc((var(--glian-text-small) * var(--glian-leading-normal) - var(--glian-icon-md)) / 2);
}
.g-notice__text { flex: 1 1 auto; min-width: 0; }
.g-notice__actions { display: flex; flex-wrap: wrap; gap: var(--glian-space-2); margin-top: var(--glian-space-2); }
.g-notice--warning {
  background: var(--glian-warning-soft);
  border-color: color-mix(in srgb, var(--glian-warning) 30%, transparent);
  color: var(--glian-warning-text);
}
.g-notice--danger {
  background: var(--glian-danger-soft);
  border-color: color-mix(in srgb, var(--glian-danger) 30%, transparent);
  color: var(--glian-danger-text);
}
.g-notice--info {
  background: var(--glian-info-soft);
  border-color: color-mix(in srgb, var(--glian-info) 30%, transparent);
  color: var(--glian-info-text);
}
.g-notice--success {
  background: var(--glian-success-soft);
  border-color: color-mix(in srgb, var(--glian-success) 30%, transparent);
  color: var(--glian-success-text);
}

/* Etiqueta de estado (§19.16): una palabra con tono, en píldora. El tono NUNCA va solo: siempre lleva
 * su palabra («Activa», «Vence pronto», «Sin registros»). Con punto opcional (.g-status__dot). */
.g-status {
  display: inline-flex;
  align-items: center;
  gap: var(--glian-space-1);
  max-width: 100%;
  min-height: var(--glian-space-5);
  padding: 0 var(--glian-space-2);
  border: 1px solid transparent;
  border-radius: var(--glian-radius-pill);
  background: var(--glian-neutral-soft);
  color: var(--glian-neutral-text);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  white-space: nowrap;
  vertical-align: middle;
}
.g-status .g-icon { width: var(--glian-icon-xs); height: var(--glian-icon-xs); flex: 0 0 auto; }
.g-status__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.g-status--brand { background: var(--glian-brand-soft); border-color: var(--glian-brand-soft-border); color: var(--glian-brand-text); }
.g-status--success { background: var(--glian-success-soft); border-color: color-mix(in srgb, var(--glian-success) 30%, transparent); color: var(--glian-success-text); }
.g-status--warning { background: var(--glian-warning-soft); border-color: color-mix(in srgb, var(--glian-warning) 35%, transparent); color: var(--glian-warning-text); }
.g-status--danger { background: var(--glian-danger-soft); border-color: color-mix(in srgb, var(--glian-danger) 30%, transparent); color: var(--glian-danger-text); }
.g-status--info { background: var(--glian-info-soft); border-color: color-mix(in srgb, var(--glian-info) 30%, transparent); color: var(--glian-info-text); }
.g-status--quiet { background: transparent; border-color: var(--glian-border); color: var(--glian-text-secondary); }

/* Barra de herramientas de una lista (§19.17): búsqueda que crece, filtros y acciones en una fila que
 * se parte en el teléfono. La búsqueda es un .g-field__control con su icono dentro (.g-toolbar__search). */
.g-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glian-space-2) var(--glian-space-3);
  margin-bottom: var(--glian-space-4);
  font-family: var(--glian-font-ui);
}
.g-toolbar__search { position: relative; flex: 1 1 16rem; min-width: 0; }
.g-toolbar__search > .g-icon {
  position: absolute; top: 50%; left: var(--glian-space-3); transform: translateY(-50%);
  width: var(--glian-icon-md); height: var(--glian-icon-md); color: var(--glian-text-muted); pointer-events: none;
}
.g-toolbar__search > .g-field__control { padding-left: calc(var(--glian-space-3) + var(--glian-icon-md) + var(--glian-space-2)); }
.g-toolbar__group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--glian-space-2); }
.g-toolbar__label { color: var(--glian-text-secondary); font-size: var(--glian-text-small); font-weight: var(--glian-weight-medium); }
.g-toolbar__end { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--glian-space-2); }
.g-toolbar .g-field__control { width: auto; }
.g-toolbar__search > .g-field__control { width: 100%; }

/* Estado de una zona (§19.18): cargando, vacío o error, en el lugar del contenido. Título, texto y una
 * acción opcional («Reintentar»). Cargando lleva aria-busy en la zona y role="status" aquí. */
.g-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--glian-space-2);
  padding: var(--glian-space-10) var(--glian-space-6);
  text-align: center;
  color: var(--glian-text-secondary);
  font-family: var(--glian-font-ui);
  font-size: var(--glian-text-small);
  line-height: var(--glian-leading-normal);
}
.g-state[hidden] { display: none; }
.g-state > .g-icon { width: var(--glian-icon-xl); height: var(--glian-icon-xl); color: var(--glian-text-muted); }
.g-state__title { margin: 0; color: var(--glian-text-primary); font-size: var(--glian-text-card-title); font-weight: var(--glian-weight-semibold); }
.g-state__text { margin: 0; max-width: 32rem; }
.g-state__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--glian-space-2); margin-top: var(--glian-space-2); }
.g-state--error > .g-icon { color: var(--glian-danger-text); }
.g-state__spinner {
  width: var(--glian-icon-xl); height: var(--glian-icon-xl); border-radius: 50%;
  border: 3px solid var(--glian-border); border-top-color: var(--glian-brand-primary);
  animation: g-state-gira .8s linear infinite;
}
@keyframes g-state-gira { to { transform: rotate(360deg); } }

/* Barra de guardar (§19.21): fija abajo mientras se edita una pantalla que guarda TODO junto (Flux,
 * editores). Una sola Primary «Guardar»; a su lado el estado con su palabra: «Cambios sin guardar»
 * (advertencia), «Guardado» (éxito) o el error (peligro, role="alert"). Sobre el fondo de la página. */
.g-save-bar {
  position: sticky;
  bottom: 0;
  z-index: var(--glian-z-sticky);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glian-space-2) var(--glian-space-3);
  margin-top: var(--glian-space-5);
  padding: var(--glian-space-3) 0 calc(var(--glian-space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--glian-bg-page);
  border-top: 1px solid var(--glian-border);
  font-family: var(--glian-font-ui);
}
.g-save-bar[hidden] { display: none; }
.g-save-bar__status {
  display: inline-flex;
  align-items: center;
  gap: var(--glian-space-1);
  color: var(--glian-text-secondary);
  font-size: var(--glian-text-small);
  font-weight: var(--glian-weight-medium);
}
.g-save-bar__status:empty { display: none; }
.g-save-bar__status .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); flex: 0 0 auto; }
.g-save-bar__status--pendiente { color: var(--glian-warning-text); }
.g-save-bar__status--ok { color: var(--glian-success-text); }
.g-save-bar__status--error { color: var(--glian-danger-text); }
@media (max-width: 560px) { .g-save-bar > .g-btn--primary { flex: 1 1 100%; order: 2; } }
@media (prefers-reduced-motion: reduce) { .g-state__spinner { animation-duration: 2.4s; } }

/* ── Modal: .g-modal (DESIGN_SYSTEM.md §19.23) ───────────────────────────────────────────────────
 * Un diálogo sobre la página: velo, tarjeta con encabezado fijo (título y cerrar), cuerpo que se
 * desplaza y pie con UNA acción principal. En el teléfono es hoja inferior. El comportamiento
 * (foco, tabulador, Escape, velo, bloqueo del scroll, devolver el foco) lo da Glian.modal(el, …)
 * en js/expedix.js; una superficie sin expedix.js (las del paciente) usa solo este dibujo.
 *
 *   <div class="g-modal [g-modal--sm|--lg]" hidden>
 *     <div class="g-modal__backdrop"></div>
 *     <div class="g-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="x-titulo">
 *       <div class="g-modal__header">
 *         <div class="g-modal__titles"><h2 class="g-modal__title" id="x-titulo">…</h2>
 *           <p class="g-modal__subtitle">…</p></div>
 *         <button type="button" class="g-icon-btn g-modal__close" data-g-modal-close aria-label="Cerrar">…x…</button>
 *       </div>
 *       <div class="g-modal__body">…</div>
 *       <div class="g-modal__footer">[<div class="g-modal__footer-start">…</div>] Secondary · Primary</div>
 *     </div>
 *   </div>
 * Capa: --glian-z-modal. Un módulo que tenga que quedar ENTRE dos capas viejas (p. ej. debajo del
 * selector de WhatsApp) la ajusta en su propia clase sobre el mismo elemento. */
.g-modal {
  position: fixed;
  inset: 0;
  z-index: var(--glian-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--glian-space-4);
  font-family: var(--glian-font-ui);
  color: var(--glian-text-primary);
}
.g-modal[hidden] { display: none; }
.g-modal__backdrop { position: absolute; inset: 0; background: var(--glian-bg-backdrop); }
.g-modal__dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 100%;
  max-width: 560px;
  max-height: calc(100dvh - 2 * var(--glian-space-4));
  overflow: hidden;
  border-radius: var(--glian-radius-xl);
  background: var(--glian-bg-elevated);
  box-shadow: 0 0 0 1px var(--glian-border-overlay), var(--glian-shadow-overlay);
  animation: g-modal-entra var(--glian-duration-normal) var(--glian-ease-enter);
  /* Su propia letra base: el diálogo cuelga del <body> y cada página trae la suya. */
  font-size: var(--glian-text-body);
  line-height: var(--glian-leading-normal);
}
.g-modal--sm .g-modal__dialog { max-width: 420px; }
.g-modal--lg .g-modal__dialog { max-width: 760px; }
.g-modal__dialog:focus { outline: none; }
.g-modal__header {
  display: flex;
  align-items: flex-start;
  gap: var(--glian-space-3);
  flex-shrink: 0;
  padding: var(--glian-space-4) var(--glian-space-4) var(--glian-space-3) var(--glian-space-5);
  border-bottom: 1px solid var(--glian-border);
}
.g-modal__titles { flex: 1 1 auto; min-width: 0; }
.g-modal__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  color: var(--glian-text-primary);
  font-size: var(--glian-text-card-title);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  overflow-wrap: anywhere;
}
/* Un paso que lleva el foco al título (para que el lector de pantalla lo anuncie) no pinta contorno: no es un control. */
.g-modal__title:focus { outline: none; }
.g-modal__subtitle { margin: var(--glian-space-1) 0 0; color: var(--glian-text-secondary); font-size: var(--glian-text-small); line-height: var(--glian-leading-normal); }
.g-modal__close { flex-shrink: 0; margin: calc(-1 * var(--glian-space-1)) 0; }
.g-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--glian-space-4) var(--glian-space-5);
}
.g-modal__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--glian-space-2);
  flex-shrink: 0;
  padding: var(--glian-space-3) var(--glian-space-5);
  border-top: 1px solid var(--glian-border);
}
.g-modal__footer[hidden] { display: none; }
.g-modal__footer-start { display: flex; flex-wrap: wrap; align-items: center; gap: var(--glian-space-2); margin-right: auto; }
/* Pregunta o aviso propio (Glian.confirmar / avisar / pedir, Issue #194): el mismo modal, chico, por
 * ENCIMA de cualquier otra capa (puede abrirse desde otro diálogo, incluso de los viejos con z-index alto). */
.g-dialogo { z-index: var(--glian-z-dialogo); }
.g-dialogo .g-modal__title { white-space: pre-line; }
.g-dialogo__p { margin: 0 0 var(--glian-space-2); color: var(--glian-text-secondary); font-size: var(--glian-text-body); line-height: var(--glian-leading-normal); overflow-wrap: anywhere; }
.g-dialogo__p:last-child { margin-bottom: 0; }
.g-dialogo__campo { width: 100%; box-sizing: border-box; margin-top: var(--glian-space-2); }
.g-dialogo .g-modal__header + .g-modal__footer { border-top: 0; }   /* solo la pregunta: una sola raya */
/* Teléfono: hoja inferior a todo el ancho; la acción principal ocupa el ancho y va abajo. */
@media (max-width: 560px) {
  .g-modal { align-items: flex-end; padding: var(--glian-space-6) 0 0; }
  .g-modal__dialog,
  .g-modal--sm .g-modal__dialog,
  .g-modal--lg .g-modal__dialog { max-width: none; max-height: calc(100dvh - var(--glian-space-6)); border-radius: var(--glian-radius-xl) var(--glian-radius-xl) 0 0; animation-name: g-modal-sube; }
  .g-modal__header { padding: var(--glian-space-4) var(--glian-space-3) var(--glian-space-3) var(--glian-space-4); }
  .g-modal__body { padding: var(--glian-space-4); }
  .g-modal__footer { padding: var(--glian-space-3) var(--glian-space-4) calc(var(--glian-space-3) + env(safe-area-inset-bottom, 0px)); }
  .g-modal__footer > .g-btn,
  .g-modal__footer-start > .g-btn { flex: 1 1 auto; }   /* las secundarias llenan su renglón: ninguna queda sola y angosta */
  .g-modal__footer-start { flex: 1 1 100%; margin-right: 0; }
  .g-modal__footer > .g-btn--primary { flex: 1 1 100%; order: 2; }
}
@keyframes g-modal-entra { from { opacity: 0; transform: translateY(var(--glian-space-2)); } }
@keyframes g-modal-sube { from { transform: translateY(var(--glian-space-6)); } }
/* Mientras haya un modal abierto la página de atrás no se desplaza (Glian.modal pone la clase). */
html.g-modal-abierto, html.g-modal-abierto body { overflow: hidden; }

/* Rejilla de campos: 2 o 3 columnas, y una sola en el teléfono. La de 3 baja a 2 sin menú lateral. */
.g-field-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--glian-space-4); }
.g-field-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g-field-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 860px) {
  .g-field-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .g-page { padding: var(--glian-space-4) var(--glian-space-4) var(--glian-space-8); }
  .g-page-header { grid-template-columns: minmax(0, 1fr); margin-bottom: var(--glian-space-4); }
  .g-page-header__actions { justify-content: flex-start; }
  .g-field-grid--2, .g-field-grid--3 { grid-template-columns: minmax(0, 1fr); }
}
/* En papel: sin navegación ni acciones, y la página a todo el ancho de la hoja. */
@media print {
  .g-page { max-width: none; padding: 0; }
  .g-page-header__back, .g-page-header__actions, .g-tabs { display: none !important; }
  .g-card { box-shadow: none; }
}

/* ── Paleta de comandos (DESIGN_SYSTEM.md §15) ────────────────────────────────────────────────
 * UNA superficie modal para buscar y navegar (js/glian-command.js): fondo, panel, campo, lista
 * agrupada y estado. Por encima de todo (--glian-z-command). En escritorio mide
 * --glian-command-width; en un teléfono, el ancho de la pantalla menos --glian-space-4 por lado.
 * El campo va a 18px: con menos de 16px, Safari en iPhone/iPad hace zoom al enfocarlo. La opción
 * activa usa el lenguaje de selección del sistema (fondo brand-soft, texto brand-text). */
.g-command { position: fixed; inset: 0; z-index: var(--glian-z-command); font-family: var(--glian-font-ui); }
.g-command[hidden] { display: none; }
.g-command__fondo { position: absolute; inset: 0; background: var(--glian-bg-backdrop); }
.g-command__panel {
  position: absolute;
  top: max(var(--glian-space-4), 12vh);
  left: 50%;
  transform: translateX(-50%);
  box-sizing: border-box;
  width: min(var(--glian-command-width), calc(100vw - 2 * var(--glian-space-4)));
  max-height: calc(100vh - max(var(--glian-space-4), 12vh) - var(--glian-space-4));
  max-height: calc(100dvh - max(var(--glian-space-4), 12vh) - var(--glian-space-4));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--glian-bg-elevated);
  border: 1px solid var(--glian-border);
  border-radius: var(--glian-radius-lg);
  box-shadow: var(--glian-shadow-overlay);
  color: var(--glian-text-primary);
}
.g-command__campo {
  display: flex;
  align-items: center;
  gap: var(--glian-space-3);
  flex-shrink: 0;
  padding: var(--glian-space-2) var(--glian-space-2) var(--glian-space-2) var(--glian-space-4);
  border-bottom: 1px solid var(--glian-border);
}
.g-command__lupa { width: var(--glian-icon-md); height: var(--glian-icon-md); flex-shrink: 0; color: var(--glian-text-tertiary); }
.g-command__input {
  flex: 1;
  min-width: 0;
  height: var(--glian-control-md);
  margin: 0;
  padding: 0;
  border: 0;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--glian-text-section-title);
  font-weight: var(--glian-weight-regular);
  color: var(--glian-text-primary);
}
.g-command__input::placeholder { color: var(--glian-text-tertiary); }
.g-command__cerrar { color: var(--glian-text-tertiary); }
.g-command__lista { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: var(--glian-space-2); }
.g-command__lista:empty { display: none; }
.g-command__grupo + .g-command__grupo { margin-top: var(--glian-space-2); }
.g-command__titulo {
  padding: var(--glian-space-2) var(--glian-space-3) var(--glian-space-1);
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-leading-tight);
  text-transform: uppercase;
  color: var(--glian-text-tertiary);
}
.g-command__op {
  display: flex;
  align-items: center;
  gap: var(--glian-space-3);
  min-height: var(--glian-control-md);
  padding: var(--glian-space-1) var(--glian-space-3);
  border-radius: var(--glian-radius-md);
  color: var(--glian-text-primary);
  cursor: pointer;
}
.g-command__op[aria-selected="true"] { background: var(--glian-brand-soft); color: var(--glian-brand-text); }
.g-command__ico { display: flex; flex-shrink: 0; color: var(--glian-text-tertiary); }
.g-command__op[aria-selected="true"] .g-command__ico { color: var(--glian-brand-text); }
.g-command__ico .g-icon { width: var(--glian-icon-md); height: var(--glian-icon-md); }
.g-command__txt { display: flex; flex-direction: column; min-width: 0; }
.g-command__op-titulo {
  overflow: hidden;
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-medium);
  line-height: var(--glian-leading-tight);
  white-space: nowrap;
  text-overflow: ellipsis;
}
.g-command__op-sub { font-size: var(--glian-text-caption); line-height: var(--glian-leading-tight); color: var(--glian-text-tertiary); }
.g-command__estado { margin: 0; padding: var(--glian-space-4); font-size: var(--glian-text-small); color: var(--glian-text-tertiary); }
.g-command__nota {
  flex-shrink: 0;
  margin: 0;
  padding: var(--glian-space-2) var(--glian-space-4);
  border-top: 1px solid var(--glian-border);
  background: var(--glian-bg-subtle);
  font-size: var(--glian-text-caption);
  color: var(--glian-text-secondary);
}
/* Solo para lectores de pantalla (el número de resultados). */
.g-command__sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (pointer: coarse) { .g-command__op { min-height: calc(var(--glian-control-sm) + var(--glian-space-2)); } }
/* Teléfono: arriba, con el margen seguro, para dejar sitio al teclado. */
@media (max-width: 560px) { .g-command__panel { top: var(--glian-space-4); } }

/* ── Menú desplegable (DESIGN_SYSTEM.md §17) ─────────────────────────────────────────────────
 * UN componente para todo menú de acciones: un botón (aria-haspopup="menu") y una lista
 * role="menu" con opciones .g-menu__item. El comportamiento (abrir, cerrar con Escape o al tocar
 * fuera, flechas, devolver el foco) lo da Glian.menu() en js/expedix.js. Nació como el «Más» de la
 * Agenda y se subió aquí cuando la barra superior necesitó el mismo (menú del usuario).
 * Se alinea a la derecha de su botón; .g-menu--izq lo alinea a la izquierda si no cabe. */
.g-menu {
  position: absolute;
  top: calc(100% + var(--glian-space-1));
  right: 0;
  z-index: var(--glian-z-dropdown);
  display: flex;
  flex-direction: column;
  min-width: 100%;
  padding: var(--glian-space-1);
  box-sizing: border-box;
  background: var(--glian-bg-elevated);
  border: 1px solid var(--glian-border);
  border-radius: var(--glian-radius-md);
  box-shadow: var(--glian-shadow-md);
  font-family: var(--glian-font-ui);
}
.g-menu[hidden] { display: none; }
.g-menu.g-menu--izq { right: auto; left: 0; }
.g-menu__item {
  display: flex;
  align-items: center;
  gap: var(--glian-space-2);
  box-sizing: border-box;
  width: 100%;
  min-height: var(--glian-control-sm);
  margin: 0;
  padding: 0 var(--glian-space-3);
  border: 0;
  border-radius: var(--glian-radius-sm);
  background: transparent;
  color: var(--glian-text-primary);
  font-family: inherit;
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-medium);
  line-height: var(--glian-leading-tight);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--glian-duration-fast) var(--glian-ease-standard);
}
.g-menu__item .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); flex-shrink: 0; color: var(--glian-text-secondary); }
.g-menu__item .g-btn__badge { margin-left: auto; }
.g-menu__item:hover, .g-menu__item:focus-visible { background: var(--glian-neutral-soft); }
.g-menu__item:focus-visible { outline: var(--glian-focus-outline); outline-offset: -2px; }
.g-menu__sep { height: 1px; margin: var(--glian-space-1) 0; background: var(--glian-border); }
@media (pointer: coarse) { .g-menu__item { min-height: calc(var(--glian-control-sm) + var(--glian-space-2)); } }

/* ── Barra superior global (DESIGN_SYSTEM.md §16) ────────────────────────────────────────────
 * Es del ARMAZÓN, no de la página: la monta Exp.mountHeader() junto al menú lateral, en Expedix,
 * Clinimetrix y Recursos. Lleva el disparador de la paleta de comandos, un hueco central para el
 * contexto (vacío por ahora) y el menú del usuario. NO lleva título ni acciones de la página.
 * Fija arriba, 48px, desde el borde del menú lateral (--glian-topbar-left lo pone el armazón:
 * 220 / 64 contraído / 0 en el teléfono). El espacio lo reserva expedix.js antes del primer
 * pintado (html.has-topbar → body con padding-top), así que montarla no mueve el contenido. */
.g-topbar {
  position: fixed;
  top: 0;
  left: var(--glian-topbar-left, 0);
  right: 0;
  z-index: var(--glian-z-shell);
  box-sizing: border-box;
  height: var(--glian-topbar-h);
  display: flex;
  align-items: center;
  gap: var(--glian-space-3);
  padding: 0 var(--glian-space-6);
  background: var(--glian-bg-surface);
  border-bottom: 1px solid var(--glian-border);
  color: var(--glian-text-primary);
  font-family: var(--glian-font-ui);
}
/* Solo en el teléfono: la hamburguesa (abre el menú lateral) y la marca. */
.g-topbar .exp-ham, .g-topbar__marca { display: none; }
.g-topbar__marca { align-items: center; flex-shrink: 0; height: var(--glian-control-md); }
.g-topbar__marca img { height: var(--glian-icon-xl); width: auto; display: block; }
/* Disparador de la paleta de comandos (Glian.command): un botón con aspecto de campo. */
.g-topbar__buscar {
  display: flex;
  align-items: center;
  gap: var(--glian-space-2);
  box-sizing: border-box;
  width: var(--glian-topbar-search-w);
  min-width: 0;
  max-width: 100%;
  height: var(--glian-control-md);
  margin: 0;
  padding: 0 var(--glian-space-2) 0 var(--glian-space-3);
  border: 1px solid var(--glian-border);
  border-radius: var(--glian-radius-md);
  background: var(--glian-bg-subtle);
  color: var(--glian-text-tertiary);
  font-family: inherit;
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-regular);
  text-align: left;
  cursor: pointer;
  transition: background-color var(--glian-duration-fast) var(--glian-ease-standard), border-color var(--glian-duration-fast) var(--glian-ease-standard);
}
.g-topbar__buscar:hover { background: var(--glian-bg-surface); border-color: var(--glian-border-strong); }
.g-topbar__buscar:focus-visible { outline: var(--glian-focus-outline); outline-offset: 2px; }
.g-topbar__buscar .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); flex-shrink: 0; }
.g-topbar__buscar-txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.g-topbar__buscar-corto { display: none; }
.g-topbar__kbd {
  flex-shrink: 0;
  padding: 0 var(--glian-space-1);
  border: 1px solid var(--glian-border);
  border-radius: var(--glian-radius-sm);
  background: var(--glian-bg-surface);
  font-family: inherit;
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-medium);
  line-height: var(--glian-space-5);
  color: var(--glian-text-tertiary);
}
/* Hueco del contexto (paciente activo, en una fase posterior). Vacío ocupa el espacio que separa
 * la búsqueda del usuario; puede encogerse hasta 0 sin dejar un hueco propio. */
.g-topbar__context { flex: 1 1 0; min-width: 0; display: flex; align-items: center; }
/* Usuario: iniciales (o un icono si no hay nombre) y su menú (.g-menu). Sin el nombre en el botón: así
 * su ancho no cambia al llegar /api/auth/me (no salta). Quién es va en la cabecera del menú. */
.g-topbar__usuario { position: relative; flex-shrink: 0; }
/* El botón es angosto (solo iniciales): el menú toma el ancho de su contenido, no el del botón. */
.g-topbar__usuario .g-menu { width: max-content; min-width: calc(var(--glian-space-12) * 4); max-width: calc(100vw - var(--glian-space-6)); }
.g-topbar__yo {
  display: flex;
  align-items: center;
  gap: var(--glian-space-2);
  box-sizing: border-box;
  height: var(--glian-control-md);
  margin: 0;
  padding: 0 var(--glian-space-2) 0 var(--glian-space-1);
  border: 1px solid transparent;
  border-radius: var(--glian-radius-md);
  background: transparent;
  color: var(--glian-text-primary);
  font-family: inherit;
  font-size: var(--glian-text-body);
  font-weight: var(--glian-weight-medium);
  cursor: pointer;
  transition: background-color var(--glian-duration-fast) var(--glian-ease-standard);
}
.g-topbar__yo:hover { background: var(--glian-neutral-soft); }
.g-topbar__yo:focus-visible { outline: var(--glian-focus-outline); outline-offset: 2px; }
.g-topbar__yo > .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); color: var(--glian-text-tertiary); }
.g-topbar__ini {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--glian-space-8);
  height: var(--glian-space-8);
  border-radius: var(--glian-radius-pill);
  background: var(--glian-brand-soft);
  color: var(--glian-brand-text);
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-semibold);
}
.g-topbar__ini .g-icon { width: var(--glian-icon-sm); height: var(--glian-icon-sm); }
/* Cabecera del menú del usuario: quién trabaja (y si es cuenta de equipo). */
.g-topbar__quien { display: flex; flex-direction: column; gap: var(--glian-space-1); padding: var(--glian-space-2) var(--glian-space-3); overflow-wrap: anywhere; }
.g-topbar__quien b { font-size: var(--glian-text-body); font-weight: var(--glian-weight-semibold); color: var(--glian-text-primary); }
.g-topbar__quien span { font-size: var(--glian-text-caption); color: var(--glian-text-tertiary); }
.g-topbar__equipo {
  align-self: flex-start;
  margin-top: var(--glian-space-1);
  padding: 0 var(--glian-space-2);
  border-radius: var(--glian-radius-pill);
  background: var(--glian-neutral-soft);
  color: var(--glian-text-secondary);
  font-size: var(--glian-text-caption);
  font-weight: var(--glian-weight-semibold);
  line-height: var(--glian-space-5);
}
/* Teléfono (≤860px, sin menú lateral visible): de borde a borde, con la hamburguesa y la marca. */
@media (max-width: 860px) {
  .g-topbar { gap: var(--glian-space-2); padding: 0 var(--glian-space-3); }
  .g-topbar .exp-ham, .g-topbar__marca { display: flex; }
  .g-topbar__buscar { flex: 1 1 auto; width: auto; }
  .g-topbar__context, .g-topbar__kbd, .g-topbar__yo > .g-icon { display: none; }
}
@media (max-width: 560px) {
  .g-topbar__buscar-largo { display: none; }
  .g-topbar__buscar-corto { display: inline; }
}
/* En papel no hay armazón. */
@media print {
  .g-topbar { display: none !important; }
  html.has-topbar body { padding-top: 0 !important; }
}
