
    :root {
      
      /* Ramp neutro (oscuro = default, igual que el sistema anterior) */
      --fox-white: #2B333F;
      --fox-100: #1C222B;
      --fox-200: #161B22;
      --fox-300: #232A34;
      --fox-400: #3A4450;
      --fox-500: #6A7686;
      --fox-600: #93A0AB;
      --fox-700: #C3CCD6;
      --fox-800: #E1E7EE;
      --fox-900: #F2F5FA;

      /* Acentos de señal */
      --signal-cyan: #2CF3E6;
      --signal-cyan-dim: #12D9CE;
      --signal-orange: #FF7A2F;
      --signal-red: #FF4A41;
      --signal-mint: #9FE8D2;
      --signal-violet: #8A8AFF;
      --signal-yellow: #F2C94C;

      --text-strong: var(--fox-900);
      --text-body: var(--fox-700);
      --text-muted: var(--fox-600);
      --text-faint: var(--fox-500);

      /* Trío base/panel/pozo (FOX-P124, artifact "Carve & Glow" def89764) —
         antes --surface-card == --surface-base (ningún tono propio para
         contenido elevado sobre el lienzo, ver .stage en el artifact).
         Valores tomados literal del artifact en vez de la rampa --fox-*
         genérica, que solo por coincidencia se le parecía. */
      --surface-base: #191B20;
      --surface-card: #1F2127;
      --surface-sunken: #15171B;
      --surface-rail: #0E1218;
      --surface-overlay: rgba(22, 27, 34, .74);

      --line-hair: rgba(255, 255, 255, .14);
      --line-soft: rgba(255, 255, 255, .07);
      --line-strong: rgba(255, 255, 255, .4);
      --line-accent: var(--signal-cyan);

      --state-info: var(--signal-cyan);
      --state-ok: var(--signal-mint);
      --state-warn: var(--signal-orange);
      --state-danger: var(--signal-red);
      --focus-ring: var(--signal-cyan);
      --glow-accent: 0 0 0 1px rgba(44, 243, 230, .55), 0 0 22px rgba(44, 243, 230, .4);
      /* Mismo patrón que --glow-accent pero en --signal-red — para marcas
         puntuales de urgencia/alerta (p.ej. la línea pasado/presente del
         plan semanal) que no deben competir con el glow cian de "lo único
         en vivo". */
      --glow-danger: 0 0 0 1px rgba(255, 74, 65, .55), 0 0 22px rgba(255, 74, 65, .4);

      /* Elevación — dos luces, siempre: realce frío arriba-izquierda, sombra
         suave abajo-derecha. Ver guidelines/elevation-*.card.html.
         Sustituido (FOX-P122) por los valores confirmados en el artifact
         "Fox — Carve & Glow": misma fórmula de dos luces que ya usaba este
         sistema, pero con colores translúcidos (antes --nu-light era un
         color sólido) y sin la capa --nu-rim adicional, que quedaba
         redundante una vez el offset/blur ya hace ese trabajo. */
      --nu-light: rgba(255, 255, 255, .06);
      --nu-dark: rgba(0, 0, 0, .7);
      --nu-dark-soft: rgba(0, 0, 0, .4);
      --nu-raise-1: 4px 4px 9px var(--nu-dark), -4px -4px 9px var(--nu-light);
      --nu-raise-2: 7px 7px 16px var(--nu-dark), -7px -7px 16px var(--nu-light);
      --nu-raise-3: 10px 10px 22px var(--nu-dark), -10px -10px 22px var(--nu-light);
      --nu-inset-1: inset 5px 5px 11px var(--nu-dark), inset -5px -5px 11px var(--nu-light);
      --nu-inset-2: inset 8px 8px 18px var(--nu-dark), inset -8px -8px 18px var(--nu-light);
      /* Caja a la vez hundida por fuera y elevada por dentro, con sombras muy pequeñas
         (campos de texto y descripción de tareas, diseño de Emilio 2026-10-01). */
      /* Relleno de los campos de texto: velo translúcido liso (sin trama) que oscurece un pelín
         lo que tenga detrás, sea cual sea la superficie; sigue al tema (--nu-dark cambia en claro). */
      --field-fill: color-mix(in srgb, var(--nu-dark) 22%, transparent);
      --groove: -1px -1px 2px color-mix(in srgb, var(--nu-dark) 40%, transparent), 1px 1px 3px var(--nu-light), inset 1px 1px 2px var(--nu-light), inset -1px -1px 2px var(--nu-dark);
      --groove-focus: -1.5px -1.5px 3px color-mix(in srgb, var(--nu-dark) 40%, transparent), 2px 2px 4px var(--nu-light), inset 1.5px 1.5px 3px var(--nu-light), inset -1.5px -1.5px 3px var(--nu-dark);
      --nu-crease: 0 1px 0 var(--nu-light), 0 -1px 0 var(--nu-dark);
      /* Glow ANTES del raise — box-shadow pinta la primera sombra de la
         lista por delante; con el glow al final quedaba tapado por las
         capas oscuras del raise y solo se veía "escapando" por fuera
         (bug real detectado en .tab.active del sidebar, ver
         board-renderer.ts). */
      --nu-raise-glow: var(--glow-accent), var(--nu-raise-1);

      /* Vidrio + neón (FOX-P122, artifact "Carve & Glow") — capa nueva que
         no existía: un panel translúcido con blur (.glass-glaze) apilado
         DENTRO de un raise/carved existente, con una fuente de color más
         pequeña (.neon-under) detrás de él. Orden de capas siempre
         neón → vidrio → contenido nítido. */
      --glass-fill: rgba(255, 255, 255, .07);
      --glass-border: rgba(255, 255, 255, .14);
      --glaze-fill: rgba(255, 255, 255, .045);
      --glaze-border: rgba(255, 255, 255, .16);
      /* Un glow por cada --signal-*, mapeado 1:1 por significado desde los
         neones del artifact — no son colores nuevos, son los mismos 5
         acentos de señal de Fox con alpha .5 para usar en box-shadow. */
      --neon-cyan-glow: rgba(44, 243, 230, .5);
      --neon-orange-glow: rgba(255, 138, 71, .5);
      --neon-red-glow: rgba(255, 69, 58, .5);
      --neon-mint-glow: rgba(159, 232, 210, .5);
      --neon-violet-glow: rgba(138, 138, 255, .5);
      /* Faltaba el color base: solo existía el glow (2026-09-29). */
      --neon-violet: #8A8AFF;
      --neon-yellow-glow: rgba(242, 201, 76, .5);

      /* 6 acentos de neón "Carve & Glow" (FOX-P124, artifact def89764,
         exhibits 00-13) — paleta propia del sistema nuevo, más amplia que
         los --signal-* / --neon-*-glow heredados de arriba (naranja/azul/
         verde/teal/rosa/rojo vs. cian/naranja/rojo/menta/violeta). --neon-
         teal reusa el mismo cian de --signal-cyan (valor idéntico en el
         artifact), el resto (rosa/azul/verde) no tenía ningún acento
         equivalente en el sistema anterior. Consumidos por la receta de 3
         capas (.neon-under/.glass-glaze) de los componentes que se
         reconstruyan en US2/US3 — los renderers aún no migrados siguen
         leyendo --signal-* / --neon-*-glow sin cambios. Ojo con este
         patrón en cualquier comentario CSS: un asterisco pegado a una
         barra, sin espacio entre medias, cierra el comentario de verdad
         para el parser del navegador aunque la intención sea solo prosa
         — un bug real encontrado aquí el 2026-09-17 que dejaba
         --neon-pink (y todo lo que viniera después hasta el siguiente
         punto y coma recuperable) sin valor, silenciosamente, en TODA la
         app. Nunca escribas ese asterisco y esa barra pegados dentro de
         un comentario que vaya a interpolarse en un <style> real. */
      --neon-pink: #FF4D93;
      --neon-pink-glow: rgba(255, 77, 147, .55);
      --neon-blue: #4DA3FF;
      --neon-blue-glow: rgba(77, 163, 255, .55);
      --neon-orange: #FF8A47;
      --neon-green: #3DDC84;
      --neon-green-glow: rgba(61, 220, 132, .55);
      --neon-teal: #2CF3E6;
      --neon-teal-glow: rgba(44, 243, 230, .5);
      --neon-red: #FF453A;
      --elev-flat: none;
      --elev-card: var(--nu-raise-2);
      /* Sombra suave real del artifact (.dh-card/.stage, tema oscuro) —
         FOX-P125, feedback de Emilio 2026-09-18: "usa este mismo estilo para
         las tarjetas de contenido de todos los sitios en fox" (comparando el
         Daily Hub real del artifact contra el de Fox). Antes .hub-card sin
         :raise se quedaba con solo un hairline de 1px y SIN background
         propio (se fundía con el fondo de página) — el artifact, incluso en
         su card "sin elevar", sí tiene background real (--field-panel =
         --surface-card, valor idéntico) + esta sombra difusa, léase "tarjeta
         visible pero discreta", no "invisible". Literal, no una
         aproximación: valores calcados byte a byte de .dh-card en tema
         oscuro. --elev-card (arriba) sigue siendo el raise fuerte reservado
         para la única card por vista que de verdad se eleva
         (.hub-card-raised / .dh-card.raise) — esto no lo sustituye. */
      --elev-card-soft: inset 0 1px 0 rgba(255,255,255,.04), 0 1px 2px rgba(0,0,0,.3);
      --elev-control: var(--nu-raise-1);
      --elev-control-pressed: var(--nu-inset-1);
      --elev-field: var(--nu-inset-1);
      --elev-modal: var(--nu-raise-3);
      --elev-rail: 8px 0 28px rgba(0, 0, 0, .5);

      /* --field-section-line sigue viva: hairline de .hub-card y de otras
         cards de retícula modular, y base de --bg-hatch (dropzones/celdas
         vacías) — no es parte del patrón de puntos/reglas del fondo de
         página que se quitó (2026-09-17), así que no se toca. */
      --field-section-line: rgba(255, 255, 255, .2);
      --backdrop-blur: blur(14px) saturate(1.1);
      --bg-hatch: repeating-linear-gradient(45deg, var(--field-section-line) 0 1px, transparent 1px 7px);

      /* Tipografía (FOX-P124, artifact "Carve & Glow" def89764) — sustituye
         a Space Grotesk/IBM Plex Mono por las pilas -apple-system reales del
         artifact. Cambio global e intencional: --font-core/--font-mono se
         interpolan en TODA la interfaz de Fox, no solo en las pantallas
         visualizadas en el artifact (US7 audita regresiones de legibilidad
         en el resto). El artifact distingue --font-display (titulares
         grandes, 'SF Pro Display') de --font-text (cuerpo, 'SF Pro Text') —
         aquí se usa la pila de --font-text para --font-core porque
         producción no separa headings/body en variables distintas y
         --font-core cubre ambos usos. */
      --font-core: -apple-system, BlinkMacSystemFont, 'SF Pro Text', ui-sans-serif, system-ui, 'Helvetica Neue', Arial, sans-serif;
      --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, monospace;
      --fs-micro: 10px;
      --fs-tag: 11px;
      --fs-caption: 12px;
      --fs-small: 13px;
      --fs-body: 15px;
      --fs-lead: 18px;
      --fs-h4: 22px;
      --fs-h3: 28px;
      --fs-h2: 38px;
      --fs-h1: 52px;
      --lh-tight: 1.04;
      --lh-snug: 1.24;
      --lh-body: 1.55;
      --fw-light: 300;
      --fw-regular: 400;
      --fw-medium: 500;
      --fw-semibold: 600;
      --ls-display: -0.03em;
      --ls-heading: -0.015em;
      --ls-mono: 0.04em;
      --ls-label: 0.16em;
      --text-h1: var(--fw-medium) var(--fs-h1)/var(--lh-tight) var(--font-core);
      --text-h2: var(--fw-medium) var(--fs-h2)/var(--lh-snug) var(--font-core);
      --text-h3: var(--fw-medium) var(--fs-h3)/var(--lh-snug) var(--font-core);
      --text-h4: var(--fw-medium) var(--fs-h4)/var(--lh-snug) var(--font-core);
      --text-lead: var(--fw-regular) var(--fs-lead)/var(--lh-body) var(--font-core);
      --text-base: var(--fw-regular) var(--fs-body)/var(--lh-body) var(--font-core);
      --text-caption: var(--fw-regular) var(--fs-caption)/1.4 var(--font-core);
      --text-label: var(--fw-medium) var(--fs-micro)/1.2 var(--font-mono);
      --text-code: var(--fw-regular) var(--fs-caption)/1.5 var(--font-mono);

      /* Espaciado y geometría */
      --sp-1: 2px; --sp-2: 4px; --sp-3: 8px; --sp-4: 12px; --sp-5: 16px;
      --sp-6: 20px; --sp-7: 24px; --sp-8: 32px; --sp-9: 40px; --sp-10: 56px;
      --r-xs: 2px; --r-sm: 3px; --r-md: 5px; --r-lg: 8px; --r-xl: 12px; --r-pill: 999px;
      /* Esquinas redondeadas (FOX-P122, artifact "Carve & Glow") — sustituyen
         al --clip-notch cyberpunk en los botones de acento sólido (ver
         BUTTONS_CSS). --r-2xl para controles/chips medianos, --r-3xl para
         cards y superficies grandes. */
      --r-2xl: 18px; --r-3xl: 20px;
      --border-hair: 1px;
      --border-mark: 2px;
      --control-h-sm: 32px;
      --control-h-md: 42px;
      --control-h-lg: 52px;
      --grid-unit: 32px;
      --grid-cell: 192px;
      --rail-w: 64px;
      --sidebar-w: 300px;

      /* Movimiento */
      --dur-instant: 80ms;
      --dur-fast: 140ms;
      --dur-base: 220ms;
      --dur-slow: 360ms;
      --ease-out: cubic-bezier(.22,.61,.36,1);
      --ease-press: cubic-bezier(.3,0,.2,1);
      /* Spring literal del artifact "Carve & Glow" (--ease-spring en su
         THEME_VARS) — reservado para elementos que se DESPLAZAN de verdad
         (una pastilla deslizante, un grip de switch), nunca para fundidos
         de opacity/color/box-shadow (esos ya tienen --ease-out/--ease-press,
         curvas pensadas para "asentarse", no para el rebote de algo que se
         mueve). Consumidor real: .seg-thumb (SEG_CARVED_CSS). */
      --ease-spring: cubic-bezier(.16,1,.3,1);
      --t-press: box-shadow var(--dur-fast) var(--ease-press), color var(--dur-fast) linear;
      --t-surface: box-shadow var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
      --t-fade: opacity var(--dur-base) var(--ease-out);

      /* Alias de acento heredados — cada uno apunta a un acento de señal
         distinto para que las páginas aún no migradas sigan leyendo colores
         diferenciados en vez de un único tono repetido. */
      --brass: var(--signal-orange);
      --brass-bright: color-mix(in srgb, var(--signal-orange) 85%, white 15%);
      --teal: var(--signal-cyan-dim);
      --purple: var(--signal-violet);
      --red: var(--signal-red);
      --blue: var(--signal-cyan);
      --gold: var(--signal-mint);
      /* Simulan una nota física pegada a la página — no forman parte del
         sistema de tema, no se tocan entre claro/oscuro (ver nota histórica
         más abajo en THEME_MEDIA_CSS). */
      --paper: #f1ead8;
      --paper-ink: #2a2117;

      /* Alias genéricos hacia el sistema nuevo, consumidos ampliamente fuera
         de los renderers ya migrados. */
      --ink: var(--surface-base);
      --panel: var(--surface-card);
      --panel-2: var(--surface-sunken);
      --line: var(--line-hair);
      --line-faint: var(--line-soft);
      --text: var(--text-body);
      --text-dim: var(--text-muted);
      --bg: var(--ink);
      --card: var(--panel);
      --border: var(--line-faint);
      --muted: var(--text-dim);
      --accent: var(--brass);
      --serif: ui-serif, 'New York', Georgia, 'Times New Roman', serif;
      --mono: var(--font-mono);
      /* Texto sobre botones de acento sólido (.btn-primary/teal/purple) —
         fijo en ambos temas: el sistema nuevo pinta esos botones con blanco
         sobre el acento de señal (ver components/core/Button.jsx), no con
         tinta oscura como el sistema anterior. */
      --btn-ink: #ffffff;
      /* Texto de .tag-pill.active sobre su neón — oscuro en ambos temas. */
      --pill-ink: var(--surface-base);
      color-scheme: dark;

      --font-core: -apple-system, BlinkMacSystemFont, 'SF Pro Text', ui-sans-serif, system-ui, 'Helvetica Neue', Arial, sans-serif;

      /* --bg/--card/--border/--muted/--accent ya vienen de THEME_VARS — estos
         son los alias adicionales específicos del board (tabs/chat). */
      --accent-tasks: var(--signal-cyan);
      --accent-digest: var(--signal-cyan-dim);
      --accent-alerts: var(--signal-red);
      --chat-bg: var(--ink);
      --chat-card: var(--panel);
      --chat-border: var(--line-faint);
      
      --accent-calendar: var(--signal-violet);
      --accent-weather: var(--signal-cyan-dim);

    }

  @media (prefers-color-scheme: light) {
    :root {
      --fox-white: #FAFBFF;
      --fox-100: #EFF2F9;
      --fox-200: #E4EBF1;
      --fox-300: #D3DBE4;
      --fox-400: #B5BFC6;
      --fox-500: #93A0AB;
      --fox-600: #6E7F8D;
      --fox-700: #4A5762;
      --fox-800: #2A323B;
      --fox-900: #161B1D;

      --signal-cyan: #12D9CE;
      --signal-cyan-dim: #0FAEA6;
      --signal-orange: #F4610F;
      --signal-red: #E8342B;
      --signal-mint: #8ED9C1;
      --signal-violet: #6C6CF0;
      --signal-yellow: #C68C0F;
      --glow-accent: 0 0 0 1px rgba(18,217,206,.5), 0 0 18px rgba(18,217,206,.35);
      --glow-danger: 0 0 0 1px rgba(232,52,43,.5), 0 0 18px rgba(232,52,43,.35);

      /* Trío base/panel/pozo claro (FOX-P124, artifact def89764) — mismos
         valores literales que el bloque oscuro de arriba, ver comentario ahí. */
      --surface-base: #EDEFF3;
      --surface-card: #F5F6FA;
      --surface-sunken: #E6E9EE;
      --surface-rail: var(--fox-900);
      --surface-overlay: rgba(239, 242, 249, .72);
      --line-hair: rgba(22, 27, 29, .16);
      --line-soft: rgba(22, 27, 29, .08);
      --line-strong: rgba(22, 27, 29, .42);
      /* --elev-card-soft claro — mismo valor que .dh-card/.stage del
         artifact en su tema claro (ver comentario junto a la declaración
         oscura de este mismo token, arriba en este archivo). */
      --elev-card-soft: inset 0 1px 0 rgba(255,255,255,.5), 0 1px 2px rgba(30,34,44,.05);

      /* Sustituido (FOX-P122) — misma fórmula de dos luces que el bloque
         oscuro, con los valores claros confirmados en el artifact. */
      --nu-light: rgba(255, 255, 255, .95);
      --nu-dark: rgba(163, 177, 198, .58);
      --nu-dark-soft: rgba(163, 177, 198, .30);
      --nu-raise-1: 4px 4px 9px var(--nu-dark), -4px -4px 9px var(--nu-light);
      --nu-raise-2: 7px 7px 16px var(--nu-dark), -7px -7px 16px var(--nu-light);
      --nu-raise-3: 10px 10px 22px var(--nu-dark), -10px -10px 22px var(--nu-light);
      --nu-inset-1: inset 5px 5px 11px var(--nu-dark), inset -5px -5px 11px var(--nu-light);
      --nu-inset-2: inset 8px 8px 18px var(--nu-dark), inset -8px -8px 18px var(--nu-light);
      --elev-rail: 8px 0 24px rgba(22, 27, 29, .16);

      --glass-fill: rgba(255, 255, 255, .42);
      --glass-border: rgba(255, 255, 255, .65);
      --glaze-fill: rgba(255, 255, 255, .22);
      --glaze-border: rgba(255, 255, 255, .5);
      --neon-cyan-glow: rgba(18, 217, 206, .5);
      --neon-orange-glow: rgba(244, 97, 15, .5);
      --neon-red-glow: rgba(232, 52, 43, .5);
      --neon-mint-glow: rgba(142, 217, 193, .5);
      --neon-violet-glow: rgba(108, 108, 240, .5);
      --neon-violet: #6C6CF0;
      --neon-yellow-glow: rgba(198, 140, 15, .5);

      /* 6 acentos de neón "Carve & Glow" claros — ver comentario en el
         bloque oscuro de arriba. */
      --neon-pink: #FF2D78;
      --neon-pink-glow: rgba(255, 45, 120, .55);
      --neon-blue: #2E8CFF;
      --neon-blue-glow: rgba(46, 140, 255, .55);
      --neon-orange: #F4610F;
      --neon-green: #22C55E;
      --neon-green-glow: rgba(34, 197, 94, .6);
      --neon-teal: #0EC7A0;
      --neon-teal-glow: rgba(14, 199, 160, .5);
      --neon-red: #E8342B;

      --field-section-line: rgba(22, 27, 29, .22);

      /* En claro el vidrio blanco aclara el neón: tinta oscura para que la
         etiqueta se lea (feedback 2026-09-28, "Guardar"/"Hoy" en blanco). */
      --btn-ink: var(--fox-900);
      --pill-ink: var(--fox-900);

      color-scheme: light;
    }
  }



  @keyframes fox-blink { 0%, 45% { opacity: 1; } 50%, 95% { opacity: .15; } 100% { opacity: 1; } }
  @keyframes fox-scan { 0% { transform: translateY(-100%); } 100% { transform: translateY(400%); } }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  }

    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    html, body { height: 100dvh; overflow: hidden; }
    body { background: var(--bg); color: var(--text); font-family: var(--font-core); font-size: var(--fs-body); line-height: var(--lh-body); display: flex; flex-direction: column; -webkit-font-smoothing: antialiased; }
    ::selection { background: var(--signal-cyan); color: var(--fox-900); }
    /* Misma especificidad que la regla UA [hidden]{display:none} — un
       elemento con clase propia que fije su propio display (.btn, .glass-
       field, etc.) le gana por orden de cascada y se sigue mostrando pese
       a hidden=true. Ya existía este mismo fix en renderTaskDetailPage()
       (más abajo en este archivo) pero nunca se había interpolado aquí, en
       el shell compartido de renderBoard() — bug real detectado en vivo en
       la modal de detalle de evento de Inbox (FOX-P130, 2026-09-23):
       "Videollamada"/"Descripción" se veían aunque su fila tuviera
       hidden=true confirmado por JS. */
    [hidden] { display: none !important; }

    .btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-family: var(--font-core); font-size: 0.8rem; font-weight: var(--fw-medium); padding: 7px 18px; border-radius: var(--r-pill); border: 1px solid transparent; cursor: pointer; transition: var(--t-press); white-space: nowrap; line-height: 1.4; background: var(--surface-base); box-shadow: var(--elev-control); color: var(--text-body); }
    .btn:focus-visible { outline: 1px solid var(--signal-cyan); outline-offset: 2px; }
    .btn:disabled { opacity: 0.42; cursor: not-allowed; }
    .btn:not(:disabled):active { box-shadow: var(--elev-control-pressed); }
    /* Estado "en curso" (Guardando…/Descartando…) — el botón sigue legible
       mientras espera la respuesta en vez de apagarse como un disabled. */
    .btn[aria-busy="true"]:disabled { opacity: 0.85; cursor: progress; }
    .btn-spin { width: 11px; height: 11px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: btn-spin 700ms linear infinite; flex-shrink: 0; }
    @keyframes btn-spin { to { transform: rotate(360deg); } }
    @media (prefers-reduced-motion: reduce) { .btn-spin { animation-duration: 2s; } }
    .btn-primary, .btn-teal, .btn-purple, .btn-red { position: relative; z-index: 0; overflow: hidden; background: var(--surface-base); color: var(--btn-ink); box-shadow: var(--nu-raise-2); --mx: 0; --my: 0; }
    .btn-primary::before, .btn-teal::before, .btn-purple::before, .btn-red::before { content: ""; position: absolute; z-index: -2; inset: 12%; border-radius: inherit; filter: blur(9px); opacity: .9; }
    .btn-primary::after, .btn-teal::after, .btn-purple::after, .btn-red::after { content: ""; position: absolute; z-index: -1; inset: 0; border-radius: inherit; background: var(--glass-fill); backdrop-filter: blur(7px) saturate(160%); -webkit-backdrop-filter: blur(7px) saturate(160%); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 rgba(255,255,255,.35); }
    .btn-primary::before { background: var(--signal-orange); }
    .btn-primary:not(:disabled):hover::before { opacity: 1; filter: blur(11px); }
    .btn-teal::before { background: var(--signal-cyan-dim); }
    .btn-teal:not(:disabled):hover::before { opacity: 1; filter: blur(11px); }
    .btn-purple::before { background: var(--signal-violet); }
    .btn-purple:not(:disabled):hover::before { opacity: 1; filter: blur(11px); }
    /* CTA crítico — acento sólido rojo (FOX-P124, artifact exhibit 07
       ".btn-filled.tone-red", ej. "Eliminar workspace"), distinto de
       .btn-danger (hairline/tinted, ya existente): mismo tratamiento vidrio
       +neón que .btn-primary/.btn-teal/.btn-purple, reservado para la
       acción destructiva de máxima severidad de una vista cuando el
       hairline de .btn-danger no basta como advertencia. */
    .btn-red::before { background: var(--neon-red); }
    .btn-red:not(:disabled):hover::before { opacity: 1; filter: blur(11px); }
    /* Hover reactivo al cursor (FOX-P124, artifact exhibit 07 + iteración
       final del CTA: "¿y si reacciona al cursor? como si presionas un
       envoltorio de plástico, que se amolda a la presión"). --mx/--my las
       actualiza BUTTON_HOVER_SCRIPT por mousemove/mouseleave (-1..1 desde
       el centro del botón, delegado en document — ver más abajo en este
       fichero). El artifact anima 3 capas (.neon-under + sus propios
       ::before/::after) con fuerza decreciente 16%/12%/9%; aquí solo hay UNA
       capa de neón real (el ::before del botón, ver comentario de la receta
       "pseudo-elemento" más arriba) porque un pseudo-elemento no puede tener
       sus propios pseudo-elementos anidados — se aplica el tirón más fuerte
       (16%) a esa única capa en vez de repartirlo en tres. */
    .btn-primary::before, .btn-teal::before, .btn-purple::before, .btn-red::before { transition: transform 260ms cubic-bezier(.34,1.56,.64,1), opacity 130ms ease, filter 130ms ease; }
    .btn-primary:not(:disabled):hover::before, .btn-teal:not(:disabled):hover::before, .btn-purple:not(:disabled):hover::before, .btn-red:not(:disabled):hover::before { transform: translate(calc(var(--mx, 0) * 16%), calc(var(--my, 0) * 16%)); }
    .btn-primary:not(:disabled):active, .btn-teal:not(:disabled):active, .btn-purple:not(:disabled):active, .btn-red:not(:disabled):active { box-shadow: var(--elev-control-pressed); }
    /* btn-ghost/btn-danger (FOX-P122, ya existían — auditados contra el
       artifact def89764 exhibit 07 en FOX-P124, no reescritos desde cero).
       Único delta real: el hover leía --signal-cyan-dim/--red (los acentos
       heredados) en vez de los --neon-* nuevos de FOX-T937 — mismo tono
       conceptual (teal/rojo) pero valor hex distinto. El hairline pill del
       artifact usa box-shadow:inset 0 0 0 1px, aquí se mantiene la
       implementación real vía border-color (heredado de .btn) — visualmente
       equivalente, sin motivo para tocarlo. */
    .btn-ghost { background: transparent; box-shadow: none; color: var(--text-body); border-color: var(--line-hair); }
    .btn-ghost:not(:disabled):hover { color: var(--neon-teal); border-color: var(--neon-teal); }
    .btn-danger { background: color-mix(in srgb, var(--neon-red) 14%, transparent); box-shadow: none; color: var(--neon-red); border-color: color-mix(in srgb, var(--neon-red) 40%, transparent); }
    .btn-danger:not(:disabled):hover { background: var(--neon-red); color: var(--btn-ink); box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 0 16px 2px var(--neon-red-glow); }
    /* btn-tinted/btn-plain (FOX-P124, artifact def89764 exhibit 07) — no
       existían en producción, se introducen aquí por primera vez (no hay
       clase real previa que "no renombrar", como sí pasaba con .btn-primary/
       .btn-filled). Valores traducidos a los tokens reales de producción:
       --field-deep del artifact es --surface-sunken aquí (mismo valor
       literal, ver FOX-T937); --neon-orange ya existe desde esa misma tarea.
       Comentario original del artifact tras feedback de Emilio: "algunos de
       los botones no tienen efectos de hover... puedes revisarlo?" — ambos
       solo tenían :active, sin :hover previo que marcara el estado de
       reposo. */
    .btn-tinted { background: transparent; box-shadow: none; color: var(--text-body); padding: 13px 12px; border-radius: var(--r-xl); transition: transform 130ms ease, opacity 130ms ease, background 160ms ease; }
    .btn-tinted:not(:disabled):hover { background: var(--surface-sunken); }
    .btn-tinted:not(:disabled):active { transform: scale(.97); opacity: .6; }
    .btn-plain { background: none; box-shadow: none; color: var(--neon-orange); padding: 13px 6px; transition: opacity 160ms ease; }
    .btn-plain:not(:disabled):hover { opacity: .75; }
    .btn-plain:not(:disabled):active { opacity: .55; }
    .btn-sm { font-size: 0.74rem; padding: 4px 14px; }
    .fx-badge-solid { position: relative; z-index: 0; overflow: hidden; }
    .fx-badge-solid::before { content: ""; position: absolute; z-index: -2; inset: 10%; border-radius: inherit; background: var(--tone-color); filter: blur(6px); opacity: .9; }
    .fx-badge-solid::after { content: ""; position: absolute; z-index: -1; inset: 0; border-radius: inherit; background: var(--glass-fill); backdrop-filter: blur(5px) saturate(160%); -webkit-backdrop-filter: blur(5px) saturate(160%); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 rgba(255,255,255,.3); }


  .neon-under { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border-radius: 999px; pointer-events: none; z-index: 0; }
  .glass-glaze { position: absolute; inset: 3px; border-radius: 8px; background: var(--glass-fill); backdrop-filter: blur(6px) saturate(160%); -webkit-backdrop-filter: blur(6px) saturate(160%); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 rgba(255,255,255,.25); display: flex; align-items: center; justify-content: center; z-index: 1; pointer-events: none; }
  .glass-glaze svg { position: relative; z-index: 2; }
  /* .icon-off/.icon-on (patrón "dos iconos que cruzan por display", exhibit
     08/12 del artifact) — feedback de Emilio 2026-09-18 comparando el
     checkbox/radio contra el artifact: "los iconos están mal alineados, no
     están en el centro". Causa real: cada consumidor conmuta
     display:block/none (ver CHECK_ICON_CSS y el checklist de protocolos en
     daily-hub-renderer.ts) — un <span> display:block con un <svg>/<i
     data-lucide> dentro no lo centra, solo lo apila en flujo normal. Estas
     dos propiedades no hacen nada mientras display:none, pero en cuanto el
     consumidor pasa el estado visible a display:flex (ver esos dos
     archivos) centran de verdad — un solo sitio para la receta en vez de
     repetirla por consumidor. */
  .icon-off, .icon-on { align-items: center; justify-content: center; }


  .field-carved { position: relative; padding: 16px 18px 16px 34px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); transition: box-shadow var(--dur-base) var(--ease-out); }
  .field-carved:focus-within { box-shadow: var(--nu-inset-2); }
  .field-carved-accent { position: absolute; left: 10px; top: 14px; bottom: 14px; width: 5px; border-radius: var(--r-sm); background: var(--text-faint); box-shadow: none; transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); }
  .field-carved:focus-within .field-carved-accent.tone-pink { background: var(--neon-pink); box-shadow: 0 0 12px 1px var(--neon-pink-glow); }
  .field-carved:focus-within .field-carved-accent.tone-teal { background: var(--neon-teal); box-shadow: 0 0 12px 1px var(--neon-teal-glow); }
  .field-carved:focus-within .field-carved-accent.tone-orange { background: var(--neon-orange); box-shadow: 0 0 12px 1px var(--neon-orange-glow); }
  .field-carved:focus-within .field-carved-accent.tone-green { background: var(--neon-green); box-shadow: 0 0 12px 1px var(--neon-green-glow); }
  .field-carved:focus-within .field-carved-accent.tone-blue { background: var(--neon-blue); box-shadow: 0 0 12px 1px var(--neon-blue-glow); }
  .field-carved:focus-within .field-carved-accent.tone-red { background: var(--neon-red); box-shadow: 0 0 12px 1px var(--neon-red-glow); }
  .field-carved-key { font: var(--fw-medium) 11px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); margin-bottom: 6px; }
  .field-carved-value { font: 400 15px/1 var(--font-core); color: var(--text-body); }
  .field-carved-value.is-placeholder { color: var(--text-faint); }
  /* Reset de chrome nativo cuando .field-carved-value es un <input> real
     (no el <div> de solo lectura del specimen de la Guía) — antes duplicado
     por cada consumidor (ver .agent-detail-password-row en
     board-renderer.ts); centralizado aquí para que cualquier campo nuevo lo
     herede sin repetir la receta. */
  input.field-carved-value { display: block; width: 100%; background: none; border: none; outline: none; padding: 0; }
  input.field-carved-value::placeholder { color: var(--text-faint); }


  .search-carved { position: relative; flex: 1; display: flex; align-items: center; gap: 10px; padding: 8px 16px 8px 8px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); }
  .search-icon-wrap { width: 28px; height: 28px; border-radius: 10px; position: relative; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); transition: box-shadow var(--dur-base) var(--ease-out); }
  .search-icon-wrap .neon-under { width: 78%; height: 78%; border-radius: 7px; background: var(--neon-blue); box-shadow: 0 0 8px 2px var(--neon-blue-glow); opacity: 0; transition: opacity var(--dur-base) var(--ease-out); }
  .search-icon-wrap .glass-glaze { inset: 2px; border-radius: 7px; opacity: 0; transition: opacity var(--dur-base) var(--ease-out); }
  .search-carved:focus-within .search-icon-wrap { box-shadow: 4px 4px 9px var(--nu-dark), -4px -4px 9px var(--nu-light); }
  .search-carved:focus-within .search-icon-wrap .neon-under,
  .search-carved:focus-within .search-icon-wrap .glass-glaze { opacity: 1; }
  .search-icon-wrap svg, .search-icon-wrap i { width: 14px; height: 14px; color: var(--text-faint); position: relative; z-index: 2; transition: color var(--dur-base) var(--ease-out); }
  .search-carved:focus-within .search-icon-wrap svg, .search-carved:focus-within .search-icon-wrap i { color: var(--text-strong); }
  .search-carved input { border: none; background: none; outline: none; font: 400 14px var(--font-core); color: var(--text-body); width: 100%; }
  .search-carved input::placeholder { color: var(--text-faint); }


  .tag-pill { display: inline-flex; align-items: center; position: relative; z-index: 0; overflow: hidden; padding: 4px 10px; border-radius: var(--r-pill); background: none; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text-muted) 45%, transparent); font: 600 12px/1 var(--font-core); color: var(--text-muted); text-decoration: none; border: none; cursor: pointer; }
  .tag-pill .pill-neon { position: absolute; inset: 10%; border-radius: inherit; background: var(--tag-color, var(--neon-teal)); filter: blur(6px); opacity: 0; z-index: 0; transition: opacity var(--dur-base) var(--ease-out); pointer-events: none; }
  .tag-pill .glass-glaze { inset: 0; border-radius: inherit; opacity: 0; transition: opacity var(--dur-base) var(--ease-out); }
  .tag-pill .pill-spark { display: none; }
  .tag-pill .tag-text { position: relative; z-index: 2; }
  .tag-pill.active { color: var(--pill-ink); box-shadow: none; }
  .tag-pill.active .pill-neon { opacity: .9; }
  .tag-pill.active .glass-glaze { opacity: 1; }
  /* Hover en las pills no-activas (feedback: "también le falta el hover en
     los Día anterior y día siguiente") — antes no daban ninguna señal
     visual al pasar el ratón, indistinguibles de un elemento inerte pese a
     ser enlaces reales. Un adelanto tenue del propio neón+vidrio de
     .active (mismo tono por defecto, --neon-teal) en vez de un color
     nuevo — se lee como "esto es interactivo", no como "esto ya está
     seleccionado" (la diferencia de opacidad frente a .active es a
     propósito). No aplica sobre .active: ya está encendida del todo, un
     hover no le suma nada. */
  .tag-pill:not(.active):hover { color: var(--text-body); }
  .tag-pill:not(.active):hover .pill-neon { opacity: .3; }
  .tag-pill:not(.active):hover .glass-glaze { opacity: .6; }
  .tag-pill.tone-pink { --tag-color: var(--neon-pink); }
  .tag-pill.tone-orange { --tag-color: var(--neon-orange); }
  .tag-pill.tone-green { --tag-color: var(--neon-green); }
  .tag-pill.tone-blue { --tag-color: var(--neon-blue); }
  .tag-pill.tone-red { --tag-color: var(--neon-red); }


  .page-hero { position: sticky; top: 14px; z-index: 5; padding: 20px 28px 22px; border-radius: var(--r-3xl); background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); }
  .hero { position: relative; }
  .eyebrow { font: var(--fw-semibold) var(--fs-tag)/1 var(--font-core); letter-spacing: .06em; text-transform: uppercase; color: var(--neon-pink); margin: 0 0 14px; }
  /* Botón de volver DENTRO de la tarjeta, al lado del eyebrow (FOX-P130,
     feedback: "mete la flechita dentro del header, al lado del daily
     standup" — antes vivía fuera de .page-hero, como un link de texto
     suelto encima de la tarjeta). .hero-top es el wrapper de esa fila;
     cuando existe, su propio margin-bottom sustituye al de .eyebrow (que
     pasa a 0 dentro de esta fila) para no duplicar el espacio antes del
     h1. Icono discreto, no un link de texto — misma receta de 3 capas
     (base raised + neon-under + glass-glaze) que el resto de iconos del
     sistema, pero con el neon-under en --text-muted en vez de un acento
     de color saturado: un glow gris, no una llamada de atención. Todo
     consumidor de .page-hero puede adoptar esta fila (ver style guide
     HEADER-001) — no es exclusiva del Daily Hub. */
  .hero-top { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
  .hero-top .eyebrow { margin: 0; }
  /* Como <button> (pantalla completa, play, transcripción, semana…) debe
     verse igual que el <a> de volver: sin padding/borde/fuente de botón. */
  button.hero-back-btn { padding: 0; border: none; font: inherit; color: inherit; cursor: pointer; -webkit-appearance: none; appearance: none; }
  .hero-back-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: var(--r-lg); flex: 0 0 auto; background: var(--surface-card); box-shadow: var(--nu-raise-1); text-decoration: none; transition: box-shadow var(--dur-fast) var(--ease-press), transform var(--dur-fast) var(--ease-press); }
  .hero-back-btn:active { box-shadow: var(--nu-inset-1); transform: scale(.96); }
  .hero-back-btn .neon-under { width: 58%; height: 58%; background: var(--text-muted); box-shadow: 0 0 8px 2px var(--text-muted); opacity: .3; transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out); }
  /* Hover en azul (feedback: "estaría bien que tuviera efecto on hover y
     ahí sí se iluminara en algún color, en azul por ejemplo") — el gris en
     reposo se queda en reposo; el acento de color solo aparece como
     respuesta a la interacción, igual que el resto de iconos del sistema. */
  .hero-back-btn:hover .neon-under { background: var(--neon-blue); box-shadow: 0 0 10px 2px var(--neon-blue-glow); opacity: .7; }
  .hero-back-btn .glass-glaze { inset: 4px; border-radius: 7px; }
  .hero-back-btn .glass-glaze i, .hero-back-btn .glass-glaze svg { color: var(--text-body); }
  .hero h1 { font: var(--fw-medium) var(--fs-h1)/1.05 var(--font-core); letter-spacing: -0.02em; margin: 0; text-wrap: balance; }
  /* Título de dos colores (FOX-P130, Daily Hub — feedback: "el título
     tiene que tener dos colores, como en el ejemplo") — calcado del
     artifact de referencia, donde el segmento variable del título (p.ej.
     el mes en "Viernes, 18 de <em>septiembre</em>") lleva el acento
     naranja mientras el resto queda en el color de texto normal. Aditivo:
     ningún consumidor actual de PAGE_HERO_CSS usa <em> en su h1 todavía,
     así que esto no cambia nada hasta que alguno lo adopte. */
  .hero h1 em { font-style: normal; color: var(--neon-orange); }


  .seg-carved { position: relative; display: inline-flex; align-items: center; gap: 2px; padding: 5px; border-radius: var(--r-pill); background: var(--surface-sunken); box-shadow: var(--elev-field); }
  .seg-thumb { position: absolute; top: 5px; left: 5px; width: 0; height: calc(100% - 10px); border-radius: var(--r-pill); z-index: 0; pointer-events: none; transition: left 240ms var(--ease-spring), width 240ms var(--ease-spring); }
  .seg-thumb .neon-under { position: absolute; inset: 0; transform: none; width: 100%; height: 100%; border-radius: var(--r-pill); background: var(--neon-blue); box-shadow: 0 0 8px 2px var(--neon-blue-glow); }
  .seg-thumb .glass-glaze { inset: 0; border-radius: var(--r-pill); }
  .seg-carved button { position: relative; z-index: 2; font: var(--fw-semibold) var(--fs-caption)/1 var(--font-core); color: var(--text-muted); background: none; border: none; padding: 6px 14px; border-radius: var(--r-pill); cursor: pointer; white-space: nowrap; transition: color var(--dur-base) var(--ease-out); }
  .seg-carved button.active { color: var(--text-body); }


  .check-icon { position: relative; width: 34px; height: 34px; border-radius: 11px; flex: 0 0 auto; background: var(--surface-card); box-shadow: var(--nu-raise-1); transition: box-shadow var(--dur-fast) var(--ease-press), transform var(--dur-fast) var(--ease-press); }
  /* Efecto de pulsado (FOX-P125, feedback de Emilio 2026-09-18 comparando
     contra el artifact — "no tienen el efecto de pulsado"): calcado de
     .toggle-demo:active del artifact — el raise se hunde a carved
     (--nu-inset-1) + encoge un 4% mientras se mantiene pulsado, en vez de
     no reaccionar en absoluto como antes. */
  .check-icon:active { box-shadow: var(--nu-inset-1); transform: scale(.96); }
  .check-icon .icon-off { display: flex; }
  .check-icon .icon-on { display: none; }
  .check-icon.on .icon-off { display: none; }
  .check-icon.on .icon-on { display: flex; }
  /* Activación genérica vía :has() para cualquier <label> que envuelva un
     <input> real + .check-icon (checkbox o radio) — consumidores con su
     propia clase de label (p.ej. .hub-protocol-label) pueden seguir
     escribiendo su propia regla más específica si lo necesitan; esta
     cubre el caso genérico (specimens de la Guía, futuros consumidores)
     sin CSS ad-hoc por sitio. */
  label:has(> input:checked) .check-icon .icon-off { display: none; }
  label:has(> input:checked) .check-icon .icon-on { display: flex; }


  .slider-wrap { display: inline-flex; flex-direction: column; gap: 10px; }
  .slider-input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
  .slider-carved { position: relative; width: 220px; height: 10px; border-radius: var(--r-pill); background: var(--surface-sunken); box-shadow: inset 3px 3px 6px var(--nu-dark), inset -3px -3px 6px var(--nu-light); cursor: pointer; touch-action: none; }
  .slider-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--r-pill); overflow: hidden; z-index: 0; pointer-events: none; }
  .slider-fill .neon-under { position: absolute; inset: 0; transform: none; border-radius: inherit; }
  .slider-fill .glass-glaze { inset: 0; border-radius: inherit; }
  .slider-thumb { position: absolute; top: 50%; width: 22px; height: 22px; border-radius: 50%; background: var(--surface-card); box-shadow: var(--nu-raise-1), 0 0 0 3px var(--surface-sunken); transform: translate(-50%, -50%); cursor: grab; z-index: 2; }
  .slider-thumb:active { cursor: grabbing; }


  .tooltip-bubble { background: var(--text-body); color: var(--surface-base); font: 600 12px/1.4 var(--font-core); padding: 7px 11px; border-radius: var(--r-lg); box-shadow: var(--nu-raise-2); }
  .tooltip-bubble.with-arrow { position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%); white-space: nowrap; }
  .tooltip-bubble.with-arrow::after { content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: 5px solid transparent; border-top-color: var(--text-body); }
  /* .tt-anchor — receta real .tt-anchor2 del exhibit 11: ancla con
     position:relative, el bubble aparece al pasar el cursor (o foco de
     teclado) en vez de estar siempre visible como en la demo estática
     del artifact. Consumidor: specimen FEEDBACK-003 de la Guía. */
  .tt-anchor { position: relative; display: inline-block; }
  .tt-anchor .tooltip-bubble.with-arrow { opacity: 0; visibility: hidden; transition: opacity var(--dur-base) var(--ease-out); }
  .tt-anchor:hover .tooltip-bubble.with-arrow, .tt-anchor:focus-within .tooltip-bubble.with-arrow { opacity: 1; visibility: visible; }


  .toast-host { position: fixed; top: 20px; left: 50%; transform: translateX(-50%) translateY(-12px); z-index: 200; opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out); }
  .toast-host.visible { opacity: 1; transform: translateX(-50%) translateY(0); }
  .toast { display: inline-flex; align-items: center; gap: 10px; padding: 10px 18px 10px 12px; border-radius: var(--r-pill); background: var(--surface-card); box-shadow: var(--nu-raise-2); }
  .toast-icon-wrap { position: relative; width: 24px; height: 24px; border-radius: 50%; flex: 0 0 auto; box-shadow: var(--nu-raise-1); }
  .toast-icon-wrap .glass-glaze { border-radius: 50%; }
  .toast-text { font: var(--fw-medium) 13px/1.3 var(--font-core); color: var(--text-body); white-space: nowrap; }


  .sheet-overlay { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; background: var(--surface-overlay); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease-out); }
  .sheet-overlay.visible { opacity: 1; pointer-events: auto; }
  .sheet { width: 100%; max-width: 300px; border-radius: var(--r-3xl); background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); padding: 24px 22px 20px; text-align: center; transform: scale(.96); transition: transform var(--dur-base) var(--ease-out); }
  .sheet-overlay.visible .sheet { transform: scale(1); }
  .sheet-icon-wrap { position: relative; width: 48px; height: 48px; border-radius: 50%; margin: 0 auto 14px; }
  .sheet-icon-wrap .glass-glaze { border-radius: 50%; }
  .sheet h3 { margin: 0 0 8px; font: 600 17px/1.3 var(--font-core); color: var(--text-strong); }
  .sheet p { margin: 0 0 20px; font: 400 13px/1.5 var(--font-core); color: var(--text-muted); }
  .sheet-actions { display: flex; flex-direction: column; gap: 10px; }
  .sheet-actions .btn { width: 100%; }


  /* Gráfico de barras verticales — regla literal del propio readme.md del
     design system ("Square bars... Bars are --fox-600; exactly one may be
     cyan to mark the point under discussion"), no un color distinto por
     nutriente como en el intento anterior. Vive DENTRO de la hub-card de
     Nutrición, no suelto en la página — el body de esa card ya reserva la
     altura (flex:1) que .nutrient-bars necesita al 100%. */
  /* Columna flex propia (no el height:100% suelto de .nutrient-bars) para
     que la nota de objetivos debajo tenga su alto reservado en vez de
     competir con las barras por el mismo 100% y forzar scroll. */
  .nutrition-card-body { display: flex; flex-direction: column; height: 100%; }
  .nutrition-card-body .nutrient-note { flex-shrink: 0; margin-top: 10px; }
  .dish-servings { flex-shrink: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line-hair); }
  .nutrition-card-body .dish-servings .nutrient-note { margin-top: 0; }
  .dish-servings .dish-occasional { flex-basis: 100%; display: flex; align-items: center; gap: 8px; cursor: pointer; }
  .dish-servings .dish-occasional input { width: 16px; height: 16px; padding: 0; box-shadow: none; accent-color: var(--neon-orange); }
  .dish-servings input { width: 64px; height: 32px; padding: 0 8px; border: none; border-radius: var(--r-md); box-shadow: var(--nu-inset-1); background: var(--surface-sunken); color: var(--text-body); font: 500 13px var(--font-mono); }
  /* max-height fijo, no flex:1 — con flex:1 las barras se estiraban a
     ocupar TODO el alto libre de la card (~370px), y la más alta (siempre
     resaltada en cian) acababa chocando visualmente con la marca de agua
     de fondo (detectado por Emilio: "001" se mezclaba con la barra de
     SODIO). Dejar las barras más bajas de lo que la card permite crea el
     colchón de aire que la marca de agua necesita para leerse limpia,
     SIN tocar el alto de la card (maxHeightPx sigue igual). */
  /* height fijo, no max-height suelto — las columnas de dentro dependen de
     height:100% para calcular el alto de cada barra (ver
     .nutrient-bar-col), y un porcentaje contra un contenedor sin alto
     propio (auto) resuelve a 0: con solo max-height, sin un valor base,
     las barras colapsaban a una raya invisible en vez de quedarse más
     bajas — justo lo que pas al primer intento de este ajuste. */
  .nutrient-bars { flex: 0 0 260px; height: 260px; display: flex; align-items: flex-end; gap: 4px; border-bottom: 1px solid var(--line-hair); }
  /* overflow:hidden — sin esto, en una card angosta (Nutrición pasó de
     ocupar las 4 columnas a solo 2, ver renderDishDetailPage) el label de
     una columna se salía visualmente sobre la vecina en vez de truncarse
     (".nutrient-bar-axis" no tiene ancho propio, así que un texto más
     ancho que la columna sangraba sin cortarse ni envolver). */
  .nutrient-bar-col { flex: 1; min-width: 0; height: 100%; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 5px; padding: 4px 2px; border-radius: var(--r-xs); transition: var(--t-press); }
  /* Reacciona al touch (mandamiento del design system, ver readme.md) —
     la columna entera es la zona de hover, no la barra sola. */
  .nutrient-bar-col:hover { box-shadow: var(--elev-control); }
  .nutrient-bar-reading { width: 100%; font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center; }
  .nutrient-bar-track { flex: 1; width: 100%; max-width: 22px; display: flex; align-items: flex-end; }
  /* Extruida de verdad (--elev-control), no un relleno de color plano —
     es la "sensación de bloque 3D" que el primer intento no tenía. La
     única resaltada en cian (ver renderNutrientBars) suma --glow-accent. */
  .nutrient-bar-fill { width: 100%; background: var(--fox-600); box-shadow: var(--elev-control); transition: height var(--dur-slow) var(--ease-out), filter var(--dur-fast) var(--ease-press), box-shadow var(--dur-fast) var(--ease-press); }
  /* Vidrio+neón REAL (FOX-P124, artifact "Carve & Glow" exhibit 09
     ".bar-col.lit") solo en la barra destacada — la única con permiso
     para "brillar" según la regla del readme ("exactly one may be
     cyan"); las de exceso (moderate/severe, debajo) se quedan en color
     plano sin highlight. Antes era una aproximación de una sola capa de
     box-shadow (comentario decía "22px demasiado estrecha para que un
     blur se note" — sin verificar contra el artifact real, que sí aplica
     backdrop-filter a una barra de 28px sin problema); ahora son los dos
     hijos reales .neon-under/.glass-glaze de GLASS_ICON_CSS, ambos
     inset:0 (cubren la barra entera, cuadrada — sin border-radius, regla
     "Square bars" del design system que el artifact no comparte y no
     hay motivo para romper aquí). */
  .nutrient-bar-fill-highlight { position: relative; overflow: hidden; background: var(--signal-cyan); box-shadow: var(--elev-control), var(--glow-accent), inset 0 1px 0 rgba(255,255,255,.4); }
  .nutrient-bar-fill-highlight .neon-under { position: absolute; inset: 0; top: 0; left: 0; transform: none; width: 100%; height: 100%; background: var(--neon-teal); }
  .nutrient-bar-fill-highlight .glass-glaze { inset: 0; border-radius: 0; }
  /* Aviso de exceso (grasa/carbo/azúc/sodio por encima del 100% de su VRN) —
     color según severidad, SIN --glow-accent: el glow marca "la única cosa
     en vivo" (la proteína, ver .nutrient-bar-fill-highlight) y aquí puede
     haber varias barras avisando a la vez, así que usan solo color + la
     misma elevación que el resto, nunca el glow reservado. */
  .nutrient-bar-fill-moderate { background: var(--signal-orange); }
  .nutrient-bar-fill-severe { background: var(--signal-red); }
  .nutrient-bar-col:hover .nutrient-bar-fill { box-shadow: var(--nu-raise-1); filter: brightness(1.15); }
  .nutrient-bar-col:hover .nutrient-bar-fill-highlight { box-shadow: var(--nu-raise-1), var(--glow-accent), inset 0 1px 0 rgba(255,255,255,.4); }

    /* RICH_ROW_CSS (theme.ts) — faltaba interpolar aquí (auditoría FOX-P130 al
       migrar Documentos): solo vivía en el <style> de renderTaskDetailPage(),
       nunca en el shell compartido de renderBoard(). Los specimens RICHROW-
       001/002/003 de la Guía de Estilo (Settings, una tab de este mismo shell)
       llevaban tiempo sin CSS real detrás de ese bug — mismo tipo de fallo que
       "importar y usar pero no interpolar" que ya rompió el layout una vez. */

  .rich-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-top: 1px solid var(--line-hair); text-decoration: none; color: inherit; }
  .rich-row:first-child { border-top: none; padding-top: 0; }
  .rich-row:last-child { padding-bottom: 0; }
  /* box-shadow/tamaño del punto igualados a .check-icon (FOX-P130, feedback:
     "el estilo de los primeros [Protocolos, .check-icon] es el que quiero
     que tengan el resto... no sé qué combinación de opacidad del cristal y
     de iluminación/color/espacio del neón de debajo, pero queda muchísimo
     mejor") — la diferencia real: .check-icon usaba var(--nu-raise-1),
     este usaba una sombra más floja (--nu-dark-soft, 3px/7px en vez de
     4px/9px), y su punto de neón era más grande en proporción (58% vs los
     ~41% de .check-icon) — un punto más pequeño sobre una caja con más
     contraste de relieve se lee como más "encendido", no menos. */
  .rich-icon { width: 36px; height: 36px; border-radius: 12px; position: relative; flex: 0 0 auto; background: var(--surface-card); box-shadow: var(--nu-raise-1); }
  .rich-icon .neon-under { width: 44%; height: 44%; }
  .rich-icon .glass-glaze { inset: 4px; border-radius: 9px; }
  .rich-icon svg, .rich-icon i { width: 15px; height: 15px; position: relative; z-index: 2; color: var(--text-body); }
  .rich-icon.muted { opacity: .45; }
  .rich-body { flex: 1; min-width: 0; }
  .rich-title { display: block; font: 500 13.5px/1.4 var(--font-core); color: var(--text-body); transition: color 160ms ease; }
  .rich-sub { display: block; font: 400 12px/1.3 var(--font-core); color: var(--text-muted); margin-top: 2px; }
  a.rich-row:hover .rich-title, button.rich-row:hover .rich-title { color: var(--signal-cyan-dim); }
  .rich-row .chev { width: 14px; height: 14px; color: var(--text-muted); flex: 0 0 auto; }
  /* Reset de <button>: el layout (flex/gap/padding/divisor) lo pone
     .rich-row de arriba — esto solo quita el chrome nativo del elemento,
     incluido text-align:center (el user-agent stylesheet centra <button>
     por defecto, lo que descentraba el título respecto al icono). */
  button.rich-row { appearance: none; border: none; background: none; width: 100%; text-align: left; font: inherit; cursor: pointer; }


    
    /* ── Sidebar (rail de iconos colapsable) ──
       Colapsado se reduce a un rail de solo iconos (nunca desaparece del
       todo, min-width fijo); expandido restaura brand + etiquetas de cada
       item. Inbox queda suelto al principio (vista por defecto, fuera de
       cualquier bloque); el resto de .tab se agrupan en .tab-group
       (Productividad, Documentos/Finanzas/Conocimiento, Salud) con un
       encabezado .tab-group-label.
       El campo es plano fuera de la retícula (rail/sidebar/header) — el
       sidebar usa --surface-base, la MISMA superficie que el resto de la
       página, y sigue el tema claro/oscuro igual que todo lo demás. Los
       alias --sidebar-text* solo evitan repetir var(--text-body) diez veces
       en este bloque; no fijan un tema propio (--surface-rail, siempre
       oscuro, no es para esto — quedó mal aplicado en un borrador previo). */
    /* Colapsa/expande SOLO con :hover (FOX-P124, artifact "Carve & Glow"
       exhibit 20, pedido explícito de Emilio: "que se colapse y se
       despliegue cuando haga hover") — sin JS, sin botón de hamburguesa
       (se quitó: hacía la misma función que el propio hover). 64px/320px
       (antes 56px/240px) — Emilio pidió ensanchar la expandida para que
       el calendario/tiempo quepan a tamaño natural en vez de comprimidos.
       Todas las reglas '.sidebar.collapsed X' de más abajo pasan a
       '.sidebar:not(:hover) X' — el estado por defecto (sin hover) ES el
       colapsado, ya no hay una clase JS que alternar. */
    /* position:fixed (FOX-P124, ronda 3, feedback de Emilio: "no voy a
       querer que la sidebar empuje al contenido, sino que se quede por
       encima cuando se expande") — antes era un flex child en flujo, así
       que pasar de 64→320px empujaba .board-col entero. Ahora sale del
       flujo del todo y flota sobre el contenido al expandirse; .board-col
       (en cada página que monta este sidebar) reserva un margin-left fijo
       de 64px — el ancho de REPOSO, no el expandido — para que el rail
       colapsado nunca tape nada, y la expansión por :hover simplemente
       se superponga por encima sin desplazar nada debajo. */
    .sidebar {
      position: fixed; top: 0; left: 0; z-index: 60; height: 100vh; width: 64px; display: flex; flex-direction: column;
      background: var(--surface-base); box-shadow: var(--elev-rail); flex-shrink: 0;
      transition: width var(--dur-base) var(--ease-out); overflow: hidden;
      --sidebar-text: var(--text-body); --sidebar-text-dim: var(--text-muted); --sidebar-line: var(--line-hair);
    }
    .sidebar:hover { width: 320px; }
    /* Tooltip de los items del sidebar (sustituye al title nativo) —
       fuera del .sidebar (overflow:hidden lo recortaría), anclado al borde
       del sidebar YA expandido: left = 320px + separación. Pastilla
       invertida como .tooltip-bubble, con la tecla de atajo en un <kbd>. */
    .sidebar-tip {
      position: fixed; left: calc(320px + 12px); z-index: 90; pointer-events: none;
      display: flex; align-items: center; gap: 10px;
      background: var(--text-strong); color: var(--surface-base);
      font: 500 13px/1 var(--font-core); padding: 7px 8px 7px 12px; border-radius: 10px;
      box-shadow: var(--nu-raise-2); white-space: nowrap;
      opacity: 0; transform: translate(-4px, -50%); transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
    }
    .sidebar-tip.show { opacity: 1; transform: translate(0, -50%); }
    .sidebar-tip:not(:has(kbd)) { padding-right: 12px; }
    .sidebar-tip kbd {
      font: 500 12px/1 var(--font-core); min-width: 20px; height: 20px; padding: 0 5px;
      display: inline-flex; align-items: center; justify-content: center; border-radius: 4px;
      background: color-mix(in srgb, var(--surface-base) 22%, transparent); color: var(--surface-base);
    }
    /* Separadores de sección — línea única var(--line-hair) (corregido,
       ronda 3: la textura "grabada" de 2 capas, --field-section-line +
       nu-light, era la receta de --elev-card/otras superficies del
       sistema, NO la del sidebar del artifact — exhibit 20 real:
       .sb-section+.sb-section{box-shadow:inset 0 1px 0 var(--sep)}, una
       sola línea de 10% de opacidad. --line-hair (14-16%) es el token ya
       existente en el sistema más cercano a ese --sep del artifact.
       Feedback de Emilio: "los separadores son más claritos en el
       artefacto". */
    .sidebar-header { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-5) var(--sp-4); flex-shrink: 0; box-shadow: inset 0 -1px 0 var(--line-hair); }
    .sidebar:not(:hover) .sidebar-header { justify-content: center; padding: var(--sp-5) var(--sp-3); }
    /* .brand — receta real .sb-brand del artifact (exhibit 20): texto "Fox"
       suelto en el propio font-display, sin chip/fondo propio — antes era
       un chip cuadrado raise con "FOX" en mono mayúscula, una aproximación
       que no coincide con el artifact (feedback de Emilio, FOX-P124: "no
       has cambiado el botón"). Sigue siendo el botón de reiniciar Fox —
       mismo comportamiento, solo la piel visual cambia. */
    .brand { background: none; box-shadow: none; border: none; padding: 0; display: flex; align-items: center; gap: 7px; font: 700 15px/1 var(--font-core); letter-spacing: -0.01em; color: var(--sidebar-text); flex-shrink: 0; overflow: hidden; white-space: nowrap; cursor: pointer; }
    .brand:hover:not(:disabled) { color: var(--signal-cyan-dim); }
    .brand:disabled { opacity: 0.6; cursor: default; }
    .brand-restart-icon { display: flex; flex-shrink: 0; color: var(--sidebar-text-dim); }
    .brand-restart-icon svg { width: 14px; height: 14px; }
    .sidebar:not(:hover) .brand-label { display: none; }
    .brand.restarting .brand-restart-icon svg { animation: sidebar-spin 0.9s linear infinite; }
    /* min-height:0 es obligatorio — un flex item se queda a min-height:auto
       por defecto (el alto de SU contenido), lo que ignoraba por completo
       el overflow-y:auto de aquí en una ventana baja: el contenido de
       .tabs empujaba al propio .sidebar (ahora position:fixed, con
       height:100vh fijo) a desbordar la pantalla en vez de scrollear por
       dentro (FOX-P124, ronda 3, feedback de Emilio: "si la pantalla es
       pequeña en altura, la sidebar no es scrolleable"). */
    .tabs { display: flex; flex-direction: column; gap: var(--sp-2); flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; padding: var(--sp-3); }
    .tabs::-webkit-scrollbar { display: none; }
    /* Receta real .side-item del artifact (exhibit 10/20) — corregida
       (FOX-P124, ronda 2): la primera pasada inventó su propio lenguaje
       (chip con listón de acento cian en ::before, hover con raise
       --elev-control + bleed de margen negativo) que nunca estuvo en el
       artifact real. La receta real es más simple: plano por defecto,
       hover con un relleno sólido apagado (--surface-sunken, el
       --field-deep del artifact), y la pestaña activa con un raise
       neumórfico (--nu-raise-1) + un glow naranja en un .neon-under/
       .glass-glaze reales (mismos hijos que cualquier otro icono con
       vidrio+neón del sistema, aquí ocultos por opacidad hasta .active).
       flex-shrink:0 es obligatorio: .tabs es un flex column con
       overflow-y:auto pensado para hacer scroll, pero sin esto el
       navegador prefería encoger cada botón (y el .tab-group que los
       envuelve) antes que activar el scroll. */
    /* border-radius:12px (--r-xl), NO --r-xs (2px) — corregido (FOX-P124,
       ronda 3): con 2px el botón quedaba casi a esquina viva mientras que
       .neon-under (heredado de GLASS_ICON_CSS, siempre border-radius:999px
       — un círculo, pensado para chips de icono cuadrados) se salía muy
       por fuera de esas esquinas casi rectas, leyéndose como un blob
       ovalado en vez de una tarjeta redondeada (feedback de Emilio: "ahora
       tenemos border radius, compruébalos en el artefacto"). --r-xl es el
       valor real de .side-item en el artifact (border-radius:12px). */
    .tab, .sidenav-link { position: relative; display: flex; align-items: center; gap: var(--sp-4); background: none; border: none; color: var(--sidebar-text); font: 400 14px/1 var(--font-core); padding: 0 var(--sp-4); height: 40px; flex-shrink: 0; cursor: pointer; border-radius: var(--r-xl); transition: background var(--dur-base) ease, color var(--dur-base) ease, box-shadow var(--dur-base) ease; white-space: nowrap; width: 100%; text-align: left; }
    .tab:not(.active):hover, .sidenav-link:not(.active):hover { color: var(--sidebar-text); background: var(--surface-sunken); }
    .tab.active, .sidenav-link.active { color: var(--sidebar-text); font-weight: var(--fw-medium); box-shadow: var(--nu-raise-1); }
    /* Ocultos por defecto (opacity:0) en TODO item — la pestaña activa los
       revela. width/height:85%/78% + centrado (heredado de GLASS_ICON_CSS)
       es el mismo tamaño relativo que el artifact define para .side-item
       .neon-under. */
    .tab .neon-under, .sidenav-link .neon-under { opacity: 0; width: 85%; height: 78%; transition: opacity var(--dur-base) ease, background var(--dur-base) ease; }
    .tab .glass-glaze, .sidenav-link .glass-glaze { opacity: 0; inset: 0; border-radius: var(--r-xl); transition: opacity var(--dur-base) ease; }
    .tab.active .neon-under, .sidenav-link.active .neon-under { opacity: 1; background: var(--signal-orange); box-shadow: 0 0 8px 2px var(--neon-orange-glow); }
    .tab.active .glass-glaze, .sidenav-link.active .glass-glaze { opacity: 1; }
    .tab-icon { position: relative; z-index: 2; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: var(--sidebar-text-dim); }
    .tab.active .tab-icon, .sidenav-link.active .tab-icon { color: var(--sidebar-text); }
    .tab-label { position: relative; z-index: 2; overflow: hidden; text-overflow: ellipsis; }
    .sidebar:not(:hover) .tab, .sidebar:not(:hover) .sidenav-link { justify-content: center; padding: 0 var(--sp-3); }
    .sidebar:not(:hover) .tab-label { display: none; }
    /* Separador SOLO en colapsado (FOX-P124, ronda 3, feedback de Emilio:
       "los separadores solo deben verse en la vista colapsada para que no
       haya saltos de altura debido a los títulos de la barra expandida")
       — receta real .sb-divider del artifact: en expandido el propio
       texto del grupo ("Productividad"/"Documentos"...) ya hace de
       separador visual, sin línea; en colapsado, sin ese texto, aparece
       una línea grabada en su lugar. La primera pasada pintaba la línea
       SIEMPRE (permanente en .tab-group), la contradicción exacta que
       señaló Emilio. */
    .tab-group { display: flex; flex-direction: column; gap: 2px; margin-top: var(--sp-5); padding-top: var(--sp-5); flex-shrink: 0; }
    .tab-group-label { font: var(--text-label);text-transform:uppercase; letter-spacing: var(--ls-label); color: var(--sidebar-text-dim); padding: 0 var(--sp-3) var(--sp-2); flex-shrink: 0; }
    .sidebar:not(:hover) .tab-group { margin-top: var(--sp-3); padding-top: var(--sp-3); box-shadow: inset 0 1px 0 var(--line-hair); }
    .sidebar:not(:hover) .tab-group-label { display: none; }
    .sidebar-calendar { flex-shrink: 0; padding: var(--sp-5) var(--sp-4); box-shadow: inset 0 -1px 0 var(--line-hair); }
    .sidebar-calendar .mini-calendar { flex: none; }
    /* Mini (colapsado) / completo (expandido, .mini-calendar de siempre) —
       AMBOS montados a la vez, cruzan por opacidad en vez de display:none
       (FOX-P124, artifact exhibit 20). Antes .sidebar-calendar entera
       desaparecía al colapsar (display:none) — sin nada que reservara su
       alto, el resto del sidebar (tabs, footer) saltaba de sitio en cada
       hover (justo el bug que este AC pide evitar). Con ambas versiones
       compartiendo la misma celda de grid (grid-area:1/1), la ALTURA de
       la sección la fija siempre la versión completa (la más alta) en los
       dos estados. */
    .sidebar-cal-stack, .sidebar-weather-stack { display: grid; align-items: start; justify-items: start; }
    .sidebar-cal-stack > *, .sidebar-weather-stack > * { grid-area: 1 / 1; }
    /* Ancho FIJO (no 100%/flex) en la versión completa — el sidebar anima
       su ancho 64→320px; si el calendario/tiempo completos fueran 100%,
       su contenido (las etiquetas "Hoy"/"Mañana" del widget de tiempo,
       sobre todo) haría reflow/wrap a medio camino de la transición y
       saltaría de alto en pleno hover — el bug concreto que este AC
       nombra ("ancho fijo estable"). 296px = 320px de sidebar expandido
       menos el padding real de --sp-4 (12px) a cada lado (ronda 3: el
       padding pasó de --sp-6/20px a --sp-4/12px para igualar la receta
       real .sb-section del artifact, 16px/12px). overflow:hidden
       del .sidebar (ver arriba) recorta visualmente el resto mientras
       está colapsado. */
    .sidebar-calendar .mini-calendar, .sidebar-weather .top-weather { width: 296px; }
    /* align-self:stretch — el raise del mini ocupa el mismo alto que la
       fila (la del completo expandido), en vez de encogerse a su
       contenido, para que la tarjeta elevada de la colapsada mida lo
       mismo que .mini-calendar/.top-weather expandidos. justify-
       content:flex-start dentro de la tarjeta deja hora+círculo (o
       icono+temp) pegados arriba. */
    /* .raise real también aquí — la primera pasada dejó estas tarjetas SIN
       elevar (solo padding/radius, sin background/box-shadow), la misma
       omisión que .mini-calendar/.top-weather de más abajo. 14px de radio,
       no --r-3xl (20px) — el artifact reduce el radio a propósito para la
       versión mini, más angosta (--sb-cal-mini.raise, .sb-wthr-mini.raise). */
    .sidebar-cal-mini, .sidebar-weather-mini { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; align-self: stretch; gap: var(--sp-3); padding: var(--sp-3) var(--sp-2); background: var(--surface-base); box-shadow: var(--nu-raise-2); border-radius: 14px; opacity: 1; transition: opacity var(--dur-base) var(--ease-out); }
    .sidebar:hover .sidebar-cal-mini, .sidebar:hover .sidebar-weather-mini { opacity: 0; pointer-events: none; }
    .sidebar-calendar .mini-calendar, .sidebar-weather .top-weather { opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease-out); }
    .sidebar:hover .sidebar-calendar .mini-calendar, .sidebar:hover .sidebar-weather .top-weather { opacity: 1; pointer-events: auto; }
    /* Mismo tamaño que .sidebar-weather-mini-icon (30px) — el círculo (no
       el número) es lo que ensancha la tarjeta raise del mini-calendario,
       así que debe medir lo mismo que el icono del mini-tiempo para que
       ambas tarjetas raise midan lo mismo de ancho. */
    .sidebar-cal-today-mini { width: 30px; height: 30px; border-radius: var(--r-lg); position: relative; flex-shrink: 0; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); display: flex; align-items: center; justify-content: center; }
    .sidebar-cal-today-mini .glass-glaze { inset: 2px; border-radius: 7px; }
    .sidebar-cal-today-mini .mnum { position: relative; z-index: 2; font: 600 11px/1 var(--font-mono); color: #0b0d10; }
    /* Receta real .sb-cal-mini-time del artifact: mono, peso 600, letter-
       spacing .03em — corregido (FOX-P124, ronda 2): la primera pasada
       decidió por error que esto debía "heredar" de .mcal-time sin
       comprobar el propio exhibit 20, que define su propio peso/spacing
       explícitos para la versión mini. */
    .sidebar-cal-mini-time { font: 600 9px/1 var(--font-mono); letter-spacing: 0.03em; color: var(--sidebar-text-dim); font-variant-numeric: tabular-nums; }
    /* Receta real .sb-cal-mini-month del artifact: font-display (no mono),
       sentence-case (no mayúsculas/letter-spacing), peso 500 — corregido
       (FOX-P124, ronda 2): la primera pasada sustituyó esto por el label
       mono/mayúscula del sistema razonando que "Emilio pidió que use la
       misma tipografía que el expandido", pero el propio .mcal-month del
       expandido (ver más abajo) TAMBIÉN es font-display sentence-case en
       el artifact real — nunca hubo que elegir entre uno u otro, los dos
       tenían que cambiar. Peso 500 aquí (600 en el expandido) sigue
       siendo la compensación real por el texto rotado 90°. */
    .sidebar-cal-mini-month { writing-mode: vertical-rl; transform: rotate(180deg); font: 500 14px/1 var(--font-core); color: var(--sidebar-text); margin-top: auto; }
    .sidebar-weather-mini-icon { width: 30px; height: 30px; border-radius: var(--r-lg); position: relative; flex-shrink: 0; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); display: flex; align-items: center; justify-content: center; }
    .sidebar-weather-mini-icon .glass-glaze { inset: 2px; border-radius: 7px; }
    .sidebar-weather-mini-temp { font: 600 12.5px/1 var(--font-mono); color: var(--sidebar-text); }
    /* Mín/máx de mañana, aprovechando el hueco que deja align-self:stretch
       abajo de la tarjeta — mismo truco de margin-top:auto que
       "Septiembre" en el mini-calendario. */
    .sidebar-weather-mini-range { display: flex; flex-direction: column; align-items: center; gap: 2px; margin-top: auto; font: 600 10px/1 var(--font-mono); color: var(--sidebar-text-dim); }
    /* Widget de tiempo — justo debajo del calendario, persistente en todas
       las vistas (antes vivía solo dentro del tab Inbox). */
    .sidebar-weather { flex-shrink: 0; padding: var(--sp-5) var(--sp-4); box-shadow: inset 0 -1px 0 var(--line-hair); }
    /* Un solo scroll para calendario, tiempo y menú (antes solo scrolleaba
       el menú y plegar las tarjetas cambiaba alturas en plena animación:
       saltos y tarjetas colándose por detrás). Ahora las tarjetas se van
       hacia arriba con el scroll y la versión compacta aparece fija
       encima, sin ocupar sitio en el flujo. */
    .sidebar-scroll { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; overscroll-behavior: contain; }
    .sidebar-scroll::-webkit-scrollbar { display: none; }
    .sidebar-scroll > .tabs { flex: 0 0 auto; min-height: auto; overflow: visible; }
    /* Sticky de alto 0 dentro del scroll: no ocupan sitio (nada salta) y
       el menú pasa por debajo del cristal, como bajo el header. */
    .sidebar-glance { position: sticky; top: 0; z-index: 6; height: 0; flex-shrink: 0; }
    .sidebar-glance-clip { position: absolute; top: 0; left: 0; right: 0; }
    /* Mismo fondo que el header de las páginas (.page-hero): cristal
       translúcido con blur, borde de vidrio y --elev-card. */
    .sidebar-glance-inner {
      display: flex; flex-direction: column; gap: 2px; margin: var(--sp-2);
      padding: var(--sp-2); border-radius: var(--r-3xl);
      background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur);
      border: 1px solid var(--glass-border); box-shadow: var(--elev-card);
      opacity: 0; transform: translateY(-8px); pointer-events: none;
      transition: opacity 240ms var(--ease-out), transform 320ms cubic-bezier(.22, 1, .36, 1);
    }
    .sidebar.is-condensed .sidebar-glance-inner { opacity: 1; transform: none; pointer-events: auto; }
    .sidebar-glance-card { width: 100%; display: flex; align-items: center; gap: 12px; padding: 8px 10px; border: none; cursor: pointer; text-align: left; color: var(--sidebar-text); background: none; border-radius: var(--r-xl); transition: background var(--dur-fast) var(--ease-out); }
    .sidebar-glance-card:hover { background: color-mix(in srgb, var(--surface-sunken) 60%, transparent); }
    .sidebar-glance-card:focus-visible { outline: 2px solid var(--signal-cyan-dim); outline-offset: -2px; }
    .sidebar-glance-card .sidebar-cal-today-mini { animation: none; }
    /* Dentro de otra superficie con backdrop-filter (.sidebar-glance-inner)
       el backdrop-filter del .glass-glaze deja de difuminar lo que tiene
       debajo y el neón se veía como un círculo nítido encima del cristal:
       aquí el difuminado va en el propio neón. */
    .sidebar-glance-card .neon-under { filter: blur(5px); }
    .sidebar-glance-card .sidebar-cal-today-mini .mnum { color: #0b0d10; }
    .sidebar-glance-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
    .sidebar-glance-main { font: 600 13px/1.15 var(--font-core); white-space: nowrap; text-transform: capitalize; }
    .sidebar-glance-sub { display: inline-flex; align-items: center; gap: 4px; font: 500 10.5px/1 var(--font-mono); color: var(--sidebar-text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
    .sidebar-glance-sub-k { color: var(--sidebar-text); margin-right: 2px; }
    .sidebar-glance-sub svg, .sidebar-glance-sub i { color: var(--signal-cyan-dim); }
    .sidebar-glance-dot { opacity: .5; }
    /* Rail colapsado (64px): solo las dos piezas, apiladas y centradas. */
    .sidebar:not(:hover) .sidebar-glance-inner { margin: var(--sp-2) 6px; padding: var(--sp-2) 0; align-items: center; border-radius: var(--r-2xl); }
    .sidebar:not(:hover) .sidebar-glance-card { justify-content: center; width: auto; padding: 6px; }
    .sidebar:not(:hover) .sidebar-glance-text { display: none; }
    @media (prefers-reduced-motion: reduce) { .sidebar-glance-inner { transition: none; transform: none; } }
    /* Separador propio del footer — receta real .sb-footer del artifact
       (box-shadow:inset 0 1px 0 var(--sep)), ausente hasta ahora: el
       botón "Chats" quedaba pegado directamente a la lista de nav sin
       ninguna línea entre ambos (feedback ronda 3 de Emilio: "el del
       chat que sí necesita separador"). */
    .sidebar-footer { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3); flex-shrink: 0; box-shadow: inset 0 1px 0 var(--line-hair); }
    .sidebar:not(:hover) .sidebar-footer { align-items: center; }
    /* "Chats" reutiliza el botón real .btn-tinted (BUTTONS_CSS, ya
       existente desde US2/FOX-T937) — corregido (FOX-P124, ronda 3): la
       primera Y la segunda pasada seguían con su propio recipe bespoke
       (chip raise con --elev-card, nunca tocado) en vez de la clase
       compartida real (feedback de Emilio: "el botón de chat es
       totalmente distinto... que no lo has cambiado siquiera"). Aquí solo
       quedan las propiedades de LAYOUT que .btn-tinted no fija por sí
       solo — ancho completo, alineado a la izquierda, el padding vertical
       más ajustado de .sb-footer-btn en el artifact (9px, no los 13px de
       .btn-tinted a secas). El HTML real (renderSidebarNav/*-renderer.ts)
       añade class="btn btn-tinted" además del id. */
    .sidebar:not(:hover)    @keyframes sidebar-spin { to { transform: rotate(360deg); } }
    /* Receta real .mcal-today-pop del artifact (exhibit 20) — dispara al
       MONTAR el indicador de "hoy" (server-render fresco de la página),
       que en esta arquitectura (sin reloj cliente en vivo, el sidebar no
       se vuelve a renderizar al cambiar de tab) es el único momento en el
       que el día puede haber cambiado, así que es el proxy correcto de
       "al cambiar de día" sin inventar un mecanismo de reloj/rollover que
       este proyecto no pide. animation-fill-mode:backwards deja el estado
       inicial (scale .85/opacity .4) activo un instante antes de correr,
       en vez de aparecer ya a tamaño completo un frame antes de animar. */
    @keyframes mcal-today-pop { 0% { transform: scale(.85); opacity: .4; } 100% { transform: scale(1); opacity: 1; } }
    .mcal-today-inner, .sidebar-cal-today-mini { animation: mcal-today-pop 180ms ease-out backwards; }

    /* ── Calendario y tiempo — CADA UNO es una card raise propia (receta
       real .raise.mcal / .raise.wthr-card del artifact, exhibit 20) —
       corregido (FOX-P124, ronda 2): la primera pasada de este proyecto
       los dejó deliberadamente planos ("solo Chats y la pestaña activa se
       elevan"), una decisión que contradecía el propio markup del
       exhibit 20 (div.raise.mcal, div.raise.wthr-card) — no se leyó con
       suficiente cuidado la primera vez. --nu-raise-2 es EXACTAMENTE el
       box-shadow que el artifact define para .raise (7px 7px 16px /
       -7px -7px 16px), así que es el token real, no una aproximación. */
    .mini-calendar, .top-weather { color: var(--sidebar-text); background: var(--surface-base); box-shadow: var(--nu-raise-2); border-radius: var(--r-3xl); padding: var(--sp-5); }
    .mcal-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--sp-4); }
    /* Cabecera del mes — receta real .mcal-month del artifact: font-
       display (no mono), sentence-case (no mayúsculas/letter-spacing),
       peso 600, color a toda potencia (--label, no un --label-3 apagado)
       — corregido (FOX-P124, ronda 2): la primera pasada sustituyó esto
       por el label mono-mayúscula del sistema razonando que "es el
       registro de máquina que toca aquí", una decisión estética propia
       que nunca estuvo en el artifact real. */
    .mcal-month { font: 600 14px/1 var(--font-core); color: var(--sidebar-text); }
    .mcal-time { font: 500 12px/1 var(--font-mono); color: var(--text-faint); font-variant-numeric: tabular-nums; }
    .mcal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
    .mcal-label { text-align: center; font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: var(--ls-mono); color: var(--text-faint); padding: 3px 0; }
    /* Fin de semana atenuado respecto a los días laborables — misma
       distinción tonal que la referencia (fox-600 entre semana, fox-400 el
       fin de semana), no un gris uniforme para los 30 días. */
    .mcal-day { position: relative; text-align: center; font-family: var(--font-mono); font-size: var(--fs-tag); line-height: 28px; border-radius: 10px; font-variant-numeric: tabular-nums; color: var(--sidebar-text); transition: var(--t-press); }
    .mcal-day .mnum { position: relative; z-index: 2; }
    .mcal-day.mcal-weekend { color: var(--text-faint); }
    .mcal-day[data-date] { cursor: pointer; }
    /* Raise real al hover, no solo un cambio de fondo — mismo motivo que
       .tab:hover (feedback de Emilio: "haría eso mismo también con las
       fechas del calendario"). :not(.mcal-today) para no competir con la
       sombra propia del día actual. */
    .mcal-day[data-date]:not(.mcal-today):hover { box-shadow: var(--elev-control); }
    /* Hoy = el único elemento "vivo" del calendario — receta real
       .mcal-today-inner del exhibit 14 (corregido: la primera pasada
       rellenaba el día entero de cian sólido con texto invertido, algo que
       el artifact nunca hizo — solo un neon-under pequeño bajo un panel de
       cristal real dentro de un pozo elevado, el número se queda en el
       mismo tono que cualquier otro día). */
    .mcal-today, .mcal-day.mcal-today.mcal-weekend { font-weight: var(--fw-semibold); }
    .mcal-today-inner { position: absolute; inset: 0; border-radius: 10px; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); z-index: 0; }
    .mcal-today-inner .glass-glaze { inset: 2px; border-radius: 7px; }
    .mcal-today .mnum { color: #0b0d10; }
    /* .top-weather ES la tarjeta raise (recibe background/box-shadow/radius
       de la regla compartida con .mini-calendar, más arriba) — .weather-row
       es solo la fila flex interna, receta real .wthr-card/.wthr-slot del
       artifact (exhibit 20/15): columna por franja horaria (label arriba,
       icono, temp abajo, todo centrado), separador vertical entre Hoy y
       Mañana — corregido (FOX-P124, ronda 2): la primera pasada ponía
       label+icono+temp en una fila horizontal plana, sin tarjeta propia. */
    .weather-row { display: flex; align-items: stretch; }
    .weather-card { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 0 14px; text-align: center; }
    .weather-card + .weather-card { box-shadow: inset 1px 0 0 var(--line-hair); }
    .label { font: 600 10px/1 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--sidebar-text-dim); }
    /* Receta real .wthr-icon del exhibit 15/20 — elevado + neon-under
       (fuente de color difuminada) + glass-glaze real (backdrop-filter). */
    .wi { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; position: relative; width: 34px; height: 34px; border-radius: 11px; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); color: var(--sidebar-text); }
    /* peso 600, no 300 — receta real .wthr-temp del artifact (corregido,
       FOX-P124 ronda 2: la primera pasada usaba un peso "light" propio,
       nunca estuvo en el exhibit real). El rango de "Mañana" anida un
       <small> más tenue para el máximo, mismo truco que .wthr-temp small
       del artifact. */
    .temp { font: 600 20px/1 var(--font-core); letter-spacing: -0.01em; color: var(--sidebar-text); }
    .temp small { font: 500 13px/1 var(--font-core); color: var(--sidebar-text-dim); }

    /* ── Móvil (≤860px): menú hamburguesa + panel lateral ─────────────────
       En escritorio la barra superior y el velo no existen (display:none).
       El sistema de diseño dice «las superficies se inflan y desinflan; no se
       deslizan»: el panel se despliega desde el borde izquierdo (clip-path +
       fundido), sin translate — como ya hace la barra de escritorio al
       ensancharse con :hover. Etiquetas siempre visibles dentro del panel. */
    .m-menu-anchor, .m-menu-btn, .m-scrim { display: none; }
    @media (max-width: 860px) {
      .layout { flex-direction: column; }
      .layout .board-col { margin-left: 0; }
      /* El botón ☰ vive en la fila superior de la cabecera de cada página (.hero-top), con la
         receta de .hero-back-btn (30px, raised, inset al pulsar): sin barra superior que
         robe altura. El script lo coloca ahí; sin cabecera (o con ella fuera de pantalla) flota
         fijo arriba a la izquierda. La zona táctil es de 44px aunque el botón pinte 30px. */
      .m-menu-anchor { display: block; }
      .m-menu-btn {
        position: relative; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
        width: 30px; height: 30px; padding: 0; border: none; border-radius: var(--r-lg);
        background: var(--surface-card); box-shadow: var(--nu-raise-1); color: var(--text-body);
        cursor: pointer; -webkit-appearance: none; appearance: none;
        transition: box-shadow var(--dur-fast) var(--ease-out);
      }
      .m-menu-btn::after { content: ''; position: absolute; inset: -7px; }
      .m-menu-btn:active { box-shadow: var(--nu-inset-1); transform: scale(.96); }
      .m-menu-btn:focus-visible { outline: none; box-shadow: var(--nu-raise-1), var(--focus-ring); }
      .m-menu-btn svg { width: 16px; height: 16px; display: block; }
      .m-menu-anchor > .m-menu-btn, .m-menu-btn.is-floating {
        position: fixed; top: calc(env(safe-area-inset-top) + 10px); left: 10px; z-index: 56;
        width: 36px; height: 36px;
      }
      .m-scrim {
        display: block; position: fixed; inset: 0; z-index: 65;
        background: var(--surface-overlay); -webkit-backdrop-filter: var(--backdrop-blur); backdrop-filter: var(--backdrop-blur);
        opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
      }
      .m-scrim.is-open { opacity: 1; }
      .m-scrim[hidden] { display: none; }
      body.m-drawer-open { overflow: hidden; }
      .sidebar {
        position: fixed; top: 0; left: 0; bottom: 0; z-index: 70; height: 100dvh;
        width: min(86vw, 320px) !important; flex-direction: column;
        padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);
        box-shadow: var(--elev-rail);
        clip-path: inset(0 100% 0 0); opacity: 0; visibility: hidden; pointer-events: none;
        transition: clip-path var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base);
      }
      .sidebar.is-open { clip-path: inset(0); opacity: 1; visibility: visible; pointer-events: auto; transition-delay: 0s; }
      .sidebar:not(:hover) .brand-label, .sidebar:not(:hover) .tab-label, .sidebar:not(:hover) .tab-group-label { display: inline; }
      /* En móvil nunca hay :hover: se ignoran las reglas del rail colapsado (items centrados,
         línea separadora entre grupos) y se aplica el aspecto expandido. */
      .sidebar:not(:hover) .tab, .sidebar:not(:hover) .sidenav-link { justify-content: flex-start; padding: 0 var(--sp-4); }
      .sidebar:not(:hover) .tab-group { margin-top: var(--sp-5); padding-top: var(--sp-5); box-shadow: none; }
      /* Fecha y tiempo compactos al hacer scroll: mismo comportamiento que en escritorio
         (los activa el script de .is-condensed), con el aspecto expandido en vez del del rail. */
      .sidebar:not(:hover) .sidebar-glance-inner { margin: var(--sp-2); padding: var(--sp-2); align-items: stretch; border-radius: var(--r-3xl); }
      .sidebar:not(:hover) .sidebar-glance-card { justify-content: flex-start; width: 100%; padding: 8px 10px; }
      .sidebar:not(:hover) .sidebar-glance-text { display: flex; }
      .tab, .sidenav-link { min-height: 44px; }
      @media (prefers-reduced-motion: reduce) {
        .sidebar, .m-scrim, .m-menu-btn { transition: none !important; }
      }
    }
    
    /* ── Panel lateral en móvil: calendario y tiempo ─────────────────────
       En escritorio se ven al ensanchar la barra (:hover). En el panel móvil están
       siempre expandidos y en el mismo orden que en escritorio (widgets arriba, navegación debajo). */
    @media (max-width: 860px) {
      .sidebar.is-open .sidebar-cal-mini, .sidebar.is-open .sidebar-weather-mini { opacity: 0; pointer-events: none; }
      .sidebar.is-open .sidebar-calendar .mini-calendar, .sidebar.is-open .sidebar-weather .top-weather { opacity: 1; pointer-events: auto; }
      .sidebar:not(:hover) .sidebar-footer { align-items: stretch; }
    }

    /* ── Cabecera condensada al hacer scroll (tablet y móvil) ───────────
       La pone/quita sidebarScript (.is-condensed). Una sola fila: botones a la izquierda,
       título pequeño en medio, controles de la derecha (modo de lectura, índice, pantalla
       completa). Todo lo demás de la tarjeta se oculta hasta volver arriba. */
    @media (max-width: 1024px) {
      body .page-hero { transition: padding var(--dur-base) var(--ease-out); }
      body .page-hero.is-condensed { top: calc(env(safe-area-inset-top, 0px) + 8px); padding: 8px 12px; border-radius: var(--r-2xl); }
      body .page-hero.is-condensed > :not(header.hero) { display: none; }
      body .page-hero.is-condensed header.hero { display: flex; align-items: center; gap: 10px; min-width: 0; }
      body .page-hero.is-condensed header.hero > :not(.hero-top):not(h1):not(:has(h1)) { display: none; }
      body .page-hero.is-condensed .hero-top { display: contents; }
      body .page-hero.is-condensed .hero-top .eyebrow { display: none; }
      body .page-hero.is-condensed .hero-top > .reader-mode-toggle,
      body .page-hero.is-condensed .hero-top > .reader-toc-btn,
      body .page-hero.is-condensed .hero-top > .reader-focus-toggle { order: 2; }
      body .page-hero.is-condensed .hero-top > .reader-mode-toggle { margin-left: auto; }
      body .page-hero.is-condensed header.hero h1,
      body .page-hero.is-condensed header.hero :has(> h1) { order: 1; flex: 1 1 0; min-width: 0; margin: 0; }
      body .page-hero.is-condensed header.hero h1 { font-size: 17px; line-height: 1.2; letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
      @media (prefers-reduced-motion: reduce) { body .page-hero { transition: none; } }
    }

    @media (max-width: 640px) {
      /* ── Cabeceras compactas ───────────────────────────────────────── */
      body .page-hero { padding: 12px 16px 14px; }
      body .page-hero .hero-top, body .page-hero .eyebrow { margin-bottom: 4px; }
      body .page-hero .eyebrow { font-size: 10px; letter-spacing: .14em; }
      body .hero h1 { font-size: 26px; line-height: 1.1; letter-spacing: -0.015em; }
      body .inbox-hero-meta, body .docs-hero-meta { display: none; }
      body .tareas-hero-count, body .iniciativas-hero-count { display: none; }
      body .tareas-hero-title-row { gap: 8px; }

      /* ── Inbox: panel de la semana ─────────────────────────────────── */
      body #weekPanel { padding: 14px 12px; }
      body #weekPanel .panel-head { flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
      body #weekPanel .panel-head > div { flex: 1 1 100%; justify-content: space-between; flex-wrap: wrap; gap: 8px !important; }
      body #weekPanel .panel-head > div > div { gap: 6px !important; flex-wrap: wrap; justify-content: flex-end; }
      body #inboxAddEventBtn { font-size: 0; gap: 0; padding: 0; width: 36px; height: 36px; justify-content: center; }
      body #weekPanel .week-nav-label { font-size: 12px; padding: 4px 2px; }
      body .week-grid-wrap { -webkit-overflow-scrolling: touch; }
      body .week-grid-inner { min-width: 560px; }
      body .week-header-row, body .week-allday-row, body .week-body-grid { grid-template-columns: 38px repeat(7, 1fr); }
      body .week-body { max-height: 380px; }

      /* ── Tareas: barra de herramientas ─────────────────────────────────
         Había dos márgenes de 22px apilados (.tab-toolbar y .tareas-toolbar) y cuatro filas
         de controles. Ahora: «+ Nueva» en la esquina de la cabecera · vista (fila 1) ·
         iniciativa (fila 2) · estados, desplazables (fila 3). */
      body .page-hero { margin-bottom: 14px; }
      body .tareas-hero { position: relative; }
      body .tareas-hero .tab-toolbar { margin: 10px 0 0; }
      body .tareas-toolbar { margin-top: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
      body .tareas-toolbar-sep { display: none; }
      body #tareas-view-toggle { order: 1; max-width: 100%; }
      body .tareas-toolbar .fox-select-wrap { order: 2; flex: 1 1 100%; min-width: 0; max-width: 100%; }
      body .tareas-toolbar .select-box, body .tareas-toolbar .select-trigger { width: 100%; max-width: 100%; box-sizing: border-box; }
      body .tareas-new-wrap { position: absolute; top: 10px; right: 12px; width: auto; }
      body .tareas-new-wrap .btn { width: auto; height: 34px; padding: 0 12px; font-size: 13px; white-space: nowrap; }
      body #tareas-status-filter { order: 4; flex-wrap: nowrap !important; gap: 6px !important; width: 100%; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
      body #tareas-status-filter::-webkit-scrollbar { display: none; }
      body #tareas-status-filter .status-chip { flex: 0 0 auto; }

      /* ── Inbox: Resumen + Propuestas ─────────────────────────────────
         «1fr» deja crecer a los hijos hasta su contenido mínimo y ensanchaba la página. */
      body .inbox-pair { grid-template-columns: minmax(0, 1fr); gap: 16px; margin-top: 16px; }

      /* ── Cabeceras de panel genéricas (Nutrición, Health…) ────────────
         Título y controles en filas separadas en vez de ensanchar la página. */
      body .panel-head { flex-wrap: wrap; gap: 8px; }
      body .panel-head > div { flex-wrap: wrap; max-width: 100%; min-width: 0; }
      body .health-range-selector { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
      body .health-range-selector::-webkit-scrollbar { display: none; }

      /* ── Ajustes: pestañas en una sola línea desplazable ───────────── */
      body .settings-subnav { flex-wrap: nowrap; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
      body .settings-subnav::-webkit-scrollbar { display: none; }
      body .settings-subnav > * { flex: 0 0 auto; white-space: nowrap; }

      /* ── Ajustes: tablas de referencia apiladas por fila ───────────── */
      body .settings-stack .hub-card-body table { display: block; width: 100%; }
      body .settings-stack .hub-card-body thead { display: none; }
      body .settings-stack .hub-card-body tbody { display: block; }
      body .settings-stack .hub-card-body tr { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 12px 6px; }
      body .settings-stack .hub-card-body td { display: block; width: auto !important; max-width: 100%; margin: 0; padding: 0; border: 0; }

      /* ── Tablas genéricas (.tbl): cada fila pasa a tarjeta ─────────────
         En vez de ocultar columnas: el nombre ocupa todo el ancho (hasta 2 líneas) y el
         resto de campos (categoría, fecha, acciones) van debajo en la misma tarjeta. */
      body .tbl .tbl-head { display: flex; flex-wrap: wrap; gap: 8px 16px; padding: 12px 16px; }
      body .tbl .tbl-head > span { display: none; }
      body .tbl .tbl-head:not(:has(.tbl-sort)) { display: none; }
      body .tbl .tbl-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 12px 16px; }
      body .tbl .tbl-row > .tbl-name { flex: 1 1 100%; }
      body .tbl .tbl-row > :not(.tbl-name) { min-width: 0; }
      body .tbl .tbl-title { white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
      body .tbl .tbl-desc { white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
      body .tbl .kn-pending-tbl-actions { margin-left: auto; }

      /* ── Documentos ────────────────────────────────────────────────── */
      body .docs-toolbar { flex-direction: column; align-items: stretch; gap: 8px; }
      body .docs-toolbar .search-carved { width: 100%; min-width: 0; }
      body .docs-dropzone { width: 100%; min-width: 0; }
      body .docs-dropzone-hint { display: none; }
      body .docs-tagnav { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
      body .docs-tagnav::-webkit-scrollbar { display: none; }
      body .docs-tagnav .tag-pill { flex: 0 0 auto; }
      body .docs-shell { gap: 10px; }
      body .docs-panel { padding: 10px; }
      body .docs-nav { flex-direction: row; gap: 8px; overflow-x: auto; scrollbar-width: none; }
      body .docs-nav::-webkit-scrollbar { display: none; }
      body .docs-section-label { display: none; }
      body .docs-nav .docs-folder-item { width: auto; flex: 0 0 auto; }
      body .doc-card { margin: 0; padding-left: 4px; padding-right: 4px; }
      body .doc-card > .rich-icon { display: none; }
      body .doc-card-header { flex-direction: column; align-items: stretch; gap: 2px; min-width: 0; }
      body .doc-title, body .doc-snippet { max-width: 100%; min-width: 0; overflow-wrap: anywhere; }
      body .doc-tags-row { flex-wrap: wrap; min-width: 0; }
      body .doc-tag-chip { max-width: 100%; overflow-wrap: anywhere; }
      body .doc-card-meta { gap: 6px; }

      /* ── Knowledge ─────────────────────────────────────────────────── */
      body .kn-composer { flex-wrap: nowrap; align-items: center; gap: 8px; padding: 6px 8px; }
      body .kn-composer .kn-composer-icon { display: none; }
      body #knw-composer-input { flex: 1 1 0; min-width: 0; text-overflow: ellipsis; }
      body .kn-composer .btn { flex: 0 0 auto; }
      /* Las 4 pestañas no caben: en móvil son un desplegable. */
      body #knw-tabs.seg-carved { display: none; }
      body .kn-tabs-select { display: flex; width: 100%; }
      body .kn-tabs-select .select-box, body .kn-tabs-select .select-trigger { width: 100%; max-width: 100%; box-sizing: border-box; }
      body .kn-lib-toolbar { flex-wrap: wrap; gap: 8px; }
      body .kn-lib-toolbar .fox-select-wrap { flex: 1 1 calc(50% - 5px); min-width: 0; }
      body .kn-lib-toolbar .select-box, body .kn-lib-toolbar .select-trigger { width: 100%; max-width: 100%; box-sizing: border-box; }
    }



    /* ── Layout ── */
    .layout { display: flex; flex: 1; overflow: hidden; }
    /* margin-left:64px reserva el ancho de REPOSO del sidebar (ahora
     position:fixed, fuera del flujo — FOX-P124 ronda 3) para que el rail
     colapsado nunca tape contenido; la expansión por :hover se superpone
     por encima sin desplazar esto. */
  .board-col { flex: 1; margin-left: 64px; overflow-y: auto; min-width: 0; 
      background-color: var(--surface-base);
 }
    #tab-content { max-width: 960px; margin: 0 auto; padding: 20px 16px 40px; transition: opacity var(--dur-fast) var(--ease-out); }
    .tab-date { font: var(--text-label);text-transform:uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); margin-bottom: 14px; }

    /* ── Retícula modular (HUB_GRID_CSS, ver Daily Hub y el detalle de
       recetas) — el wrapper que usan las 9 pestañas del Board (sustituyó
       por completo al viejo .card/h2 con acento en el flanco). #tab-content
       centra a 960px con margin:auto por defecto — ese auto desalinea el
       contenido de la trama de puntos según el ancho de ventana (mismo
       motivo documentado en daily-hub-renderer.ts), así que .tab-flush
       cancela ambos y pone su propio padding de 48px, EL MISMO valor real
       de .wrap en daily-hub-renderer.ts — el "96px, ya confirmado contra
       Daily Hub" de antes era falso (nunca se confirmó contra nada real;
       Daily Hub siempre usó 48px), mismo error de comentario que ya
       apareció y se corrigió en cuestionarios/dish/protocolo-renderer.ts
       (commit 99537b8) y ahora aquí (pedido explícito de Emilio: "quiero
       que todos tengan el ancho del header del daily hub", sin
       excepciones). Una sola regla aquí, reutilizada por todas las
       pestañas en vez de repetirla por cada una. ── */
    #tab-content:has(> .tab-flush) { max-width: none; margin: 0; padding: 0; }
    .tab-flush { padding: 20px 48px 40px; }
    @media (max-width: 640px) { .tab-flush { padding: 20px 18px 40px; } }
    /* Settings (FOX-P125/T983+ — quitar el límite autoimpuesto de
       .hub-grid a 4×192px=768px fijos): las cards de Guía de Estilo/
       Agentes usan renderHubCard(..., { fullWidth:true }) en vez de
       colSpan, así que no viven dentro de .hub-grid — solo necesitan
       apilarse en columna con separación entre secciones. Sin ancho fijo
       ni límite de columnas: ocupan el 100% de .tab-flush en cualquier
       viewport. */
    .settings-stack { display: flex; flex-direction: column; gap: 16px; }

  /* Columnas fluidas (1fr), no 192px fijos — FOX-P125, feedback de Emilio
     2026-09-18: "ya sin estar acotadas al grid de referencia que teníamos
     de fondo... y ser responsive". Las proporciones de colSpan (p.ej. el
     8/4 de Nutrición/Ingredientes en dish-renderer.ts) se conservan igual
     — son relativas al número de columnas, no al ancho en px de cada una
     — así que este cambio no reordena ningún layout ya afinado, solo deja
     de exigir que el ancho total sea un múltiplo exacto de 192px. */
  /* gap:1px + background:var(--line-hair) — mismo truco que .hairgrid en
     knowledge-detail-renderer.ts: el fondo del propio grid se cuela por el
     hueco de 1px entre celdas, dibujando divisores horizontales Y
     verticales a la vez sin CSS por celda ni saber qué card cae en qué
     columna. FOX-P125, feedback de Emilio 2026-09-18: "hay que poner
     divisores verticales cuando haya dos secciones una al lado de la
     otra" — con border-top (intento anterior) solo había línea encima de
     cada fila, nunca entre columnas. */
  .hub-grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(192px, auto); gap: 1px; background: var(--line-hair); margin-top: 0; }
  @media (max-width: 860px) {
    .hub-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: auto; }
    .hub-grid .hub-card { grid-column: span 2 !important; height: auto; }
  }
  /* Sin caja (FOX-P125, feedback de Emilio 2026-09-18): las SECCIONES del
     propio artifact (.exhibit) — "las cards deberían tener este estilo,
     más limpio... separadas con un divider sin más" — ni fondo distinto,
     ni sombra, ni radio; .hub-card necesita SÍ un background propio
     (--surface-base, el mismo tono que .wrap) para que el line-hair del
     grid solo se vea en el hueco de 1px entre celdas y no también DENTRO
     de cada celda. padding subido de 24/4/20 a 32/20/28 — "hay que darles
     algo de espacio entre el divisor y el contenido de las secciones", el
     valor anterior dejaba el título casi pegado a la línea. */
  .hub-card { position: relative; background: var(--surface-base); padding: 32px 20px 28px; display: flex; flex-direction: column; overflow: visible; }
  /* PAUSADO (FOX-P125, feedback de Emilio 2026-09-18: "vamos a dejarle de
     momento a todos en el estado base, sin raised ni carved") — antes esta
     regla le daba a la única card "importante" por vista un raise fuerte
     de verdad (--elev-card); de momento cae en la misma receta plana que
     el resto de .hub-card, sin ninguna regla propia. renderHubCard() sigue
     aceptando "raised: true" y añadiendo la clase — basta con reintroducir
     aquí el override (ver git history de esta línea) para reactivarlo. */
  .hub-card-body { flex: 1; min-height: 0; overflow-y: auto; }
  .hub-card-head { flex-shrink: 0; display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
  .hub-card-head h2 { font-size: 16.5px; font-weight: var(--fw-semibold); margin: 0; letter-spacing: -0.005em; }
  .hub-card-head .tag { font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); white-space: nowrap; }
  .hub-card > * { position: relative; z-index: 1; }


  /* Well tallado que envuelve el SVG del chart — receta real .trend-plot. */
  .health-plot { position: relative; border-radius: 16px; box-shadow: inset 3px 3px 8px var(--nu-dark-soft), inset -3px -3px 8px var(--nu-light); padding: 16px 6px 4px; }
  .health-legend-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot-color); box-shadow: 0 0 5px 0 var(--dot-color); flex-shrink: 0; }
  .health-caption { font-size: .7rem; color: var(--text-muted); margin-top: 6px; }

  /* .health-range-selector ya no dibuja la pista — eso es .seg-carved
     (SEG_CARVED_CSS, theme.ts), aquí solo queda el margen respecto al
     gráfico de debajo. */
  .health-range-selector { margin-bottom: 10px; }

  /* Comparar peso con varias métricas a la vez (ver initHealthCompare,
     board-renderer.ts) — cada .health-mini-tile activa añade un chip aquí,
     EL MISMO componente .health-headline que Peso (misma cifra de 32px,
     misma estructura de 3 líneas), solo con .is-compact para el reset de
     <button>. El margen vertical vive en el wrap, no en cada item, para
     que Peso y los chips arranquen a la misma altura. */
  .health-headline-wrap { display: flex; align-items: flex-start; gap: 32px; flex-wrap: wrap; margin-bottom: 16px; }
  .health-headline-wrap > .health-headline { margin: 0; }
  .health-headline-chips { display: flex; flex-wrap: wrap; gap: 32px; }
  .health-headline.is-compact { cursor: pointer; appearance: none; border: none; background: none; font: inherit; text-align: left; padding: 0; }
  .health-headline.is-compact:hover .health-headline-label { color: var(--text-body); }

  .health-headline-label { display: block; font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); margin-bottom: 6px; }
  .health-headline-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
  .health-headline-value { font: 300 32px/1 var(--font-core); letter-spacing: -0.01em; color: var(--text-strong); }
  .health-headline-unit { font-size: 15px; font-weight: var(--fw-regular); color: var(--text-muted); margin-left: 2px; }
  .health-headline-delta { display: inline-flex; align-items: center; gap: 4px; padding: 4px 9px; border-radius: var(--r-pill); font: 600 12px/1 var(--font-mono); color: var(--delta-color); background: color-mix(in srgb, var(--delta-color) 14%, transparent); }
  .health-headline-sub { display: block; font-size: 12.5px; color: var(--text-muted); margin-top: 6px; }
  .health-source { font-size: .7rem; color: var(--text-muted); margin-top: 6px; text-align: right; }

  /* Small multiples — grasa/músculo/hidratación/hueso, cada una su propia
     mini-card (stat-tile) en vez de compartir eje con el peso. */
  .health-mini-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 20px; }
  @media (max-width: 760px) { .health-mini-grid { grid-template-columns: repeat(2, 1fr); } }
  .health-mini-tile { appearance: none; border: none; width: 100%; text-align: left; font: inherit; cursor: pointer; padding: 14px 16px; border-radius: var(--r-xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); transition: box-shadow var(--dur-fast) var(--ease-press); }
  .health-mini-tile:hover { box-shadow: var(--nu-inset-2); }
  .health-mini-tile.active { box-shadow: var(--nu-inset-1), inset 0 0 0 1.5px var(--neon-blue); }
  .health-mini-label { display: block; font: 600 10px/1 var(--font-mono); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); margin-bottom: 8px; }
  .health-mini-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
  .health-mini-value { font: 600 17px/1 var(--font-core); color: var(--text-strong); }
  .health-mini-delta { font: 600 11px/1 var(--font-mono); color: var(--delta-color); }
  .health-mini-spark { display: block; width: 100%; height: 36px; margin-top: 10px; pointer-events: none; }

    /* PANEL_CSS (theme.ts) — mismo bug de "importar y usar pero no
       interpolar" que ya tuvo RICH_ROW_CSS más arriba: theme.ts la
       exporta, este archivo la importa, pero solo se interpolaba dentro
       del <style> propio de renderTaskDetailPage() — nunca en este shell
       compartido, así que ningún tab del Board podía usar .panel de
       verdad (necesario para el rediseño de Health, feedback de Emilio
       2026-09-22: "la parte de tendencias de salud debe de ir en un
       elemento raised, tal y como está en el artefacto"). */

  .panel { position: relative; padding: 28px 24px 32px; border-radius: var(--r-3xl); background: var(--surface-card); box-shadow: var(--elev-card-soft); }
  .panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; gap: 12px; }
  .panel-head h2 { font: var(--fw-semibold) 16.5px/1.3 var(--font-core); margin: 0; color: var(--text-strong); }

    /* Variantes literales del artefacto de Health: .panel.raised es una
       superficie MÁS elevada que el .panel base (surface-base + elev-card,
       en vez de surface-card + elev-card-soft) para el bloque
       protagonista de la pestaña; .hub-row.tareas-proto es la fila 50/50
       de Protocolos activos + Tareas de Scully ("van juntos 50/50"). */
    .panel.raised { background: var(--surface-base); box-shadow: var(--elev-card); }
    .panel + .panel, .panel-standalone { margin-bottom: 36px; }
    .hub-row { display: grid; gap: 20px; margin-bottom: 36px; }
    .hub-row.tareas-proto { grid-template-columns: 1fr 1fr; }
    @media (max-width: 860px) { .hub-row.tareas-proto { grid-template-columns: 1fr; } }
    /* Recetario (70%) + Lista de la compra (30%) lado a lado (feedback de
       Emilio: "para poder introducir comidas más fácilmente con drag and
       drop en el planner" — con la lista de la compra ya no de por medio
       a todo lo ancho, el recetario queda más cerca del menú semanal de
       arriba). align-items:start para que la columna corta (Lista de la
       compra) no se estire a la altura del recetario. */
    .hub-row.recetario-shop { grid-template-columns: 7fr 3fr; align-items: start; }
    /* La fila ya separa con su margin-bottom; el del panel sumaba 36px a la
       fila y el recetario (estirado a su altura) acababa más largo que la lista. */
    .hub-row.recetario-shop > .panel-standalone { margin-bottom: 0; }
    @media (max-width: 860px) { .hub-row.recetario-shop { grid-template-columns: 1fr; } }
    /* 26-28px de aire entre el .page-hero sticky y el contenido (feedback
       de Emilio: "deja unos 24-32px de espacio entre el header y el
       contenido de abajo") — medido contra el Daily Hub real (referencia
       ya validada por Emilio): el margin-top real de .hub-stat-strip tras
       .page-hero produce ahí un hueco de 26px de verdad en pantalla. Salud
       era el único tab migrado sin ese margen — Tendencias arrancaba
       pegada al header. */
    .page-hero + .panel, .page-hero + .hub-row { margin-top: 28px; }
    .tab-toolbar { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 22px 0 14px; }
    .tab-toolbar-label { font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); }
    .tab-empty, .hub-empty { color: var(--text-muted); font-size: 0.875rem; padding: 8px 2px; }
    /* Select tallado real (FOX-P125, artifact exhibit 08 — "forms": "el
       select despliega de verdad, con opciones carved dentro de un panel
       carved") — corregido sobre la aproximación anterior (<select>
       nativo con appearance:none y una "▾" de texto, sin panel ni
       apertura animada). El <select> nativo original se mantiene en el
       DOM pero oculto (initFoxSelectJs, más abajo, construye el widget
       real al lado y lo mantiene sincronizado vía .value/'change') para
       que los consumidores existentes (filtros de Protocolos/Nutrición)
       no necesiten tocar su propio JS. */

    .fox-select-wrap { position: relative; display: inline-flex; }
    .fox-select { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
    .select-box { position: relative; min-width: 150px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); }
    /* Dentro de una superficie de cristal (modal/formulario flotante) el
       disparador también es plano: hairline en vez de pozo tallado. */
    :is(.glass-modal-box, .proto-new-form, .ini-new-form) .select-box { background: none; box-shadow: none; border-radius: 0; border-bottom: 1px solid var(--line-hair); }
    :is(.glass-modal-box, .proto-new-form, .ini-new-form) .select-trigger { padding-left: 0; }
    .select-trigger { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px; appearance: none; border: none; background: none; padding: 7px 8px 7px 12px; font: 400 0.8rem/1 var(--font-core); color: var(--text-body); text-align: left; cursor: pointer; }
    .select-chev { position: relative; width: 26px; height: 26px; flex: 0 0 auto; border-radius: var(--r-lg); box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); }
    /* El botón del chevron se enciende (teal, chevron negro) solo con hover o desplegado. */
    .select-chev { color: var(--text-muted); transition: color 160ms ease; }
    .select-chev .neon-under { width: 75%; height: 75%; border-radius: 6px; background: var(--neon-teal); box-shadow: 0 0 8px 2px var(--neon-teal-glow); opacity: 0; transition: opacity 160ms ease; }
    .select-box:hover .select-chev .neon-under, .select-box.open .select-chev .neon-under { opacity: .7; }
    .select-box:hover .select-chev, .select-box.open .select-chev { color: #0b0d10; }
    .select-chev .glass-glaze { inset: 2px; border-radius: 6px; }
    .select-chev svg, .select-chev i { transition: transform 220ms ease; }
    .select-box.open .select-chev svg, .select-box.open .select-chev i { transform: rotate(180deg); }
    /* Panel de opciones flotante de cristal plano, misma superficie que
       .glass-modal-box/.cal-picker (feedback de Emilio, 2026-09-24: "todas
       las modales de Fox y los dropdowns [con] el mismo estilo de cristal
       y elementos planos que la modal de creación de un evento"). Antes
       era un acordeón tallado dentro de la propia caja que empujaba el
       contenido de debajo; flotando encima es cuando el blur del cristal
       tiene algo detrás que difuminar. Opciones planas: sin raise, la
       elegida solo con un tinte. */
    .select-box.open { z-index: 60; }
    .select-options { position: absolute; left: 0; top: calc(100% + 6px); min-width: 100%; z-index: 60; max-height: min(360px, 60vh); overflow-y: auto; border-radius: var(--r-2xl); background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(-4px); transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), visibility 0s linear var(--dur-fast); }
    /* Dentro de otra superficie con backdrop-filter (hero, modal, formulario
       flotante) el navegador no deja que un cristal anidado difumine lo que
       hay debajo del padre — el contenido se transparentaba nítido. Ahí el
       panel lleva el tinte de cristal sobre base opaca y sin blur. */
    :is(.page-hero, .glass-modal-box, .proto-new-form, .ini-new-form, .agent-detail-panel, .image-prompt-dialog, .nutricion-recetario-header) .select-options { background: linear-gradient(var(--glass-fill), var(--glass-fill)), var(--surface-base); backdrop-filter: none; -webkit-backdrop-filter: none; }
    .select-box.open .select-options { opacity: 1; visibility: visible; pointer-events: auto; transform: none; transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), visibility 0s; }
    .select-options-inner { padding: 6px; }
    .select-opt { display: block; width: 100%; appearance: none; border: none; background: none; text-align: left; padding: 12px 14px; min-height: 44px; margin-top: 2px; border-radius: var(--r-md); font: 400 0.9rem/1.2 var(--font-core); color: var(--text-muted); white-space: nowrap; cursor: pointer; transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
    .select-opt:first-child { margin-top: 0; }
    .select-opt:hover, .select-opt:focus-visible { color: var(--text-body); background: var(--glass-border); outline: none; }
    .select-opt.chosen { color: var(--text-strong); background: color-mix(in srgb, var(--neon-blue) 14%, transparent); }
    /* Variante "raised" (FOX-P130, mock de detalle de tarea validado) — el
       trigger deja de ser un pozo tallado (nu-inset-1) para ser una tarjeta
       elevada (surface-base + nu-raise-1), acorde al resto de tarjetas de
       propiedad de esa página. Solo cambia el fondo/sombra del propio
       .select-box; .select-chev/.select-options/.select-opt no cambian. */
    .select-box.select-box-raised { background: var(--surface-base); box-shadow: var(--nu-raise-1); }
    .select-trigger-rich { padding: 12px 12px 12px 14px; gap: 12px; }

    /* Selector de días de semana del formulario de alta de la tab Protocolos
       (createProtocolo, dias_semana 0=lunes..6=domingo) — 32px y colores
       planos (sin el degradado/inset de --elev-control-pressed, que perdía
       contraste con la letra) tras el feedback de Emilio sobre la versión
       de solo-lectura equivalente en protocolo-renderer.ts
       (.protocolo-day-chip): "las sombras y el tamaño... hacen difícil su
       lectura" — mismo ajuste aplicado aquí para no reintroducir el mismo
       problema en el único otro sitio con el mismo widget. */
    .protocolo-day-btn { width: 32px; height: 32px; border-radius: var(--r-xs); border: none; cursor: pointer; font-size: 0.8rem; font-weight: var(--fw-semibold); font-family: var(--font-mono); background: var(--surface-sunken); color: var(--text-muted); box-shadow: inset 0 0 0 1px var(--line-hair); transition: var(--t-press); }
    .protocolo-day-btn.selected { background: var(--signal-cyan-dim); color: var(--surface-base); box-shadow: none; }
    /* Catálogo de la tab Protocolos (buildProtocolosTab, server.ts) —
       rediseño Salud 2026-09-21: sustituye la <table> HTML por .tbl real
       (exhibit 16, ya en Iniciativas/Knowledge — CSS base compartida más
       arriba en este mismo <style>, no se duplica aquí). Confirmado por
       Emilio sobre el mock: "está perfect, no hay que hacerle nada" — Nombre
       ordenable + Categoría + Frecuencia, mismo patrón que Iniciativas
       (Nombre/Etapa/Actualizada). Solo estas 3 líneas son específicas de
       Protocolos: el ancho de columnas (Iniciativas usa 150px/150px, aquí
       Categoría/Frecuencia son más cortas) y el header con contador +
       formulario "+ Nuevo protocolo" en dropdown (antes siempre visible
       debajo del botón, ahora oculto hasta abrir — mismo patrón que
       "+ Nueva iniciativa"). */
    .tbl.protocolos-tbl .tbl-head, .tbl.protocolos-tbl .tbl-row { grid-template-columns: 1fr 130px 140px; }
    .protocolos-hero { margin-bottom: 36px; }
    .protocolos-hero-title-row { display: flex; align-items: baseline; gap: 14px; }
    .protocolos-hero-count { font: 600 11px/1 var(--font-mono); letter-spacing: var(--ls-label); color: var(--text-faint); background: var(--surface-sunken); box-shadow: var(--elev-field); border-radius: var(--r-pill); padding: 6px 12px; }
    .protocolos-toolbar { display: flex; align-items: flex-start; gap: 14px; row-gap: 14px; flex-wrap: wrap; margin-top: 22px; }
    .proto-new-wrap { position: relative; }
    /* Mismo patrón de formulario inline en dropdown que .ini-new-form
       (Iniciativas) — copiado en vez de reutilizado 1:1 porque cada tab
       mantiene su propio bloque de CSS con nombre propio en este fichero
       (mismo criterio que los otros bloques .tareas-.../.iniciativas-...
       ya existentes — ojo al escribir ejemplos de nombres de clase aquí
       dentro: un asterisco seguido de barra sin espacio entre medias
       cierra el comentario CSS antes de tiempo, y eso se comía la regla
       .proto-new-form siguiente entera, dejando el formulario "+ Nuevo
       protocolo" visible por defecto en vez de oculto). */
    .proto-new-form { display: none; flex-direction: column; gap: 14px; padding: 18px; margin-top: 10px; position: absolute; left: 0; top: calc(100% + 8px); z-index: 10; width: 360px; border-radius: var(--r-3xl); background: linear-gradient(var(--glass-fill), var(--glass-fill)), var(--surface-base); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); }
    .proto-new-form.open { display: flex; }
    .proto-new-form input, .proto-new-form textarea { border: none; border-bottom: 1px solid var(--line-hair); border-radius: 0; background: none; box-shadow: none; padding: 4px 0 10px; font: 400 13px var(--font-core); color: var(--text-body); outline: none; font-family: inherit; }
    .proto-new-form textarea { min-height: 70px; resize: vertical; }
    .proto-new-form input::placeholder, .proto-new-form textarea::placeholder { color: var(--text-faint); }
    .proto-new-form-actions { display: flex; align-items: center; gap: 8px; justify-content: flex-end; margin-top: 2px; }
    /* Header de Settings (FOX-P101, feedback de Emilio 2026-08-21: "hazlo
       como" el header del Daily Hub; 2026-09-17, tres rondas de feedback
       sobre el artifact "Carve & Glow"):
       — h1: subido de --fw-light a --fw-medium ("ponle más peso a la
         tipo del título" — --fw-light se veía demasiado fino con el
         font-stack real, aunque el exhibit del artifact use weight 300).
       — .eyebrow ("Settings · Fox") eliminado del todo, markup y CSS —
         pedido explícito ("lo de Settings-fox lo puedes eliminar").
       — Las tabs dejan el intento anterior (.seg-carved/.hub-nav-thumb,
         una pista compartida con una pastilla deslizante) por la receta
         REAL del exhibit 02 del artifact ("PILLS"): cada tab es su propio
         .tag-pill (theme.ts, ya usado en TAG-001 de la Guía) — carved
         SIEMPRE (hundido en reposo, mismo --nu-inset-1 que un input), con
         neón+vidrio propios encendiéndose solo si lleva .active. "Es el
         mismo bloque, dos usos" (cita literal del artifact) — no hay pieza
         compartida que desplazar, así que se cae también
         moveSettingsSubnavThumb() (initSettingsJs, más abajo): el toggle
         vuelve a ser tan simple como .classList.toggle('active'), igual
         que initSettingsTagDemoJs() para el specimen TAG-001.
       — La tarjeta de cristal flotante (blur real, borde sutil, esquinas
         redondeadas, sombra — mismo tratamiento que .chrome en el
         artifact, sin los 3 puntos de tráfico ni un título de ventana
         añadido, eso es decoración de la propia página-demo del artifact)
         y el h1/.eyebrow en --fw-medium/rosa se extrajeron a PAGE_HERO_CSS
         (theme.ts, ".page-hero"/".hero"/".eyebrow") — pedido explícito
         ("extender ese header al resto de headers... ponlo como
         componente"). Ya NO son solo de Settings: daily-hub-renderer.ts y
         el resto de renderers con .hero (dish/protocolo/cuestionarios/
         idea/objetivo/knowledge-detail) consumen la MISMA receta en vez de
         cada uno su propia copia ligeramente distinta. .settings-sticky-
         header queda como clase ADEMÁS de .page-hero.is-sticky (solo un
         gancho estable para hideNlaSwitchBehindHeader()/initSettingsJs,
         sin CSS propio) — Settings es la única pantalla que pide el header
         fijo de verdad; el resto se sigue desplazando con la página
         (comportamiento existente, esta receta no lo cambia).
       .hub-nav aquí es solo el layout de fila de las pills — el carved+
       neón de cada una vive en .tag-pill (TAG_PILL_CSS), no aquí. */
    .hub-nav { display: flex; align-items: center; gap: 10px; margin: 18px 0 0; }
    /* Sección 'Agentes' de Settings (buildSettingsAgentesSection, server.ts,
       FOX-P101 US3, click al detalle en US5) — mismo lenguaje tabular que
       .protocolo-table (hairline inferior por fila, thead en mayúsculas
       mono). La fila ahora es clickeable (US5): mismo hover-raise que
       .protocolo-row (raise solo en la celda de nombre, no en las tres —
       ver comentario largo junto a .protocolo-row más arriba en este mismo
       archivo, "Never nest two raised surfaces in adjacent cells"). */
    .settings-agent-table { width: 100%; border-collapse: separate; border-spacing: 0; table-layout: fixed; }
    .settings-agent-table th { text-align: left; padding: 6px 10px 6px 0; font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); border-bottom: 1px solid var(--line-hair); }
    .settings-agent-table td { padding: 10px 10px 10px 0; vertical-align: top; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; color: var(--text-body); }
    /* .settings-agent-name compensa su propio margin izquierdo negativo (ver
       más abajo) con este padding — sin él, el nombre queda recortado contra
       el borde izquierdo de la card (feedback de Emilio, 2026-08-21). */
    .settings-agent-table th:first-child, .settings-agent-table td:first-child { padding-left: 6px; }
    .settings-agent-table tbody tr:last-child td { border-bottom: none; }
    .settings-agent-row { cursor: pointer; transition: var(--t-press); }
    .settings-agent-row:hover .settings-agent-name { box-shadow: var(--elev-control); color: var(--signal-cyan-dim); border-radius: var(--r-xs); }
    .settings-agent-name { font-weight: var(--fw-semibold); white-space: nowrap; display: inline-block; padding: 2px 6px; margin: -2px -6px; transition: var(--t-press); }
    .settings-agent-purpose { font-size: 0.85rem; color: var(--text-body); }
    /* Switch "Display NLA" (FOX-P101 US6, nlaSwitchHtml en
       buildSettingsAgentesSection, server.ts) — traducción HTML/CSS de
       components/forms/Switch.jsx (skill fox-design): mismas medidas
       (track 52x28, thumb 22x22) y mismo mecanismo que
       .hub-protocol-checkbox (daily-hub-renderer.ts) — checkbox real
       oculto (accesible, dispara 'change') + track/thumb visual conmutados
       vía el selector de hermano :checked, nunca el widget nativo del SO.
       Al activarse pide contraseña en la fila de abajo, que reusa
       .agent-detail-password-row/.agent-detail-password-error (mismo gate
       visual que el detalle de US4/US5) en vez de duplicar esos estilos. Va
       ANTES de la tabla, en flujo normal — es .settings-sticky-header (más
       arriba, fijo) quien lo tapa al bajar, y hideNlaSwitchBehindHeader()
       (más abajo en este fichero) arranca el scroll ya bajado su altura al
       entrar en Agentes, para que aparezca oculto por defecto y
       solo se revele subiendo el scroll (feedback de Emilio, 2026-08-21:
       "como un pull-to-refresh o el buscador de algunas apps de iOS"). */
    .settings-display-nla-row { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
    /* Relleno invisible al pie de la card Agentes — arranca en 0;
       hideNlaSwitchBehindHeader() (más abajo en este fichero) le asigna
       exactamente la altura que falte (si falta alguna) para garantizar
       margen de scroll suficiente en .board-col, sin depender de cuántos
       agentes reales haya ni de la altura de la ventana del usuario. */
    .settings-agentes-scroll-spacer { height: 0; }
    .settings-switch { display: inline-flex; align-items: center; gap: var(--sp-4); cursor: pointer; width: fit-content; }
    .settings-switch input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
    /* Track — carved puro en reposo (--elev-field, el mismo --nu-inset-1 de
       siempre), sin vidrio permanente. Receta real .nu-switch del artifact
       (exhibit 03): el vidrio (.glass-glaze compartido, GLASS_ICON_CSS) y
       una barra de neón horizontal centrada aparecen SOLO al marcar
       :checked, fundidos en opacidad — "el vidrio en sí es la señal de
       'on', no solo el color" (corregido, FOX-P125: la primera versión no
       tenía ni vidrio ni neón en ningún estado).
       El thumb ya NO se mueve por justify-content (ese cambio no anima: un
       flip de flex-start/flex-end simplemente teletransporta el grip, sin
       deslizamiento — "no está bien terminado", feedback de Emilio
       2026-09-17 tras comparar contra el switch real del artifact). Ahora
       es position:absolute + transition en "left" con --ease-spring, la
       MISMA receta que .nu-grip del artifact y que .seg-thumb
       (SEG_CARVED_CSS) ya usa en este mismo archivo — 240ms literal, no
       --dur-base/--ease-out: esas curvas son para fundidos, no para algo
       que se desliza de verdad. Sin overflow:hidden en el track — el
       artifact tampoco lo tiene, y recortaría el glow que se añade al
       grip en :checked. */
    .settings-switch-track { width: 52px; height: 28px; border-radius: var(--r-pill); background: var(--surface-sunken); box-shadow: var(--elev-field); position: relative; }
    /* Vidrio cubriendo el track ENTERO (inset:0, no el inset:3px/radius:8px
       por defecto de GLASS_ICON_CSS pensado para iconos pequeños) — receta
       real .switch-glass del artifact, mismo criterio que .seg-thumb
       .glass-glaze de arriba (SEG_CARVED_CSS): el cristal tiene que calzar
       exacto con la forma del contenedor pill, no dejar esquinas cuadradas
       asomando dentro de un track totalmente redondeado. */
    .settings-switch-track .glass-glaze { inset: 0; border-radius: inherit; opacity: 0; transition: opacity var(--dur-base) var(--ease-out); z-index: 1; }
    .settings-switch-track .settings-switch-neon { position: absolute; left: 6px; right: 6px; top: 50%; transform: translateY(-50%); height: 10px; border-radius: var(--r-md); background: var(--neon-green); filter: blur(6px); opacity: 0; transition: opacity var(--dur-base) var(--ease-out); pointer-events: none; z-index: 0; }
    .settings-switch input:checked ~ .settings-switch-track .glass-glaze { opacity: 1; }
    .settings-switch input:checked ~ .settings-switch-track .settings-switch-neon { opacity: .85; }
    /* Thumb — grip de 4 puntos en rejilla 2x2 (receta real .nu-grip del
       artifact), no un círculo liso (corregido, FOX-P125: la primera
       versión era var(--r-pill), un círculo plano sin grip). 12px de
       radio (var(--r-xl)), igual que el exhibit real. position:absolute
       con left:3px/27px (3px de margen igual que el padding que tenía
       antes el track) en vez de la caja flex de antes — el desplazamiento
       real es lo que hace falta transicionar. */
    .settings-switch-thumb { position: absolute; top: 3px; left: 3px; z-index: 2; width: 22px; height: 22px; border-radius: var(--r-xl); background: var(--surface-base); box-shadow: var(--nu-raise-1); transition: left 240ms var(--ease-spring), box-shadow var(--dur-fast) var(--ease-press); display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px; place-items: center; padding: 5px; }
    .settings-switch-thumb i { width: 3px; height: 3px; border-radius: 50%; background: var(--text-faint); display: block; }
    .settings-switch input:checked ~ .settings-switch-track .settings-switch-thumb { left: 27px; box-shadow: var(--nu-raise-1), 0 0 8px 1px var(--neon-green-glow); }
    .settings-switch-label { font: var(--text-base); font-size: var(--fs-small); color: var(--text-body); }
    .settings-display-nla-password-row { max-width: 320px; }
    /* Detalle de agente (FOX-P101 US5) — mismo lenguaje de modal que
       .doc-preview-overlay/.doc-preview-panel (ver esa regla más abajo en
       este archivo): único otro sitio, junto con los diálogos globales,
       donde --elev-modal / raise-3 aplica. Solo lectura (ver, no editar) —
       igual que la Guía de Estilo, sin ningún control de edición aquí. */
    .agent-detail-overlay { position: fixed; inset: 0; background: var(--surface-overlay); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); z-index: 1000; display: flex; align-items: center; justify-content: center; animation: ql-fade-in var(--dur-base) var(--ease-out); }
    .agent-detail-panel { background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); border-radius: var(--r-3xl); width: min(720px, 92vw); height: min(780px, 88vh); display: flex; flex-direction: column; animation: ql-scale-in var(--dur-slow) var(--ease-out); overflow: hidden; }
    .agent-detail-header { display: flex; align-items: center; justify-content: space-between; padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--line-hair); flex-shrink: 0; gap: var(--sp-4); }
    .agent-detail-title { font-size: var(--fs-lead); font-weight: var(--fw-semibold); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .agent-detail-close { background: none; border: none; color: var(--text-muted); font-size: 1rem; cursor: pointer; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: var(--t-press); flex-shrink: 0; }
    .agent-detail-close:hover { background: var(--line-soft); color: var(--text-body); }
    .agent-detail-body { flex: 1; overflow-y: auto; padding: var(--sp-5); }
    .agent-detail-password-hint { font-size: 0.85rem; color: var(--text-muted); margin: 0 0 12px; }
    .agent-detail-password-row { display: flex; gap: 8px; align-items: center; }
    /* Campo de contraseña — receta real .field-carved (FOX-P125, exhibit
       01: "campos tallados, con acento propio"), acento rosa (el propio
       ejemplo del artifact es literalmente "Scully — password del
       agente") en vez del pozo plano sin acento que había antes. El reset
       de chrome del <input> (background/border/outline/placeholder) vive
       ahora en FIELD_CARVED_CSS (theme.ts) — aquí solo el layout propio de
       esta fila (ancho flexible, padding vertical reducido). */
    .agent-detail-password-row .field-carved { flex: 1; padding-top: 10px; padding-bottom: 10px; }
    /* Dentro de la modal de cristal el campo pasa a plano (hairline, sin
       pozo), como los .glass-field de "Añadir evento" — el acento rosa se
       queda como única señal de dato sensible. */
    .agent-detail-password-row .field-carved { background: none; box-shadow: none; border-radius: 0; border-bottom: 1px solid var(--line-hair); padding: 0 0 10px 16px; }
    .agent-detail-password-row .field-carved-accent { left: 0; top: 2px; bottom: 12px; width: 3px; }
    .agent-detail-password-row .field-carved-key { font-size: 10px; letter-spacing: var(--ls-label); color: var(--text-faint); }
    .agent-detail-password-row .field-carved-value { font-size: 14px; }
    .agent-detail-password-error { color: var(--signal-red); font-size: 0.8rem; margin: 10px 0 0; min-height: 1em; }
    .agent-detail-skill { margin-bottom: 20px; }
    .agent-detail-skill:last-child { margin-bottom: 0; }
    .agent-detail-skill h3 { font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--signal-cyan-dim); margin: 0 0 8px; }
    .agent-detail-skill pre { margin: 0; padding: 14px 16px; background: var(--surface-sunken); box-shadow: inset 0 0 0 1px var(--line-hair); border-radius: var(--r-md); font-size: 0.8rem; line-height: 1.55; color: var(--text-body); font-family: var(--font-mono); white-space: pre-wrap; word-break: break-word; }
    /* Retícula propia de la tab Protocolos — 5 columnas (feedback de
       Emilio: primero "un par de unidades más ancha", luego "ahora es
       demasiado ancha" con 6 — 5 es el punto intermedio) en vez de las 4 de
       HUB_GRID_CSS. Fluidas (1fr), no 192px fijos — FOX-P125, feedback de
       Emilio 2026-09-18: "ya sin estar acotadas al grid de referencia que
       teníamos de fondo... y ser responsive" (mismo motivo que el resto de
       .hub-grid de la app). */
    .hub-grid.protocolos-grid { grid-template-columns: repeat(5, 1fr); }
    /* Retícula propia del recetario — 9 sub-columnas (la misma granularidad
       que el detalle de receta, ver dish-renderer.ts) en vez de las 4 de
       HUB_GRID_CSS, para que una card "3x3" pueda caber 3 por fila sin
       resto. Fluidas (1fr), no 96px fijos — mismo motivo que
       .protocolos-grid de arriba. */
    /* Compactado (rediseño Salud 2026-09-22, feedback de Emilio: "Las
       cards que tenemos ahora mismo en fox son demasiado grandes, las
       quiero como en el artefacto") — auto-fill/minmax(220px,1fr) en vez
       de 9 sub-columnas fijas de 96px×3, receta literal del artefacto
       (.reco-grid). grid-auto-rows:auto porque la card ya no fuerza una
       altura múltiplo de 96px (grid-row:span 3 se retira del markup, ver
       buildNutricionTab en server.ts) — su altura la decide el contenido
       (foto de 120px + cuerpo de texto), no la retícula. */
    /* gap:16px real (exhibit .reco-grid del artefacto) — el gap:1px por
       defecto de HUB_GRID_CSS asume cards a hueso con hairline compartido
       de fondo, pensado para su retícula de celdas cuadradas sin radio;
       .nutricion-recetario-card ya tiene su propio border-radius/sombra/
       hover-raise independientes, así que 1px se leía como "sin
       separación" (feedback de Emilio: "no hay separación entre cards"). */
    /* .nutricion-recetario-scroll — max-height+scroll propio, igual que
       .nutricion-shop-items (feedback de Emilio: "igual que pudiera hacer
       scroll dentro de [la lista de la compra] sería buena idea [en]
       recetas también") — sin tope, decenas de recetas reales estiran
       esta columna muy por encima de la lista de la compra de al lado,
       descompensando la fila 70/30. Mismos 520px para que ambas columnas
       queden a la misma altura visual. El propio max-height/overflow vive
       en el WRAPPER, no en el grid — el header necesita quedar dentro del
       mismo contenedor con scroll para poder ser sticky respecto a él. */
    /* Altura (2026-09-25, feedback: "el recetario es demasiado pequeño,
       mínimo que se vean 2 cards de alto"): contain:size hace que su
       contenido no empuje la fila, así mide lo mismo que la lista de la
       compra de al lado (align-self: stretch) y nunca menos de 640px — dos
       filas de cards completas más la mini cabecera. */
    .nutricion-recetario-scroll { position: relative; contain: size; align-self: stretch; min-height: 640px; overflow-y: auto; border-radius: var(--r-2xl); box-shadow: var(--elev-card-soft); }
    /* Mini page-hero — misma receta de cristal que .page-hero (PAGE_HERO_CSS)
       pero contenida al ancho de esta columna y sticky respecto a SU
       propio scroll, no al de la página (feedback de Emilio: "un mini
       header al título del recetario, como el de la página, pero
       contenido a lo que ocupa la sección... las cards pasarían por
       debajo. Así no viola las reglas del neumorfismo" — un título suelto
       flotando sobre las cards sin superficie propia sí las violaría). */
    /* z-index:4, no 2 — una card en :hover sube a z-index:3 (.nutricion-recetario-card:hover,
   más abajo) para proyectar su sombra sobre las vecinas; con el header
   mini a solo 2 quedaba POR DEBAJO de esa card elevada y se veía "romper"
   a través del cristal sticky (bug real, feedback de Emilio: "hay una
   tarjeta que no se mete igual, se rompe un poco"). 4 lo deja siempre por
   encima de cualquier card, en reposo o en hover. */
