
  :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;

    
      --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; }
  }

  * { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; }
  html, body { height: 100dvh; overflow: hidden; }
  body {
    margin: 0; background: var(--bg); color: var(--text);
    font-family: var(--font-core);
    font-size: var(--fs-body); line-height: var(--lh-body); -webkit-font-smoothing: antialiased;
    display: flex; flex-direction: column;
  }
  a { color: var(--signal-cyan-dim); }

  /* ── Layout (sidebar persistente + contenido scrollable) ── */
  .layout { display: flex; flex: 1; overflow: hidden; }
  /* margin-left:64px reserva el ancho de reposo del sidebar (ahora
     position:fixed — FOX-P124 ronda 3). */
  .board-col { flex: 1; margin-left: 64px; overflow-y: auto; overflow-x: hidden; 
      background-color: var(--surface-base);
 }
  
    /* ── 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; }
    }



  /* 96px — confirmado visualmente con Emilio contra la línea de regla real
     (--field-rule), no contra los puntos: el modelo teórico del
     radial-gradient centrado en su tile de 32px no coincidía con lo que se
     ve en pantalla, pero 96px (una línea de regla completa) sí encaja
     exactamente. Sin margin:0 auto tampoco: centrar con max-width hacía
     que el offset izquierdo del contenido dependiera del ancho de
     ventana, así que nunca coincidía con la trama salvo por casualidad a
     un ancho concreto. */
  /* Márgenes laterales reducidos a 48px (FOX-P130, feedback: "en el
     artefacto del daily hub, había menos espacio hacia los lados y
     quedaba mejor" — calcado de .main{padding:40px 48px} del artifact).
     Antes 96px, un valor propio de esta página (no compartido con otras)
     tuneado para la retícula modular de 192px que ya no existe aquí — sin
     esa retícula, no hay motivo para mantener un margen más ancho que el
     resto de páginas solo por inercia. */
  .wrap { padding: 0 48px 70px; }
  @media (max-width: 640px) { .wrap { padding: 0 18px 60px; } }

  /* .hero/.eyebrow/.hero h1 — receta compartida (PAGE_HERO_CSS, theme.ts),
     ver el comentario largo ahí. Sin text-transform:capitalize en el h1
     real (formatDateLabelHtml() ya devuelve el string con la primera letra
     en mayúscula) — CSS lo aplicaría a CADA palabra ("De Agosto De"), no
     solo a la inicial de la frase, así que PAGE_HERO_CSS no lo fuerza. */

  /* ---------- Navegación prev/hoy/siguiente ----------
     Cada pill es un .tag-pill real (TAG_PILL_CSS, theme.ts) — mismo carved+
     neón+vidrio que las tabs de Settings. "Hoy" es la única pill que puede
     llevar .active — el chip "vivo" que te devuelve al día de hoy — y
     SOLO cuando NO estás ya en él (isToday=false); viendo el día de hoy no
     tiene sentido destacar un enlace a sí mismo, así que ahí se sirve como
     <span> inerte sin .active (ver dateNavPill más abajo).
     Agrupadas y a la izquierda (FOX-P130, feedback: "los controles de día
     anterior, siguiente y hoy deben estar mucho más agrupados y a la
     izquierda") — antes justify-content:space-between las repartía por
     todo el ancho de la tarjeta; calcado del artifact de referencia
     (.hub-nav{gap:8px}, sin justify-content: flex-start por defecto). */
  .hub-nav { display: flex; align-items: center; gap: 8px; margin: 18px 0 0; flex-wrap: wrap; }

  /* ---------- Resumen — 4 tiles (FOX-P130, migración del rediseño del
     artefacto de referencia): tiempo, tareas, próximo evento, cumpleaños.
     Mismo truco gap:1px+background que usaba .hub-grid — dibuja divisores
     entre celdas sin CSS por celda. ---------- */
  /* border-radius:var(--r-3xl) (20px, no var(--r-lg)=8px) — mismo radio que
     .page-hero, "un border radius más agradable" (feedback del artifact).
     box-shadow calcado de .stat-strip real del artifact (inset 0 1px 0 +
     sombra suave hacia abajo, un bisel muy sutil, NO un raise de verdad) —
     var(--nu-light)/var(--nu-dark-soft) en vez de las rgba(255,255,255,.5)
     literales del artifact: esos dos tokens ya cambian solos entre claro/
     oscuro (ver THEME_VARS/THEME_MEDIA_CSS), así que no hace falta la
     duplicación con media query que sí necesitaba el artifact. */
  /* margin-top subido de 28px a 40px (feedback: "separa verticalmente más
     la barra-resumen del header? queda un poco justo") — el bottom se deja
     en 28px, solo el hueco contra .page-hero necesitaba más aire. */
  .hub-stat-strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--line-hair); border-radius: var(--r-3xl); overflow: hidden; box-shadow: inset 0 1px 0 var(--nu-light), 0 1px 2px var(--nu-dark-soft); margin: 40px 0 28px; }
  @media (max-width: 760px) { .hub-stat-strip { grid-template-columns: repeat(2, 1fr); } }
  .hub-stat-tile { background: var(--surface-card); padding: 18px 22px; min-width: 0; }
  a.hub-stat-tile { display: block; color: inherit; text-decoration: none; transition: background var(--dur-fast) var(--ease-out); }
  a.hub-stat-tile:hover { background: color-mix(in srgb, var(--surface-card) 92%, var(--text-body)); }
  a.hub-stat-tile:hover .hub-stat-k { color: var(--neon-blue); }
  a.hub-stat-tile:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: -2px; }
  .hub-stat-event-row { display: flex; align-items: center; gap: 12px; min-width: 0; }
  .hub-stat-event-row .hub-stat-v { flex: 0 1 auto; min-width: 0; }
  .hub-meet-btn { flex: 0 0 auto; margin-top: 9px; text-decoration: none; }
  .hub-stat-eta { margin-left: 6px; color: var(--signal-cyan-dim); text-transform: none; letter-spacing: 0; white-space: nowrap; }
  .hub-stat-k { white-space: nowrap; font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); }
  .hub-stat-v { font: var(--fw-semibold) 27px/1.1 var(--font-display, var(--font-core)); margin-top: 9px; color: var(--text-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hub-stat-v small { font: 500 14px/1 var(--font-core); color: var(--text-muted); margin-left: 3px; }
  .hub-stat-sub { font: 400 12.5px/1.3 var(--font-core); color: var(--text-muted); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Tiempo como el resto de tiles: cifra + línea gris, con el icono suelto
     junto a la cifra (sin la caja de cristal, que desentonaba en la barra). */
  .hub-stat-wthr { display: flex; align-items: center; gap: 10px; }
  .hub-stat-wthr-ico { display: inline-flex; color: var(--neon-orange); }
  .hub-stat-wthr-ico svg, .hub-stat-wthr-ico i { width: 18px; height: 18px; }

  /* ---------- Panel de sección (FOX-P130) — sustituye a .hub-card/.hub-grid
     de celda 2x2. Plano por defecto, con hairline propio (no comparte fondo
     de divisores entre panels distintos — solo dentro de .hub-bottom-row,
     ver más abajo); "raised" (Tareas) se separa del campo de verdad.
     background corregido a --surface-card (feedback: "el background de los
     elementos... no lo has puesto") — --surface-base es literalmente el
     mismo token que --bg (ver alias en theme.ts), el fondo de la propia
     página: un panel "plano" con ese fondo es indistinguible del campo que
     lo rodea. .hub-panel-raised invierte la lógica a propósito (igual que
     .panel.raised en el artifact): usa --surface-base (el tono del campo)
     y se separa por la SOMBRA (--elev-card), no por el color de fondo. ---------- */
  .hub-panel { position: relative; background: var(--surface-card); box-shadow: inset 0 1px 0 var(--nu-light), 0 1px 2px var(--nu-dark-soft); border-radius: var(--r-3xl); padding: 28px 24px 32px; display: flex; flex-direction: column; }
  .hub-panel-raised { background: var(--surface-base); box-shadow: var(--elev-card); }
  .hub-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; flex-shrink: 0; }
  .hub-panel-head h2 { font-size: 16.5px; font-weight: var(--fw-semibold); margin: 0; letter-spacing: -0.005em; }
  /* Buffer de sombra (feedback del artifact: "parece un poco bugueado el
     raised, no se ven las sombras, se ven como cortadas") — overflow-y:auto
     fuerza a overflow-x a dejar de ser "visible" (spec CSS Overflow: si un
     eje no es visible, el otro pasa a auto), así que la sombra de
     renderTaskCard()/.tcard se recortaba justo en el borde de esta caja.
     El padding le da sitio a la sombra dentro de la zona de scroll; el
     margen negativo compensa para que el layout no se mueva. */
  .hub-panel-body { flex: 1; min-height: 0; overflow-y: auto; margin: -16px; padding: 16px; }

  .hub-empty { margin: 0; font-size: 13.5px; color: var(--text-muted); }

  .hub-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
  .hub-list-item { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 14px; }
  /* Separador entre ítems — calcado de la fila de Tareas en
     Sidebar.dc.html (hairline entre las dos tareas de PENDIENTE). Vive en
     el gap de arriba, no suma alto extra. */
  .hub-list-item:not(:first-child) { border-top: 1px solid var(--line-hair); }
  .hub-list-title { flex: 1; min-width: 0; transition: var(--t-press); }
  .hub-meta { font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
  .hub-meta-label { display: block; font: var(--text-label);text-transform:uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); margin-bottom: 8px; }
  .hub-subgroup { margin-bottom: 16px; }
  .hub-subgroup:last-child { margin-bottom: 0; }
  .hub-badge { font: var(--text-label);text-transform:uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); box-shadow: inset 0 0 0 1px var(--line-hair); padding: 2px 7px; border-radius: var(--r-xs); flex-shrink: 0; }
  /* "Ver todas" en la cabecera del panel de Tareas (FOX-P130, calcado del
     artifact: .panel-link) — antes vivía como pie de página en gris
     apagado (.hub-card-footer-link, ya retirada). */
  .hub-panel-link { font: 600 12px/1 var(--font-core); color: var(--neon-orange); text-decoration: none; flex-shrink: 0; }

  /* ---------- Agenda — panel colapsable a todo lo ancho (FOX-P130) ----------
     Mismo truco grid-template-rows:0fr/1fr que .select-options real
     (board-renderer.ts) para colapsar/expandir de verdad, no display:none
     brusco. Toggle en toda la cabecera (feedback del artifact: "preferiría
     que fuera dándole a cualquier parte del componente, no solo al
     chevron"), ver el listener en #hub-agenda-head más abajo en el <script>. */
  .hub-collapse-btn { appearance: none; border: none; background: none; padding: 4px; margin: 0; cursor: pointer; color: var(--text-muted); display: flex; align-items: center; flex-shrink: 0; }
  .hub-collapse-btn svg { width: 16px; height: 16px; transition: transform 220ms ease; }
  #hub-agenda-panel:not(.hub-panel-collapsed) .hub-collapse-btn svg { transform: rotate(180deg); }
  #hub-agenda-head { cursor: pointer; }
  .hub-cal-btn { appearance: none; border: none; flex-shrink: 0; margin-left: auto; }
  /* Plegada, la cápsula ya empuja a la derecha con su margin-left:auto; si el botón también lo llevara, se repartirían el hueco y la cápsula quedaría centrada. */
  .hub-panel-collapsed .hub-cal-btn { margin-left: 0; }
  .hub-cal-btn[aria-expanded="true"] .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; }

  /* Preview colapsada — dos chips distinguibles con un punto de color por
     tipo (comida/evento) y el primero con cuenta atrás relativa cuando se
     ve el día de hoy (feedback del artifact: "le pondría en el primer
     elemento cuánto falta para ese elemento... algo que permitiera
     distinguir fácilmente un elemento del siguiente"), calculados en el
     <script> final contra .hub-tl-item[data-time]. */
  /* Siempre presente (sin display:none) para poder animar plegado/desplegado: desplegada mide 0 y es invisible. */
  .hub-agenda-preview { display: flex; flex: 0 1 0; min-width: 0; align-items: center; justify-content: flex-end; gap: 10px; overflow: hidden; white-space: nowrap; padding: 10px 0; border-radius: var(--r-2xl); background: var(--glass-fill); backdrop-filter: var(--backdrop-blur); -webkit-backdrop-filter: var(--backdrop-blur); box-shadow: var(--elev-card-soft); border: 1px solid var(--glass-border); opacity: 0; transform: translateX(14px) scale(.96); transform-origin: right center; pointer-events: none; transition: flex-basis 280ms var(--ease-out, ease), padding 280ms var(--ease-out, ease), opacity 200ms ease, transform 280ms var(--ease-out, ease); }
  @media (prefers-reduced-motion: reduce) { .hub-agenda-preview { transition: none; } }
  .hub-preview-chip { display: inline-flex; align-items: center; gap: 7px; min-width: 0; max-width: 260px; font: 500 13px/1.4 var(--font-core); color: var(--text-muted); }
  .hub-preview-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .hub-preview-chip b { flex: 0 0 auto; font-weight: 600; color: var(--text-body); font-variant-numeric: tabular-nums; }
  .hub-preview-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
  .hub-preview-sep { width: 1px; height: 16px; background: var(--line-hair); flex: 0 0 auto; }
  .hub-panel-collapsible { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 280ms ease; }
  /* padding+margin negativos: el overflow:hidden del plegado no recorta la
     sombra ni el brillo del botón "Crear tareas", que queda justo en el borde. */
  .hub-panel-collapsible > div { overflow: hidden; min-height: 0; padding: 0 8px 12px; margin: 0 -8px -12px; }
  .hub-panel-collapsed .hub-panel-collapsible { grid-template-rows: 0fr; }
  /* Plegado, el relleno del contenido asomaba como una franja bajo la cápsula: se oculta al terminar la animación. */
  .hub-panel-collapsible > div { transition: visibility 0s; }
  .hub-panel-collapsed .hub-panel-collapsible > div { visibility: hidden; padding-bottom: 0; margin-bottom: 0; transition: visibility 0s linear 280ms; }
  /* Colapsada, la card gana un raise sutil — señal de que ahora es un
     control resumen/clicable, no una sección de contenido normal. */
  .hub-panel-collapsed { background: var(--surface-card); box-shadow: var(--nu-raise-1); }
  .hub-panel-collapsed .hub-panel-head { margin-bottom: 0; }
  /* Resumen del día / Agenda a 50/50 dentro de la misma sección. */
  .hub-agenda-split { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; }
  @media (max-width: 760px) { .hub-agenda-split { grid-template-columns: 1fr; } }
  /* Textarea real de "Resumen del día" (FOX-P130, feedback: "no hay un
     espacio para escribir, cuando debería de haberlo") — calcado de
     .standup-input del artifact. --surface-base (el tono del campo, NO
     --surface-card) para que se lea como un hueco tallado dentro del
     panel, mismo criterio que .hub-panel-raised (ver comentario ahí). */
  .hub-standup-input { width: 100%; min-height: 180px; border: none; border-radius: var(--r-2xl); background: var(--surface-base); box-shadow: var(--nu-inset-1); padding: 14px 16px; font: 400 13.5px/1.6 var(--font-core); color: var(--text-body); resize: none; transition: box-shadow var(--dur-base) var(--ease-out); }
  .hub-standup-input::placeholder { color: var(--text-faint); }
  .hub-plan-actions { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 10px; }
  .hub-plan-status { flex: 1; min-width: 0; font: 400 12.5px/1.4 var(--font-core); color: var(--text-muted); }
  .hub-plan-status.is-error { color: var(--signal-red); }
  .hub-standup-input:focus { outline: none; box-shadow: var(--nu-inset-2); }
  /* Eventos-tarea (FOX-P146): el propio icono es el check. Pendiente =
     icono de calendario (al pasar el ratón anticipa el check); hecho = check
     verde, fila tachada y atenuada. */
  button.hub-tl-task-icon { appearance: none; border: none; padding: 0; cursor: pointer; font: inherit; color: inherit; }
  .hub-tl-task-icon .hub-tl-icon-pending { display: inline-flex; }
  .hub-tl-task-icon .hub-tl-icon-done { display: none; }
  .hub-tl-task-icon:hover .hub-tl-icon-pending, .hub-tl-item.is-done .hub-tl-icon-pending { display: none; }
  .hub-tl-task-icon:hover .hub-tl-icon-done, .hub-tl-item.is-done .hub-tl-icon-done { display: inline-flex; }
  .hub-tl-item.is-done .hub-tl-task-icon .neon-under { background: var(--neon-green) !important; box-shadow: 0 0 8px 2px var(--neon-green-glow) !important; }
  .hub-tl-item.is-done { opacity: .55; }
  .hub-tl-item.is-done .rich-title { text-decoration: line-through; }
  /* Bloques de fondo (FOX-P145) en la agenda: planos y atenuados. */
  .hub-tl-bg { display: flex; align-items: center; gap: 10px; padding: 6px 12px; margin: 2px 0; border-radius: var(--r-lg); background: color-mix(in srgb, var(--band-color) 10%, transparent); }
  .hub-tl-bg-bar { flex: 0 0 3px; align-self: stretch; min-height: 16px; border-radius: 2px; background: var(--band-color); opacity: .7; }
  /* Ítem más próximo (o en curso): solo la hora en naranja; lo marca centerTimelineOnNow. */
  .hub-tl-item.is-next .hub-tl-time { color: var(--neon-orange); }
  .hub-tl-bg-title { flex: 1; min-width: 0; font: 600 10.5px/1.3 var(--font-mono); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .hub-tl-bg .hub-tl-time { font-size: 11px; }
  .hub-tl-bg.is-live .hub-tl-bg-title { color: var(--text-body); }
  .hub-tl-list { display: flex; flex-direction: column; max-height: 320px; overflow-y: auto; margin: -16px; padding: 16px; }
  /* Difuminado arriba/abajo para que se vea que la agenda tiene más contenido al hacer scroll. */
  #hub-agenda-timeline.is-scrollable { -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%); mask-image: linear-gradient(to bottom, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%); }
  /* Mini cabecera de cristal de la Agenda — misma receta que el mini header
     del Recetario (.nutricion-recetario-header, board-renderer.ts): la
     columna es una superficie propia con su scroll, y la cabecera es una
     tarjeta de cristal fija arriba por la que los eventos pasan difuminados
     (feedback de Emilio, 2026-09-25: la versión anterior era solo la
     etiqueta "AGENDA" montándose sobre el primer evento). La cabecera
     arranca a la altura de la etiqueta "Resumen del día" de la otra columna. */
  .hub-agenda-split > .hub-agenda-col { min-width: 0; }
  #hub-agenda-timeline.has-glass-head { margin: 0; padding: 0 12px 12px; max-height: 340px; border-radius: var(--r-2xl); }
  .hub-tl-glass-head { position: sticky; top: 0; z-index: 3; margin: 0 -12px 10px; padding: 13px 18px; 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-soft); }
  .hub-tl-glass-head h3 { margin: 0; font: var(--fw-semibold) 16.5px/1.3 var(--font-core); color: var(--text-strong); letter-spacing: -0.005em; }
  /* La cabecera es el :first-child real de la lista — el primer ítem
     necesita el mismo reset que .rich-row:first-child (theme.ts). */
  .hub-tl-glass-head + .rich-row { border-top: none; padding-top: 0; }
  /* Sin mask-image aquí: una máscara en el contenedor lo convierte en raíz
     del backdrop y el cristal de la cabecera deja de difuminar (el texto
     pasaba nítido por debajo). La pista de "hay más abajo" es un degradado
     fijo al pie con el color del panel. */
  #hub-agenda-timeline.has-glass-head.is-scrollable { -webkit-mask-image: none; mask-image: none; }
  #hub-agenda-timeline.has-glass-head.is-scrollable::after { content: ""; position: sticky; bottom: -12px; display: block; flex: 0 0 auto; height: 22px; margin: -22px -12px -12px; background: linear-gradient(to bottom, transparent, var(--surface-card)); pointer-events: none; z-index: 2; }
  /* Plegada: los próximos eventos en una cápsula de cristal que ocupa la
     mitad derecha — el mismo sitio que la columna de la Agenda abierta. */
  .hub-panel-collapsed .hub-agenda-preview { flex: 0 1 calc(50% - 14px); margin-left: auto; padding: 10px 16px; opacity: 1; transform: none; pointer-events: auto; transition-delay: 60ms; }
  .hub-tl-tag { display: block; font: var(--text-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--text-muted); margin-bottom: 1px; }
  a.hub-tl-item { cursor: pointer; }
  /* Hora de cada ítem — peso e intensidad rebajados (feedback: "en la
     agenda hay que bajarle un poco los pesos a las horas") — antes sin
     regla propia, heredaba el peso/color por defecto del texto y quedaba
     tan marcada como el título. Mono + --text-muted, igual que el resto de
     metadatos secundarios del sistema (.hub-meta). El naranja de "en
     curso" (item.live) se aplica aparte, inline, por encima de esto. */
  .hub-tl-time { font: 600 12px/1 var(--font-mono); color: var(--text-muted); font-variant-numeric: tabular-nums; flex: 0 0 auto; margin-left: 12px; align-self: center; }

  /* ---------- Fila Tareas(70%) + Proyectos(30%) (FOX-P130) ---------- */
  /* margin-bottom subido de 28px a 36px (feedback: "el espaciado vertical
     entre secciones sigue siendo un problema") — separa Tareas/Proyectos
     del bloque inferior. El hueco ANTES de esta fila (entre Agenda y
     Tareas/Proyectos) es aparte: ver margin-bottom en #hub-agenda-panel,
     que antes no existía — Agenda y esta fila quedaban tocándose porque
     ninguno de los dos ponía margen ahí. */
  .hub-row { display: grid; gap: 20px; margin-bottom: 36px; }
  .hub-row-tareas-proyectos { grid-template-columns: 7fr 3fr; }
  @media (max-width: 860px) { .hub-row-tareas-proyectos { grid-template-columns: 1fr; } }

  /* ---------- Bloque inferior — Protocolos/Emails/Noticias en 3 columnas
     con fondo compartido y divisores verticales (FOX-P130, feedback del
     artifact: "lo pondría todo dentro del mismo background y separado por
     dividers verticales... rompemos un poco el estilo y aprovechamos mejor
     el ancho"). Mismo truco gap:1px+background que .hub-stat-strip. ---------- */
  .hub-bottom-row { display: grid; grid-template-columns: 1fr 2fr; gap: 1px; background: var(--line-hair); border-radius: var(--r-3xl); overflow: hidden; box-shadow: inset 0 1px 0 var(--nu-light), 0 1px 2px var(--nu-dark-soft); }
  .hub-bottom-panel { background: var(--surface-card); padding: 28px 24px 32px; min-width: 0; }
  @media (max-width: 1100px) { .hub-bottom-row { grid-template-columns: 1fr; } }
  @media (max-width: 640px) { .hub-bottom-row { grid-template-columns: 1fr; } }

  /* Noticias de hoy (2026-09-25): 2/3 de la fila, con scroll propio si el día viene cargado. */
  /* Hover de fila como las tablas (.tbl-row): solo fondo hundido, el título
     no cambia de color. Sin scroll propio: la lista crece con la página. El
     margen negativo deja que el fondo del hover sobresalga del texto. */
  /* Separadores rectos como los de Protocolos y las tablas: una línea de ancho
     de contenido en un pseudo-elemento, no un border-top, que seguía la curva
     del radio de la fila y se doblaba en las esquinas. */
  :is(.hub-news-item, .hub-table-row).rich-row { position: relative; border-top: none; }
  :is(.hub-news-item, .hub-table-row).rich-row + :is(.hub-news-item, .hub-table-row).rich-row::before { content: ""; position: absolute; top: 0; left: 12px; right: 12px; height: 1px; background: var(--line-hair); }
  .hub-news-discard { flex: 0 0 auto; width: 28px; height: 28px; border-radius: var(--r-lg); display: grid; place-items: center; color: var(--text-muted); opacity: 0; cursor: pointer; transition: opacity 140ms ease, color 140ms ease, background 140ms ease; }
  .hub-news-item:hover .hub-news-discard, .hub-news-discard:focus-visible { opacity: 1; }
  .hub-news-discard:hover { color: var(--neon-red); background: color-mix(in srgb, var(--neon-red) 12%, transparent); }
  .hub-news-discard:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: 0; }
  /* En táctil no hay hover: se descarta deslizando la fila a la izquierda (ver el script). */
  @media (hover: none) { .hub-news-discard { display: none; } }
  .hub-news-item { touch-action: pan-y; }
  .hub-news-item.is-read { opacity: .62; }
  .hub-news-item.swiping { background: linear-gradient(to left, color-mix(in srgb, var(--neon-red) calc(var(--swipe, 0) * 28%), transparent), transparent 70%); }
  .hub-news-item.swiping .hub-news-discard { display: grid; opacity: var(--swipe, 0); color: var(--neon-red); }
  
    .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; }

  .hub-news-filter.select-box { min-width: 130px; margin-left: 8px; }
  .hub-news-actions { display: inline-flex; gap: 8px; margin-left: auto; }
  .hub-news-actions [hidden], .hub-news-list [hidden] { display: none !important; }
  .hub-news-undo { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; font: 500 12px/1 var(--font-core); color: var(--text-muted); }
  .hub-news-undo[hidden] { display: none; }
  .hub-news-list { margin: 0 -12px; }
  a.hub-news-item:hover .rich-title { color: var(--text-body); }
  .hub-news-item.rich-row { padding: 12px; border-radius: var(--r-lg); transition: background 160ms ease; }
  a.hub-news-item { color: inherit; text-decoration: none; }
  :is(a, button).hub-news-item:hover { background: var(--surface-sunken); }
  /* Proyectos: mismo hover de tabla que Noticias. */
  .hub-table-row.rich-row { margin: 0 -12px; padding: 12px; border-radius: var(--r-lg); transition: background 160ms ease; }
  /* La primera/última fila de .rich-row pierden su relleno (RICH_ROW_CSS);
     aquí no, o el fondo del hover queda cortado a ras del texto. */
  :is(.hub-table-row, .hub-news-item).rich-row:is(:first-child, :last-child) { padding: 12px; }
  a.hub-table-row:hover { background: var(--surface-sunken); }
  a.hub-table-row:hover .rich-title { color: var(--text-body); }
  button.hub-news-item:hover .rich-title { color: var(--text-body); }
  /* Correos que el juez ocultaría (FOX-P169): más discretos, con la etiqueta siempre a la vista. */
  .hub-news-item.is-judge-skip .rich-title { color: var(--text-muted); }
  .hub-news-item.is-judge-skip .rich-icon { opacity: .55; }
  .hub-news-item.is-judge-skip:hover .rich-title { color: var(--text-body); }
  .hub-news-hidden-tag { display: inline-block; margin-right: 6px; padding: 1px 7px; border: 1px solid var(--line-hair); border-radius: var(--r-lg); font-size: 11px; line-height: 1.4; color: var(--text-muted); white-space: nowrap; }
  button.hub-news-item:focus-visible { outline: 2px solid var(--neon-blue); outline-offset: -2px; }
  /* Diálogo de detalle de una novedad. */
  .hub-news-dialog-kicker { font: var(--fw-medium) 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); margin: 0 0 10px; }
  .hub-news-dialog h3 { margin-bottom: 16px; }
  .hub-news-dialog a.btn { text-decoration: none; }
  .hub-news-dialog .glass-modal-box { max-height: calc(100dvh - 40px); overflow-y: auto; }
  .hub-news-dialog-status { font: 400 12.5px/1.4 var(--font-core); color: var(--text-muted); margin: 0 0 12px; min-height: 0; }
  .hub-news-dialog-status:empty { display: none; }
  .hub-news-dialog-status.is-error { color: var(--neon-red); }
  .hub-news-dialog-status.is-ok { color: var(--signal-cyan-dim); font-weight: 500; }
  .hub-news-meta { font: 400 12.5px/1.5 var(--font-core); color: var(--text-muted); margin: 0 0 12px; }
  .hub-news-email-body { max-height: 38vh; overflow-y: auto; white-space: pre-wrap; overflow-wrap: anywhere; font: 400 13.5px/1.6 var(--font-core); color: var(--text-body); padding: 14px 0; border-top: 1px solid var(--line-hair); border-bottom: 1px solid var(--line-hair); margin-bottom: 16px; }
  .hub-news-reply { width: 100%; min-height: 110px; resize: vertical; border: 1px solid var(--glass-border); border-radius: var(--r-xl); background: var(--glass-fill); padding: 12px 14px; font: 400 13.5px/1.55 var(--font-core); color: var(--text-body); margin-bottom: 18px; }
  .hub-news-reply:focus { outline: 2px solid var(--neon-blue); outline-offset: 1px; }
  .hub-compose-fields { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; }
  .hub-compose-label { display: flex; flex-direction: column; gap: 6px; font: var(--fw-medium) 11px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); }
  .hub-compose-input { border: 1px solid var(--glass-border); border-radius: var(--r-xl); background: var(--glass-fill); padding: 10px 14px; font: 400 13.5px/1.4 var(--font-core); color: var(--text-body); text-transform: none; letter-spacing: normal; }
  .hub-compose-input:focus { outline: 2px solid var(--neon-blue); outline-offset: 1px; }
  .hub-compose-fields .hub-news-reply { margin-bottom: 0; text-transform: none; letter-spacing: normal; }
  .hub-compose-notes { min-height: 70px; }
  .hub-compose-assist-group { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
  .hub-compose-assist-group[hidden], .hub-compose-attachments[hidden] { display: none; }
  .hub-compose-attachments { border: 1px solid var(--glass-border); border-radius: var(--r-xl); background: var(--glass-fill); padding: 10px 14px 12px; margin: 0; min-width: 0; }
  .hub-compose-attachments legend { font: var(--fw-medium) 11px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-faint); padding: 0 6px; }
  .hub-compose-attachments ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
  .hub-compose-attachment { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font: 400 13.5px/1.4 var(--font-core); color: var(--text-body); }
  .hub-compose-attachment input { flex-shrink: 0; }
  .hub-compose-attachment-name { overflow-wrap: anywhere; min-width: 0; }
  .hub-compose-attachment-size { color: var(--text-muted); font-size: 12.5px; }
  .hub-compose-attachment-warn { color: var(--neon-red); font-size: 12.5px; font-weight: 500; }
  .hub-compose-attachment-total { margin: 8px 0 0; font: 400 12.5px/1.4 var(--font-core); color: var(--text-muted); }
  .hub-compose-attachment-total.is-over { color: var(--neon-red); font-weight: 500; }
  .hub-compose-fields > .btn { align-self: flex-start; }
  .hub-news-unsub { display: flex; flex-direction: column; gap: 10px; border: 1px solid var(--glass-border); border-radius: var(--r-xl); background: var(--glass-fill); padding: 12px 14px; margin-bottom: 18px; }
  .hub-news-unsub-text { margin: 0; font: 400 13.5px/1.55 var(--font-core); color: var(--text-body); }
  .hub-news-unsub-actions { display: flex; gap: 8px; flex-wrap: wrap; }
  .hub-news-summary { font: 400 14px/1.65 var(--font-core); color: var(--text-body); margin: 0 0 20px; }
  .hub-news-interest { padding: 14px 16px; border-radius: var(--r-xl); background: var(--glass-fill); border: 1px solid var(--glass-border); margin-bottom: 20px; }
  .hub-news-score { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
  .hub-news-score strong { font: var(--fw-semibold) 22px/1 var(--font-display, var(--font-core)); color: var(--text-strong); }
  .hub-news-score span { font: var(--fw-medium) 10px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-muted); }
  .hub-news-explanation { font: 400 13px/1.55 var(--font-core); color: var(--text-muted); margin: 0 0 14px; }
  .hub-news-verdict { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .hub-news-verdict-q { font: 500 13px/1 var(--font-core); color: var(--text-body); margin-right: 4px; }
  .hub-news-verdict .btn[aria-pressed="true"] { box-shadow: inset 0 0 0 1.5px var(--neon-blue); }
  .hub-news-reason { width: 100%; min-height: 64px; margin-top: 12px; resize: vertical; border: 1px solid var(--glass-border); border-radius: var(--r-lg); background: transparent; padding: 10px 12px; font: 400 13px/1.5 var(--font-core); color: var(--text-body); }
  .hub-news-item .rich-title { white-space: normal; }


  /* ---------- Checklist de protocolos (FOX-T516, FOX-P125/FOX-T975) ----------
     Migrado de una caja 22x22 raise/inset con una "✕" de texto (receta
     pre-Carve&Glow, components/forms/Checkbox.jsx) a .check-icon real
     (CHECK_ICON_CSS, theme.ts) — receta del exhibit 08 del artifact
     ("checkbox y radio son literalmente el mismo .task-icon... no había
     motivo para inventar una versión más pequeña"). El <input> real se
     mantiene (accesible, dispara el change de abajo) pero invisible;
     :has() conmuta los dos iconos que cruzan por display — mismo patrón
     ya usado en board-renderer.ts/cuestionarios-renderer.ts. Envuelto en
     .rich-row (FOX-P130) — el check-icon hace de icono de la fila, el
     nombre/categoría van en .rich-body/.rich-title/.rich-sub. */
  /* Tareas agrupadas por estado (2026-09-25). */
  .hub-task-groups { display: flex; flex-direction: column; gap: 18px; }
  .hub-task-group-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; font: var(--fw-medium) 10.5px/1 var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-muted); }
  .hub-task-group-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
  .hub-task-group-count { margin-left: auto; color: var(--text-faint); }
  .hub-task-group-items { display: flex; flex-direction: column; gap: 10px; }
  .hub-task-group.is-closed .hub-task-group-items { opacity: .6; }
  .hub-protocol-label { position: relative; display: flex; align-items: center; cursor: pointer; flex: 1; }
  .hub-protocol-checkbox { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
  .hub-protocol-label:has(.hub-protocol-checkbox:checked) .check-icon .icon-off { display: none; }
  .hub-protocol-label:has(.hub-protocol-checkbox:checked) .check-icon .icon-on { display: flex; }

  /* ---------- Card de Sugerencias (FOX-P119/US6) ----------
     Cada sugerencia es su propia tarjeta (no un .rich-row de una línea:
     necesita cabecera + título + problema + evidencia + fila de acciones,
     demasiado contenido para esa receta) — mismo fondo/radio "tallado" que
     .hub-standup-input (--surface-base + --nu-inset-1) para leerse como un
     hueco dentro del panel, igual que el resto de superficies internas de
     esta página. */
  .hub-suggestions-list { display: flex; flex-direction: column; gap: 14px; }
  .hub-suggestion-item { background: var(--surface-base); box-shadow: var(--nu-inset-1); border-radius: var(--r-2xl); padding: 16px 18px; }
  .hub-suggestion-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
  .hub-suggestion-agent { font: var(--fw-semibold) 12px/1 var(--font-mono); text-transform: uppercase; letter-spacing: var(--ls-label); }
  .hub-suggestion-project { font: 400 12px/1 var(--font-core); color: var(--text-muted); }
  /* Nivel de aprobación siempre visible (AC de esta tarea) — 'auto' plano
     como el resto de .hub-badge, 'explicit_required' con el mismo acento de
     atención (naranja) que el resto del sistema usa para "requiere que
     decidas algo" (p.ej. .reader-pending-label en knowledge-detail-renderer.ts). */
  .hub-suggestion-approval.is-explicit { color: var(--signal-orange); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--signal-orange) 45%, transparent); }
  .hub-suggestion-approval { margin-left: auto; }
  .hub-suggestion-subject { margin: 0 0 4px; font: var(--fw-semibold) 14.5px/1.4 var(--font-core); color: var(--text-body); }
  .hub-suggestion-problem { margin: 0 0 6px; font: 400 13px/1.5 var(--font-core); color: var(--text-body); }
  .hub-suggestion-evidence { margin: 0 0 12px; font: 400 12px/1.5 var(--font-core); color: var(--text-muted); font-style: italic; }
  .hub-suggestion-actions { display: flex; justify-content: flex-end; gap: 8px; }

  
    .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; }

  
  .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); }

  
  .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; }

  
  .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; }

  
  .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; }