.nutricion-recetario-header { position: sticky; top: 0; z-index: 4; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 18px; background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border-bottom: 1px solid var(--glass-border); border-radius: var(--r-2xl) var(--r-2xl) 0 0; }
    .nutricion-recetario-header h2 { margin: 0; font: var(--fw-semibold) 16.5px/1.3 var(--font-core); color: var(--text-strong); }
    .hub-grid.nutricion-recetario-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); grid-auto-rows: auto; gap: 16px; background: none; padding: 16px; }
    /* Igual que en dish-renderer.ts: estos overrides viven fuera de
       cualquier @media, así que pisan incondicionalmente el colapso a 2
       columnas del media query (≤860px) de HUB_GRID_CSS — con columnas
       fluidas, 5 o 9 tracks de 1fr en un viewport estrecho amontonan el
       contenido en vez de simplemente desbordar con scroll (como pasaba
       con los px fijos de antes). */
    @media (max-width: 860px) {
      .hub-grid.protocolos-grid, .hub-grid.nutricion-recetario-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
      .hub-grid.protocolos-grid .hub-card, .hub-grid.nutricion-recetario-grid .hub-card { grid-column: 1 / -1 !important; height: auto; }
    }
    /* Card foto-primero: la imagen ocupa el ancho/alto completo de la card
       SIN el padding heredado de .hub-card (feedback de Emilio: "las
       imágenes no deberían tener padding alguno") — de ahí padding:0 aquí y
       un bloque de info propio debajo que reintroduce su propio padding.
       overflow:hidden ya en .hub-card recorta la imagen a los bordes de la
       card, así que no hace falta redondear ninguna esquina (mismo motivo
       por el que HUB_GRID_CSS no redondea: las cards van a hueso unas
       contra otras en la retícula). */
    /* transition + hover-raise — mandamiento "la UI reacciona al touch" del
       design system (readme.md, Data visualisation): la card se eleva de
       verdad (var(--elev-card), no solo un cambio de color) al pasar el
       ratón, en vez de quedarse plana como el resto de hub-cards. */
    /* Rediseño Salud 2026-09-22 (mock de referencia def89764 exhibit 13/
       "recetario") — pedido explícito de Emilio tras avisar que el .plan-
       grid del mock es una maqueta simplificada sin fotos/quitar/totales:
       "quiero que efectúes el rediseño que hiciste en el artefacto, tal y
       como está ahí" para la PIEL visual (radio de esquina, elevación en
       reposo). Se mantiene toda la funcionalidad real (drag&drop, fotos,
       ⠿/×, 14 días, totales, hoy) — solo cambian border-radius/box-shadow,
       ninguna clase ni atributo de datos se toca. overflow:hidden para que
       la foto (a sangre, sin padding propio) respete la esquina redondeada
       — antes no hacía falta porque las cards no tenían radio.
       Reemplaza el radio 0 deliberado de HUB_GRID_CSS ("las cards van a
       hueso unas contra otras en la retícula") — decisión previa que
       Emilio confirma que quiere cambiar aquí, no en el resto del sistema
       (el resto de .hub-card se queda como está). */
    .nutricion-recetario-card { padding: 0; overflow: hidden; border-radius: var(--r-2xl); box-shadow: var(--elev-card); transition: box-shadow 160ms ease; }
    /* z-index en :hover — sin esto la sombra de una card se pinta DEBAJO
       de la card siguiente en el orden del DOM (p.ej. Desayuno, que va
       antes que Comida, no proyectaba sombra hacia abajo sobre Comida:
       el fondo opaco de Comida se pintaba encima y la tapaba). Elevar el
       z-index solo al hacer hover deja que la sombra se vea sobre
       cualquier vecina, sin afectar el z-index en reposo. */
    .nutricion-recetario-card:hover { box-shadow: var(--nu-raise-3); z-index: 3; }
    .nutricion-recetario-img { width: 100%; height: 120px; flex-shrink: 0; object-fit: cover; display: block; background: var(--surface-sunken); }
    /* Degradado con tinte naranja en vez del --surface-sunken plano
       anterior (mock: .reco-photo) — el degradado sobre una foto REAL ya
       se probó y se descartó explícitamente en el artifact; este cambio
       es solo sobre el placeholder sin foto, caso distinto. */
    .nutricion-recetario-img-placeholder { display: flex; align-items: center; justify-content: center; color: color-mix(in srgb, var(--neon-orange) 55%, var(--text-faint)); background: linear-gradient(155deg, color-mix(in srgb, var(--neon-orange) 20%, var(--surface-base)) 0%, var(--surface-base) 65%); }
    /* Sin handle de arrastre dedicado (rediseño Nutrición 2026-09-22,
       feedback de Emilio: "hay que quitar el handle de drag") — la card
       ENTERA ya lleva draggable="true" (mismo criterio que el resto del
       sistema: el navegador solo dispara dragstart tras un desplazamiento
       real, así que el click de navegación al detalle sigue intacto). */
    .nutricion-recetario-link { display: block; text-decoration: none; color: inherit; }
    /* padding:14px uniforme (exhibit 13, .reco-body) — antes 12px arriba /
       14px lados/abajo, un valor inventado que nunca estuvo en el
       artifact. Auditado contra el markup real del exhibit al construir la
       card de detalle del plato (tarea siguiente de esta misma US7). */
    .nutricion-recetario-info { flex: 1; min-height: 0; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; }
    .nutricion-recetario-title-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
    .nutricion-recetario-title-row h2 { font-size: 14px; font-weight: var(--fw-semibold); margin: 0; line-height: 1.3; letter-spacing: -0.005em; }
    /* El tag vive en su propia línea BAJO el título (exhibit 13, .reco-tag:
       display:block, margin-bottom:10px) — no inline junto al título como
       tenía esta card antes (esa era una composición ad-hoc, nunca la del
       artifact real). Escopeado a .nutricion-recetario-info > .tag para no
       tocar el .tag compartido de .hub-card-head (theme.ts), que sigue
       inline en el resto del sistema. */
    .nutricion-recetario-info > .tag { display: block; margin-top: 4px; margin-bottom: 10px; font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--signal-orange); }
    .nutricion-recetario-macro { display: block; font-size: 0.68rem; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-muted); }
    /* gap:4px (exhibit 13, .reco-days) — antes 2px. */
    .nutricion-recetario-days { display: flex; gap: 4px; margin-top: 8px; }
    /* Lista de la compra (rediseño Salud 2026-09-21) — mismo idioma que
       .stat-tile/.shop-summary del artifact de referencia, traducido a
       .hub-card-body (esta card vive en el .hub-grid genérico, no en un
       componente propio nuevo). */
    .nutricion-shop-summary { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
    .nutricion-shop-stat { flex: 1; min-width: 100px; }
    .nutricion-shop-k { display: block; font: 600 10.5px/1 var(--font-mono); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); }
    .nutricion-shop-v { display: block; font: 600 22px/1.15 var(--font-core); margin-top: 8px; color: var(--text-strong); }
    .nutricion-shop-v small { font: 500 11px/1 var(--font-core); color: var(--text-faint); margin-left: 2px; }
    .nutricion-shop-warn { display: flex; align-items: center; gap: 6px; margin: 14px 0 0; font-size: 12px; color: var(--signal-orange); }
    .nutricion-shop-note { margin: 10px 0 0; padding-top: 10px; box-shadow: inset 0 1px 0 var(--line-soft); font-size: 11.5px; color: var(--text-faint); }
    /* Desglose real por ingrediente (feedback de Emilio: "me gustaría que
       se pudiera ver la lista de la compra ahí, no en telegram") — hasta
       ahora solo el resumen agregado vivía aquí, la lista item a item
       solo se veía en el mensaje de Telegram del cron. max-height+scroll
       propio: sin tope, 40-50 ingredientes típicos de una semana estiran
       esta columna (30% del ancho) muy por encima de la altura real del
       Recetario (70%), que gobierna el alto de la fila entera. */
    .nutricion-shop-items { margin-top: 14px; padding-top: 12px; box-shadow: inset 0 1px 0 var(--line-soft); max-height: 520px; overflow-y: auto; }
    /* Checkbox (selección múltiple) + cuerpo + × individual, en fila —
       feedback de Emilio: "una manera fácil de eliminar items... uno o
       varios, para ir más rápido". */
    .nutricion-shop-item { display: flex; align-items: flex-start; gap: 8px; padding: 7px 0; border-top: 1px solid var(--line-hair); }
    .nutricion-shop-item:first-child { border-top: none; padding-top: 0; }
    .nutricion-shop-item-check { flex-shrink: 0; padding-top: 2px; cursor: pointer; }
    .nutricion-shop-item-checkbox { width: 14px; height: 14px; cursor: pointer; accent-color: var(--signal-orange); }
    .nutricion-shop-item-body { flex: 1; min-width: 0; }
    .nutricion-shop-item-remove { flex-shrink: 0; border: none; background: none; padding: 0 0 0 4px; color: var(--text-faint); cursor: pointer; font-size: 13px; line-height: 1.3; opacity: 0; transition: opacity var(--dur-fast) var(--ease-out); }
    .nutricion-shop-item:hover .nutricion-shop-item-remove { opacity: 1; }
    .nutricion-shop-item-remove:hover { color: var(--signal-red); }
    .nutricion-shop-item-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
    .nutricion-shop-item-name { font: 500 12.5px/1.3 var(--font-core); color: var(--text-body); text-transform: capitalize; }
    .nutricion-shop-item-price { flex-shrink: 0; font: 600 11.5px/1 var(--font-mono); color: var(--text-strong); }
    .nutricion-shop-item-product { margin-top: 2px; font-size: 11px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .nutricion-shop-item.is-ambiguo .nutricion-shop-item-price { color: var(--signal-orange); }
    .nutricion-shop-item.is-sin-match .nutricion-shop-item-name { color: var(--text-muted); }
    .nutricion-shop-item.is-sin-match .nutricion-shop-item-price { color: var(--text-faint); }
    .nutricion-shop-item-flag { color: var(--signal-orange); font-weight: 600; }
    /* Barra de acción masiva — oculta hasta que haya ≥1 checkbox marcado
       (toggle vía JS, ver initNutricionShopExclude). */
    .nutricion-shop-bulkbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 14px; padding: 8px 10px; border-radius: var(--r-lg); background: var(--surface-sunken); box-shadow: var(--nu-inset-1); }
    /* [hidden] normal (UA stylesheet) pierde contra esta regla de autor
       por origen de cascada aunque la especificidad sea igual — sin este
       override explícito, la barra quedaba visible siempre pese al
       atributo hidden real en el DOM (bug detectado al probar en vivo). */
    .nutricion-shop-bulkbar[hidden] { display: none; }
    .nutricion-shop-bulkbar-count { font: 600 11.5px/1 var(--font-mono); color: var(--text-muted); }
    /* Heading de página para "Plan semanal", fuera de cualquier card (ver
       buildWeeklyPlanGridHtml) — el rango de fechas en naranja como único
       acento de énfasis, igual que el resto de CTAs primarios del sistema. */
    .nutricion-plan-range { color: var(--signal-orange); }
    /* Retícula real del plan semanal — 1 columna de etiqueta pegajosa +
       7 columnas de día, cards de 2x3 unidades (192x288px) fijas para que
       ninguna crezca según el nombre del plato (feedback de Emilio). Sin
       gap: el hairline lo pone cada celda vía box-shadow/las propias
       .hub-card, igual que el Kanban de Tareas. */
    /* Receta literal .plan-grid del artefacto (rediseño Salud 2026-09-22,
       ronda 2 — Emilio comparó el artefacto contra Fox punto por punto:
       "la fila de las fechas está mucho más elegante en el artefacto" /
       "las celdas vacías... los otros parecen legacy" / "la columna de
       Desayuno, comida, merienda parece legacy también"). gap:1px +
       background:line-hair (en vez de bordes manuales por celda) dibuja el
       hairline compartido entre TODAS las celdas a la vez, y con
       border-radius+overflow:hidden el grid entero se lee como una sola
       superficie tallada — ya no hace falta ni el sangrado a los bordes
       (vive dentro de un .panel ahora). El scroll horizontal SÍ hace
       falta (vuelta atrás, ronda 3) — columnas 1fr puras dejaban que un
       título real sin espacios de sobra empujara la pista de grid a su
       ancho de contenido completo (~430px), y los días que no cabían
       desaparecían sin forma de llegar a ellos. Con columnas de ancho
       fijo (170px, ver gridColsRule en server.ts) + overflow-x:auto aquí,
       cada columna tiene un tope real y el resto de la semana se alcanza
       con scroll dentro del panel (Emilio: "hay que ponerle un límite de
       ancho... y que se pueda scrollear dentro del elemento"). */
    .nutricion-plan-grid { display: grid; gap: 1px; background: var(--line-hair); border-radius: var(--r-2xl); overflow-x: auto; margin-top: 18px; }
    .plan-corner, .plan-slot-label { position: sticky; left: 0; z-index: 3; }
    .plan-corner, .plan-slot-label, .plan-day-header, .plan-total-cell { background: var(--surface-card); }
    /* Columna de etiquetas de cristal al hacer scroll lateral (feedback:
       "en el scroll lateral del menú semanal deberíamos tener algo
       parecido" al header del Recetario) — en reposo sigue opaca (el fondo
       del grid es --line-hair, un cristal translúcido lo dejaría ver y la
       columna se leería gris); en cuanto hay columnas pasando por debajo
       (.is-scrolled-x, ver initNutricionGridScroll) pasa a cristal con
       blur + filo derecho, en vez del corte seco de antes. */
    .plan-corner, .plan-slot-label { transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); }
    .nutricion-plan-grid.is-scrolled-x .plan-corner, .nutricion-plan-grid.is-scrolled-x .plan-slot-label { background: color-mix(in srgb, var(--surface-card) 62%, transparent); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); box-shadow: inset -1px 0 0 var(--glass-border), 6px 0 12px -8px var(--nu-dark-soft); }
    .plan-day-header { padding: 10px 6px; text-align: center; }
    .plan-day-header .d { display: block; font: 600 10px/1 var(--font-mono); text-transform: uppercase; letter-spacing: .03em; color: var(--text-faint); }
    .plan-day-header .n { display: block; margin-top: 3px; font: 600 12px/1 var(--font-core); color: var(--text-strong); }
    /* .wd-today-chip real solo centra bajo .week-day-head (Inbox) — sin
       este scope propio para .plan-day-header, el margin:auto no
       aplicaba nunca y el badge quedaba pegado al borde izquierdo
       (feedback de Emilio: "esto está descentrado"). */
    .plan-day-header .wd-today-chip { margin: 6px auto 0; }
    .plan-slot-label { display: flex; align-items: center; padding: 0 10px; font: 600 10px/1 var(--font-mono); text-transform: uppercase; letter-spacing: .03em; color: var(--text-faint); }
    .plan-cell { position: relative; background: var(--surface-base); min-height: 92px; padding: 6px; display: flex; align-items: center; justify-content: center; }
    .plan-cell.plan-cell-empty { background: repeating-linear-gradient(135deg, var(--surface-sunken), var(--surface-sunken) 4px, var(--surface-base) 4px, var(--surface-base) 10px); }
    /* .plan-cell-filled — receta literal del artefacto: chip de texto solo
       (sin foto, confirmado por Emilio: "en el planner no es necesario"),
       toda la celda es el <a> arrastrable (draggable="true" en
       .nutricion-grid-cell, ver buildWeeklyPlanGridHtml — Emilio: "el drag
       handle lo quiero fuera, que sea toda la card draggable"). */
    .plan-cell-filled { width: 100%; height: 100%; border-radius: var(--r-lg); background: var(--surface-sunken); box-shadow: var(--nu-raise-1); padding: 6px 8px; display: flex; flex-direction: column; justify-content: center; gap: 2px; cursor: grab; text-decoration: none; overflow: hidden; }
    /* 2 líneas mínimo, no 1 (feedback de Emilio: "en estas cards les
       permitiría 2 líneas al menos") — line-clamp en vez de nowrap, el
       título ya no se recorta a la primera palabra o dos. */
    .plan-cell-filled .t { font: 600 13px/1.3 var(--font-core); color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
    .plan-cell-filled .k { font: 400 12px/1 var(--font-mono); color: var(--text-faint); }
    .nutricion-grid-cell-remove { position: absolute; top: 6px; right: 6px; width: 18px; height: 18px; border-radius: var(--r-xs); background: rgba(0,0,0,0.55); color: #fff; border: none; font-size: 0.8rem; line-height: 1; cursor: pointer; z-index: 2; padding: 0; }
    .plan-total-cell { display: flex; align-items: center; justify-content: center; }
    /* Fila "Extras" ("+ Añadir extra", rediseño Salud 2026-09-22, ronda 3)
       — vive por día (columna), no por slot de comida. Altura auto (ver
       gridRowsRule en server.ts): crece con el número de extras de ese
       día en vez de recortar. */
    .plan-extra-cell { background: var(--surface-base); display: flex; flex-direction: column; gap: 4px; padding: 6px; }
    .plan-extra-list { display: flex; flex-direction: column; gap: 4px; }
    .plan-extra-chip { display: flex; align-items: center; gap: 4px; padding: 3px 6px; border-radius: var(--r-md); background: var(--surface-sunken); box-shadow: var(--nu-inset-1); }
    .plan-extra-chip-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-body); font: 500 10px/1.2 var(--font-core); }
    .plan-extra-chip-k { flex-shrink: 0; color: var(--text-faint); font: 400 9px/1 var(--font-mono); }
    .plan-extra-chip-remove { flex-shrink: 0; border: none; background: none; padding: 0 0 0 2px; color: var(--text-faint); cursor: pointer; font-size: 11px; line-height: 1; }
    .plan-extra-chip-remove:hover { color: var(--signal-red); }
    .plan-extra-add-btn { align-self: flex-start; border: none; background: none; padding: 2px 5px; border-radius: var(--r-xs); color: var(--text-faint); font: 600 12px/1 var(--font-mono); cursor: pointer; }
    .plan-extra-add-btn:hover { color: var(--signal-orange); background: var(--surface-sunken); }
    .plan-extra-input { width: 100%; padding: 4px 6px; border: none; border-radius: var(--r-md); box-shadow: var(--elev-field); background: var(--surface-sunken); color: var(--text-body); font-size: 10.5px; box-sizing: border-box; }
    .plan-extra-input:disabled { opacity: .6; }
    .empty { color: var(--muted); font-size: 0.875rem; }

    /* ── Inbox — fusión Inbox+Social (FOX-P130), migración a producción del
       mock artifact validado por Emilio (2026-09-22): page-hero + .panel,
       mismo patrón que Documentos/Finances/Tareas. La pieza nueva sin
       precedente es la card "Semana" (grid horario de 7 días) — no existía
       ningún grid de este tipo en Fox; construida con el mismo vocabulario
       de tokens (surface/signal/nu-*/line-hair) en vez de uno propio.
       prop-card/prop-icon (Maya) tampoco tenían precedente real — solo
       existían en el mock de Detalle de Tarea, nunca portado; centralizadas
       aquí para sus 3 filas. ── */
    .inbox-hero-meta { margin-top: 8px; font: 400 13.5px/1.4 var(--font-core); color: var(--text-muted); }
    .inbox-hero-meta b { color: var(--text-body); font-weight: var(--fw-semibold); }
    .panel.raised { background: var(--surface-base); box-shadow: var(--elev-card); }
    .panel + .panel { margin-top: 28px; }
    /* Paneles al 50/50: resumen de la semana + Maya (feedback de Emilio, 2026-09-25). */
    .inbox-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 28px; align-items: start; margin-top: 28px; }
    .inbox-pair > .panel { margin-top: 0; }
    /* Vista de la agenda: Día / Semana / Mes (selector dentro del menú de calendarios). */
    .cal-view-row { display: flex; gap: 6px; margin: 0 0 14px; }
    .cal-view-btn { flex: 1; appearance: none; border: none; cursor: pointer; padding: 7px 4px; border-radius: var(--r-lg); background: var(--surface-card); box-shadow: var(--nu-raise-1); color: var(--text-muted); font: 500 12.5px var(--font-core); transition: box-shadow var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
    .cal-view-btn[aria-pressed="true"] { box-shadow: var(--nu-inset-1); color: var(--text-strong); font-weight: var(--fw-semibold); }
    .cal-view-btn:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: 2px; }
    .month-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
    .month-dow { font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); text-align: center; padding: 6px 0; }
    .month-cell { appearance: none; border: none; cursor: pointer; text-align: left; min-height: 92px; min-width: 0; padding: 6px; border-radius: var(--r-lg); background: color-mix(in srgb, var(--surface-sunken) 55%, transparent); color: var(--text-body); display: flex; flex-direction: column; gap: 4px; transition: background var(--dur-fast) var(--ease-out); font: inherit; }
    .month-cell:hover { background: var(--surface-sunken); }
    .month-cell:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: 1px; }
    .month-cell.is-other { opacity: .45; }
    .month-num { font: 600 12px/1 var(--font-core); color: var(--text-muted); align-self: flex-start; padding: 3px 5px; border-radius: var(--r-pill); }
    .month-cell.is-today .month-num { background: var(--signal-cyan-dim); color: #0b0d10; }
    .month-events { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .month-chip { display: flex; align-items: center; gap: 4px; min-width: 0; font: 500 11px/1.25 var(--font-core); color: var(--text-body); }
    .month-dot { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: var(--tone-color); }
    .month-chip-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
    .month-more { font: 600 10px/1 var(--font-mono); color: var(--text-faint); }
    @media (max-width: 640px) {
      .month-grid { gap: 3px; }
      .month-cell { min-height: 58px; padding: 4px 2px; align-items: center; }
      .month-num { align-self: center; }
      .month-events { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 3px; }
      .month-chip-text { display: none; }
      .month-more { font-size: 9px; }
    }
    @media (max-width: 900px) { .inbox-pair { grid-template-columns: 1fr; } }
    /* Filas desplegables del resumen de la semana — la propia tarjeta es el
       disparador (summary), la lista completa aparece debajo. */
    .summary-row > summary { list-style: none; cursor: pointer; }
    .summary-row > summary::-webkit-details-marker { display: none; }
    .summary-chev { margin-left: auto; display: inline-flex; color: var(--text-faint); transition: transform var(--dur-fast) var(--ease-out); }
    .summary-row[open] .summary-chev { transform: rotate(180deg); }
    .summary-row > summary:hover .prop-title { color: var(--text-strong); }
    .summary-list { list-style: none; margin: 4px 0 0; padding: 4px 14px 6px 58px; display: flex; flex-direction: column; max-height: 360px; overflow-y: auto; scrollbar-width: thin; }
    .summary-list li { display: flex; gap: 12px; align-items: baseline; padding: 7px 0; border-bottom: 1px solid var(--line-hair); font: 400 13px/1.4 var(--font-core); color: var(--text-body); min-width: 0; }
    .summary-list li:last-child { border-bottom: none; }
    .summary-list li.is-past { opacity: .5; }
    .summary-when { flex: 0 0 96px; font: var(--fw-medium) 11px/1.4 var(--font-mono); color: var(--text-faint); }
    .summary-list a, .summary-list li > span:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .summary-list a { color: var(--text-body); text-decoration: none; }
    .summary-list a:hover { color: var(--text-strong); text-decoration: underline; }

    .prop-list { display: flex; flex-direction: column; gap: 10px; }
    /* FOX-P86 — bloqueos compartidos sobre el tablero de Tareas: la lista de tareas va siempre visible (sin acordeón). */
    .panel.task-clusters { margin-bottom: 28px; }
    /* FOX-P162/US4 — propuestas de mejora de Fox sobre Tareas e Iniciativas. */
    .panel.improvement-proposals { margin-bottom: 28px; }
    .improvement-proposal { display: flex; flex-direction: column; gap: 8px; }
    .improvement-proposal .prop-text { min-width: 0; }
    .improvement-proposal .prop-sub { white-space: normal; }
    .improvement-proposal-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
    .improvement-proposal-error { margin-right: auto; font: 400 12px/1.4 var(--font-core); color: var(--signal-orange); }
    .improvement-proposal-error:empty { display: none; }
    .task-cluster .summary-list { max-height: none; }
    .prop-card { position: relative; display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px 14px; border-radius: var(--r-2xl); background: var(--surface-base); box-shadow: var(--nu-raise-1); }
    .prop-icon { width: 36px; height: 36px; border-radius: var(--r-xl); position: relative; flex: 0 0 auto; background: var(--surface-sunken); box-shadow: var(--nu-raise-1); }
    .prop-icon .neon-under { width: 44%; height: 44%; background: var(--tone-color); box-shadow: 0 0 8px 2px color-mix(in srgb, var(--tone-color) 55%, transparent); }
    .prop-icon .glass-glaze { inset: 4px; border-radius: 9px; }
    .prop-icon svg { width: 15px; height: 15px; position: relative; z-index: 2; color: var(--text-strong); }
    .prop-text { flex: 1; min-width: 0; }
    .prop-title { display: block; font: 600 13.5px/1.3 var(--font-core); color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .prop-sub { display: block; font: 400 12px/1.3 var(--font-core); color: var(--text-muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

    /* Semana — header + fila "todo el día" fuera del scroll; cuerpo por
       horas con su propio scroll. Mismo template de columnas (56px+7x1fr)
       en los tres bloques para que las columnas coincidan entre sí. */
    .week-grid-wrap { overflow-x: auto; }
    .week-grid-inner { min-width: 760px; }
    .week-header-row, .week-allday-row, .week-body-grid { display: grid; grid-template-columns: 56px repeat(7, 1fr); }
    .week-day-head { text-align: center; padding: 6px 4px 10px; }
    .week-day-head .wd-label { display: block; font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-faint); }
    .week-day-head .wd-num { display: inline-flex; align-items: center; justify-content: center; margin-top: 6px; width: 26px; height: 26px; font: 600 13px/1 var(--font-core); color: var(--text-strong); }
    .week-day-head .wd-today-chip { margin: 6px auto 0; }
    /* .hero-back-btn (PAGE_HERO_CSS) nunca se había usado sobre un
       <button> real, solo sobre el <a> de "volver" — nunca necesitó
       resetear el borde nativo de formulario porque un <a> no lo tiene.
       Aplicado tal cual a estos dos <button> de navegar semana, ese borde
       se colaba (feedback de Emilio, 2026-09-22: "estos no son como el
       del header del daily hub" — comparado en vivo contra el real, la
       única diferencia real era este borde). */
    .week-nav-btn { appearance: none; border: none; }
    /* Feedback al navegar con el teclado (←/→/↓): mismo hundido de :active
       y mismo neón azul de :hover, aplicados un instante desde JS. */
    .week-nav-btn.is-pressed { box-shadow: var(--nu-inset-1); transform: scale(.96); }
    .week-nav-btn.is-pressed .neon-under { background: var(--neon-blue); box-shadow: 0 0 10px 2px var(--neon-blue-glow); opacity: .9; }
    
  /* Selector de calendarios visibles (core/calendar/visibility.ts). Vive en
     <body> y no dentro del panel de agenda, que se sustituye al repintar. */
  /* Overlay flotante (no empuja el contenido) con la misma superficie de
     cristal plano que .glass-modal-box (modal de "Añadir evento"). */
  .cal-picker { position: fixed; z-index: 70; width: 300px; max-height: 60vh; overflow-x: hidden; overflow-y: auto; padding: 18px; border-radius: var(--r-3xl); background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); display: flex; flex-direction: column; gap: 2px; }
  .cal-picker[hidden] { display: none; }
  .cal-picker > * { flex-shrink: 0; }
  .cal-picker-title { font: var(--fw-semibold) 15px/1.3 var(--font-core); color: var(--text-strong); margin: 0 0 8px; }
  .cal-picker-account { font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-faint); margin: 12px 0 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cal-picker-row { display: flex; align-items: center; gap: 6px; padding: 2px 2px 2px 4px; border-radius: 8px; min-width: 0; }
  .cal-picker-label { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; padding: 4px 0; cursor: pointer; }
  .cal-bg-toggle { appearance: none; flex: 0 0 auto; width: 26px; height: 26px; display: inline-grid; place-items: center; border: none; border-radius: 6px; background: transparent; color: var(--text-faint); cursor: pointer; opacity: .55; transition: opacity var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
  .cal-picker-row:hover .cal-bg-toggle { opacity: 1; }
  .cal-bg-toggle[aria-pressed="true"] { opacity: 1; color: var(--neon-blue); background: color-mix(in srgb, var(--neon-blue) 16%, transparent); }
  .cal-bg-toggle:disabled { opacity: .2; cursor: default; }
  .cal-picker-row:hover { background: var(--glass-border); }
  .cal-picker-label span { font: 500 13px/1.3 var(--font-core); color: var(--text-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  /* Checkbox con el color del calendario en Google (--cal-color). */
  .cal-check { appearance: none; -webkit-appearance: none; flex: 0 0 auto; width: 16px; height: 16px; margin: 0; border-radius: 4px; border: 1.5px solid color-mix(in srgb, var(--cal-color) 70%, #000); background: transparent; display: grid; place-content: center; cursor: pointer; transition: background var(--dur-fast) var(--ease-out); }
  .cal-check::after { content: ""; width: 4px; height: 8px; border: solid var(--cal-check-ink, #fff); border-width: 0 2px 2px 0; transform: translateY(-1px) rotate(45deg) scale(0); transition: transform var(--dur-fast) var(--ease-out); }
  .cal-check:checked { background: var(--cal-color); }
  .cal-check:checked::after { transform: translateY(-1px) rotate(45deg) scale(1); }
  .cal-check:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: 2px; }
  .cal-check:disabled { opacity: .5; }
  .cal-picker-msg { font: 500 12.5px/1.4 var(--font-core); color: var(--text-muted); margin: 0; }

    /* Panel de bloques de fondo (FOX-P145): misma superficie que .cal-picker. */
    .blocks-panel { width: 340px; gap: 10px; }
    .blocks-row { display: flex; align-items: center; gap: 10px; padding: 8px 6px; border-radius: 8px; cursor: pointer; }
    .blocks-row:hover { background: var(--glass-border); }
    .blocks-row-dot { flex: 0 0 10px; height: 10px; border-radius: 50%; background: var(--band-color); }
    .blocks-row-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
    .blocks-row-title { font: 500 13px/1.3 var(--font-core); color: var(--text-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .blocks-row-meta { font: 500 11px/1.2 var(--font-mono); color: var(--text-faint); }
    .blocks-days { display: flex; gap: 6px; }
    .blocks-day { appearance: none; border: 1px solid var(--line-hair); background: transparent; color: var(--text-muted); width: 30px; height: 30px; border-radius: 50%; font: 600 11px/1 var(--font-core); cursor: pointer; }
    .blocks-day[aria-pressed="true"] { background: var(--neon-blue); border-color: var(--neon-blue); color: #fff; }
    .blocks-colors { display: flex; gap: 8px; flex-wrap: wrap; }
    .blocks-color { appearance: none; width: 22px; height: 22px; border-radius: 50%; border: 2px solid transparent; background: var(--swatch); cursor: pointer; }
    .blocks-color[aria-checked="true"] { border-color: var(--text-strong); }
    .blocks-actions { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
    .blocks-actions .spacer { flex: 1; }
    .blocks-error { font: 500 12px/1.4 var(--font-core); color: var(--neon-red); margin: 0; }
    #weekBlocksBtn[aria-expanded="true"] .neon-under,
    #weekCalendarsBtn[aria-expanded="true"] .neon-under { background: var(--neon-blue); box-shadow: 0 0 10px 2px var(--neon-blue-glow); opacity: .9; }
    .week-nav-label { appearance: none; border: none; background: none; cursor: pointer; font: 500 12.5px var(--font-core); color: var(--text-muted); padding: 4px 6px; border-radius: 6px; transition: color var(--dur-base) var(--ease-out), text-shadow var(--dur-base) var(--ease-out); }
    .week-nav-label:hover, .week-nav-label.is-pressed { color: var(--neon-blue); text-shadow: 0 0 8px var(--neon-blue-glow); }
    .week-allday-row { min-height: 32px; align-items: center; border-top: 1px solid var(--line-hair); border-bottom: 1px solid var(--line-hair); margin-bottom: 4px; }
    .week-allday-gutter { font: 500 9px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-faint); padding-right: 8px; text-align: right; }
    .week-allday-cell { min-height: 32px; display: flex; align-items: center; gap: 4px; padding: 4px; border-left: 1px solid var(--line-hair); flex-wrap: wrap; }
    .week-allday-chip { display: inline-flex; align-items: center; gap: 4px; font: 600 10px/1 var(--font-core); padding: 3px 8px 3px 6px; border-radius: var(--r-pill); color: var(--signal-violet); background: color-mix(in srgb, var(--signal-violet) 12%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--signal-violet) 45%, transparent); white-space: nowrap; }
    .week-allday-chip svg { width: 10px; height: 10px; }
    .week-body { max-height: 456px; overflow-y: auto; border-radius: var(--r-xl); position: relative; }
    .week-hour-label { height: 48px; font: 500 10px/1 var(--font-mono); color: var(--text-faint); text-align: right; padding-right: 8px; transform: translateY(-6px); box-sizing: border-box; }
    .week-day-track { position: relative; height: 768px; border-left: 1px solid var(--line-hair); background-image: linear-gradient(to bottom, var(--line-soft) 1px, transparent 1px); background-size: 100% 48px; }
    .week-event { position: absolute; left: 4px; right: 4px; border-radius: var(--r-lg); padding: 4px 7px; overflow: hidden; background: color-mix(in srgb, var(--tone-color) 15%, var(--surface-card)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone-color) 45%, transparent); }
    .week-event-title { display: block; font: 500 11px/1.25 var(--font-core); color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .week-event-loc { display: flex; align-items: center; gap: 3px; margin-top: 2px; font: 400 9.5px/1.2 var(--font-mono); color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .week-event-loc svg { width: 9px; height: 9px; flex: 0 0 auto; }
    .week-now-line { position: absolute; left: 0; right: 0; height: 2px; background: var(--signal-cyan); box-shadow: 0 0 6px 1px var(--neon-cyan-glow); z-index: 2; }
    .week-now-line::before { content: ''; position: absolute; left: -3px; top: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--signal-cyan); box-shadow: 0 0 6px 1px var(--neon-cyan-glow); }
    @media (max-width: 640px) { .week-body { max-height: 380px; } }
    .week-day-track { cursor: pointer; user-select: none; }
    .week-select-overlay { position: absolute; left: 4px; right: 4px; background: color-mix(in srgb, var(--signal-violet) 22%, transparent); box-shadow: inset 0 0 0 1px var(--signal-violet); border-radius: var(--r-lg); pointer-events: none; z-index: 3; }
    .week-event { cursor: pointer; }
    /* Evento marcado como hecho en Fox (FOX-P146): check visible al pasar
       el ratón (fijo si está hecho); hecho = tachado y atenuado. */
    .week-event-check { position: absolute; top: 3px; right: 3px; z-index: 1; width: 16px; height: 16px; padding: 0; display: grid; place-items: center; border-radius: 50%; border: 1.5px solid color-mix(in srgb, var(--tone-color) 70%, var(--text-muted)); background: var(--surface-card); color: transparent; cursor: pointer; opacity: 0; transition: opacity var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out); }
    .week-event:hover .week-event-check, .week-event-check:focus-visible { opacity: 1; }
    .week-event-check:hover { color: var(--text-muted); }
    .week-event.is-done .week-event-check { opacity: 1; background: var(--neon-green); border-color: var(--neon-green); color: #fff; }
    .week-event:has(.week-event-check) .week-event-title { padding-right: 16px; }
    .week-event.is-done { opacity: .55; }
    .week-event.is-done .week-event-title { text-decoration: line-through; }
    /* Bloques de fondo (FOX-P145): relleno suave sin borde, por debajo de
       los eventos (z-index 0 vs 1) y del indicador de "ahora" (2). */
    .week-bg-band { position: absolute; left: 0; right: 0; z-index: 0; background: color-mix(in srgb, var(--band-color) 16%, transparent); border-radius: 4px; padding: 3px 6px; overflow: hidden; }
    /* Solo el título es clicable (abre la edición); el resto de la franja
       deja pasar el ratón para que arrastrar sobre ella siga creando eventos. */
    .week-bg-band { pointer-events: none; }
    .week-bg-band-title { pointer-events: auto; cursor: pointer; display: inline-block; max-width: 100%; vertical-align: top; padding: 1px 4px; margin: -1px -4px; border-radius: 3px; font: 600 9.5px/1.2 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: color-mix(in srgb, var(--band-color) 55%, var(--text-muted)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
    /* Franjas de calendarios de Google en modo fondo: se editan en Google, no aquí. */
    .week-bg-band-title.is-static { pointer-events: none; }
    .week-bg-band-title:hover, .week-bg-band-title:focus-visible { background: color-mix(in srgb, var(--band-color) 30%, transparent); color: var(--text-strong); outline: none; }
    .blocks-day-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-radius: 10px; background: color-mix(in srgb, var(--band-color) 12%, transparent); }
    .blocks-day-box .glass-field-key { margin-bottom: 2px; }
    .blocks-day-actions { display: flex; flex-wrap: wrap; gap: 6px; }
    .blocks-section-title { font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); margin: 6px 0 0; }
    .week-day-track > .week-event { z-index: 1; }
    /* Color real del calendario de Google: más relleno y borde oscurecido
       que los tonos del sistema, porque Google admite colores muy pálidos. */
    .week-event.has-cal-color, .week-allday-chip.has-cal-color { background: color-mix(in srgb, var(--tone-color) 30%, var(--surface-card)); box-shadow: inset 0 0 0 1px color-mix(in srgb, color-mix(in srgb, var(--tone-color) 70%, #000) 45%, transparent); }
    .week-allday-chip.has-cal-color { color: var(--text-body); }

    /* Modales "Añadir evento"/detalle — glass, no neumórfico (feedback de
       Emilio, 2026-09-22: "el neumorfismo y las modales se llevan regular
       [...] las modales sean con el fondo de vidrio y elementos menos
       neumórficos dentro", con una referencia editorial de labels mono
       pequeños + hairlines finos en vez de pozos tallados). Reusa
       --glass-fill/--backdrop-blur literal (mismo recipe que .page-hero,
       PAGE_HERO_CSS) — no un vocabulario de vidrio nuevo. Alcance
       confirmado con Emilio: solo modales por ahora, .field-carved se
       queda intacto en el resto de la app (Documentos, Finanzas, Detalle
       de Tarea) hasta que se decida extenderlo. */
    
  .glass-modal { position: fixed; inset: 0; margin: auto; border: none; padding: 0; background: transparent; max-width: 420px; width: calc(100% - 40px); border-radius: var(--r-3xl); box-shadow: none; }
  .glass-modal-wide { max-width: 680px; }
  .glass-modal::backdrop { background: var(--surface-overlay); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); }
  .glass-modal-box { position: relative; background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); border: 1px solid var(--glass-border); border-radius: var(--r-3xl); padding: 26px 24px; box-shadow: var(--elev-card); }
  .glass-modal-box h3 { font: var(--fw-semibold) 17px/1.3 var(--font-core); color: var(--text-strong); margin: 0 0 20px; }
  .glass-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }

    .glass-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; margin-bottom: 22px; }
    .glass-detail-grid .glass-field-stack { margin-bottom: 0; }
    @media (max-width: 640px) { .glass-detail-grid { grid-template-columns: 1fr; gap: 16px; } }
    .glass-field-stack { display: flex; flex-direction: column; gap: 16px; margin-bottom: 22px; }
    .glass-field-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
    .glass-field { display: flex; flex-direction: column; gap: 6px; padding-bottom: 10px; border-bottom: 1px solid var(--line-hair); }
    .glass-field-key { font: var(--fw-medium) 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); }
    .glass-field-value { display: block; width: 100%; background: none; border: none; outline: none; padding: 0; font: 400 14px var(--font-core); color: var(--text-body); }
    .glass-field-value::placeholder { color: var(--text-faint); }
    /* Fila de entrada en lenguaje natural (FOX-P130, feedback de Emilio con
       Notion Calendar de referencia: "simplemente describir lo que quiero
       [...] y con eso se crea") — plana, no un pozo tallado (nada de
       --nu-inset-* aquí: es justo el vocabulario neumórfico que Emilio
       pidió sacar de las modales en esta misma ronda). Distinguida de los
       glass-field de debajo solo con un tinte y un borde sutil sobre el
       propio cristal, no con relieve. */
    /* Modal Evento/Tarea (FOX-P147): el conmutador cambia qué campos se ven. */
    .inbox-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
    .inbox-modal-head h3 { margin: 0; }
    #inboxEvModeToggle button { gap: 6px; }
    #inboxEventModal[data-mode="task"] .inbox-ev-only, #inboxEventModal:not([data-mode="task"]) .inbox-task-only { display: none; }
    .inbox-task-hint { margin: 0; font-size: 0.78rem; color: var(--text-muted); }
    .glass-nl-row { display: flex; align-items: center; gap: 8px; margin-bottom: 20px; padding: 10px 14px; border-radius: var(--r-2xl); background: var(--glass-fill); border: 1px solid var(--glass-border); }
    .glass-nl-row .glass-field-value { font-size: 13px; }
    .glass-nl-row .btn { flex: 0 0 auto; padding: 6px 10px; }
    .inbox-ev-conflict { margin: -6px 0 16px; padding: 10px 12px; border-radius: var(--r-lg); background: color-mix(in srgb, var(--neon-red) 10%, transparent); color: var(--text-body); font: 400 12.5px/1.5 var(--font-core); }
    .inbox-ev-conflict p { margin: 0 0 8px; }
    /* Modal de detalle ampliada (FOX-P130, feedback de Emilio: "me
       gustaría ver ahí la información disponible [...] y si tiene un link
       de meet, poder clicar e ir directamente"). */
    .inbox-ev-link { color: var(--signal-cyan-dim); text-decoration: none; }
    .inbox-ev-link:hover { text-decoration: underline; }
    /* max-height+scroll propio — antes una descripción larga estiraba
       toda la modal sin límite (motivo real del feedback de las dos
       columnas: con la columna ya acotada a la mitad del ancho, sin esto
       seguiría desbordando en vertical). */
    .inbox-ev-desc { display: block; white-space: pre-wrap; font-size: 13px; line-height: 1.5; max-height: 220px; overflow-y: auto; }
    .inbox-ev-attachments, .inbox-ev-attendees { display: flex; flex-direction: column; gap: 6px; max-height: 160px; overflow-y: auto; }
    .inbox-ev-attachments a { display: flex; align-items: center; gap: 6px; color: var(--signal-cyan-dim); text-decoration: none; font-size: 13px; }
    .inbox-ev-attachments a:hover { text-decoration: underline; }
    .inbox-ev-attachments img { width: 14px; height: 14px; flex: 0 0 auto; }
    .inbox-ev-attendees div { font-size: 13px; color: var(--text-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .inbox-ev-attendees div.organizer { color: var(--text-strong); font-weight: var(--fw-semibold); }
    textarea.glass-field-value { resize: none; font-family: var(--font-core); }
    /* Añadir Meet al crear + Aceptar/Quizás/No en invitaciones recibidas
       (FOX-P130, feedback de Emilio: "¿cómo hago para crear un evento
       desde ahí e incluirle un meet? [...] también me hace falta las
       opciones de aceptar/no/quizás"). */
    .inbox-ev-meet-toggle { flex-direction: row; align-items: center; gap: 8px; cursor: pointer; }
    .inbox-ev-meet-toggle input { width: 16px; height: 16px; accent-color: var(--signal-cyan); }
    .inbox-ev-meet-toggle span { font: 400 13px/1.4 var(--font-core); color: var(--text-body); }
    .inbox-ev-rsvp-row { flex-direction: row; align-items: center; justify-content: space-between; margin-bottom: 4px; }
    .inbox-ev-rsvp-buttons { display: flex; gap: 6px; }

    /* ── Knowledge — rediseño Carve & Glow (FOX-P130+), migración a
       producción de un mock artifact validado por Emilio (2026-09-20).
       Composer fusionado (Añadir URL + Nueva nota, detecta solo cuál es —
       sin selector manual) + .seg-carved de 3 pestañas, cada una su propio
       .panel o .tbl real, en vez de 7 renderHubCard() sueltas en
       .hub-grid. "Preguntar al grafo" y "Cola de procesamiento" se
       quitaron del todo (pedido explícito: redundante con el chat /
       "con el composer ya cumplimos la función"). ── */
    .kn-composer { margin-top: 16px; position: relative; display: flex; align-items: center; gap: 10px; padding: 8px 10px 8px 12px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); flex-wrap: wrap; row-gap: 6px; transition: box-shadow var(--dur-base) var(--ease-out); }
    .kn-composer:focus-within { box-shadow: var(--nu-inset-2); }
    .kn-composer-icon { width: 26px; height: 26px; border-radius: var(--r-lg); position: relative; flex: 0 0 auto; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); transition: box-shadow var(--dur-base) var(--ease-out); }
    .kn-composer:focus-within .kn-composer-icon { box-shadow: 4px 4px 9px var(--nu-dark), -4px -4px 9px var(--nu-light); }
    .kn-composer-icon .neon-under { width: 70%; height: 70%; opacity: 0; transition: opacity var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out); }
    .kn-composer-icon .glass-glaze { inset: 2px; border-radius: 7px; opacity: 0; transition: opacity var(--dur-base) var(--ease-out); }
    .kn-composer.has-content .kn-composer-icon .neon-under, .kn-composer.has-content .kn-composer-icon .glass-glaze { opacity: 1; }
    .kn-composer.type-url .kn-composer-icon .neon-under { background: var(--neon-blue); box-shadow: 0 0 8px 2px var(--neon-blue-glow); }
    .kn-composer.type-note .kn-composer-icon .neon-under { background: var(--neon-teal); box-shadow: 0 0 8px 2px var(--neon-teal-glow); }
    .kn-composer-icon svg, .kn-composer-icon i { width: 12px; height: 12px; color: var(--text-faint); position: relative; z-index: 2; transition: color var(--dur-base) var(--ease-out); }
    .kn-composer.has-content .kn-composer-icon svg, .kn-composer.has-content .kn-composer-icon i { color: var(--text-strong); }
    .kn-composer input { flex: 1 1 160px; min-width: 0; border: none; background: none; outline: none; font: 400 14px/1.4 var(--font-core); color: var(--text-body); padding: 0; }
    .kn-composer input::placeholder { color: var(--text-faint); }
    .kn-type-chip { display: none; align-items: center; gap: 6px; font: 500 11.5px/1 var(--font-core); color: var(--text-faint); flex: 0 0 auto; white-space: nowrap; }
    .kn-composer.has-content .kn-type-chip { display: inline-flex; }
    .kn-type-chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-faint); flex: 0 0 auto; }
    .kn-composer.type-url .kn-type-chip { color: var(--neon-blue); }
    .kn-composer.type-url .kn-type-chip .dot { background: var(--neon-blue); box-shadow: 0 0 5px 1px var(--neon-blue-glow); }
    .kn-composer.type-note .kn-type-chip { color: var(--signal-cyan-dim); }
    .kn-composer.type-note .kn-type-chip .dot { background: var(--neon-teal); box-shadow: 0 0 5px 1px var(--neon-teal-glow); }
    .kn-composer-status { font: 400 12px/1 var(--font-core); color: var(--text-faint); flex: 0 0 auto; }
    .kn-composer-status.ok { color: var(--signal-mint); }

    /* Más aire entre el composer y las pestañas (feedback: "hay que
       dejar más espacio entre la barra de pegar nota/URL y el selector
       de tabs") — #knw-tabs es el único .seg-carved de la app que
       necesita este margen, de ahí el id en vez de tocar SEG_CARVED_CSS
       global. Igual con el hueco entre el header y el contenido de abajo
       (feedback: "más espacio entre la tabla que hagas y el header") —
       .page-hero no lleva margin-bottom propio (cada consumidor pone el
       suyo, ver .iniciativas-hero), así que sin este modificador el
       hero y el primer panel quedaban pegados. */
    #knw-tabs { margin-top: 22px; }
    .kn-tabs-select { display: none; margin-top: 22px; }
    .page-hero.knowledge-hero { margin-bottom: 36px; }

    /* Contador — solo en la pestaña de Reconciliación pendiente (pedido
       explícito), azul oscuro cuando la pestaña está activa (feedback:
       "ponle un fondo azul oscuro al badge, que quedará mejor" — el negro
       plano de --surface-sunken quedaba mal sobre el thumb de neón). */
    .kn-tab-count { font: 600 10.5px/1 var(--font-mono); color: var(--text-faint); background: var(--surface-sunken); border-radius: var(--r-pill); padding: 2px 7px; }
    .kn-tab-count[hidden] { display: none; }
    .seg-carved button.active .kn-tab-count { color: #fff; background: color-mix(in srgb, var(--neon-blue) 35%, #051226); }

    /* Pendientes de decisión — .tbl real (exhibit 16, ya en Iniciativas/
       Biblioteca) en vez de .rich-row (feedback: "haz la lista... como
       esta tabla, con las opciones de guardar-descartar a la derecha en
       cada línea y los efectos de hover"). 3 columnas (Nombre/Ámbito/
       Acciones) — modificador propio sobre grid-template-columns, no
       coincide con las 3 de Iniciativas/Biblioteca (esas son Nombre/
       Categoría/Fecha). La fila entera NO es <a> (a diferencia de
       Iniciativas): el nombre navega vía un <a> interno, los botones de
       acción viven fuera de ese enlace como hermanos, para no anidar
       controles interactivos uno dentro de otro. El hover del icono
       reutiliza --icon-glow tal cual (misma receta que .tbl-row de
       Biblioteca). Nombre/Ámbito ordenables — mismo .tbl-sort real que
       Iniciativas (initIniciativasJs); Acciones no lleva sort. */
    .tbl.kn-pending-tbl .tbl-head, .tbl.kn-pending-tbl .tbl-row { grid-template-columns: 1fr 110px 230px; }
    .kn-pending-tbl-row { cursor: default; }
    .kn-pending-tbl-row .tbl-name { text-decoration: none; color: inherit; cursor: pointer; }
    .kn-pending-tbl-row .tbl-desc { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    .kn-pending-tbl-actions { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
    .kn-row-exit { animation: kn-fade-out 260ms var(--ease-press) forwards; }
    @keyframes kn-fade-out { to { opacity: 0; transform: translateX(6px); height: 0; margin: 0; padding: 0; overflow: hidden; } }

    /* Reconciliación pendiente — comparación lado a lado (Existente/Nuevo),
       4 acciones (mantener/actualizar/guardar ambos/fusionar). */
    .kn-reconcile-item { padding: 18px 0; border-top: 1px solid var(--line-hair); display: flex; flex-direction: column; gap: 12px; }
    .kn-reconcile-item:first-child { border-top: none; padding-top: 0; }
    .kn-reconcile-item:last-child { padding-bottom: 0; }
    .kn-reconcile-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
    .kn-reconcile-explain { font: 400 12.5px/1.55 var(--font-core); color: var(--text-muted); margin: 0; white-space: pre-wrap; }
    .kn-split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
    @media (max-width: 640px) { .kn-split { grid-template-columns: 1fr; } }
    .kn-split-side { border-radius: var(--r-xl); background: var(--surface-sunken); box-shadow: var(--nu-inset-1); padding: 12px 14px; }
    .kn-split-label { display: block; font: var(--fw-medium) 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); margin-bottom: 8px; }
    .kn-split-side p { margin: 0; font: 400 12.5px/1.6 var(--font-core); color: var(--text-body); white-space: pre-wrap; }
    .kn-reconcile-actions { display: flex; gap: 8px; flex-wrap: wrap; }
    .kn-themes { margin-bottom: 16px; }
    .kn-theme-items { margin: 0; padding-left: 18px; font: 400 13px/1.6 var(--font-core); color: var(--text-muted); }
    .kn-theme-items a { color: var(--text); }
    .knw-ins { margin-bottom: 40px; }
    .knw-ins[hidden] { display: none; }
    .knw-ins-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 28px; padding-bottom: 18px; border-bottom: 1px solid var(--line-hair); }
    .knw-ins-eyebrow { display: block; margin-bottom: 10px; font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--neon-pink); }
    .knw-ins-head h2 { margin: 0; font: 400 24px/1.2 var(--font-core); letter-spacing: -0.02em; color: var(--text-strong); }
    .knw-ins-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
    .knw-ins-meta { font: 500 11px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); margin-right: 4px; }
    .knw-topic { margin-bottom: 20px; padding: 26px 28px 22px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--elev-card); }
    .knw-topic-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
    .knw-topic-title h3 { margin: 0 0 8px; font: var(--fw-semibold) 19px/1.25 var(--font-core); color: var(--text-strong); text-transform: capitalize; }
    .knw-topic-counts { font: 500 10.5px/1.4 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); }
    .knw-topic-synth { max-width: 78ch; margin: 0 0 26px; font: 400 15px/1.75 var(--font-core); color: var(--text-body); }
    .knw-topic-wait { margin: 0 0 24px; font-size: 13px; color: var(--text-muted); }
    .knw-md > :first-child { margin-top: 0; }
    .knw-md > :last-child { margin-bottom: 0; }
    .knw-md h3 { margin: 26px 0 12px; font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); }
    .knw-md p { margin: 0 0 14px; }
    .knw-md ul, .knw-md ol { margin: 0 0 14px; padding-left: 22px; }
    .knw-md li { margin: 0 0 8px; padding-left: 4px; }
    .knw-md li::marker { color: var(--text-faint); }
    .knw-md strong { font-weight: var(--fw-semibold); color: var(--text-strong); }
    .knw-rows { list-style: none; margin: 0; padding: 0; }
    .knw-row { position: relative; display: flex; gap: 16px; align-items: flex-start; padding: 22px 12px; margin: 0 -12px; border-radius: var(--r-xl); transition: background var(--dur-base) var(--ease-out); }
    .knw-row::before { content: ''; position: absolute; top: 0; left: 12px; right: 12px; height: 1px; background: var(--line-hair); }
    .knw-row:first-child::before { display: none; }
    .knw-row:hover { background: var(--surface-sunken); }
    .knw-row-icon { width: 32px; height: 32px; margin-top: 2px; border-radius: 11px; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); }
    .knw-row-icon .neon-under { width: 50%; height: 50%; }
    .knw-row-icon .glass-glaze { inset: 3px; border-radius: 8px; }
    .knw-row-icon svg, .knw-row-icon i { width: 14px; height: 14px; }
    .knw-row-body { flex: 1; min-width: 0; }
    .knw-row-kind { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; }
    .knw-also { font-weight: 500; color: var(--text-faint); }
    .knw-row-pair { font: 500 15px/1.45 var(--font-core); color: var(--text-strong); }
    .knw-row-pair a { color: inherit; text-decoration: none; }
    .knw-row-pair a:hover { color: var(--signal-cyan-dim); }
    .knw-vs { margin: 0 10px; color: var(--text-faint); }
    .knw-row-text { margin: 8px 0 0; max-width: 78ch; font: 400 14px/1.65 var(--font-core); color: var(--text-muted); }
    .knw-row-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
    .knw-more, .knw-more-topics { margin-top: 14px; }
    .knw-saved { margin-bottom: 20px; padding: 24px 28px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); }
    .knw-saved h3 { margin: 0 0 12px; font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); }
    .knw-saved[hidden] { display: none; }
    #knw-insights .kn-reconcile-head > span:last-child { min-width: 0; overflow-wrap: anywhere; }

    /* Podcasts (FOX-P148) — suscripciones dentro de Knowledge. Buscador
       tallado real (SEARCH_CARVED_CSS) + tablas .tbl como Biblioteca. El
       podcast lleva --neon-pink como color de identidad (los vídeos y
       documentos usan naranja): mismo acento en la lista de pendientes. */
    .pod-subscribe { display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: var(--r-2xl); flex-wrap: wrap; }
    .pod-subscribe .search-carved { min-width: 220px; }
    .pod-import { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
    .pod-status { min-height: 18px; margin: 10px 4px; font: 400 12.5px/1.5 var(--font-core); color: var(--text-muted); }
    .pod-status.error { color: var(--neon-red); }
    .pod-results { margin-bottom: 18px; }
    .pod-results[hidden] { display: none; }
    .tbl.pod-tbl .tbl-head, .tbl.pod-tbl .tbl-row,
    .pod-results .tbl-row { grid-template-columns: 1fr 170px 150px; }
    .pod-tbl .tbl-row, .pod-results .tbl-row { cursor: default; }
    .pod-art { width: 34px; height: 34px; border-radius: 10px; object-fit: cover; flex: 0 0 auto; box-shadow: var(--nu-raise-1, 0 2px 6px rgba(0,0,0,.2)); }
    .pod-counts { font: 400 12px/1.4 var(--font-core); color: var(--text-muted); }
    .pod-counts .fail { color: var(--neon-red); }
    .pod-actions { display: flex; justify-content: flex-end; gap: 8px; }
    .kn-pod-show { color: var(--neon-pink); }
    .kn-pod-show.is-youtube { color: var(--neon-red); }
    .pod-episodes { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 2px; padding: 4px 0 10px 46px; }
    .pod-ep-row { display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center; font: 400 12.5px/1.4 var(--font-core); color: var(--text-body); padding: 4px 0; }
    .pod-ep-row a { color: inherit; }
    .pod-ep-status { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }
    .pod-ep-status.failed { color: var(--neon-red); }
    .pod-ep-status.pending { color: var(--signal-orange); }
    .pod-ep-status.library { color: var(--signal-mint); }
    .pod-tbl .tbl-desc a { color: var(--neon-pink); text-decoration: none; }
    .pod-link-btn { border: none; background: none; padding: 0; font: inherit; color: var(--text-muted); text-decoration: underline; cursor: pointer; }
    .pod-link-btn:hover { color: var(--text-strong); }
    .pod-channel-input { width: min(100%, 340px); border: none; outline: none; border-radius: var(--r-lg); padding: 5px 10px; background: var(--surface-sunken); box-shadow: var(--nu-inset-1); color: var(--text-body); font: 400 12px var(--font-core); }
    @media (max-width: 640px) {
      .tbl.pod-tbl .tbl-head, .tbl.pod-tbl .tbl-row, .pod-results .tbl-row { grid-template-columns: 1fr auto; }
      .pod-counts { display: none; }
      .tbl.pod-tbl .tbl-head span:nth-child(2) { display: none; }
    }

    /* Biblioteca guardada — .tbl (exhibit 16 real, ya en Iniciativas) en
       vez de la lista agrupada por pilar: la categoría vive como columna
       por fila, no como cabecera de grupo aparte. El toolbar (Personal/
       Últimos 3 días) vive dentro de .page-hero, justo debajo de las
       pestañas — no como bloque suelto antes de la tabla (feedback:
       "esto debe de ir junto a la barra de seleccionar tabs"); por eso
       lleva margin-top en vez de margin-bottom, y comparte el toggle
       [data-knw-panel] con los otros paneles de contenido aunque viva
       dentro del hero. */
    /* align-items: flex-start — sin esto (default "normal" = stretch en
       flex) al abrir un .select-box su hermano en el mismo toolbar se
       estira a la misma altura vía flexbox, así que el cerrado muestra
       una sombra vacía del mismo tamaño que el panel abierto — parece
       que "se abren los dos" (feedback de Emilio) cuando en realidad
       solo uno tiene la clase .open; el otro solo se ha estirado. */
    .kn-lib-toolbar { display: flex; align-items: flex-start; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
    .kn-lib-toolbar[hidden] { display: none; }
    /* Desplegables — .select-box/.fox-select-wrap REAL (SELECT_BOX_CSS,
       exhibit 08 de Carve & Glow, ya en producción vía initFoxSelectJs)
       en vez de un <select> nativo con chevron pegado (feedback: "los
       desplegables no son así, sino como los de aquí, bloque 8"). El
       :disabled no existía porque ningún consumidor anterior deshabilitaba
       el trigger — Biblioteca sí lo necesita (Últimos 3 días se apaga con
       un proyecto elegido), así que se añade aquí en vez de inventar una
       clase aparte. */
    .select-trigger:disabled { opacity: .45; pointer-events: none; }

    code { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-muted); }


    /* ── Documents — rediseño Carve & Glow (FOX-P130), artefacto de mock
       aprobado por Emilio 2026-09-18 (fase 1). Reutiliza PAGE_HERO_CSS,
       RICH_ROW_CSS (.rich-icon en .doc-card y en los folder-row), TAG_PILL_CSS
       (filtro de tags) y SEARCH_CARVED_CSS (buscador) — ya interpolados arriba
       en este mismo <style>. Los nombres de clase con lógica JS detrás
       (.doc-card/.doc-title/.doc-tags-row/.doc-tag-chip/.doc-tag-remove/
       .doc-add-tag-btn/.doc-add-tag-input/.doc-reingest-btn/.docs-folder-item/
       #docs-search/#docs-results/#docs-tag-chips/#docs-dropzone/
       #docs-upload-status) se MANTIENEN sin tocar
       (initDocumentsJs, más abajo, sigue enganchando por esos mismos
       selectores) — solo cambia su CSS. .doc-chip/.doc-chips (filtro de tags
       plano) desaparecen del todo, sustituidos por .tag-pill real dentro de
       #docs-tag-chips (mismo id, JS actualizado para ese único selector). ── */
    /* margin-bottom en .docs-hero (modificador local, no en PAGE_HERO_CSS
       compartido) — feedback de Emilio 2026-09-18: "un poco más de espacio
       entre header y la parte de búsqueda". Tocar el PAGE_HERO_CSS de
       theme.ts habría movido TODOS sus consumidores (Settings, Daily Hub,
       Nutrición-receta...), así que el espacio extra se añade solo aquí. */
    .page-hero.docs-hero { margin-bottom: 28px; }
    .docs-hero-meta { margin-top: 8px; font: 400 13.5px/1.4 var(--font-core); color: var(--text-muted); }
    .docs-hero-meta b { color: var(--text-body); font-weight: var(--fw-semibold); }
    .docs-toolbar { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
    .docs-toolbar-warn { flex: 1; min-width: 260px; display: flex; align-items: center; gap: 8px; color: var(--text-muted); font-size: 0.8rem; }
    /* Dropzone — carved con borde discontinuo (antes trama plana .bg-hatch),
       franja horizontal compacta en vez de la caja de 28px de alto fijo:
       misma función (clic o arrastre), menos peso visual junto al buscador.
       Icono y padding igualados a .search-icon-wrap (28px/8px, antes 34px/
       12px) — feedback de Emilio: "los elementos de búsqueda e inclusión de
       documentos deben ser menos altos" — la caja de arrastre quedaba más
       alta que la de búsqueda al llevar un icono y un padding mayores sin
       motivo real. */
    .docs-dropzone { flex: 1; min-width: 260px; display: flex; align-items: center; gap: 10px; padding: 8px 16px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); border: 1.5px dashed var(--line-hair); cursor: pointer; transition: box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); outline: none; }
    .docs-dropzone:hover, .docs-dropzone:focus-visible { border-color: var(--neon-teal); }
    .docs-dropzone.drag-active { border-color: var(--neon-teal); box-shadow: var(--nu-inset-2); }
    .docs-dropzone-icon { width: 28px; height: 28px; border-radius: 10px; position: relative; flex: 0 0 auto; background: var(--surface-base); box-shadow: var(--nu-raise-1); }
    .docs-dropzone-icon .neon-under { width: 55%; height: 55%; background: var(--neon-orange); box-shadow: 0 0 8px 2px var(--neon-orange-glow); }
    .docs-dropzone-icon .glass-glaze { inset: 3px; border-radius: 7px; }
    .docs-dropzone-icon svg, .docs-dropzone-icon i { width: 14px; height: 14px; position: relative; z-index: 2; color: var(--text-strong); }
    /* flex:1 en .docs-dropzone-copy (antes solo min-width:0, sin flex-grow
       propio) — feedback de Emilio 2026-09-19: "estos elementos siguen
       siendo muy altos, quizá por el texto... que se trunca". Causa real:
       .docs-upload-status (el tercer hijo del flex, casi siempre VACÍO)
       tenía flex-basis:100% para poder ocupar su propia línea completa
       cuando SÍ hay texto de subida en curso — pero un flex-basis grande
       cuenta en el reparto de espacio aunque el contenido esté vacío, así
       que aplastaba a .docs-dropzone-copy a un puñado de píxeles (~90px)
       incluso con el cajón de sobra ancho. Con .docs-upload-status:empty
       fuera del flujo (display:none) esto ya no puede pasar, y
       .docs-dropzone-copy con flex:1 explícito reclama el espacio real
       que le sobra en vez de encogerse al tamaño de su contenido. */
    .docs-dropzone-copy { flex: 1; min-width: 0; }
    .docs-dropzone-label { display: block; font: 500 13px/1.3 var(--font-core); color: var(--text-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .docs-dropzone-hint { display: block; font: 400 11.5px/1.3 var(--font-core); color: var(--text-faint); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .docs-upload-status { font-size: 0.8rem; color: var(--text-muted); min-height: 1.2em; margin-top: 4px; flex-basis: 100%; }
    .docs-upload-status:empty { display: none; }
    /* Filtro de tags — .tag-pill real (TAG_PILL_CSS), sustituye .doc-chip.
       .docs-tagnav pisa el margin-top:18px de .hub-nav (pensado para vivir
       justo debajo de .hero dentro de .page-hero) — aquí vive fuera, en el
       toolbar de la tab. flex-wrap (feedback de Emilio 2026-09-19: "mejor
       que sean en dos o más líneas, pero que no tengan ese scroll
       horizontal") — .hub-nav base no envuelve (pensado para 2-3 pills que
       sí caben en una fila, como Settings/Daily Hub), aquí con ~15 tags
       reales se salía de la fila. flex-shrink:0 + white-space:nowrap en el
       texto ("ponlos en una línea cada uno, que no tengan saltos") —sin
       esto, al no caber, el motor de flex encogía cada pill por debajo del
       ancho de su propio texto y "agencia-tributaria" partía en dos
       líneas DENTRO de la pill en vez de simplemente pasar a la fila
       siguiente. */
    .hub-nav.docs-tagnav { margin: 0 0 20px; flex-wrap: wrap; }
    .docs-tagnav .tag-pill { flex-shrink: 0; }
    .docs-tagnav .tag-pill .tag-text { white-space: nowrap; }
    /* Panel — superficie plana (surface-card + sombra suave), literal del
       mock aprobado: contenedor de la nav de carpetas y de la lista. */
    .docs-panel { position: relative; padding: 20px; border-radius: var(--r-3xl); background: var(--surface-card); box-shadow: var(--elev-card-soft); display: flex; flex-direction: column; }
    .docs-section-label { display: block; font: var(--fw-medium) 10.5px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-muted); margin-bottom: 12px; }
    .docs-shell { display: flex; gap: 20px; align-items: flex-start; }
    /* top:20px (antes) pegaba .docs-nav casi a ras del viewport, por DEBAJO
       de .page-hero.docs-hero (sticky, top:14px) — como ambos son sticky y
       .docs-nav no reserva hueco para el header, subía hasta esconderse
       DETRÁS de él (feedback de Emilio 2026-09-21: "sube hasta el límite
       superior del header... en realidad solo debería subir hasta la
       altura de la barra de búsqueda"). 198px = la posición real de
       .docs-toolbar (la barra de búsqueda) en reposo, verificado en vivo
       con getBoundingClientRect (14 top + ~150 alto de .docs-hero + 28
       margin-bottom + ~6 de ajuste) — .docs-nav ahora se detiene ahí en
       vez de seguir subiendo hasta pisar el header. Sigue siendo un valor
       fijo, no calculado (mismo patrón que .page-hero/.docs-hero de más
       arriba): si el alto de .docs-hero cambia algún día, hay que volver a
       medir. */
    .docs-nav { width: 220px; flex: 0 0 auto; position: sticky; top: 198px; align-self: flex-start; gap: 2px; }
    .docs-list { flex: 1; min-width: 0; }
    @media (max-width: 860px) { .docs-shell { flex-direction: column; } .docs-nav { width: 100%; position: static; } }
    /* Carpetas — cada una con un color de identidad FIJO y siempre
       encendido (mismo idioma que .rich-icon en los doc-card: el color no es
       un estado, es quién es la carpeta — documentCategoryColor() en
       server.ts, mismo patrón determinista que iniciativaColor() más abajo
       en este archivo). La carpeta activa se distingue por elevación
       (nu-raise-1), no por color, para no competir con el naranja que ya
       significa "tab activa" en el rail global. */
    
    .notas-toolbar-fmt { display: flex; gap: 2px; align-items: center; margin: 4px 0; flex-wrap: wrap; }
    .notas-fmt { border: none; background: none; cursor: pointer; min-width: 28px; height: 26px; border-radius: var(--r-sm); color: var(--text-muted); font: 600 13px var(--font-core); }
    .notas-fmt:hover:not(:disabled) { background: var(--surface-sunken); color: var(--text-strong); }
    .notas-fmt:disabled { opacity: 0.35; cursor: default; }
    .notas-fmt.sep { margin-left: auto; font-weight: 500; font-size: 12px; padding: 0 8px; }
    .notas-mode { margin-left: 6px; }
    .notas-preview { min-height: 46vh; padding: 8px 2px; font-size: 14px; line-height: 1.6; color: var(--text-body); }
    .notas-preview h1, .notas-preview h2, .notas-preview h3 { color: var(--text-strong); margin: 0.9em 0 0.3em; line-height: 1.25; }
    .notas-preview h1 { font-size: 21px; } .notas-preview h2 { font-size: 18px; } .notas-preview h3 { font-size: 16px; }
    .notas-preview p { margin: 0.4em 0; }
    .notas-preview ul, .notas-preview ol { margin: 0.4em 0; padding-left: 1.4em; }
    .notas-preview li.done { color: var(--text-muted); text-decoration: line-through; }
    .notas-preview code { font-family: var(--font-mono); font-size: 12.5px; background: var(--surface-sunken); border-radius: 4px; padding: 1px 5px; }
    .notas-preview blockquote { margin: 0.5em 0; padding-left: 10px; border-left: 3px solid var(--line-hair); color: var(--text-muted); }
    .notas-preview a { color: var(--accent, #ff7a1a); }
    .notas-preview .notas-tbl { overflow-x: auto; margin: 0.7em 0; }
    .notas-preview table { border-collapse: collapse; font-size: 13px; width: 100%; }
    .notas-preview th, .notas-preview td { text-align: left; padding: 6px 12px; border-bottom: 1px solid var(--line-hair); vertical-align: top; }
    .notas-preview th { font: 600 11px var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); }
    .notas-thread { margin-top: 12px; border-top: 1px solid var(--line-hair); padding-top: 10px; }
    .notas-thread-list { display: flex; flex-direction: column; gap: 8px; max-height: 38vh; overflow-y: auto; padding-right: 4px; }
    .notas-msg { max-width: 92%; padding: 7px 11px; border-radius: 12px; font-size: 13.5px; line-height: 1.5; white-space: pre-wrap; }
    .notas-msg.me { align-self: flex-end; background: rgba(255, 122, 26, 0.18); }
    .notas-msg.agent { align-self: flex-start; background: var(--surface-sunken); }
    .notas-msg.error { align-self: flex-start; background: rgba(217, 83, 79, 0.14); }
    .notas-msg-who { display: block; font: 600 11px var(--font-core); color: var(--text-muted); margin-bottom: 2px; }
    .notas-msg-sel { display: block; font-size: 11px; color: var(--text-faint); margin-bottom: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .notas-msg-actions { display: flex; gap: 10px; margin-top: 4px; }
    .notas-msg-actions button { border: none; background: none; padding: 0; cursor: pointer; font: 500 11.5px var(--font-core); color: var(--text-muted); }
    .notas-msg-actions button:hover { color: var(--text-strong); }
    .notas-thinking { font-size: 12px; color: var(--text-muted); }
    .notas-composer { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; padding-right: 64px; }
    .notas-composer textarea { flex: 1; min-width: 0; resize: none; border: none; outline: none; border-bottom: 1px solid var(--line-hair); background: none; color: var(--text-body); font: 400 13.5px var(--font-core); padding: 4px 0; }
    .notas-composer-sel { font-size: 11.5px; color: var(--text-muted); margin-top: 6px; display: flex; gap: 6px; align-items: center; }
    .notas-composer-sel button { border: none; background: none; cursor: pointer; color: var(--text-muted); }
    .notas-thread-error { font-size: 12px; color: #d9534f; margin-top: 4px; }

    .notas-meet { display: flex; flex-direction: column; gap: 28px; padding-bottom: 12px; }
    .notas-meet-head { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
    .notas-meet-head-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
    .notas-meet-readout { display: flex; align-items: center; gap: 12px; margin: 0; font: 500 11px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); }
    .notas-meet-readout i { width: 3px; height: 3px; border-radius: 50%; background: var(--text-faint); }
    .notas-meet-eyebrow { font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--neon-violet); }
    .notas-meet-title { flex: 1; min-width: 0; margin: 0; font: 400 26px/1.15 var(--font-core); letter-spacing: -0.02em; color: var(--text-strong); }
    .notas-meet-sub { font: 500 11.5px var(--font-mono); color: var(--text-faint); }
    .notas-meet-cols { display: flex; flex-direction: column; gap: 44px; }
    .notas-meet-side, .notas-meet-transcript { min-width: 0; }
    .notas-meet-h { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 16px; padding-bottom: 12px; border-bottom: 1px solid var(--line-hair); font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); }
    .notas-meet-h-n { font-weight: 500; color: var(--text-faint); }
    .notas-meet-item-n { flex-shrink: 0; font: 600 10.5px/1 var(--font-mono); color: var(--text-faint); letter-spacing: .06em; }
    .notas-meet-summary { margin: 0 0 32px; max-width: 78ch; font-size: 15px; line-height: 1.75; color: var(--text-body); }
    .notas-meet-empty { margin: 0 0 24px; font-size: 13px; color: var(--text-muted); }
    .notas-meet-items { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; }
    .notas-meet-item { display: flex; gap: 14px; align-items: center; position: relative; padding: 16px; border-radius: var(--r-xl); font-size: 14px; line-height: 1.55; color: var(--text-body); transition: background var(--dur-base) var(--ease-out); }
    .notas-meet-item::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: var(--line-hair); }
    .notas-meet-item:first-child::before, .notas-meet-item.approved::before, .notas-meet-item.approved + .notas-meet-item::before { display: none; }
    .notas-meet-item.discarded { opacity: .55; text-decoration: line-through; }
    .notas-meet-item.approved { margin: 6px 0; background: var(--surface-card); box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); }
    .notas-meet-item:hover { background: var(--surface-sunken); }
    .notas-meet-item.approved:hover { background: var(--surface-card); box-shadow: var(--nu-raise-1); }
    .notas-meet-item-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
    .notas-meet-item-state { flex-shrink: 0; font: 500 11px var(--font-core); color: var(--text-muted); }
    .notas-meet-add { display: flex; gap: 10px; align-items: center; margin-top: 4px; }
    .notas-meet-add input { flex: 1; min-width: 0; height: 42px; box-sizing: border-box; padding: 0 16px; border: none; border-radius: var(--r-2xl); box-shadow: var(--nu-inset-1); background: var(--surface-sunken); color: var(--text-body); font: 400 13.5px var(--font-core); outline: none; }
    .notas-meet-hint { margin: 12px 0 0; font-size: 11.5px; color: var(--text-faint); }
    .notas-meet-search { width: 100%; margin-bottom: 16px; height: 42px; box-sizing: border-box; padding: 0 16px; border: none; border-radius: var(--r-2xl); box-shadow: var(--nu-inset-1); background: var(--surface-sunken); color: var(--text-body); font: 400 13.5px var(--font-core); outline: none; }
    .notas-meet-lines { display: flex; flex-direction: column; gap: 4px; max-height: 64vh; overflow-y: auto; }
    .notas-meet-line { display: flex; gap: 14px; align-items: baseline; width: 100%; text-align: left; border: none; cursor: pointer; padding: 9px 12px; border-radius: var(--r-sm); background: none; font: inherit; font-size: 14px; line-height: 1.6; color: var(--text-body); }
    .notas-meet-line:hover { background: var(--surface-sunken); }
    .notas-meet-line.selected { background: var(--surface-sunken); box-shadow: var(--nu-raise-1); }
    .notas-meet-line.focus { outline: 2px solid var(--accent, #ff7a1a); outline-offset: -2px; }
    .notas-meet-line-time { flex-shrink: 0; font: 500 10.5px var(--font-mono); color: var(--text-faint); }
    .notas-meet-line-who { flex-shrink: 0; font: 600 11px var(--font-core); color: var(--text-muted); min-width: 36px; }
    .notas-meet-line.me .notas-meet-line-who { color: var(--accent, #ff7a1a); }
    .notas-meet-line-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
    .notas-meet-md > :first-child { margin-top: 0; }
    .notas-meet-md > :last-child { margin-bottom: 0; }
    .notas-meet-md p { margin: 0 0 14px; }
    .notas-meet-md ul, .notas-meet-md ol { margin: 0 0 14px; padding-left: 22px; }
    .notas-meet-md li { margin: 0 0 6px; padding-left: 4px; }
    .notas-meet-md li::marker { color: var(--text-faint); }
    .notas-meet-md strong { font-weight: var(--fw-semibold); color: var(--text-strong); }
    .notas-meet-md code { font-size: .88em; background: var(--surface-sunken); padding: 1px 6px; border-radius: var(--r-xs); }
    .notas-meet-h-gap { margin-top: 48px; }
    .notas-meet-secs { display: flex; flex-direction: column; }
    .notas-meet-sec { display: grid; grid-template-columns: 44px minmax(0, 1fr); column-gap: 8px; padding: 28px 0; border-top: 1px solid var(--line-hair); }
    .notas-meet-sec:first-child { border-top-color: transparent; padding-top: 4px; }
    .notas-meet-sec-n { padding-top: 5px; font: 600 10.5px/1 var(--font-mono); letter-spacing: .06em; color: var(--text-faint); }
    .notas-meet-sec-body { min-width: 0; max-width: 78ch; }
    .notas-meet-sec h4 { margin: 0 0 12px; font: var(--fw-semibold) 17px/1.4 var(--font-core); color: var(--text-strong); }
    .notas-meet-sec .notas-meet-md { font-size: 15px; line-height: 1.75; color: var(--text-body); }
    .notas-meet-sec-links { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 16px; }
    .notas-meet-sec-links-k { font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-faint); margin-right: 4px; }
    .notas-meet-link { border: none; cursor: pointer; display: inline-flex; gap: 8px; align-items: baseline; max-width: 100%; padding: 7px 12px; border-radius: var(--r-lg); background: var(--surface-sunken); color: var(--text-body); font: 400 12.5px/1.3 var(--font-core); text-align: left; }
    .notas-meet-link b { flex-shrink: 0; font: 600 10.5px/1 var(--font-mono); color: var(--signal-cyan-dim); }
    .notas-meet-link span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .notas-meet-link:hover { color: var(--text-strong); box-shadow: var(--nu-raise-1); }
    .notas-meet-regen { margin-top: 8px; padding-top: 24px; border-top: 1px solid var(--line-hair); }
    .notas-meet-item.flash { background: color-mix(in srgb, var(--signal-cyan) 14%, transparent); }
    .notas-meet-wait { margin: 40px 0 0; font-size: 13px; color: var(--text-muted); }
    .notas-meet-count { margin: 14px 0 10px; font-size: 11.5px; color: var(--text-faint); }
    .notas-meet-error { margin: 0; padding: 12px 16px; border-radius: var(--r-lg); font-size: 13.5px; line-height: 1.5; color: var(--neon-red); background: color-mix(in srgb, var(--neon-red) 10%, transparent); }
    .notas-meet-error[hidden] { display: none; }

    .notas-tab .page-hero { margin-bottom: 32px; }
    .notas-layout { display: grid; grid-template-columns: minmax(260px, 340px) minmax(0, 1fr); gap: 48px; align-items: start; }
    @media (max-width: 860px) { .notas-layout { grid-template-columns: 1fr; } }
    .notas-list-pane, .notas-detail { min-width: 0; }
    .notas-list-pane { position: sticky; top: var(--notas-sticky-top, 16px); max-height: calc(100vh - var(--notas-sticky-top, 16px) - 16px); display: flex; flex-direction: column; }
    .notas-toolbar { display: flex; gap: 10px; align-items: center; margin-top: 20px; }
    .notas-search { flex: 1; min-width: 0; height: 44px; box-sizing: border-box; padding: 0 18px; border: none; border-radius: var(--r-2xl); box-shadow: var(--nu-inset-1); background: var(--surface-sunken); color: var(--text-body); font: 400 14px var(--font-core); outline: none; }
    .notas-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
    .notas-tags:empty { display: none; }
    .notas-tag { border: none; cursor: pointer; padding: 6px 11px; border-radius: var(--r-lg); font: 500 11px/1 var(--font-mono); color: var(--text-muted); background: var(--surface-sunken); }
    .notas-tag.active { color: var(--text-strong); box-shadow: var(--nu-raise-1); background: var(--surface-base); }
    .notas-list { display: flex; flex-direction: column; gap: 4px; min-height: 0; overflow-y: auto; padding: 6px 8px 16px; margin: -6px -8px 0; }
    .notas-list-head { display: flex; align-items: baseline; justify-content: space-between; margin: 2px 4px 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line-hair); }
    .notas-list-label { font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); }
    .notas-list-count { font: 500 10.5px/1 var(--font-mono); color: var(--text-faint); }
    .notas-row { display: flex; align-items: flex-start; gap: 12px; width: 100%; text-align: left; border: none; cursor: pointer; padding: 12px; border-radius: var(--r-xl); background: none; font: inherit; color: var(--text-body); transition: background var(--dur-base) var(--ease-out); }
    .notas-row:hover { background: var(--surface-sunken); }
    .notas-row:hover .notas-row-title { color: var(--signal-cyan-dim); }
    .notas-row.active { box-shadow: var(--nu-raise-1); background: var(--surface-base); }
    .notas-row.active .notas-row-title { color: var(--text-strong); }
    .notas-row-icon { width: 30px; height: 30px; border-radius: 10px; margin-top: 1px; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); }
    .notas-row-icon .neon-under { width: 50%; height: 50%; }
    .notas-row-icon .glass-glaze { inset: 3px; border-radius: 7px; }
    .notas-row-icon svg, .notas-row-icon i { width: 13px; height: 13px; }
    .notas-row-body { flex: 1; min-width: 0; }
    .notas-row-top { display: flex; gap: 8px; align-items: baseline; }
    .notas-row-title { flex: 1; min-width: 0; font: 500 14px/1.35 var(--font-core); color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .notas-row-when { flex-shrink: 0; font: 500 10.5px var(--font-mono); letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
    .notas-row-excerpt { margin-top: 4px; font: 400 12.5px/1.5 var(--font-core); color: var(--text-muted); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
    .notas-row-tags { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
    .notas-row-tags span { font: 500 10.5px/1 var(--font-mono); color: var(--text-muted); background: var(--surface-sunken); border-radius: var(--r-md); padding: 4px 8px; }
    .notas-more { width: 100%; margin-top: 8px; }
    .notas-empty { color: var(--text-muted); font-size: 13px; padding: 12px 4px; }
    .notas-detail-head { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
    .notas-title { flex: 1; min-width: 0; border: none; background: none; outline: none; font: var(--fw-semibold) 18px var(--font-core); color: var(--text-strong); padding: 4px 0; border-bottom: 1px solid var(--line-hair); }
    .notas-iconbtn { border: none; background: none; cursor: pointer; color: var(--text-muted); padding: 4px 6px; border-radius: var(--r-sm); font: 500 12px var(--font-core); }
    .notas-iconbtn:hover { background: var(--surface-sunken); color: var(--text-strong); }
    .notas-iconbtn.on { color: var(--accent, #ff7a1a); }
    .notas-iconbtn.danger.confirming { color: #fff; background: #c0392b; }
    .notas-body { width: 100%; min-height: 46vh; resize: none; overflow: hidden; border: none; outline: none; background: none; color: var(--text-body); font: 400 14px/1.6 var(--font-core); padding: 8px 0; }
    .notas-meta { display: flex; gap: 10px; align-items: center; margin-top: 6px; font-size: 12px; color: var(--text-muted); }
    .notas-tags-input { flex: 1; min-width: 0; border: none; border-bottom: 1px solid var(--line-hair); background: none; outline: none; font: 400 12.5px var(--font-core); color: var(--text-body); padding: 4px 0; }
    .notas-status { flex-shrink: 0; font-size: 11.5px; }
    .notas-status.error { color: #d9534f; }

    .docs-folder-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border-radius: var(--r-xl); background: none; border: none; cursor: pointer; font: inherit; text-align: left; transition: var(--t-press); color: var(--text-body); }
    .docs-folder-item:hover { background: var(--surface-sunken); }
    .docs-folder-item.active { box-shadow: var(--nu-raise-1); background: var(--surface-base); }
    .docs-folder-item .rich-icon { width: 30px; height: 30px; box-shadow: 3px 3px 7px var(--nu-dark-soft), -3px -3px 7px var(--nu-light); }
    .docs-folder-item .rich-icon .neon-under { width: 50%; height: 50%; }
    .docs-folder-item .rich-icon .glass-glaze { inset: 3px; border-radius: 6px; }
    .docs-folder-item .rich-icon svg, .docs-folder-item .rich-icon i { width: 13px; height: 13px; }
    .docs-folder-label { flex: 1; min-width: 0; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .docs-folder-item.active .docs-folder-label { color: var(--text-strong); font-weight: var(--fw-semibold); }
    .docs-folder-count { flex-shrink: 0; font: 600 10.5px/1 var(--font-mono); color: var(--text-faint); background: var(--surface-sunken); border-radius: var(--r-xs); padding: 2px 6px; }
    .docs-folder-item.active .docs-folder-count { color: var(--text-muted); }
    /* Lista de documentos — .doc-card reutiliza el layout base de .rich-row
       (RICH_ROW_CSS: flex, gap, hairline entre filas) + un cuerpo propio de
       varias líneas (título+meta, fragmento, tags), algo que el rich-row de
       una sola línea no cubre. */
    /* Hover propio — .doc-card es un <div>, no un <a>/<button>, así que las
       reglas de RICH_ROW_CSS (a.rich-row:hover/button.rich-row:hover) no le
       aplicaban nunca: sin ningún indicio visual de que la fila era
       interactiva (feedback de Emilio 2026-09-18: "no hay efecto de hover
       alguno"). Mismo acento que el resto de rich-row clicables
       (--signal-cyan-dim) + un tinte de fondo sutil en la propia fila, ya
       que aquí SÍ hace falta algo más que el cambio de color del título
       para leerse como "esto se puede abrir" (toda la tarjeta es el
       objetivo del clic, no solo el título). */
    .doc-card { align-items: flex-start; cursor: pointer; border-radius: var(--r-xl); margin: 0 -12px; padding-left: 12px; padding-right: 12px; transition: background var(--dur-base) var(--ease-out); }
    .doc-card:hover { background: var(--surface-sunken); }
    .doc-card:hover .doc-title { color: var(--signal-cyan-dim); }
    .doc-card .rich-icon { margin-top: 2px; }
    .doc-card-header { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
    .doc-card-meta { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }
    .doc-title { font: 500 14.5px/1.4 var(--font-core); color: var(--text-strong); }
    .doc-category-badge { display: inline-flex; align-items: center; flex-shrink: 0; font: 600 12px/1 var(--font-core); color: var(--text-muted); box-shadow: inset 0 0 0 1px var(--line-hair); padding: 4px 10px; border-radius: var(--r-lg); }
    .doc-date { font: 400 11.5px/1 var(--font-mono); color: var(--text-faint); white-space: nowrap; }
    .doc-snippet { margin: 6px 0 0; font: 400 13px/1.55 var(--font-core); color: var(--text-muted); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
    .doc-tags-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
    .doc-tag-chip { display: inline-flex; align-items: center; gap: 5px; font: 600 12px/1 var(--font-core); color: var(--text-muted); box-shadow: inset 0 0 0 1px var(--line-hair); padding: 4px 10px; border-radius: var(--r-lg); cursor: pointer; transition: color var(--dur-fast) linear; }
    .doc-tag-chip:hover { color: var(--text-strong); }
    .doc-tag-remove { background: none; border: none; color: inherit; font-size: 11px; line-height: 1; cursor: pointer; padding: 0; opacity: 0; transition: opacity var(--dur-fast) ease; }
    .doc-tag-chip:hover .doc-tag-remove { opacity: .7; }
    .doc-tag-remove:hover { opacity: 1 !important; color: var(--neon-red); }
    .doc-add-tag-btn { appearance: none; cursor: pointer; font: 600 11.5px/1 var(--font-core); color: var(--text-faint); background: none; border: 1.5px dashed var(--line-hair); border-radius: var(--r-lg); padding: 4px 9px 4px 8px; transition: var(--t-press); }
    .doc-add-tag-btn:hover { color: var(--neon-teal); border-color: var(--neon-teal); }
    .doc-add-tag-input { box-shadow: inset 0 0 0 1px var(--neon-teal); border-radius: var(--r-lg); padding: 3px 10px; font-size: 11.5px; font-family: inherit; background: var(--surface-sunken); color: var(--text-body); outline: none; width: 100px; border: none; }
    .doc-snippet-warn { color: var(--neon-orange); font-size: 0.78rem; display: flex; align-items: center; gap: 4px; margin-top: 6px; }
    .doc-reingest-btn { background: none; box-shadow: inset 0 0 0 1px var(--neon-orange); color: var(--neon-orange); border-radius: var(--r-lg); padding: 3px 9px; font-size: 0.72rem; cursor: pointer; margin-left: 4px; font-family: inherit; transition: var(--t-press); }
    .doc-reingest-btn:hover { background: color-mix(in srgb, var(--neon-orange) 15%, transparent); }

    /* ql-fade-in/ql-scale-in — compartidas con .agent-detail-overlay/
       .agent-detail-panel (más arriba en este archivo), no borrar aunque el
       resto de reglas .doc-preview-* (modal Quick Look, FOX-P131/US6:
       sustituido por la página del lector, /reports/documents/:id) ya no
       tenga ningún consumidor. */
    @keyframes ql-fade-in { from { opacity: 0; } to { opacity: 1; } }
    @keyframes ql-scale-in { from { transform: scale(0.96); opacity: 0; } to { transform: scale(1); opacity: 1; } }

    /* ── Finanzas — rediseño Carve & Glow, migración a producción del mock
       artifact validado por Emilio (2026-09-21), mismo patrón que
       Documentos/Tareas arriba: page-hero (header) + selector de mes nuevo
       + .fin-panel (superficie plana, misma receta que .docs-panel) en vez
       de las renderHubCard() sueltas en .hub-grid que tenía antes.
       RICH_ROW_CSS/GLASS_ICON_CSS/PAGE_HERO_CSS/BUTTONS_CSS/FIELD_CARVED_CSS
       ya están interpolados en este mismo shell — solo lo nuevo va aquí.
       "Tareas de Skinner" reutiliza renderTaskCard() real (.kanban-card,
       exhibit 12) en vez de una fila propia — solo necesita separación
       vertical entre cards (.fin-tasks). ── */
    .page-hero.fin-hero { margin-bottom: 24px; }
    .fin-month-switch { display: inline-flex; align-items: center; gap: 2px; margin-top: 16px; background: var(--surface-card); border-radius: var(--r-pill); box-shadow: var(--nu-inset-1); padding: 4px; }
    .fin-month-nav-btn { position: relative; width: 28px; height: 28px; border-radius: var(--r-lg); flex: 0 0 auto; background: var(--surface-base); box-shadow: var(--nu-raise-1); border: none; cursor: pointer; transition: box-shadow var(--dur-fast) var(--ease-press), transform var(--dur-fast) var(--ease-press); }
    .fin-month-nav-btn:active { box-shadow: var(--nu-inset-1); transform: scale(.96); }
    .fin-month-nav-btn .neon-under { width: 60%; height: 60%; background: var(--text-muted); box-shadow: 0 0 8px 2px var(--text-muted); opacity: .3; transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out); }
    .fin-month-nav-btn:hover .neon-under { background: var(--neon-teal); box-shadow: 0 0 10px 2px var(--neon-teal-glow); opacity: .7; }
    .fin-month-nav-btn .glass-glaze { inset: 4px; border-radius: 6px; }
    .fin-month-nav-btn .glass-glaze svg { width: 13px; height: 13px; color: var(--text-strong); }
    .fin-month-label { font: var(--fw-semibold) 13px/1 var(--font-core); color: var(--text-strong); padding: 0 14px; min-width: 132px; text-align: center; }

    .fin-toolbar { display: flex; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; align-items: stretch; }
    .fin-toolbar .btn { flex: 0 0 auto; align-self: center; }
    .fin-dropzone { flex: 1; min-width: 260px; display: flex; align-items: center; gap: 10px; padding: 8px 16px; border-radius: var(--r-2xl); background: var(--surface-card); box-shadow: var(--nu-inset-1); border: 1.5px dashed var(--line-hair); cursor: pointer; transition: box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); outline: none; }
    .fin-dropzone:hover, .fin-dropzone:focus-visible { border-color: var(--neon-teal); }
    .fin-dropzone.drag-active { border-color: var(--neon-teal); box-shadow: var(--nu-inset-2); }
    .fin-dropzone-icon { width: 28px; height: 28px; border-radius: 10px; position: relative; flex: 0 0 auto; background: var(--surface-base); box-shadow: var(--nu-raise-1); }
    .fin-dropzone-icon .neon-under { width: 55%; height: 55%; background: var(--neon-orange); box-shadow: 0 0 8px 2px var(--neon-orange-glow); }
    .fin-dropzone-icon .glass-glaze { inset: 3px; border-radius: 7px; }
    .fin-dropzone-icon svg { width: 14px; height: 14px; position: relative; z-index: 2; color: var(--text-strong); }
    .fin-dropzone-copy { flex: 1; min-width: 0; }
    .fin-dropzone-label { display: block; font: 500 13px/1.3 var(--font-core); color: var(--text-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .fin-dropzone-hint { display: block; font: 400 11.5px/1.3 var(--font-core); color: var(--text-faint); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .fin-status { font-size: 0.8rem; color: var(--text-muted); min-height: 1.2em; margin-top: 4px; flex-basis: 100%; }
    .fin-status:empty { display: none; }

    .fin-panel { position: relative; padding: 24px 24px 28px; border-radius: var(--r-3xl); background: var(--surface-card); box-shadow: var(--elev-card-soft); display: flex; flex-direction: column; margin-bottom: 24px; }
    .fin-panel:last-child { margin-bottom: 0; }
    .fin-panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; gap: 12px; }
    .fin-panel-head h2 { font: var(--fw-semibold) 16.5px/1.3 var(--font-core); margin: 0; color: var(--text-strong); }
    .fin-row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-bottom: 24px; }
    @media (max-width: 860px) { .fin-row { grid-template-columns: 1fr; } }

    .fin-amount { flex: 0 0 auto; font: 600 14px/1 var(--font-mono); text-align: right; white-space: nowrap; }
    .fin-amount.is-gasto { color: var(--signal-red); }
    .fin-amount.is-ingreso { color: var(--signal-mint); }

    .fin-invoice-row .rich-body { padding-right: 4px; }
    .fin-invoice-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; margin-left: 8px; }
    .fin-invoice-action-btn { position: relative; width: 30px; height: 30px; border-radius: var(--r-lg); flex: 0 0 auto; background: var(--surface-base); box-shadow: var(--nu-raise-1); border: none; cursor: pointer; transition: box-shadow var(--dur-fast) var(--ease-press), transform var(--dur-fast) var(--ease-press); }
    .fin-invoice-action-btn:active { box-shadow: var(--nu-inset-1); transform: scale(.96); }
    .fin-invoice-action-btn:disabled { opacity: .5; pointer-events: none; }
    .fin-invoice-action-btn .neon-under { width: 58%; height: 58%; background: var(--text-muted); box-shadow: 0 0 8px 2px var(--text-muted); opacity: .3; transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out); }
    .fin-invoice-action-btn.confirm:hover .neon-under { background: var(--neon-green); box-shadow: 0 0 10px 2px var(--neon-green-glow); opacity: .75; }
    .fin-invoice-action-btn.reject:hover .neon-under { background: var(--neon-red); box-shadow: 0 0 10px 2px var(--neon-red-glow); opacity: .75; }
    .fin-invoice-action-btn .glass-glaze { inset: 4px; border-radius: 7px; }
    .fin-invoice-action-btn .glass-glaze svg { width: 14px; height: 14px; color: var(--text-strong); }
    .fin-invoice-row.is-leaving { opacity: 0; transform: translateX(6px); transition: opacity 200ms ease, transform 200ms ease; }

    .fin-stat-strip { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--line-hair); border-radius: var(--r-3xl); overflow: hidden; box-shadow: var(--elev-card-soft); margin-bottom: 24px; }
    @media (max-width: 520px) { .fin-stat-strip { grid-template-columns: 1fr; } }
    .fin-stat-tile { background: var(--surface-card); padding: 22px 24px; }
    .fin-stat-k { font: var(--fw-medium) 10.5px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-muted); }
    .fin-stat-v { font: var(--fw-semibold) 30px/1.15 var(--font-core); margin-top: 9px; }
    .fin-stat-trend { display: inline-flex; align-items: center; gap: 4px; margin-top: 8px; font: var(--fw-medium) 12px/1 var(--font-mono); }

    .fin-cat-bar-row { margin-bottom: 14px; }
    .fin-cat-bar-row:last-child { margin-bottom: 0; }
    .fin-cat-bar-top { display: flex; justify-content: space-between; font: 400 13px/1.4 var(--font-core); color: var(--text-body); margin-bottom: 6px; }
    .fin-cat-bar-amount { font-family: var(--font-mono); color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .fin-cat-bar-track { background: var(--surface-sunken); box-shadow: inset 0 0 0 1px var(--line-hair); border-radius: var(--r-xs); height: 7px; }
    .fin-cat-bar-fill { background: var(--signal-orange); height: 7px; border-radius: var(--r-xs); }

    .fin-tasks .kanban-card + .kanban-card { margin-top: 12px; }


    /* ── Tareas — rediseño Carve & Glow (FOX-P132), migración a producción
       de un mock artifact validado por Emilio (2026-09-19), mismo patrón
       que Documents arriba: reutiliza PAGE_HERO_CSS (header), SEG_CARVED_CSS
       (toggle Lista/Kanban/Planner, sin tocar), .fox-select (filtro de
       iniciativa vía initFoxSelectJs) y TAG_PILL_CSS (chips de estado).
       Los selectores con lógica JS/tests detrás (#tareas-kanban/
       #tareas-list/#planner-grid/#planner-tab/.kanban-cell/.planner-cell/
       .kanban-card/.kanban-empty/data-agent/data-status/data-day/
       .tareas-status-checkbox) se MANTIENEN sin tocar — solo cambia su CSS
       (initTareasJs/initKanbanDnd/initTareasListDnd/initPlannerDnd, más
       abajo, siguen enganchando por esos mismos selectores). ── */
    .page-hero.tareas-hero { margin-bottom: 24px; }
    /* Título + contador en la misma línea (literal del mock artifact
       aprobado por Emilio — a diferencia de .docs-hero-meta, que va como
       subtítulo debajo del h1, aquí el contador es una píldora inline
       junto al título, mismo idioma que el resto de "chips" del sistema:
       fondo --surface-sunken + --elev-field, sin vidrio ni neón porque no
       es interactivo). */
    .tareas-hero-title-row { display: flex; align-items: baseline; gap: 14px; }
    .tareas-hero-count { font: 600 11px/1 var(--font-mono); letter-spacing: var(--ls-label); color: var(--text-faint); background: var(--surface-sunken); box-shadow: var(--elev-field); border-radius: var(--r-pill); padding: 6px 12px; }
    .tareas-toolbar { display: flex; align-items: flex-start; gap: 14px; row-gap: 16px; flex-wrap: wrap; margin-top: 22px; }
    .tareas-toolbar-sep { width: 1px; height: 34px; align-self: center; background: var(--line-hair); }
    .tareas-new-wrap { position: relative; margin-left: auto; }
    /* .btn-toolbar — variante de altura SOLO para "+ Nueva tarea" (feedback
       de Emilio comparando contra el mock: "el botón... no tiene el mismo
       tamaño"): iguala el padding vertical de .tag-pill/.select-trigger de
       al lado (12px) en vez del padding compacto de .btn-sm (4px), que lo
       dejaba visiblemente más bajo que el resto de controles del toolbar.
       No se toca .btn-sm en sí — sigue siendo el tamaño correcto en
       cualquier otro sitio de la app. */
    .btn.btn-toolbar { padding: 12px 20px; }
    /* Chips de estado — .tag-pill real (TAG_PILL_CSS) envuelto en un
       <label> con el checkbox real oculto (mismo patrón de ocultación que
       .fox-select/.slider-input): el checkbox sigue siendo la fuente de
       verdad que lee initTareasJs/getHiddenStatusesFromCheckboxes (y el
       test de regresión), el <label> solo añade la capa visual y lo
       togglea de forma nativa al hacer clic. */
    .status-chip { cursor: pointer; }
    .status-chip input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }

    /* Armazón del board Kanban/Planner — ligero a propósito (feedback de
       Emilio comparando contra Kanban de referencia planos: "algo super
       ligero sin raised elements"; las .kanban-card, SIN TOCAR, son las
       únicas que aportan peso visual). Columnas separadas por aire (gap),
       no por bordes — sustituye el box-shadow inset por celda que había
       antes en .kanban-cell/.planner-cell (ambas sin CSS propia hasta
       ahora, todo era inline style server-side). */
    .tareas-board-scroll { overflow-x: auto; padding: 8px 4px 28px; margin: -8px -4px -28px; }
    .kgrid { display: grid; column-gap: 24px; row-gap: 16px; grid-auto-rows: min-content; position: relative; }
    /* Recalibrado del raise SOLO en modo claro — .kanban-card (renderTaskCard)
       define su propio box-shadow inline leyendo --nu-dark/--nu-dark-soft en
       tiempo de render; redefinidas aquí (el ANCESTRO real de las cards, a
       diferencia de .kcol-bg que es hermano suyo) se recalculan solas sin
       tocar renderTaskCard. En oscuro los dos bloques de abajo reafirman los
       valores normales — el problema de contraste no existe ahí. */
    .kgrid { --nu-dark: rgba(163,177,198,.74); --nu-dark-soft: rgba(163,177,198,.44); }
    @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .kgrid { --nu-dark: rgba(0,0,0,.7); --nu-dark-soft: rgba(0,0,0,.4); } }
    :root[data-theme="dark"] .kgrid { --nu-dark: rgba(0,0,0,.7); --nu-dark-soft: rgba(0,0,0,.4); }
    .kcol-bg { grid-row: 1 / -1; background: var(--surface-sunken); border-radius: var(--r-2xl); }
    .khead-bar { display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px; }
    .khead-label { font: var(--fw-medium) 13px/1 var(--font-core); color: var(--text-body); }
    .khead-count { font: 600 10.5px/1 var(--font-mono); color: var(--text-faint); background: var(--surface-card); box-shadow: var(--nu-inset-1); border-radius: var(--r-xs); padding: 3px 7px; }
    .klabel { display: flex; flex-direction: column; justify-content: center; gap: 4px; padding: 0 16px; height: 56px; appearance: none; border: none; background: none; text-align: left; cursor: pointer; border-radius: var(--r-lg); }
    .klabel:hover { background: var(--surface-sunken); }
    .klabel:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: -2px; }
    .klabel-name { font: 500 11px/1.2 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; display: inline-flex; align-items: center; gap: 4px; }
    .klabel-chevron { width: 12px; height: 12px; flex: 0 0 auto; color: var(--text-faint); transition: transform var(--dur-fast) var(--ease-out); }
    .klabel[aria-expanded="false"] .klabel-chevron { transform: rotate(-90deg); }
    /* Fila plegada: solo queda el recuento de cada celda, a la altura de la etiqueta. */
    .kanban-cell.is-collapsed { padding: 0 16px; justify-content: center; min-height: 56px; }
    /* !important: la tarjeta lleva display:flex en su style en línea. */
    .kanban-cell.is-collapsed > * { display: none !important; }
    .kanban-cell.is-collapsed::after { content: attr(data-collapsed-count); font: 400 11px/1.2 var(--font-mono); letter-spacing: var(--ls-label); color: var(--text-faint); }
    .klabel-count { font: 400 10px/1.2 var(--font-mono); letter-spacing: var(--ls-label); color: var(--text-faint); }
    .kanban-cell { padding: 0 16px 16px; display: flex; flex-direction: column; gap: 14px; }
    /* .kanban-empty — celda vacía, antes trama diagonal a fuego, ahora un
       hueco discontinuo (dashed) que se lee como "sitio de sobra para
       soltar una tarjeta", coherente con el resto de vacíos del sistema
       (.planner-empty del mock aprobado). makeEmptyKanbanPlaceholder()
       (más abajo) ya no fija su propio style.cssText — lee esta clase. */
    .kanban-empty { width: 100%; min-height: 64px; border: 1.5px dashed var(--line-hair); border-radius: var(--r-xl); display: grid; place-items: center; font: 400 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); }

    /* Lista — reutiliza .rich-row/.rich-icon reales (RICH_ROW_CSS): el
       icono pasa a mostrar la prioridad (P0-P3) en vez de un glifo, mismo
       patrón de 3 capas (raise + neon-under + glass-glaze) que el resto de
       rich-icon del sistema. Hover con fondo (feedback de Emilio comparando
       contra la tabla 16 del artifact Carve & Glow) — RICH_ROW_CSS de base
       solo cambia el color del título al pasar el ratón. */
    li.tareas-row.rich-row:hover { background: var(--surface-sunken); margin: 0 -12px; padding-left: 12px; padding-right: 12px; border-radius: var(--r-lg); }
    li.tareas-row.rich-row:hover .rich-title { color: var(--signal-cyan-dim); }
    /* Letras P0-P3 en negro con transparencia fija (no --text-body) — en
       claro, --text-body es casi negro pero el vidrio ahí es mucho más
       opaco y aclara tanto el color de fondo que el texto seguía sin
       leerse bien; un negro directo con alpha se lee igual de bien encima
       de cualquiera de los cuatro colores de prioridad, en los dos temas. */
    .tareas-row-icon .p-label { position: relative; z-index: 2; font: 700 10px/1 var(--font-mono); letter-spacing: .02em; color: rgba(0,0,0,.72); }

    /* ── Planner — backlog/"Sin fecha" primero, columnas = agente (antes
       fila), filas = día, colapsables (feedback de Emilio: "el backlog/sin
       fecha debería estar arriba para ponerlos más fácilmente, y los días
       poder ser colapsables"). #planner-grid/.planner-cell/data-agent/
       data-day se mantienen — initPlannerDnd no distingue esta reestructura
       de la rejilla agente×día anterior, solo le importa que cada
       .planner-cell cuelgue en algún punto de #planner-grid. */
    .planner-agents-head { display: grid; column-gap: 24px; margin-bottom: 4px; }
    .planner-day { margin-bottom: 16px; }
    .planner-day-toggle { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 16px; border-radius: var(--r-xl); background: none; border: none; cursor: pointer; font: inherit; text-align: left; color: var(--text-body); }
    .planner-day-toggle:hover { background: var(--surface-sunken); }
    .planner-day-chevron { width: 14px; height: 14px; color: var(--text-muted); transition: transform 200ms var(--ease-out); flex: 0 0 auto; }
    .planner-day.collapsed .planner-day-chevron { transform: rotate(-90deg); }
    .planner-day-bar { width: 3px; height: 16px; border-radius: 2px; background: var(--text-faint); flex: 0 0 auto; }
    .planner-day-bar.live { background: var(--signal-cyan); box-shadow: 0 0 8px 1px color-mix(in srgb, var(--signal-cyan) 50%, transparent); }
    .planner-day.is-backlog .planner-day-bar { background: var(--signal-orange); }
    .planner-day-label { font: var(--fw-medium) 13px/1 var(--font-core); flex: 1; }
    .planner-day-count { font: 600 10.5px/1 var(--font-mono); color: var(--text-faint); background: var(--surface-sunken); box-shadow: var(--elev-field); border-radius: var(--r-xs); padding: 3px 7px; }
    .planner-day-collapsible { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 240ms var(--ease-out); }
    .planner-day.collapsed .planner-day-collapsible { grid-template-rows: 0fr; }
    .planner-day-collapsible > div { overflow: hidden; }
    .planner-day-cells { display: grid; column-gap: 24px; padding: 12px 16px; background: var(--surface-sunken); border-radius: var(--r-2xl); }
    .planner-cell { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

    /* ── Iniciativas — rediseño Carve & Glow (FOX-P125+), migración a
       producción de un mock artifact validado por Emilio (2026-09-19).
       Reutiliza PAGE_HERO_CSS (header) igual que Tareas; el resto (clases
       iniciativas-* y tbl-*) es nuevo. Ronda de feedback ya incorporada
       (no reabrir): sin select "Ordenar" ni toggle "Agrupar por etapa" —
       las cabeceras de columna ORDENAN AL VUELO (initIniciativasJs, más
       abajo), eso los sustituye del todo. "+ Nueva iniciativa" a la
       IZQUIERDA del toolbar (no en el borde derecho). ── */
    /* Causa real del "sigue viendo la tabla pegada al header" tras varias
       rondas subiendo el valor (24 a 36 a 44 a 56px, ninguna con efecto):
       el comentario de arriba en una versión anterior nombraba las clases
       como ".iniciativas-" seguido de asterisco-barra-.tbl-asterisco" —
       ese asterisco seguido de barra es, LITERALMENTE, el cierre de
       comentario CSS: todo lo que venía después (incluida esta regla)
       quedaba tragado por el parser hasta el siguiente cierre real, más
       abajo en la hoja de estilos. Verificado en el navegador real:
       getComputedStyle() devolvía margin-bottom 0px pese a que curl
       mostraba 56px en el HTML servido — el HTML era correcto, la regla
       nunca llegaba a parsearse como CSS. Ningún asterisco-barra literal
       dentro de un comentario de aquí en adelante, ni para describir el
       propio bug. */
    /* 44px -> 24px (feedback de Emilio: "normaliza el espacio, que tiene
       más espacio la que has hecho que la de tareas modo lista") — mismo
       valor real que .page-hero.tareas-hero (verificado con
       getBoundingClientRect en el navegador, no a ojo: 24px en ambas). */
    .page-hero.iniciativas-hero { margin-bottom: 24px; }
    .iniciativas-hero-title-row { display: flex; align-items: baseline; gap: 14px; }
    .iniciativas-hero-count { font: 600 11px/1 var(--font-mono); letter-spacing: var(--ls-label); color: var(--text-faint); background: var(--surface-sunken); box-shadow: var(--elev-field); border-radius: var(--r-pill); padding: 6px 12px; }
    .iniciativas-toolbar { display: flex; align-items: flex-start; gap: 14px; row-gap: 14px; flex-wrap: wrap; margin-top: 22px; }
    .iniciativas-new-wrap { position: relative; }
    /* Mismo patrón de formulario inline que "+ Nueva tarea" real
       (initTareasNewTaskForm) — oculto por defecto, el botón lo abre al
       lado (left:0, no right:0 — el botón vive a la izquierda del
       toolbar, no en el borde derecho). */
    .ini-new-form { display: none; flex-direction: column; gap: 14px; padding: 18px; margin-top: 10px; position: absolute; left: 0; top: calc(100% + 8px); z-index: 10; width: 360px; border-radius: var(--r-3xl); background: linear-gradient(var(--glass-fill), var(--glass-fill)), var(--surface-base); border: 1px solid var(--glass-border); box-shadow: var(--elev-card); }
    .ini-new-form.open { display: flex; }
    .ini-new-form input, .ini-new-form textarea { border: none; border-bottom: 1px solid var(--line-hair); border-radius: 0; background: none; box-shadow: none; padding: 4px 0 10px; font: 400 13px var(--font-core); color: var(--text-body); outline: none; font-family: inherit; }
    .ini-new-form textarea { min-height: 90px; resize: vertical; }
    .ini-new-form input::placeholder, .ini-new-form textarea::placeholder { color: var(--text-faint); }
    .ini-new-form-actions { display: flex; align-items: center; gap: 8px; justify-content: flex-end; margin-top: 2px; }
    #ini-new-status { font-size: 0.72rem; color: var(--text-muted); }

    /* Tabla — exhibit 16 del artefacto "Carve & Glow" ("El catálogo de
       protocolos"), traducida al vocabulario de tokens real (--field→
       --surface-base, --sep→--line-hair, --label→--text-strong/-muted/
       -faint, --field-deep→--surface-sunken). Columnas Nombre/Etapa/
       Actualizada — Iniciativas no tiene frecuencia, tiene última
       actividad. */
    .tbl { border-radius: var(--r-3xl); overflow: hidden; background: var(--surface-base); box-shadow: var(--elev-card); }
    .tbl-head, .tbl-row { display: grid; grid-template-columns: 1fr 150px 150px; align-items: center; gap: 16px; }
    .tbl-head { padding: 20px 20px 12px; border-bottom: 1px solid var(--line-hair); }
    /* Cabeceras clicables — ordenan la tabla (initIniciativasJs); sustituyen
       al select "Ordenar" + toggle "Agrupar por etapa" de la primera
       pasada (feedback de Emilio: si la tabla ya ordena clicando en las
       columnas, ambos controles sobran). */
    .tbl-sort { appearance: none; border: none; background: none; padding: 0; margin: 0; display: inline-flex; align-items: center; gap: 4px; font: 600 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); cursor: pointer; transition: color 160ms ease; }
    .tbl-sort:hover { color: var(--text-body); }
    .tbl-sort.active { color: var(--text-body); }
    .tbl-sort-arrow { width: 9px; height: 9px; flex: 0 0 auto; opacity: 0; transition: opacity 160ms ease, transform 160ms ease; }
    .tbl-sort.active .tbl-sort-arrow { opacity: 1; }
    .tbl-sort.active.desc .tbl-sort-arrow { transform: rotate(180deg); }
    .tbl-row { padding: 14px 20px; border-bottom: 1px solid var(--line-hair); cursor: pointer; transition: background 160ms ease; border-radius: var(--r-xl); text-decoration: none; color: inherit; --icon-glow: transparent; }
    .tbl-row:last-child { border-bottom: none; }
    .tbl-row:hover { background: var(--surface-sunken); }
    .tbl-name { display: flex; align-items: center; gap: 12px; min-width: 0; }
    .tbl-icon { width: 34px; height: 34px; border-radius: 11px; position: relative; flex: 0 0 auto; background: var(--surface-base); }
    .tbl-icon.raise { box-shadow: var(--nu-raise-1); }
    .tbl-icon.carved { box-shadow: var(--nu-inset-1); }
    .tbl-icon .glass-glaze { inset: 3px; border-radius: 8px; }
    .tbl-icon .glass-glaze i, .tbl-icon .glass-glaze svg { width: 15px; height: 15px; }
    .tbl-icon .neon-under { box-shadow: none; transition: box-shadow 200ms ease; }
    .tbl-row:hover .tbl-icon .neon-under { box-shadow: 0 0 10px 2px var(--icon-glow); }
    .tbl-name-text { min-width: 0; }
    .tbl-title { font: 600 13.5px/1.3 var(--font-core); color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .tbl-desc { font: 400 12px/1.4 var(--font-core); color: var(--text-muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .tbl-cat { display: inline-flex; padding: 5px 10px; border-radius: var(--r-pill); font: 600 10px/1 var(--font-mono); }
    .tbl-freq { font: 400 13px/1 var(--font-core); color: var(--text-muted); }

  