/*
 * Bifrost 3 — Design tokens del sistema Bifrost-3 (chore 90.060 · unidad U-1 del
 * estudio docs/archivo/estudio-traspaso-diseno-bifrost3.md).
 *
 * Fuente ÚNICA de los tokens de tema: son los mismos custom properties --bf-*
 * que expone el shell (atraviesan el Shadow DOM). No definir un set paralelo.
 *
 * FUENTE DEL DISEÑO: desde la decisión T20 del 2026-09-15, el COLOR lo fija
 * docs/bifrost-diseno-contrato.md —el sistema de Hugin— y no la maqueta. La chore
 * 90.250 trajo §2.2 (superficies y tinta) y §2.3 (marca, acción, estado y datos), la
 * 90.251 el §2.4 (cristal, sombra y bisel), la 90.252 el §2.5 (tipografía) y la
 * 90.253 el §2.6 (radios, alto de mando, curvas y duraciones). CON ESA ÚLTIMA EL
 * VOCABULARIO ESTÁ COMPLETO: esta hoja ya no es mixta.
 *
 * Desde el 2026-10-07 (chore 90.961) esta hoja es también una de las fuentes del
 * paquete de diseño de la familia: design/tools/extraer.mjs la copia a design/dragsa/
 * (Dragsa.Design, contrato 00.008).
 *
 * Con el vocabulario completo vino su uso: el catálogo de bifrost.css lo viste en
 * las chores 90.254 a 90.261 (mandos, campos, tarjeta, lo que flota, tabla y las
 * piezas propias), el shell en las 90.263 a 90.271 y las páginas C# en la 90.272.
 * Mientras tanto hubo aquí tokens declarados y sin usar —el alto de mando único,
 * las curvas, las duraciones, la escala de radios entera—, puestos antes para que
 * quien vistiera cada pieza no tuviera que inventarse la medida.
 *
 * Antes de eso la fuente fue la maqueta Bifrost-3 (54 pantallas, doble tema),
 * versión validada del 2026-08-14 (chore 90.111), y antes la "paridad 2.x" de la
 * chore 90.014. La maqueta no se ha regenerado: queda como archivo (90.277), y
 * docs/maqueta/README.md dice hasta cuándo mandó y para qué sigue sirviendo.
 *
 * CONVENIOS DEL SISTEMA (M-1…M-13): el catálogo de bifrost.css los aplica pieza a
 * pieza. Su texto vigente está en el libro de marca, design/libro/0-principios.md,
 * que es el que leen los demás productos. El porqué de cada cambio y lo descartado
 * están en el §6 de docs/bifrost-diseno-contrato.md y en el historial de git de
 * docs/bifrost3-convenciones.md, que tuvo la versión larga hasta el 2026-10-20.
 *
 * Doble tema (mecanismo INTACTO desde la chore 90.014):
 *   - Valores "light" por defecto en :root.
 *   - Valores "dark" cuando data-theme="dark" (preferencia explícita del usuario,
 *     que el shell refleja en <html>) o cuando el sistema pide oscuro
 *     (prefers-color-scheme) y no hay override explícito a "light".
 *   Son dos INTERRUPTORES para el mismo juego de valores: CSS no permite unir un
 *   selector de atributo y una media query en una sola regla. Para que no vuelvan
 *   a divergir a mano (deuda D10 del estudio), la suite del Host compara los dos
 *   bloques token a token: si uno cambia y el otro no, los tests fallan.
 *   Al tocar uno, tocar el otro.
 *
 * White-label: los valores de marca por instalación se sobreescriben cargando
 * /appearance.css DESPUÉS de esta hoja (config por capas 01.003: la BD pisa el
 * fichero). Aquí viven el catálogo y los valores por defecto; allí, los overrides.
 * Los nombres de token EXISTENTES se conservan siempre que el concepto siga
 * vivo, para no romper una instalación que ya los sobreescriba; los que el
 * sistema nuevo deja sin uso propio quedan como ALIAS al final de :root.
 */

/* Tipografía (contrato de diseño §2.5 · chore 90.252, que SUSTITUYE a la decisión
   P-2 «Bifrost 3 mantiene Rubik»). La pila pasa a ser
   "Segoe UI", Selawik, Rubik, system-ui, sans-serif, y cada nombre está por un
   motivo distinto:

   SEGOE UI va delante porque en Windows —que es donde vive este parque— está
   ajustada a la rejilla de píxeles mejor que nada que podamos empaquetar. Y no se
   puede empaquetar: es de Microsoft y su licencia no permite redistribuirla. Eso es
   justo lo que no podía quedarse así, porque una máquina que la tenga y otra que no
   maquetaban distinto.

   SELAWIK es la respuesta a eso: Microsoft la publicó bajo SIL OFL 1.1 como
   sustituta MÉTRICAMENTE COMPATIBLE de Segoe UI —mismos anchos, mismas líneas de
   base—, así que las dos máquinas maquetan igual: mismos saltos de línea, mismo alto
   de fila, mismo PDF. Va empaquetada en tres cortes, 400, 600 y 700, y el 600 es lo
   que motivó esta chore: el catálogo lo pide en 47 sitios y no había cara para él,
   así que el navegador lo sintetizaba engordando el 400. El 500 que usan algunas
   piezas cae al 400, que es lo que manda la especificación cuando el peso pedido no
   existe. La licencia VIAJA con los ficheros (fonts/Selawik/LICENSE.txt): es
   condición de la OFL, no una cortesía — y por lo mismo se le ha puesto la suya a
   Rubik (fonts/Rubik/LICENSE.txt), que llevaba desde la 90.083 servida sin ella.

   RUBIK se queda detrás y sólo para lo que Selawik no cubra. Cambia de forma: en vez
   de tres caras completas (132 KB) son dos SUBCONJUNTOS latinos con unicode-range
   —latina y latina extendida, 54 KB—, y son VARIABLES de 300 a 900, así que un solo
   fichero da todos los pesos en lugar de sintetizarlos. Un navegador no descarga una
   fuente cuyos glifos no necesita, de modo que en la práctica ya casi no se pide.
   Que son variables está comprobado, no supuesto: con font-variation-settings el
   ancho del mismo texto cambia de 300 a 900, y en una cara estática no se movería.
   Los idiomas servidos son es y en (Program.cs), así que los dos subconjuntos
   latinos cubren todo lo que hay que pintar.

   FORMATO: sólo .woff2, como decidió la 90.083. Lo entiende todo navegador que
   entienda el resto del sistema (Grid, custom properties, Shadow DOM), así que un
   segundo formato sólo arrastraría peso que nadie pide.

   SE SIRVEN DESDE EL PROPIO HOST: la CSP de 01.006 es default-src 'self' y no admite
   tipografías de red.

   Y YA NO SE PRECARGA NINGUNA, que es un cambio deliberado sobre la 90.083. Entonces
   Rubik era la PRIMERA de la pila y se descargaba siempre, así que adelantarla con un
   preload evitaba ver cambiar la letra en el primer pintado. Ahora la primera es una
   fuente del SISTEMA: en un puesto Windows no se descarga ninguna, y precargar
   Selawik sería pagar 14 KB en cada carga por un caso que aquí es minoría. Quien no
   tenga Segoe UI recibe Selawik con `swap` —texto legible desde el primer pintado,
   nunca invisible— y en métrica compatible, así que lo que cambia es la forma de la
   letra, no la maquetación. Si algún día el parque sin Segoe UI pesa, volver a
   ponerlo es una constante en BifrostPageChrome. */
@font-face {
  font-family: "Selawik";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/Selawik/selawik-400.woff2") format("woff2");
}

@font-face {
  font-family: "Selawik";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/Selawik/selawik-600.woff2") format("woff2");
}

@font-face {
  font-family: "Selawik";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/Selawik/selawik-700.woff2") format("woff2");
}

/* Los dos subconjuntos de Rubik. El unicode-range es lo que hace que el navegador
   sólo pida el que necesite —y que no pida ninguno si Segoe UI o Selawik ya han
   pintado el texto—. El rango de pesos declarado es el del eje del fichero. */
@font-face {
  font-family: "Rubik";
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url("/fonts/Rubik/rubik-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Rubik";
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url("/fonts/Rubik/rubik-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  color-scheme: light dark;

  /* ── Tokens canónicos del contrato 00.004 §3.4 (NOMBRES CONGELADOS) ─────────
     No renombrar: el shell y los productos los consumen por contrato. */
  --bf-accent: #ee7b24;        /* M-1 · el MISMO naranja en los dos temas */
  --bf-sidebar-bg: #2b3640;    /* pizarra · la del panel de acceso; el shell ya no la pinta (M-3) */
  --bf-sidebar-fg: #e7edf2;    /* texto sobre la pizarra */

  /* ── Acento (M-1) ──────────────────────────────────────────────────────────
     Tres piezas distintas: el relleno (--bf-accent), el naranja LEGIBLE para
     texto sobre fondo claro/oscuro (--bf-accent-text) y el texto SOBRE naranja
     (--bf-accent-fg), que es blanco en los dos temas.

     M-1 congela --bf-accent (#ee7b24) y dice que SOBRE el naranja el texto es
     blanco; no dice nada de --bf-accent-text, que es otra pieza: el naranja
     calibrado para LEER, por tema. Ya tomaba dos valores que no se parecen
     —#d96a17 en claro, #f5a061 en oscuro—, y el comentario de la maqueta lo dice
     con todas las letras: «--accent-2 sigue claro para texto naranja sobre fondo
     oscuro». O sea que es un token de legibilidad, no la marca.

     CALIBRADO en la chore 90.240. Valía #d96a17 y en claro medía 3.07:1 contra el
     fondo de página, 3.49:1 contra la tarjeta y 2.96:1 contra el relleno hundido:
     por debajo de 4.5:1 en los tres planos, o sea texto que no cumple AA en
     ninguna parte donde se pinte. En OSCURO el mismo token mide 8.36:1: allí sí
     estaba calibrado, y esa asimetría es la que se corrige. Baja a #a75212 —misma
     familia (H26 S81 %), sólo cambia la luminosidad— y pasa a 4.80:1 / 5.44:1 /
     4.62:1, más 4.82:1 sobre --bf-accent-soft, que es donde se apoya en cinco de
     sus siete usos. La tabla completa, en el bloque de tinta. */
  --bf-accent-text: #994a10;
  --bf-accent-fg: #ffffff;
  --bf-accent-soft: #fcefe3;
  --bf-accent-edge: #f49a5c;
  --bf-cta: #ec9559; /* 90.908: un poquito más naranja (un 15 % hacia el acento; era #ec9a63, y antes #e68a50) */
  --bf-cta-hover: #f1a167; /* 90.908 (era #f2a873) */
  --bf-on-cta: #1a1a1a;
  /* El CTA como CANTO (chore 90.255). El foco de un campo es su canto en el color
     de la acción, y el CTA a secas no llega a 3:1 en claro —2,39 sobre la
     superficie, 2,52 sobre el propio campo—: el canto ENFOCADO se vería menos que
     el de reposo (3,5). Se oscurece hacia la tinta igual que --bf-accent-text
     oscurece el acento para leer: al 30 % da 3,40:1 en el peor plano (el hundido),
     3,56 sobre el control y 4,14 sobre la tarjeta. En oscuro el CTA ya pasa solo
     (3,38 sobre la superficie elevada, que es la peor), y allí vale él mismo. */
  --bf-cta-edge: color-mix(in srgb, var(--bf-cta), var(--bf-surface-fg) 30%);

  /* ── Pizarra (panel de acceso) ────────────────────────────────────────────
     Hasta la chore 90.264 también el lateral del shell; hoy el shell pinta su
     propia lámina en el Shadow DOM y no lee estos tokens (M-3). */
  --bf-sidebar-hover-bg: #39454f; /* superficie secundaria del lateral */
  --bf-sidebar-muted: #93a5b3;    /* texto secundario sobre pizarra */
  --bf-sidebar-muted-2: #aebfcc;  /* texto de apoyo del panel de acceso */
  --bf-sidebar-line: rgba(255, 255, 255, 0.09);

  /* Estado SOBRE la pizarra (chore 90.062): el lateral y el panel de acceso son
     oscuros en los DOS temas, así que el LED usa siempre los semánticos del tema
     oscuro —los del claro no contrastan sobre #2b3640—. Valor único: el bloque
     oscuro no los redefine.
     Desde la chore 90.261 son los colores GRÁFICOS del oscuro del sistema nuevo
     (--bf-success / -warning / -danger de ese bloque, §2.3 del contrato): el LED es
     luz, no letra (S-7). Seguían en los de la maqueta. Sobre la pizarra miden 8,53,
     8,57 y 3,86:1, todos por encima de los 3:1 de un objeto gráfico. Van escritos y
     no con var() porque en tema claro esos tokens valen otra cosa. */
  --bf-sidebar-ok: #68f09d;
  --bf-sidebar-warn: #eed85b;
  --bf-sidebar-bad: #ff5252;

  /* ── La escalera de superficies (contrato de diseño §2.2 · chore 90.250) ─────
     Cinco peldaños, de fuera adentro: LIENZO (--bf-canvas) < MESA (--bf-app-bg) <
     PANEL (--bf-surface-bg, con --bf-card-bg para la tarjeta) < ELEVADA
     (--bf-elevated-bg) < CAMPO (--bf-field-bg). Aparte va el HUNDIDO
     (--bf-panel-bg), que no es un peldaño de la escalera sino un rebaje DENTRO de
     una superficie, y el desplegable opaco (--bf-control-bg), que existe porque un
     menú que flota sobre contenido no puede ser translúcido.

     TRES COSAS CAMBIAN DE NATURALEZA, y conviene saberlo antes de leer un valor:
     el hundido y el campo pasan a ser VELOS —rgba— en vez de rellenos opacos, así
     que se recomponen sobre lo que tengan detrás; y la tarjeta deja de separarse
     del panel por relleno.

     POR ESO EL CLARO SE VE MÁS PLANO QUE ANTES, y no es un fallo de esta chore:
     medida la escalera peldaño a peldaño, mesa ↔ panel da 1.01:1 y panel ↔
     tarjeta 1.08:1 (antes página ↔ tarjeta medía 1.13:1). En este sistema el borde
     de una pieza NO lo dibuja el relleno: lo dibujan el filete, el bisel y el
     cristal, y eso llegó con las chores 90.251 (cristal, sombra y bisel) y 90.257
     (el velo del 54 % de la tarjeta, que la dibuja por su canto). Sin ellos el
     catálogo se veía correcto pero soso en claro —en oscuro no, porque allí la escalera sí tiene saltos:
     1.09 / 1.22 / 1.00 / 1.26—. Subir el contraste de los rellenos aquí para
     compensar sería pelearse con el paso siguiente.

     La auditoría de planos de la chore 90.214 —que calibró página, tarjeta, botón y
     filete del sistema anterior— queda SUPERADA: medía contra #eef1f4 / #ffffff /
     #e8edf1, planos que ya no existen. No se conserva su tabla para no dejar
     números que hoy son mentira; la decisión que la motivó sigue viva en el acta de
     la chore. */
  /* EL CLARO DE LA PUERTA, EN TODA LA APLICACIÓN (chore 90.611). El claro de la casa era
     un gris plano y frío; el del inicio de sesión, papel cálido con luz de día. La petición
     fue que todo el claro se pareciera al segundo, y se hace AQUÍ, por la escalera, para que
     alcance a cada pantalla sin tocar una regla: el lienzo y la mesa pasan al papel de la
     puerta (--bf-door-stage), el panel a un blanco roto cálido y la tarjeta a un blanco con
     un punto de calor. La luz que entra arriba a la izquierda es --bf-view-glow (más abajo)
     y el cristal de la tarjeta, --bf-card-glass-*. Las tintas de texto NO se tocan: sobre el
     papel #f0e8df siguen pasando AA —muted 8.9:1, accent-text 5.4:1, el peor de estado
     (warning-text) 5.9:1— y sobre el lienzo #e7ddd1, el peor de todos, accent-text da 4.9:1. */
  /* MÁS DISCRETO (chore 90.640): el papel de la puerta en toda la vista se notaba demasiado.
     El fondo pasa a ser casi blanco con un SUSURRO del acento —el naranja base, no un beige
     propio—, así que sigue a la marca de cada instalación. Mesa al 4 %, lienzo al 7 % sobre un
     gris claro y panel al 2 %; la tarjeta se sigue separando por su filete y su cristal. Las
     tintas mejoran respecto a la 90.611: el lienzo es MÁS claro que #e7ddd1, así que el peor
     caso (accent-text sobre el lienzo) sube de 4.9 a unos 5.4:1. */
  /* BLANCO ROTO NEUTRO desde la chore 90.783: el usuario no quería que la vista partiera de un fondo «naranjita»; el
     naranja va sólo en el trazo de la imagen de fondo. Sigue un punto por debajo de las tarjetas para que se despeguen. */
  --bf-canvas: #e9ecf0; /* PORCELANA (2026-10-16): el suelo frío del tema claro, un punto hondo para que las piezas blancas se despeguen */
  --bf-app-bg: #eaedf1;
  --bf-surface-bg: #eceff3;
  /* EL FONDO DE LOS DESPLEGABLES (chore 90.746): la base del cristal de lo que flota —la lista de un <select>, el
     menú «···», el elegidor, el calendario— sin su alfa. Claro: el blanco al 78 % sobre el lienzo (#fcfaf9); oscuro:
     el lienzo pelado (#0c1014), porque allí --bf-glass-white vale 0 %. La tarjeta y el centro de control toman este
     color de fondo (el usuario los quería del color de los desplegables); la tarjeta conserva su velo y su desenfoque.
     Una sola expresión para los dos temas: se resuelve en :root con los valores del tema que haya. */
  --bf-drop-base: color-mix(in srgb, #ffffff var(--bf-glass-white), var(--bf-canvas));
  --bf-card-bg: #ffffff; /* Porcelana: la tarjeta es blanca, con su velo al 60 % (abajo) */
  /* LOS MODALES (chores 90.746 y 90.732): cristal y no lámina maciza. En claro, el de los desplegables; en oscuro,
     EXACTAMENTE el de las ventanas del modo sencillo del editor de paneles de Hugin (.editor-pop / .editor-add:
     --editor-chrome-color = #171B1E al 93 % con negro = #15191C, al 88 %), redeclarado en los bloques oscuros.
     --bf-modal-solid es lo macizo de las tres vías de «sin cristal». */
  --bf-modal-tint: var(--bf-glass-color);
  /* El tinte va aparte para que las tres vías de «sin cristal» (al final, con :root a secas) puedan reapuntar
     --bf-modal-bg sin que el bloque oscuro, más específico, se lo vuelva a quitar. */
  --bf-modal-bg: var(--bf-modal-tint);
  --bf-modal-solid: var(--bf-card-bg);
  --bf-modal-line: var(--bf-edge-line); /* 90.760: el filete del buscador global (lo lee el modal del shell) */
  --bf-panel-bg: rgba(29, 48, 64, 0.05);
  --bf-elevated-bg: #ffffff;
  --bf-control-bg: #eceff2;   /* del acento, chore 90.640 */
  /* El realce del control OPACO (chore 90.255): el desplegable no puede apilar el
     velo del hover como el botón —su background-image es la flecha—, así que
     sube de plano: al blanco en claro, un peldaño por encima en oscuro. */
  --bf-control-hover-bg: #ffffff;
  /* La flecha PROPIA del desplegable, un galón fino en la tinta apagada del tema
     (--bf-muted). Va en un data: y no en currentColor porque es un fondo; por eso
     se declara en cada tema. La del navegador es lo que más delata un tema oscuro
     a medias. */
  --bf-select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233E3936' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  /* Chore 90.904: los carets arriba/abajo de los campos numéricos, en la misma tinta que la flecha del desplegable. */
  --bf-number-carets: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233E3936' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.5 9.5 12 5l4.5 4.5M7.5 14.5 12 19l4.5-4.5'/%3E%3C/svg%3E");
  --bf-surface-fg: #1d3040;
  /* El filete de superficies pasa a rgba (contrato §2.2) para recomponerse sobre
     el plano que le toque, igual que ya hacían el del campo y el del botón. Medido
     al 0.16 compone entre #c9ced1 y #dde0e3 según el plano y da 1.31-1.32:1 contra
     él: es un SEPARADOR, no el límite de un control, así que el 3:1 de SC 1.4.11 no
     le aplica —el que sí lo carga es --bf-field-line, aquí debajo—. En oscuro, al
     mismo 0.16 sobre tintas claras, mide 1.35-1.39:1. */
  /* Filetes con la tinta CÁLIDA del cristal de la puerta (chore 90.611): sobre papel un
     filete pizarra se leía azulado. Misma fuerza, mismo papel de separador. */
  --bf-border: rgba(29, 48, 64, 0.17);  /* filete de superficies */
  --bf-border-soft: #e4e8ec;   /* filete interior (filas de tabla, listas) */
  /* El separador DE DENTRO de la tarjeta (M-5): cabecera y pie de una tarjeta,
     cabecera de tabla, filas de una lista. Hasta la chore 90.146 este trabajo lo
     hacía --bf-field-line, que ADEMÁS es el filete de los controles; al subir ese
     a los 3:1 que exige SC 1.4.11, los separadores se habrían vuelto grises
     medios por toda la aplicación. Así que se partió en dos: el control sube y el
     separador se quedó donde estaba. Escala: soft < border < strong.
     Va en rgba —ya lo era— para recomponerse sobre la superficie que le toque.

     La 90.214 sí lo mueve, y por el mismo motivo que al filete de arriba: al 0.25
     componía #c6cdd3 sobre la tarjeta blanca y medía 1.65:1, contra los 2.13:1 que
     da el mismo separador en oscuro. Sube a 0.34 —compone #b3b7bb, 2.02:1— y la
     escala se mantiene: soft 1.16 < border 1.50 < strong 2.02 contra la tarjeta. */
  --bf-border-strong: rgba(29, 48, 64, 0.34);

  /* ── Tinta (contrato §2.2 · chore 90.250) ─────────────────────────────
     DOS peldaños, no tres: --bf-surface-fg (texto) y --bf-muted (secundario).
     --bf-dim pasa a ALIAS de --bf-muted porque el sistema nuevo no tiene un tercer
     gris. Sus 64 usos siguen funcionando, sólo que ahora pintan el secundario; el
     nombre se conserva por la regla de §2.1 (los nombres publicados no se borran).

     MEDIDO sobre los planos NUEVOS (chore 90.250), peor caso de cada token contra
     lienzo, mesa, panel, tarjeta, hundido, campo y desplegable:

       CLARO           valor     peor    ¿AA 4.5?
       surface-fg     #1d3040    9.88      sí
       muted / dim    #383d51    7.83      sí
       accent-text    #994a10    4.58      sí
       success-text   #096429    5.34      sí
       warning-text   #794a05    5.48      sí
       danger-text    #a12323    5.50      sí
       info-text      #1f52a5    5.45      sí

       OSCURO          valor     peor    ¿AA 4.5?
       surface-fg     #fefefe   11.22      sí
       muted / dim    #b4bdc7    5.95      sí
       accent-text    #f5a061    5.43      sí
       success-text   #68f09d    7.84      sí
       warning-text   #eed85b    7.87      sí
       danger-text    #ff9b9b    5.61      sí
       info-text      #96b7eb    5.53      sí

     --bf-accent-text es el ÚNICO que esta chore recalibra, y es deuda propia, no de
     Hugin: la 90.240 lo dejó en #a75212 midiendo 4.62:1 contra el peor plano de
     ENTONCES, y contra los planos nuevos se queda en 4.48:1 —el hundido nuevo es un
     pelo más oscuro—. Baja a #994a10, misma familia (H25° S81 %, sólo cambia la
     luminosidad), y pasa a 4.58:1 incluso contra el LIENZO, que es el plano más
     hostil de los siete y el único que no existía antes. El criterio es el de la
     90.136/90.146/90.240: el PEOR caso medido, no el más favorable.

     Lo que NO se toca: --bf-accent (#ee7b24) es M-1. El contrato lo remapea al
     #EE7A24 de Hugin, que difiere en UNA unidad de rojo; no se mueve porque ese
     token es el único white-label (§2.1) y cambiarlo por un valor indistinguible
     sólo serviría para invalidar la maqueta, los tests y las instalaciones que ya
     lo sobreescriben. Las piezas oscuras en tema claro —lateral, panel de acceso,
     toast— siguen teniendo su propia tinta (--bf-sidebar-*) y ninguna de estas
     variables cae sobre ellas. */
  /* Cálida desde la chore 90.611: la pizarra azulada se leía fría sobre el papel de la puerta,
     y es también el TONO neutro de las piezas (la vista de la tarjeta de instancia). Medida: 8.9:1
     sobre la mesa y 8.1:1 sobre el lienzo, el peor plano. */
  --bf-muted: #45505b;
  --bf-dim: var(--bf-muted);

  /* ── El campo, ahora un VELO (contrato §2.2 · chore 90.250) ────────────────
     M-6 decía que el campo es un POZO calibrado por tema: blanco en claro, hundido
     por debajo de su tarjeta en oscuro. En el sistema nuevo es un VELO sobre lo que
     haya detrás —blanco al 66 % en claro, gris claro al 7 % en oscuro—, así que ya
     no hay dos calibrados que mantener en paralelo: hay uno, y se recompone solo.
     El convenio M-6 lo reescribió la chore 90.274; aquí sólo cambian los valores.

     M-12 SIGUE MANDANDO y es lo que obliga a medir: a un control WCAG 2.1
     SC 1.4.11 le pide 3:1 en el límite. --bf-field-line sube al 0.62 en claro y
     compone entre #72808a y #7a8892 según el plano: 3.52 (mesa), 3.53 (panel),
     3.64 (tarjeta y campo), 3.42 (desplegable) y 3.36:1 (hundido), que es el peor.
     En oscuro va al 0.64 y da entre 3.10 y 4.10:1, con el peor sobre la superficie
     ELEVADA. Los dos temas pasan en los siete planos. */
  --bf-field-bg: rgba(29, 48, 64, 0.045); /* Porcelana: hundido, un velo de la tinta sobre la tarjeta */
  --bf-field-line: rgba(41, 63, 79, 0.62);

  /* ── El pomo del interruptor ENCENDIDO (chore 90.159) ──────────────────────
     Con el interruptor encendido, lo único que dice en qué posición está es el
     pomo — y en claro apenas se separaba de la pista: relleno blanco sobre el
     naranja, 2.80:1, con su filete de campo en 1.34:1, o sea desaparecido. Es
     el mismo 2.80:1 del naranja de marca que ya salió en la 90.136 y en la
     90.147, pero aquí NO hace falta tocarlo: basta con que el pomo traiga su
     propio filete sobre el naranja.
     Sube a 0.88 y compone #3a454e sobre el blanco del pomo: 3.49:1 contra la
     pista encendida, con margen sobre los 3:1 que SC 1.4.11 pide a lo que hace
     falta para identificar el ESTADO. Sólo se aplica al pomo ENCENDIDO: el
     apagado se queda con --bf-field-line como lo dejó la 90.146 (2.93:1). */
  --bf-switch-knob-line-on: rgba(31, 44, 54, 0.88);
  /* El interruptor de Hugin (rediseño general): carril sin filete y pulgar oscuro, en los dos temas. */
  --bf-switch-track: #fefefe;
  --bf-switch-thumb: rgba(41, 45, 51, 0.78);

  /* ── El botón de acción ────────────────────────────────────────────
     Escala propia del botón, de las chores 90.136 y 90.214. SUS RATIOS ESTÁN
     MEDIDOS CONTRA LOS PLANOS ANTERIORES y por tanto ya no valen: se conservan los
     valores —para que el catálogo siga pintando— pero no sus números. Quien los
     rehace es la chore 90.254, que trae el mando de Hugin (1,7 rem, --bf-cta y el
     estado ocupado) y con él el calibrado nuevo. La regla que sí sobrevive: quien
     carga el 3:1 de SC 1.4.11 es el FILETE, no el relleno, y va en rgba para
     recomponerse sobre el plano que le toque. */
  /* EL MANDO YA NO TIENE RELLENO PROPIO (chore 90.254): es el VELO del campo, que
     es lo que pone a botón, campo y desplegable en la misma familia. En una fila con
     un buscador y un botón los dos son lo mismo —algo que se pulsa o donde se
     escribe— y hasta ahora se pintaban con dos grises distintos. El filete es el del
     control, que ya está calibrado a los 3:1 de SC 1.4.11 (M-12).
     Los dos nombres de estado que había aquí (--bf-button-hover-bg y
     --bf-button-press-bg) quedan SIN USO: sobre un velo, el realce no es otro color
     sino OTRO VELO encima, y eso lo apila la pieza en bifrost.css. Se conservan por
     la regla §2.1 y se les deja el valor de siempre por si alguna instalación los
     está sobreescribiendo. */
  --bf-button-bg: var(--bf-field-bg);
  --bf-button-bg-legacy: #dfe6ed;            /* el relleno opaco hasta la 90.254 */
  --bf-button-fg: var(--bf-surface-fg);
  --bf-button-line: var(--bf-field-line);    /* M-12: el filete del control, a 3:1 */
  --bf-button-hover-bg: #d3dce5;
  --bf-button-press-bg: #c7d2dd;

  /* ── Semánticos: el estado se parte en DOS (contrato §2.3 · chore 90.250) ─────
     Nunca son acento: el naranja es acción, esto es estado (M-2).

     Hasta aquí cada estado era UN token que hacía dos trabajos incompatibles —ser
     el relleno de una luz y ser texto legible—, y ganaba siempre el segundo: en
     claro --bf-success valía #1f7a4d, un verde oscuro que como LED se ve apagado.
     Ahora son dos: --bf-*  es el RELLENO (luz, barra, punto, medidor) y
     --bf-*-text el TEXTO. En oscuro los dos valores coinciden en success y warning
     porque allí el mismo tono sirve para las dos cosas.

     MEDIDO (chore 90.250). Como texto, los cuatro -text pasan AA de sobra: peor
     caso 5.34:1 en claro y 5.53:1 en oscuro (tabla completa en el bloque de tinta).
     Como relleno, SC 1.4.11 pide 3:1 al indicador y hay DOS celdas que no llegan,
     las dos heredadas del sistema de Hugin y anotadas aquí a propósito para que no
     se descubran dos veces:

       claro   success #0e9f42 → 3.18 mesa · 3.21 panel · 3.47 tarjeta · 2.85 hundido
       oscuro  info    #3373d9 → 3.47 mesa · 2.83 panel · 2.83 tarjeta · 3.58 hundido

     No se corrigen aquí, y el motivo es que M-2 ya los cubre: el color NUNCA va
     solo —LED más texto—, así que ninguna de las dos celdas es el único portador de
     la información. Apartarse del contrato de diseño por 0,15-0,17 de ratio en un
     indicador que siempre lleva rótulo sería peor negocio que anotarlo. Si alguna
     pieza acaba usándolos SIN texto, ahí sí hay que subirlos.

     El naranja como relleno mide 2.32-2.82:1, que es justo lo que dice el §2.3 del
     contrato y por lo que la acción primaria deja de llevar texto blanco: eso lo
     arregla la chore 90.254 con --bf-cta / --bf-on-cta, ya declarados arriba. */
  --bf-success: #0e9f42;
  --bf-success-text: #096429;
  --bf-warning: #a16207;
  --bf-warning-text: #794a05;
  /* El destructivo MACIZO, sólo para la confirmación (S-6): lo que borra va tintado
     mientras pide la orden, y sólo cuando ya estás confirmando se pinta lleno.
     En claro el rojo baja un 6 % hacia negro, y no es gusto: #d64545 con letra blanca
     da 4,38:1 y suspende AA por poco; al 6 % compone #c94141 y sube a 4,87:1. En
     oscuro el rojo es claro y quien cambia es la tinta, que pasa a oscura (5,45:1).
     Por eso son dos tokens y no uno: el mismo relleno no puede servir a las dos. */
  --bf-danger-solid: color-mix(in srgb, var(--bf-danger), #000 6%);
  --bf-on-danger: #ffffff;
  --bf-danger: #d64545;
  --bf-danger-text: #a12323;
  /* El tono «info» es NEUTRO, no azul (chore 90.648): el usuario no quiere ningún
     mensaje azul en la plataforma. Un aviso informativo se distingue del error (rojo)
     y del aviso (ámbar) precisamente por no llevar color: tinta apagada y superficie.
     Los dos tokens siguen existiendo para que todo .info los herede sin regla aparte. */
  --bf-info: var(--bf-muted);
  --bf-info-text: var(--bf-surface-fg);
  --bf-off: #7b8794;

  /* Los rellenos suaves se CALCULAN sobre su propio estado, al 12 % (el contrato
     da 9-15 %). Salen translúcidos a propósito: el mismo chip se recompone sobre
     la tarjeta, sobre el hundido o sobre el fondo sin declarar tres valores. Como
     dependen de var(--bf-*), el tema oscuro NO los redeclara: cambian solos. */
  --bf-success-soft: color-mix(in srgb, var(--bf-success) 12%, transparent);
  --bf-warning-soft: color-mix(in srgb, var(--bf-warning) 12%, transparent);
  --bf-danger-soft: color-mix(in srgb, var(--bf-danger) 12%, transparent);
  --bf-info-soft: color-mix(in srgb, var(--bf-info) 12%, transparent);

  /* Serie de datos (contrato §2.3): tres tintas validadas para distinguir series
     entre sí, no para pintar estado. Todavía sin uso en el catálogo. */
  --bf-data-1: #2a78d6;
  --bf-data-2: #c25716;
  --bf-data-3: #2e8b3d;
  /* LOS TONOS DE PRODUCTO: la paleta de iniciales de Productos (AdminPieces.ColorFor), que el Inicio elige por
     `data-hue` para la baldosa de una instancia y la carta de licencia. Igual en los dos temas, como era en hex. La
     copia en C# (AdminPieceTagHelpers) sigue allí para las pantallas que pintan `style=`. */
  --bf-hue-0: #8a6a4b;
  --bf-hue-1: #4b6d8a;
  --bf-hue-2: #6a8a4b;
  --bf-hue-3: #8a4b6d;
  --bf-hue-4: #4b8a86;
  --bf-hue-5: #5c6470;
  --bf-hue-6: #7a5c8a;
  --bf-hue-7: #8a7a4b;
  --bf-hue-8: #4b7a8a;
  /* SU LETRA (chore 90.261, S-7): el color de dato es GRÁFICO —3:1 para un trazo o
     una barra— y como texto sobre su propio tinte al 15 % se queda en 3,15-3,61:1.
     La primera pieza que los escribe, la celda de la matriz de acceso, necesita
     letra de 4,5:1, y se saca como la tinta de lo seleccionado: el mismo color, un
     35 % hacia la tinta del texto. Medido sobre el tinte al 15 % encima del velo de
     la tarjeta: 5,15-5,47:1 en claro y 5,23-5,57:1 en oscuro. Con var(): siguen al
     tema sin repetirse en los bloques oscuros. */
  --bf-data-1-text: color-mix(in srgb, var(--bf-data-1), var(--bf-surface-fg) 35%);
  --bf-data-2-text: color-mix(in srgb, var(--bf-data-2), var(--bf-surface-fg) 35%);
  --bf-data-3-text: color-mix(in srgb, var(--bf-data-3), var(--bf-surface-fg) 35%);

  /* ── Realce e interacción (los consume el shell dentro de su Shadow DOM) ──── */
  --bf-hover: rgba(29, 48, 64, 0.07);
  --bf-selected: rgba(255, 255, 255, 0.7);
  /* La TINTA de lo seleccionado (chore 90.256): la de marca, pero la legible
     (S-7: la letra pide 4,5:1 y el naranja gráfico da 2,8:1 sobre este velo).
     En claro basta --bf-accent-text; el oscuro la sube (ver allí). */
  --bf-selected-ink: var(--bf-accent-text);

  /* Bisel (contrato §2.4): las dos tintas con las que una pieza finge relieve —la
     luz arriba, la sombra abajo—. Las declara este bloque porque son color; quien
     las usa es el cristal, aquí debajo. */
  --bf-bevel-hi: rgba(255, 255, 255, 0.85);
  --bf-bevel-lo: rgba(29, 48, 64, 0.12);

  /* ── Cristal y sombra (contrato de diseño §2.4 · chore 90.251) ──────────────
     HASTA AQUÍ el sistema no sabía pintar cristal: M-4 decía «sin sombras» y
     --bf-shadow valía none, así que nada podía flotar. Este bloque trae el
     vocabulario entero —velo, desenfoque, canto, bisel y las tres sombras—, y con
     él M-4 queda como «sin sombras en las SUPERFICIES». Quién lleva cuál:
       · lo que FLOTA (menú, aviso, buscador, marco) → cristal + --bf-glass-drop
       · lo que se ARRASTRA (una tarjeta cogida)     → --bf-elevated-shadow
       · el DIÁLOGO, que es macizo y no cristal      → --bf-shadow
     La redacción del convenio (M-4) la firmó la chore 90.274; aquí van los tokens.
     Las piezas llegan después: lo que flota en la 90.258 y el marco del shell en la
     90.264 (la tarjeta, 90.257, no es cristal: es un velo con su sombra propia, más
     abajo). Los gasta UNA regla, la receta .bf-glass del catálogo (bifrost.css §5),
     y desde la 90.258 la heredan por su lista de selectores el menú «···», el aviso
     emergente y la pista; el diálogo se queda macizo con --bf-shadow.

     EL VELO, EL CANTO Y EL BISEL se escriben con var() a propósito: una custom
     property se sustituye donde se USA, no donde se declara, así que siguen al tema
     sin repetirse en los dos bloques oscuros. Lo que sí se repite abajo es lo que no
     puede calcularse: el canto de lo elevado y las tres sombras. */
  --bf-elevated-edge: rgba(29, 48, 64, 0.22);
  /* EN CLARO, MÁS BLANCO (chore 90.657): lo flotante translúcido (desplegables, menús, popovers, cajones) tiraba al
     papel cálido; ahora el velo es casi blanco y más opaco sobre el desenfoque. El oscuro vuelve a 0 % de blanco y
     74 % de velo, como estaba. */
  --bf-glass-white: 78%;
  --bf-glass-alpha: 86%;
  /* LO FLOTANTE, IDÉNTICO A LA CABECERA AL RODAR (chore 90.757, a petición del usuario): desplegables, menús y
     popovers llevan el velo y el filtro de la cabecera del shell (--bf-head-*), no los suyos. Las tarjetas y los
     modales conservan el cristal de la casa (22 px, 1,55) por sus propios tokens. */
  --bf-glass-color: var(--bf-head-glass);
  /* El canto del cristal, al 15 % del de lo elevado: sobre una superficie
     translúcida un filete a plena fuerza dibuja una CAJA, y lo que tiene que separar
     la pieza de lo que hay detrás es el desenfoque. Va como aro interior
     (inset 0 0 0 1px) donde haga falta; la receta base no lo gasta. */
  --bf-glass-edge: transparent; /* 90.760: el canto lo pone --bf-lg-edge */
  /* El canto por el que entra la LUZ, que no es una línea: claro arriba a la
     izquierda, apagado abajo a la derecha. Es lo que hace que una pieza translúcida
     se lea como vidrio y no como un rectángulo con transparencia. Las dos tintas van
     por token porque en oscuro las dos son luz y en claro la de abajo tiene que ser
     SOMBRA: sobre casi blanco, un canto blanco no existe. El medio píxel de
     extensión negativa deja el trazo en uno escaso, más fino que el borde de 1 px al
     que sustituye. */
  /* EL CANTO DEL CRISTAL (chores 90.709 y 90.710): el del centro de control para todo el cristal de la app
     —flotante (--bf-glass-bevel), tarjeta (--bf-card-glass-bevel) y General—. Fino y disimulado a propósito
     (el usuario lo pidió así en la 90.710): un brillo de un píxel escaso arriba a la izquierda, por donde
     entra la luz de la vista, y una sombra igual de tenue abajo a la derecha. */
  /* EL CANTO DE TODO EL CRISTAL, IDÉNTICO AL DEL BUSCADOR GLOBAL Y SUS FILTROS (chore 90.760, a petición del usuario):
     un filete de 1 px de --bf-edge-line (el --bf-chrome-line del shell), sin bisel ni luces. Va por dentro (inset)
     para que valga igual en las piezas sin borde propio; las que lo tienen lo dejan transparente (--bf-lg-line,
     --bf-glass-edge). Antes, el bisel de dos tonos. */
  --bf-lg-edge: inset 0 0 0 1px var(--bf-edge-line);
  --bf-glass-bevel: var(--bf-lg-edge);
  /* LOS CANTOS DEL CRISTAL (chore 90.741): arriba y abajo, un tono más claro que el fondo de la tarjeta; a los lados,
     uno más oscuro. Se derivan de `--bf-card-bg`, así que siguen al tema sin redeclararlos; sólo cambia cuánto. */
  /* Translúcidos (chore 90.758): una luz blanca y una sombra negra con su transparencia, no tintas macizas, para que
     el canto deje ver lo de detrás como el resto del cristal. */
  --bf-edge-hi: rgba(255, 255, 255, var(--bf-edge-hi-a, 0.55));
  --bf-edge-lo: rgba(0, 0, 0, var(--bf-edge-lo-a, 0.15));
  /* El filete de base, el del buscador global y sus filtros (chore 90.744: el canto es la MEZCLA de los dos). */
  --bf-edge-line: rgba(29, 48, 64, 0.16);
  /* EL CANTO DEL BUSCADOR GLOBAL, para lo que flota (petición del usuario, 2026-10-06): un anillo de 1 px que se
     enciende arriba a la izquierda, se apaga por los lados y recoge un reflejo abajo a la derecha. Mismos tonos que el
     buscador del shell (--mg-edge-*). Lo pinta bifrost.css con background-clip: border-area. */
  --bf-rim-hi: rgba(29, 48, 64, 0.2);
  --bf-rim-mid: rgba(29, 48, 64, 0.08);
  --bf-rim-lo: rgba(29, 48, 64, 0.12);
  --bf-glass-rim: linear-gradient(135deg, var(--bf-rim-hi) 0%, var(--bf-rim-mid) 18%, transparent 42%, transparent 64%,
    var(--bf-rim-lo) 86%, var(--bf-rim-mid) 100%);

  /* MÉTRICA del cristal (M-7: común a los dos temas, como el resto de la métrica).
     22 px de desenfoque y no más: su coste sube con el radio, y estas superficies se
     recalculan en CADA fotograma del desplazamiento porque lo que tienen detrás se
     mueve. El velo del 74 % ya tapa lo de abajo, así que subirlo no se ve y sí se
     paga. El radio de 1.1rem es el del contrato y convive con los --bf-radius-* en
     píxeles hasta que la chore 90.253 pase la métrica a rem. */
  --bf-glass-blur: 22px;
  --bf-glass-saturate: 1.55;
  --bf-glass-radius: 1.1rem;

  /* LA RECETA, en dos tokens. Una pieza de cristal pinta
     background: var(--bf-glass-surface) y backdrop-filter: var(--bf-glass-filter),
     y no escribe nada más. Quien apaga la transparencia reapunta estos DOS, una sola
     vez, al final de esta hoja: así ninguna pieza repite la consulta @supports —que
     es justo como se olvida en la siguiente— y el atributo reduce="transparency" del
     contrato 00.004 §5.1 entra por el mismo sitio. */
  --bf-glass-surface: var(--bf-glass-color);
  --bf-glass-filter: var(--bf-head-filter);
  /* El filtro del modal (chore 90.732): el del cristal de la casa, 22 px y 1,55, que es el de .editor-pop de Hugin. */
  /* Desde la 90.769, el velo y el filtro de la CABECERA AL RODAR, como todo lo flotante (90.757). */
  --bf-modal-filter: var(--bf-head-filter);
  /* EL CRISTAL DE LA CABECERA, para el aviso emergente (chore 90.713: «los toast, con la misma transparencia y
     desenfoque que el header»). Copia EXACTA del velo y el filtro de la cabecera del shell (--bf-chrome-glass y
     --bf-chrome-filter en shell.ts, que no cruzan el Shadow DOM hacia fuera): si cambian allí, cámbialos aquí.
     El oscuro sólo redeclara el velo; las tres vías de «sin cristal», al final, lo hacen macizo. */
  --bf-head-glass: var(--bf-pane-glass); /* 90.911: el material de las tarjetas (era blanco al 55 %, 90.784) */
  --bf-head-filter: blur(20px) saturate(1.4);

  /* LA TINTA DE MARCA SOBRE CRISTAL (chore 90.258). Una letra naranja encima de un
     cristal no se mide contra UN fondo: se mide contra lo que el cristal deja pasar,
     y el rótulo del hito del visor de reproducción va encima de la pantalla de OTRO
     producto, que puede ser clara u oscura con independencia del tema de Bifrost. El
     naranja legible a secas (--bf-accent-text) aguanta sobre la hoja propia y se cae
     en cuanto detrás hay lo contrario. Se acerca un 35 % a la tinta del texto —la
     misma receta que --bf-selected-ink en oscuro— y sigue leyéndose naranja. Medido
     sobre el cristal, con fondo liso detrás (clave de 11,5 px, pide 4,5:1):
                      detrás blanco   gris 50 %   negro    la mesa
       claro   antes      4.99          3.60       2.49      4.89
               ahora      6.83          4.93       3.41      6.70
       oscuro  antes      3.55          5.93       8.88      8.16
               ahora      4.60          7.69      11.51     10.58
     Lo que no llega es una página NEGRA detrás del cristal CLARO: ahí tampoco llega el
     gris apagado (4.26) y la tinta del texto se queda en 5.37. Es el límite del velo
     del 74 % del contrato, no de esta tinta. Se declara UNA vez: con var() sigue al
     tema sin repetirse en los bloques oscuros. */
  --bf-glass-accent-ink: color-mix(in srgb, var(--bf-accent-text), var(--bf-surface-fg) 35%);

  /* LAS TRES SOMBRAS, por tema. La del cristal va corta y floja a propósito: un
     cristal se sostiene con el desenfoque y el bisel, y la sombra sólo tiene que
     despegarlo —la de lo elevado, debajo de un menú, dibuja una mancha oscura que se
     ve antes que la lista—. La de lo elevado es la de lo que se coge con el ratón y
     pide sentir el peso. La del diálogo es la más corta de las tres: una pieza en el
     centro de la pantalla, con un velo detrás, ya está separada de todo. */
  /* En claro, sombras de la tinta cálida de la puerta (chore 90.611). */
  --bf-glass-drop: 0 10px 26px rgba(29, 48, 64, 0.12);
  /* LA PISTA (chore 90.973): la del carril del shell, con sus valores. Más translúcida que el cristal de lo que flota
     —un velo del 34 % y lo que sostiene la letra es el desenfoque de detrás, con un punto de luz por tema—, la tinta
     del cromo, su caída larga y su bisel. Sin cristal, maciza (las tres vías del final). */
  --bf-tip-glass: rgba(255, 252, 247, 0.34);
  --bf-tip-solid: var(--bf-card-bg);
  --bf-tip-filter: blur(24px) saturate(1.8) brightness(1.08);
  --bf-tip-fg: #1d3040;
  --bf-tip-drop: 0 18px 45px rgba(29, 48, 64, 0.18);
  --bf-tip-bevel: inset 1.5px 1.5px 0 -0.5px rgba(255, 255, 255, 0.85), inset -1.5px -1.5px 0 -0.5px rgba(29, 48, 64, 0.13);
  --bf-elevated-shadow: 0 18px 45px rgba(29, 48, 64, 0.18);
  --bf-shadow: 0 1px 2px rgba(29, 48, 64, 0.05), 0 4px 12px -2px rgba(29, 48, 64, 0.1);
  /* La tinta de las sombras de las cartas del Inicio: el `--shadow-ink` de Hugin (contrato de diseño, punto 9). Negra
     en los dos temas, que es lo que hacían los literales; por eso el oscuro no la redeclara. */
  --bf-shadow-ink: #000000;
  /* EL RELIEVE de las cartas del Inicio y de las tarjetas de Productos e Instancias (la receta de Vor que porta
     Hugin): el suelo es la tinta de la sombra que proyecta la carta y la luz, la del reflejo, el halo y el canto.
     En claro el suelo es el azul tinta de Hugin; en oscuro, negro (lo redeclaran los dos bloques del oscuro). */
  --bf-relief-floor: #0b1620;
  --bf-relief-light: #ffffff;
  /* La misma luz con el punto cálido del papel, solo en claro: la de las cartas del Inicio y las tarjetas de
     Productos e Instancias, y su canto, que llevan también las cifras del Inicio. En oscuro esas reglas vuelven a
     --bf-relief-light, así que el oscuro no los redeclara. */
  --bf-relief-light-warm: #fffcf8;
  --bf-relief-rim-warm: #fff8f0;
  /* Las dos tintas de los cantos, los realces y las caídas que bifrost.css escribía con su alfa: la cálida (rgb 72 48
     26) es la de --bf-hover y --bf-edge-line en claro, y la fría (rgb 174 182 191), la de --bf-border en oscuro. Cada
     regla ya va por tema y las toma con color-mix y su alfa, así que valen lo mismo en los dos temas. */
  --bf-tint-warm: #48301a;
  --bf-tint-cool: #aeb6bf;
  /* El `--panel-color` de Hugin (manage.css): el fondo de las tablas `.bf-htable` (chore 90.781). Lo redeclara el
     oscuro. */
  --bf-panel-color: #f6f6f6;
  /* El realce de fila de `.bf-htable` en oscuro: un gris neutro, un punto cálido (chore 90.731). */
  --bf-row-hover-warm: #34322f;
  /* El destello cálido del canto de la baldosa encendida y del carril de los modales. */
  --bf-glint-warm: #ffd9b0;
  /* El gris que templa la luz de la mesa del Inicio. Lo redeclara el oscuro. */
  --bf-lit-tint: #a99c90;
  /* El pie del degradado del pulgar del interruptor (chore 90.839). */
  --bf-knob-low: #f6f7f8;

  /* ── La tarjeta es un VELO (contrato §3 · S-3 · chore 90.257) ───────────────
     La tarjeta deja de ser una superficie maciza: es --bf-card-bg al 54 %, y lo que
     dice que hay una tarjeta es el CANTO (--bf-border) y el radio, no el relleno.
     Puede bajar tanto precisamente porque el canto está: sin el filete, a este velo
     la tarjeta dejaría de existir. Es el valor de Hugin (72 → 62 → 54 % en su
     historia), y la razón es la misma: una tarjeta casi maciza corta la profundidad
     de la escalera justo donde tenía que respirar.

     Se escriben con var() como el velo del cristal: siguen al tema sin repetirse
     en los bloques oscuros. El porcentaje va en su propio token (chore 90.259)
     porque hay quien se calcula A PARTIR de él: la cabecera pegajosa de la tabla,
     que tiene que tapar las filas que pasan por debajo de ESTE velo. Medido sobre
     la mesa, el velo compone #fafafa en claro y #262b31 en oscuro (1.05:1 y
     1.11:1 contra ella): la diferencia de relleno es casi nula en claro A
     PROPÓSITO —allí la tarjeta la dibuja su canto—.

     La que se ABRE (un enlace entero, [data-open]) sube un punto al apuntarla: 62 %
     de velo, el canto al 38 % de la tinta apagada y la sombra corta de debajo. La
     sombra lleva tinta NEGRA en los dos temas, como en Hugin: a un 45 % y con
     extensión negativa sólo asoma por abajo, y en claro una tinta de pizarra se
     quedaba en un borrón azulado. */
  /* Más transparente (petición del usuario, 2026-10-02): el desenfoque sostiene la lectura. En claro, menos que en
     oscuro (chore 90.723): sobre papel un velo muy fino deja la tarjeta sin cuerpo. Los bloques oscuros lo bajan. */
  /* Porcelana (2026-10-16): al 60 % y no al 30 % del oscuro. Blanco al 30 % sobre el suelo porcelana queda a menos de
     un punto de L* y la tarjeta se pierde («se nota poco»); al 60 % se separa y deja ver el eclipse un poco. */
  --bf-card-veil-mix: 60%;
  --bf-card-veil: color-mix(in srgb, var(--bf-card-bg) var(--bf-card-veil-mix), transparent);
  --bf-card-veil-hover: color-mix(in srgb, var(--bf-card-bg) calc(var(--bf-card-veil-mix) + 12%), transparent);
  --bf-card-edge-hover: color-mix(in srgb, var(--bf-muted) 38%, transparent);
  /* SIN SOMBRA DE CAÍDA (chore 90.639): las tarjetas de toda la app se quedan con su canto y su bisel; la caída sólo
     la lleva lo que flota de verdad (menú, diálogo, aviso, pista), que tiene sus propios tokens. */
  --bf-card-lift: 0 0 0 transparent;
  /* EL CRISTAL DE LA TARJETA (chore 90.611): la tarjeta del claro se viste como la caja
     del inicio de sesión —velo, desenfoque, bisel fino y la sombra cálida corta—. Es
     material del tema CLARO: los bloques oscuros los apagan (allí la tarjeta ya la dibuja
     su escalera) y las tres vías de «sin cristal», al final, apagan el desenfoque. */
  --bf-card-glass-filter: none; /* 90.768: sin desenfoque en las tarjetas (rendimiento) */
  --bf-card-glass-bevel: var(--bf-lg-edge);
  /* Porcelana: con el velo al 30 % la tarjeta se separa del suelo por su canto y por una sombra corta y otra larga,
     en la tinta pizarra. Los bloques oscuros la vuelven a apagar. */
  --bf-card-glass-drop: 0 1px 2px rgba(29, 48, 64, 0.05), 0 10px 26px -14px rgba(29, 48, 64, 0.2);
  /* EL CANTO DE CRISTAL de las piezas de las vistas (petición del usuario, 2026-10-16: «un borde fino cristal en los
     items de las vistas»): el del buscador y los secundarios (--bf-glass-rim), en claro: luz blanca arriba a la
     izquierda que se apaga, y la tinta pizarra abajo a la derecha. Lo pinta bifrost.css en el área del borde. */
  --bf-item-rim: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.55) 22%,
    rgba(29, 48, 64, 0.10) 48%, rgba(29, 48, 64, 0.16) 78%, rgba(29, 48, 64, 0.22) 100%);
  /* EL CRISTAL DE GENERAL (chore 90.708): sobre el material común, un velo más fino y un filete casi
     imperceptible para que en claro la pieza exista sobre el papel.
     Sin naranja en claro (90.657). Los dos bloques oscuros lo redeclaran. */
  --bf-lg-fill: var(--bf-card-veil); /* 90.755: el mismo velo que las tarjetas del Menú de Instancias */
  --bf-lg-fill-hover: var(--bf-card-veil-hover);
  --bf-lg-line: transparent; /* 90.760: el canto es --bf-lg-edge */

  /* ── Piezas puntuales ──────────────────────────────────────────────────────
     El velo de drawer/modal y el avatar sin foto: mismos valores en los dos
     temas (el velo ya oscurece, el avatar es un neutro de pizarra). */
  --bf-scrim: rgba(12, 22, 30, 0.34);
  --bf-avatar-bg: #55677a;

  /* EL RELIEVE DE LOS MANDOS PEQUEÑOS (chore 90.256): lo que hace que dos
     rectángulos redondeados se lean como un interruptor —el carril HUNDIDO, con
     su sombra interior, y el pulgar que LEVANTA encima— y que la pastilla de las
     pestañas despegue del carril. No son elevación de superficie (M-4 sigue): es el
     dibujo de la pieza. Tinta negra en los dos temas, como en el sistema de origen:
     a estos tamaños la sombra no tiñe, sólo da volumen. */
  --bf-sunk-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.22);
  --bf-lift-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  --bf-pill-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);

  /* LA MARCA DE LA CASILLA y la raya del indeterminado (chore 90.256). Van de
     MÁSCARA y no de fondo: el trazo lo pinta `currentColor`, así que un solo
     dibujo vale para los dos temas —de fondo, el color iría cocido dentro del
     data: y harían falta dos—. Por eso se declaran una vez y el oscuro no las toca. */
  --bf-check-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
  /* El aviso de los errores de la puerta (círculo con exclamación), como máscara: lo pinta currentColor. */
  --bf-alert-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5M12 16.5h.01'/%3E%3C/svg%3E");
  /* La marca de cada calle del Inicio: cuatro baldosas, como la cabecera de sección de Hugin. */
  --bf-home-lane-mark: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23000%27 stroke-width=%272%27 stroke-linejoin=%27round%27%3E%3Crect x=%274%27 y=%274%27 width=%276.5%27 height=%276.5%27 rx=%271.5%27/%3E%3Crect x=%2713.5%27 y=%274%27 width=%276.5%27 height=%276.5%27 rx=%271.5%27/%3E%3Crect x=%274%27 y=%2713.5%27 width=%276.5%27 height=%276.5%27 rx=%271.5%27/%3E%3Crect x=%2713.5%27 y=%2713.5%27 width=%276.5%27 height=%276.5%27 rx=%271.5%27/%3E%3C/svg%3E");
  /* La del bloque de LICENCIAS (chore 90.621): el certificado con sello, el `license` del vocabulario del shell
     (BifrostShellGlyphs), que es el que pinta PortalPage.LicenseGlyph. */
  --bf-home-license-mark: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23000%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M12.5 21H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5%27/%3E%3Cpath d=%27M8 7.5h8M8 11.5h4.5%27/%3E%3Ccircle cx=%2717%27 cy=%2715%27 r=%273%27/%3E%3Cpath d=%27M15.4 17.6 15 21.5l2-1.1 2 1.1-.4-3.9%27/%3E%3C/svg%3E");
  --bf-check-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");

  /* LA MARCA DEL VACÍO (chore 90.257 · contrato §3). Máscara por lo mismo que la de
     la casilla: el trazo lo pinta el acento, que es white-label, y un color cocido
     en el data: se quedaría en el naranja de fábrica. La marca dice QUÉ falta, no
     qué módulo es: la bandeja es «aquí no hay nada todavía» y la lupa, «el filtro
     no ha dejado nada» (es la misma lupa de .bf-field-search). */
  --bf-empty-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
  --bf-empty-mark-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='6.5'/%3E%3Cpath d='m20 20-4.2-4.2'/%3E%3C/svg%3E");

  /* LAS MARCAS DE LO QUE FLOTA (chore 90.258): el «hecho» del aviso emergente, el
     «mira» del que se hizo pero conviene mirar, y el aspa que lo cierra. Máscara por
     lo mismo que la casilla: el trazo lo pinta `currentColor`, así que un dibujo vale
     para los dos temas. Son los TRES DE HUGIN trazo a trazo (chore 90.485: el aviso,
     idéntico al suyo) —su visto, su triángulo y su aspa, a trazo 2—; el «hecho» ya no
     es el visto de la casilla, que es más grueso y de otra forma. */
  --bf-done-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5l5 5L20 6.5'/%3E%3C/svg%3E");
  --bf-warn-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.9L1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E");
  --bf-close-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");

  /* ── Métrica (M-7: COMÚN a los dos temas; el oscuro no la toca) ─────────────
     EN REM DESDE LA CHORE 90.253 (contrato de diseño §2.6). Hasta aquí todo iba en
     píxeles, y eso tenía dos consecuencias: el tamaño de letra que la persona elige
     en su navegador agrandaba el texto pero no el marco que lo contiene —texto
     grande en cajas del mismo alto—, y la raíz fluida que trajo la 90.252 (16 px
     hasta 1280, 18 desde 2560) hacía lo mismo en un monitor de sala de control.
     Con la métrica en rem, letra y caja crecen JUNTAS.

     A la raíz de 16 px los valores son EXACTAMENTE los de antes: 2.125rem son los 34
     píxeles del botón de siempre. Lo único que se mueve son los radios, y a
     propósito (ver abajo).

     DOS COSAS QUE NO PASAN A REM, y conviene decirlo para que nadie las «arregle»:
     los tres cortes canónicos de MF-3 siguen en píxeles —CSS no admite var() dentro
     de una @media, y un rem dentro de una media query resuelve contra los 16 px
     iniciales, no contra la raíz fluida—, y el área táctil de MF-4 conserva su suelo
     de 44 px (ver --bf-tap-size).

     LA ESCALA DE RADIOS del contrato, seis peldaños, cada uno un TAMAÑO DE OBJETO:
     la muesca, el campo, el mando, el diálogo y las dos superficies grandes. */
  --bf-radius-xs: 0.25rem;      /* 4 px — la muesca: LED, pastilla diminuta */
  --bf-radius-input: 0.4rem;    /* 6,4 px — el campo */
  --bf-radius-btn: 0.5rem;      /* 8 px — el mando */
  --bf-radius-modal: 0.65rem;   /* 10,4 px — el diálogo */
  --bf-radius-lg: 1rem;         /* 16 px — la tarjeta y el panel */
  --bf-radius-xl: 1.35rem;      /* 21,6 px — la superficie grande */

  /* Los tres nombres publicados se conservan (regla §2.1) y caen en el peldaño que
     les toca. Dos se mueven al redondear, y es el efecto buscado —la escala existe
     para que no vuelva a haber un radio por gusto—: s pasa de 6 a 6,4 px y l de 12 a
     10,4. El del medio cuadra exacto. */
  --bf-radius-s: var(--bf-radius-input);
  --bf-radius-m: var(--bf-radius-btn);
  --bf-radius-l: var(--bf-radius-modal);

  /* Tipografía (contrato §2.5 · chore 90.252). --bf-font-size es el tamaño RAÍZ —la
     base del rem— y ahora es FLUIDO: 16 px hasta 1280 y 18 px desde 2560, con la
     interpolación por medio. En un monitor de sala de control el texto crece; en un
     portátil no cambia nada. Va en un token, y no en un porcentaje suelto en `html`,
     porque el tamaño de letra del navegador MULTIPLICA a éste en vez de sustituirlo.

     OJO mientras la hoja sea MIXTA: la métrica de las piezas sigue en píxeles (botón
     34, campo 36, radios), así que por encima de 1280 el texto crece y las cajas no.
     Cabe de sobra —a 2560 el cuerpo mide 15,75 px dentro de un botón de 34—, pero
     quien cierra esto es la chore 90.253, que pasa la métrica a rem. */
  --bf-font-family: "Segoe UI", Selawik, Rubik, system-ui, sans-serif;
  --bf-font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
  --bf-font-size: clamp(16px, calc(16px + (100vw - 1280px) / 640), 18px);

  /* LA ESCALA, por PAPEL y en rem (contrato §2.5). Ocho peldaños y ni uno más: el
     nombre dice PARA QUÉ sirve el tamaño, no cuánto mide, y eso es lo que evita que
     vuelvan a aparecer un 13,5 y un 14,5 sueltos como los que había aquí. Quien
     añada una pieza elige «esto es una pista», no «esto mide once». */
  --bf-text-micro: 0.625rem;   /* 10 px — recuentos, siglas, marcas */
  --bf-text-mini: 0.6875rem;   /* 11 px — pistas y notas al pie de un campo */
  --bf-text-label: 0.75rem;    /* 12 px — rótulos, metadatos, pastillas */
  --bf-text-ui: 0.8125rem;     /* 13 px — navegación, menús, pestañas */
  --bf-text-body: 0.875rem;    /* 14 px — texto corriente, campos, botones */
  --bf-text-lead: 1rem;        /* 16 px — la marca y las entradillas */
  --bf-text-head: 1.25rem;     /* 20 px — título de pantalla */
  --bf-text-hero: 1.5rem;      /* 24 px — cifras grandes */

  /* Los nombres de tamaño ya publicados se CONSERVAN (regla §2.1 del contrato) y
     pasan a ser el peldaño que les toca. Tres se mueven al caer en la escala, y es
     el efecto buscado: el título de página baja de 21 a 20, la cabecera de tarjeta
     de 14,5 a 14 y el campo de 13,5 a 14. Qué peldaño usa cada pieza lo revisan las
     chores de mandos y campos (90.254 y 90.255); aquí sólo se deja de medir a ojo. */
  --bf-body-font-size: var(--bf-text-body);
  --bf-title-font-size: var(--bf-text-head);          /* título de página */
  --bf-panel-title-font-size: var(--bf-text-body);    /* cabecera de tarjeta */
  --bf-sidebar-font-size: var(--bf-text-ui);
  --bf-legend-font-size: var(--bf-text-label);        /* etiqueta de campo */

  /* Controles. Las medidas son las de siempre, dichas en rem: 2.125rem son 34 px y
     2.25rem son 36, a la raíz de 16.

     EL ALTO DE MANDO ÚNICO del contrato (S-11: botón, campo, desplegable, buscador y
     paginador, todos iguales) queda DECLARADO y sin usar todavía. Son 1,7rem, unos
     27 px, y aplicarlo es bajar el botón siete píxeles y el campo nueve: eso no es
     una conversión de unidades, es vestir los mandos, y lo hace la chore 90.254.
     Aquí se deja el nombre para que aquello sea cambiar dos líneas.

     Y CON ÉL UNA DECISIÓN que quedó abierta y cerró la chore de campos (90.255): con
     el mando base en 1,7rem, el .bf-input.sm de 30 px de nuestras filas de tabla se
     quedaba SIN SITIO por debajo. En Hugin el base es 1,6rem, pero entonces el
     pequeño y el normal quedan a un píxel uno de otro, que es como no tener dos
     tamaños. Se SEPARA en vez de retirarse: el pequeño baja a 1,5rem, el campo con
     el botón, y el porqué está junto a la regla de .bf-input.sm. */
  --bf-button-font-size: var(--bf-text-ui);
  --bf-button-font-size-small: var(--bf-text-label);
  --bf-button-radius: var(--bf-radius-btn);
  --bf-button-padding: 0 0.875rem;
  --bf-button-height: 2.125rem;
  --bf-field-font-size: var(--bf-text-body);
  --bf-field-padding: 0 0.625rem;
  /* EL CAMPO BAJA CON EL BOTÓN, y esto es parte de S-11 (chore 90.254): el alto de
     mando es único para botón, campo, desplegable, buscador y paginador. Bajar sólo
     el botón dejaría descuadrada cualquier fila que tenga los dos —un buscador con
     su botón al lado—, y un renglón torcido se ve antes que un control alto. El
     resto del vestido del campo (el velo, el foco sin halo, la flecha propia del
     desplegable) lo puso la chore 90.255. */
  --bf-field-height: var(--bf-control-height);
  /* El campo lleva el radio de CAMPO, no el de mando (chore 90.255): 6,4 px contra
     los 8 del botón. Es lo que el contrato le asigna, y la diferencia se lee a
     propósito —en lo que se pulsa y en lo que se escribe—: en una barra donde salen
     juntos, el que tiene las esquinas más vivas es donde se teclea. */
  --bf-field-radius: var(--bf-radius-input);
  /* UN ÚNICO ALTO DE BOTÓN (chore 90.719, a petición del usuario): había tres —el pequeño
     de 1,5rem, el normal de 1,7rem y los de 2,25rem de General y de `.lg`— y CTA y
     secundario no medían lo mismo según dónde salieran. Ahora todos miden
     --bf-btn-height, un alto MEDIO: los altos bajan un pelo y los pequeños suben. El
     campo va detrás (S-11: botón y campo, a la misma altura en una barra). El login
     (`.bf-door`) conserva sus botones grandes: es otro chasis. */
  --bf-control-height: 1.75rem;
  --bf-btn-height: var(--bf-control-height);

  /* ── Responsive (convenios MF · docs/bifrost3-convenciones.md) ─────────────
     CONTRATO DE CORTES. CSS no admite var() en @media, así que los tres cortes
     canónicos no pueden ser tokens: viven aquí como prosa citable y son los
     ÚNICOS valores admitidos en una media query de layout:
       620px   compacto ↔ medio    (teléfono | tableta vertical)
       900px   medio ↔ ancho       (tableta | apaisada, ventana partida)
       1100px  ancho ↔ completo    (desde aquí manda el sistema tal cual, MF-1)
     El CSS nuevo se escribe mobile-first (MF-2): la regla base es la de móvil
     y @media (min-width: …) ENSANCHA. Prohibido añadir un max-width o un
     cuarto corte sin excepción anotada junto a la regla (MF-3). Antes que una
     media query, el patrón intrínseco que la evita: repeat(auto-fit,
     minmax(…)), flex-wrap, minmax(0, 1fr).
     Táctil (MF-4): bajo (pointer: coarse) todo objetivo interactivo alcanza
     --bf-tap-size de área efectiva con --bf-tap-gap de separación. La métrica
     visual de ESCRITORIO no cambia: botón y campo miden --bf-control-height,
     1,7rem (27,2 px a la raíz de 16), y sus tallas .sm 1,5rem (24 px); el
     cromo del shell mide sus mandos con --bf-button-height, 2,125rem (34 px).
     El convenio M-7 habla del tema; el medio de entrada es otra dimensión.
     Altura de pantalla (MF-6): 100dvh con 100vh de respaldo en la línea
     anterior, nunca 100vh solo. */
  /* El área táctil crece con la raíz PERO NO BAJA DE 44 px, y por eso lleva max():
     los 2,75rem del contrato son exactamente 44 a la raíz de 16, así que cuadran sin
     relajar nada; lo que el max() impide es que alguien que haya reducido el tamaño
     de letra de su navegador se quede con objetivos de 33 px. MF-4 es un MÍNIMO de
     accesibilidad, no una medida de diseño: puede subir, nunca bajar. */
  --bf-tap-size: max(44px, 2.75rem);
  --bf-tap-gap: 0.5rem;

  /* Tabla y tarjeta. Rótulos a 11 px y filas a 14, como la tabla de los módulos de
     Latanur (`.datatable thead th` a --text-mini, filas `text-sm`; chore 90.394). */
  --bf-table-header-font-size: var(--bf-text-mini);
  --bf-table-header-height: 2.125rem;
  --bf-table-row-font-size: var(--bf-text-body);
  --bf-table-row-radius: var(--bf-radius-input);

  /* LA TABLA RUEDA POR DENTRO (contrato §3 · chore 90.259). Desde el corte medio el
     envoltorio de la tabla (.bf-table-wrap) tiene un alto acotado y la cabecera se
     queda pegada arriba mientras las filas pasan por debajo. El alto es la ventana
     MENOS lo que tiene que seguir a la vista alrededor de la tabla, y eso es este
     token. MEDIDO a 1280×800 con las hojas reales y el shell real sobre el
     marcado de Usuarios, la lista más cargada de mandos: cabecera del shell 61 px
     medida sin la miga (unos 67 con ella), cuerpo y panel 18 + 1 + 18 arriba, cabecera de página
     31 + 16, barra de filtros 28 + 14, pie con el paginador 48 más los dos cantos
     de la tarjeta, y 18 + 1 + 18 abajo. Son 280 px, y 305 cuando el recuento de la
     barra baja a un segundo renglón (pasa con cuatro filtros). 19rem son 304 a la
     raíz de 16: cubre los dos casos. Pasarse no rompe nada —la página rueda un
     poco—; quedarse corto dejaría el paginador fuera de la vista, que es justo lo
     que esto existe para evitar.
     Con una ventana baja (un portátil apaisado con la barra del navegador), la
     resta dejaría una rendija de tres filas: el SUELO se lo impide, y entonces lo
     que rueda un poco es la página, que es el mal menor.
     Desde la chore 90.420 la vista ya no va dentro de un panel pintado: se van sus
     dos cantos de 1 px y sus dos rellenos de 1,125rem, 2,375rem en total a la raíz
     de 16 (la franja de 0,25rem que conserva para el anillo de foco la devuelve con
     un margen negativo y no cuenta). MEDIDO a 1904×897 tras quitarlo, con la tabla
     llena: la auditoría, con la banda de filtros y el cursor dentro de la tarjeta,
     deja el pie justo en el suelo (antes era el canto del panel el que llegaba), y
     Usuarios, con la banda fuera, lo deja a 47 px. */
  --bf-table-chrome: 16.625rem;
  --bf-table-floor: 16rem;
  /* El pie de la tabla (recuento y paginador, 48 px más el filete): lo que la
     tarjeta de una tabla suma al tope del envoltorio para llegar hasta abajo
     (chore 90.393). */
  --bf-table-foot: 3.0625rem;
  /* LA CABECERA ES CRISTAL, calculado DESDE el velo de la tarjeta y no a mano: lo
     que tiene que hacer es tapar las filas que le pasan por debajo, así que su
     grosor sólo significa algo en relación con el de la tarjeta. Escrito suelto se
     quedaría fijo mientras la tarjeta adelgaza, que es como una banda opaca acaba
     partiendo la lista en dos. Lo demás lo hace el desenfoque: con él se lee que lo
     de debajo sigue siendo la misma tabla. Más corto que el del cristal que flota
     (12 px contra 22): detrás hay letra de doce píxeles a un dedo, no una página
     entera, y este se recalcula en cada fotograma del desplazamiento. Sin cristal
     (más abajo, las mismas tres vías que --bf-glass-*) pasa a la tarjeta maciza. */
  --bf-table-head-veil: color-mix(in srgb, var(--bf-card-bg) calc(var(--bf-card-veil-mix) + 44%), transparent);
  --bf-table-head-filter: blur(12px) saturate(1.3);
  /* Baja de 12 a 10,4 porque su peldaño más cercano es el del diálogo. La chore
     90.257 lo decidió mirándola: la tarjeta NO sube al de 16. Es el radio de la
     tarjeta de Hugin (--radius-modal), y a 16 el velo del 54 % dibujaba una
     burbuja, no una hoja. */
  --bf-panel-radius: var(--bf-radius-modal);
  /* Relleno de panel y tarjeta, MOBILE-FIRST (MF-2/MF-3 · chore 90.099): la base
     es la del TELÉFONO (12) y el corte de 620 devuelve los 18 de la maqueta. En
     un móvil los 18+18 de la tarjeta salen del ancho útil del contenido, y hay
     contenido que no puede encogerse sin dejar de cumplir: seis casillas de
     código con objetivo táctil de 44 px piden 304 px libres (MF-4). El valor de
     escritorio no cambia — a ≥620 este token vuelve a valer exactamente 18. */
  --bf-panel-padding: 0.75rem;
  --bf-panel-font-size: var(--bf-text-ui);

  /* ── Movimiento (contrato de diseño §2.6 · chore 90.253) ───────────────────
     Hasta aquí el sistema NO TENÍA VOCABULARIO DE MOVIMIENTO: ni una `transition`
     ni una `animation` en esta hoja ni en el catálogo, así que cada pieza que
     quisiera moverse se inventaba su curva y su duración —que es como aparecen un
     0,15s y un 0,2s sueltos por el fichero—. Aquí están los dos ingredientes.

     DOS CURVAS, y cada una dice una cosa distinta: la de salida es para lo que
     APARECE en su sitio (un menú, un aviso) y arranca rápido para frenar al final;
     la lateral es para lo que ENTRA DESDE UN BORDE (el cajón, el lateral que se
     pliega) y es simétrica, porque el ojo sigue el recorrido entero.

     CUATRO DURACIONES. Que -side y -slow valgan lo mismo no es un descuido: son
     papeles distintos —lo que recorre una pantalla y lo que se toma su tiempo—, y si
     mañana uno se ajusta, el otro no tiene por qué seguirlo.

     Quien las GASTA llega después: el shell en las chores 90.265 a 90.269 y las
     piezas en las suyas. Aquí sólo se nombran. */
  --bf-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --bf-ease-side: cubic-bezier(0.4, 0, 0.2, 1);
  --bf-dur-fast: 120ms;   /* un realce, un cambio de estado */
  --bf-dur-base: 200ms;   /* lo que aparece o desaparece en su sitio */
  --bf-dur-side: 360ms;   /* lo que entra desde un borde */
  --bf-dur-slow: 360ms;   /* lo que se toma su tiempo a propósito */

  /* DOS MOVIMIENTOS CON NOMBRE PROPIO (chore 90.256), porque son dos gestos que el
     sistema de origen mide aparte y ninguna de las cuatro de arriba los dice:

       · el PULGAR del interruptor —y el naranja que se abre con él—, 280 ms con un
         rebote corto: llega, se pasa un pelo y se asienta. Recto, un interruptor
         que se desliza parece una barra de progreso;
       · la PASTILLA de las pestañas, que viaja de la puesta a la nueva en 340 ms
         con la curva de salida: es un recorrido, no un rebote.

     Son tokens y no números en la pieza por lo mismo que las cuatro de arriba: el
     apagado del movimiento (al final de esta hoja) los pone a un milisegundo, y
     un 280ms escrito a mano se le escaparía. */
  --bf-ease-spring: cubic-bezier(0.34, 1.32, 0.5, 1);
  --bf-dur-thumb: 280ms;  /* el pulgar del interruptor y el color que lo sigue */
  --bf-dur-glide: 340ms;  /* la pastilla que pasa de una pestaña a otra */

  /* LO QUE CUELGA DE UN MANDO y LO QUE SE PLANTA DELANTE (chore 90.268, T9 y T13),
     los otros dos gestos que el sistema de origen mide aparte:

       · el DESPLEGABLE —el menú de usuario, la campana, el centro de control—:
         asoma desde medio dedo más arriba y un pelo encogido, con la opacidad en
         190 ms y el recorrido en 210. Son dos números y no uno porque el color
         llega antes que la forma: así el cuadro se lee ya puesto cuando todavía
         está acomodándose;
       · el MENÚ GLOBAL, que no cuelga de nada: entra centrado en 520 ms con un
         rebote corto (la curva se pasa de 1 y vuelve) y se va en 230, que es lo
         que se tarda en quitar algo de en medio sin que parezca que se rompe.

     El velo que lo acompaña no estrena token: son --bf-dur-slow y --bf-ease-side,
     que es justo lo que hace —un fondo que se apaga tomándose su tiempo—. */
  --bf-ease-pop: cubic-bezier(0.16, 1, 0.3, 1);
  --bf-dur-pop: 190ms;        /* la opacidad del desplegable */
  --bf-dur-pop-move: 210ms;   /* su recorrido, un pelo más largo */
  --bf-ease-mega: cubic-bezier(0.22, 1.12, 0.36, 1);
  --bf-dur-mega: 520ms;       /* el menú global entrando */
  --bf-dur-mega-out: 230ms;   /* y yéndose */

  /* EL CAMBIO DE TEMA (chore 90.271, T14): el documento entero se funde en 320 ms.
     Es el único movimiento que no es de una pieza sino de TODA la página, y por eso
     no lo hace una transición de CSS sino una transición de VISTA: el tema conmuta
     sobre :root y alcanza a cualquier cosa pintada, también a lo que el producto
     proyecta en los slots del shell, y una transición del Shadow DOM no cruza ahí.
     320 ms es lo que tarda en leerse como «ha cambiado» sin que nadie espere. */
  --bf-ease-theme: cubic-bezier(0.22, 1, 0.36, 1);
  --bf-dur-theme: 320ms;

  /* LA BARRA DEL AVISO EMERGENTE (chore 90.258): se vacía en 3,2 s, lo que tarda el
     aviso en irse solo. Es un movimiento y por eso es token y se apaga con los demás
     (abajo), pero ojo con lo que NO es: el tiempo que el aviso se QUEDA es tiempo de
     lectura, no movimiento, y vive en el módulo (ts/ui/toast.ts, SHOWN_MS) con el
     mismo número. Con el movimiento apagado el aviso sigue durando 3,2 s; lo que deja
     de correr es la barra, que se queda llena y quieta —sigue diciendo que esto se va
     a ir solo—. Los dos números tienen que ir a la par: la suite lo vigila. */
  --bf-dur-toast: 3200ms;

  /* EL BARRIDO DEL ESQUELETO (chore 90.259): 1,4 s por pasada. Es uno de los DOS
     bucles que admite el sistema —el otro es el aro del botón ocupado—, porque
     «esperando» es lo único que se dice con movimiento continuo. Y por ser bucle NO
     entra en el apagado de abajo: un bucle infinito a un milisegundo no se para,
     PARPADEA. La pieza (.bf-skel, bifrost.css §3) lleva su propio apagado por las
     dos vías y se queda quieta con su tinte. */
  --bf-dur-sweep: 1400ms;

  /* LA ENTRADA TRAS EL LOGIN (chore 90.653): la primera carga de la pestaña tras iniciar
     sesión coloca el chasis y la página en ~1,2 s, una vez. Cada pieza dura --bf-dur-entry
     (el lateral, que recorre más, --bf-dur-entry-side); los destinos del lateral se escalonan
     de --bf-dur-entry-step en --bf-dur-entry-step y los bloques del contenido, de
     --bf-dur-entry-cascade. Con el movimiento apagado no se enciende siquiera (shell.ts,
     playEntry; bifrost.css, «La entrada»), y además va en las listas de abajo. */
  --bf-dur-entry: 560ms;
  --bf-dur-entry-side: 620ms;
  --bf-dur-entry-step: 32ms;
  --bf-dur-entry-cascade: 45ms;

  /* ── Alias y tokens declarados SIN USO ─────────────────────────────────
     Nombres ya publicados que el sistema no necesita por sí mismos. NO se borran
     (regla de §2.1 del contrato de diseño): una instalación puede estar
     sobreescribiéndolos en su appearance.css, y el contrato 00.004 §3.4 publica
     algunos de ellos. Se mantienen APUNTANDO al token vivo equivalente. No usar
     en piezas nuevas.

     INVENTARIO de los que hoy no consume NADIE —contado sobre .css, .ts, .cshtml y
     .cs del repo, chore 90.250—: --bf-header-bg, --bf-header-fg, --bf-panel-fg,
     --bf-sidebar-hover-bg, --bf-active, --bf-table-header-height,
     --bf-table-row-radius, --bf-panel-radius, --bf-subtle-bg, --bf-disabled-bg y
     --bf-danger-bg. Fe de erratas del §8.2 del contrato de diseño,
     que da por muertos los --bf-table-* enteros: --bf-table-row-font-size tiene 9
     usos y --bf-table-header-font-size uno, así que esos dos están vivos.

     Dos han salido de aquí: --bf-off dejó de ser un tercer nombre de --bf-dim y
     tiene valor propio arriba (#7b8794 claro, #808080 oscuro), que es el gris de
     «apagado» del contrato §2.3 (chore 90.250); y --bf-shadow dejó de valer `none`
     y es la sombra del diálogo, arriba con las otras dos del §2.4 (chore 90.251).
     Sigue sin consumidores hasta que la 90.258 vista el diálogo, pero ya no es un
     alias: tiene valor propio y por tema. */
  --bf-header-bg: var(--bf-surface-bg);
  --bf-header-fg: var(--bf-surface-fg);
  --bf-panel-fg: var(--bf-surface-fg);
  --bf-active: var(--bf-selected);
  --bf-subtle-bg: var(--bf-panel-bg);
  --bf-disabled-bg: var(--bf-panel-bg);
  --bf-danger-bg: var(--bf-danger-soft);

  /* ── CAMPOS COMO LOS DE HUGIN (rediseño general) ─────────────────────────────
     El campo se ve IGUAL que en Hugin (styles/editor-panel.css, `.input`): su filete
     propio —en claro el mismo que el de mando; en oscuro, más suave—, el foco en el
     CTA tal cual y el mismo relleno lateral. Y el resplandor de las vistas: Hugin
     pinta arriba a la izquierda un radial de su tono de lienzo (sólo de noche; en
     claro es transparente). */
  /* SIN CANTO (chore 90.399): campos y desplegables van SIN BORDE, como en Hugin
     (`.field-search`, `.select`, `.input-page`: «SIN CANTO»). El borde se APAGA y no se
     quita —el token vale transparente y la regla sigue con su 1 px— para que el foco y el
     error, que sí son estados con canto, no muevan la maqueta al encenderse. Esto se
     aparta a sabiendas del filete a 3:1 de M-12 (SC 1.4.11) en los campos: lo que dice
     «aquí se escribe» es el velo y, en el desplegable, su flecha. */
  --bf-field-edge: transparent;
  /* El apagado de Hugin (`--disabled-ink`): el campo deshabilitado se DESVANECE entero,
     no cambia de relleno. En oscuro baja más (0,45) porque allí la tinta pesa menos. */
  --bf-disabled-ink: 0.6;
  /* Chore 90.903 («el foco de campos y buscadores, más fino y más transparente»): la CTA al 55 %, en un solo píxel. */
  --bf-field-focus: color-mix(in srgb, var(--bf-cta) 55%, transparent);
  --bf-select-hover-veil: #ffffff;
  /* El buscador con el foco dentro: el peldaño de panel de Hugin (`--panel-2-color`). */
  --bf-field-search-focus-bg: #e7ebef;
  /* La LUZ DE DÍA de la puerta (chore 90.611): en claro el resplandor de arriba a la
     izquierda deja de ser transparente y es la zona luminosa del inicio de sesión. La
     pintan body y el .layout del shell con este mismo token, así que llega sin tocar ni
     una regla ni el shell compilado. */
  --bf-view-glow: rgba(255, 255, 255, 0.75);   /* blanco cálido, a medio camino (90.643) */
  /* LAS MANCHAS DE COLOR DE LA VISTA (chore 90.762, idénticas al mockup del seleccionado del lateral): una cálida del
     acento arriba a la derecha y una azul acero abajo a la izquierda, para que el cristal de lo que hay encima se
     note. Las dos vías de «menos transparencia» las apagan (al final de esta hoja). */
  --bf-view-blob: rgba(238, 123, 36, 0.2);
  --bf-view-blob2: rgba(70, 130, 180, 0.16);
  /* EL FONDO DE LA VISTA, ELABORADO (chore 90.765). En CLARO, las manchas del mockup del seleccionado (a petición
     del usuario): la brasa del acento arriba a la derecha y el azul acero abajo a la izquierda con sus opacidades
     (20 % y 16 %), cada una con un núcleo más definido, y un brillo blanco en diagonal que las une como un reflejo
     sobre vidrio. En oscuro, «impactante pero no tan cargante»: un foco y un eco; y una
     aurora quieta en capas, de arriba abajo (sin grano desde la 90.768, por rendimiento): el grano
     finísimo que le quita lo digital; la brasa del acento arriba a la derecha con su halo ámbar desplazado hacia
     el centro; un velo iris que la enlaza en diagonal; el azul acero abajo a la izquierda; y el resplandor de
     siempre arriba a la izquierda. Fija al hacer scroll, para que el cristal de encima la deje ver al moverse. */
  /* EL FONDO DE LA VISTA ES UNA IMAGEN QUIETA (chore 90.783, elegida por el usuario tras muchas rondas de mockups):
     el horizonte de un astro abajo y un eclipse que asoma por detrás del lateral, con su trazo naranja —en claro, «E3 ·
     Eclipse sobre el horizonte» sobre papel blanco roto, intensidad 0,5, tinta 0,4, viñeta 0,35, desenfoque 36 px—. Se
     compuso como capas CSS en el mockup y se exportó una vez a WebP (2880×1800, ~15 kB): en la app no se calcula nada,
     el compositor sólo la deja quieta detrás del lateral, la cabecera y el contenido. Anclada a la izquierda para que el
     eclipse siga cruzando el lateral a cualquier ancho. Bajo ella, el lienzo.
     RETOCADA (chores 90.998 y 90.999, 2026-10-08): sobre la imagen de entonces, en coma flotante, el eclipse del lateral
     1,2 veces más marcado respecto al lienzo y SÓLO SU ARCO —el aro tiene el centro fuera, a la izquierda; lo de dentro
     (el disco negro de noche, el relleno albaricoque de día) toma el fondo de más allá del arco, sin círculo—; el
     horizonte (de la mitad hacia abajo) a 0,8; el resplandor de arriba a la izquierda compuesto dentro, con el arco
     delante (le quita un 30 % de luz en su zona); y vuelta a tramar con Bayer 8×8. Las dos de antes y los scripts, en
     docs/mockups-vista/fondo/ (fuera del repositorio).
     EL CLARO, PORCELANA (petición del usuario, 2026-10-16): la imagen clara ya no es la suya, sino el eclipse del
     OSCURO calcado —misma forma, posición y desenfoque— sobre el suelo porcelana (#eceff3 → #e5e9ee): donde el oscuro
     luce, el claro baja su L* en el naranja de la marca, y donde se hunde, la sube hacia el blanco, con el salto del
     oscuro ×0,7. La genera design/tools/eclipse-claro.py a partir de eclipse-oscuro.webp (WebP sin pérdida, tramado
     Bayer 8×8); si cambia la oscura, se vuelve a generar la clara. */
  /* Chore 90.969: encima de la imagen, otra vez el resplandor de siempre arriba a la izquierda —el que enmarca el rastro de
     migas—, que se perdió al pasar el fondo a imagen quieta. */
  /* EL RESPLANDOR, SUAVIZADO (chore 90.998, «el gradiente de arriba a la izquierda no está suavizado como el resto de la
     vista»). Caía en LÍNEA RECTA hasta el 65 % y ahí se acababa de golpe: medido en oscuro, una meseta y un filo de
     #1B2127 a #111419 en 180 px, con escalones, junto a una imagen que está desenfocada. Ahora cae como la luz, con el
     cuadrado de la distancia —(1 − t)² en ocho tramos hasta el 100 %—: en el tramo medio pinta lo mismo que la recta
     (25 % a media distancia, 23 % antes) y se apaga sin borde, con la pendiente a cero al final. Un solo token para
     los dos temas y las vías de menos transparencia: el var() se resuelve con el --bf-view-glow de cada tema.
     Y DENTRO DE LA IMAGEN (misma chore, «no veo el suavizado, sigo viendo las ondas»): las ondas no eran la forma de la
     curva sino sus ESCALONES, y un degradado CSS no se trama. Con imagen, el resplandor va compuesto en las dos WebP
     —(1 − t)² exacto, en coma flotante y tramado Bayer 8×8 como el resto del fondo— y --bf-view-art ya no lo pinta
     encima. Este token queda para las vías de menos transparencia, que no llevan imagen. */
  --bf-view-halo: radial-gradient(115% 85% at 0% 0%,
    var(--bf-view-glow) 0%,
    color-mix(in srgb, var(--bf-view-glow) 77%, transparent) 12.5%,
    color-mix(in srgb, var(--bf-view-glow) 56%, transparent) 25%,
    color-mix(in srgb, var(--bf-view-glow) 39%, transparent) 37.5%,
    color-mix(in srgb, var(--bf-view-glow) 25%, transparent) 50%,
    color-mix(in srgb, var(--bf-view-glow) 14%, transparent) 62.5%,
    color-mix(in srgb, var(--bf-view-glow) 6%, transparent) 75%,
    color-mix(in srgb, var(--bf-view-glow) 1.5%, transparent) 87.5%,
    transparent 100%);
  --bf-view-art: url("/img/vista/eclipse-claro.webp") left center / cover no-repeat, var(--bf-canvas);
  /* El lateral: lo lee el shell (--bf-side-glass, --bf-side-filter); sin cristal, la lámina maciza.
     Chore 90.892: sin desenfoque, porque borraba el tramado del fondo (90.865) y salían bandas.
     Chores 90.911 y 90.917 («el sidebar debe tener blur en su transparencia»; «coexistencia de color y transparencia
     de las tarjetas, el menú y el centro de control con las del sidebar»): el lateral es EXACTAMENTE el velo de las
     tarjetas (--bf-card-veil: card-bg al 50 % en claro, al 30 % en oscuro) con su desenfoque, y lleva encima un GRANO
     finísimo (--bf-grain: ruido gris con un alfa de 0 a 4,5 %) que vuelve a tramar lo desenfocado y deja el degradado
     sin bandas. El grano es un SVG en data:, como la flecha del desplegable. */
  --bf-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 .045 0 0 0 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  /* Porcelana (2026-10-16): el lateral, como en el oscuro, es el LIENZO al 30 % con el desenfoque (no el velo de las
     tarjetas): el eclipse se ve a través. */
  --bf-side-veil: color-mix(in srgb, var(--bf-canvas) 30%, transparent);
  --bf-side-glass: var(--bf-grain), var(--bf-side-veil);
  /* Y lo que FLOTA —el menú, el centro de control, los desplegables del shell, los modales— es del mismo material: el
     color de las tarjetas, algo más opaco porque va sobre contenido (60 % en claro, 44 % en oscuro). El shell lo lee
     en --bf-pane-glass (sin él, su cristal de siempre); los modales, por --bf-head-glass. Chore 90.911. */
  --bf-pane-mix: 60%;
  --bf-pane-glass: color-mix(in srgb, var(--bf-card-bg) var(--bf-pane-mix), transparent);
  --bf-side-filter: blur(16px) saturate(1.3);
  --bf-side-dim: transparent; /* sin el oscurecido de noche del marco: el lateral del mockup elegido */
  /* ── LA PUERTA · las pantallas sin sesión (rediseño general) ──────────────────
     El vestido del login de Latanur: la tarjeta de cristal, ahora sobre la
     constelación de Dragsa (chore 90.500: Ui/BifrostDoorSky.cs y door-sky.ts, reglas
     en bifrost.css §4; la escena 3D lee de aquí sus tintas: ember, flame y core). Estas tintas no existen en el
     resto del sistema —fuera de la puerta no significan nada— pero son COLOR y
     cambian con el tema, así que viven aquí, en los tres bloques. Los valores son
     los de Latanur (css/auth/tints.css), con el acento de la instalación donde
     aquél pone el suyo.
     EN CLARO LA OLA NO ES LUZ: ES TINTA. Sumar luz sobre papel no cambia nada, así
     que el núcleo pasa a ser el MÁS OSCURO de los tres, las tintas se lavan y los
     halos bajan a menos de la mitad: sobre papel las capas anchas son suciedad.
     El escenario es papel cálido, y el acento de la marca, un naranja hondo que da
     3,72:1 sobre él (WCAG 2.2 SC 1.4.11); el de siempre se queda en 2,38:1. */
  /* CON MÁS IMPACTO (rediseño general): el papel liso con una ola lavada se quedaba plano
     al lado del de noche. Ahora el papel es algo más cálido, tiene una zona LUMINOSA
     arriba a la izquierda —la luz de día entra por algún sitio— y el halo de la ola pesa
     más abajo; una viñeta cálida muy leve cierra las esquinas y da fondo; y la ola sube
     de tinta con el núcleo como trazo principal. El acento de la marca sigue el hondo. */
  --bf-door-stage: #f0e8df;
  --bf-door-halo: radial-gradient(60rem 32rem at 10% 6%, rgba(255, 255, 255, 0.75), transparent 70%), radial-gradient(72rem 34rem at 58% 76%, color-mix(in srgb, var(--bf-accent) 10%, transparent), transparent 72%);
  --bf-door-vignette: radial-gradient(125% 95% at 45% 40%, transparent 55%, rgba(120, 72, 32, 0.12));
  --bf-door-ember: #e8a26b;
  --bf-door-flame: #ec8636;
  --bf-door-core: #d4661a;
  --bf-door-mark: #c25a0e;
  /* Final del degradado de la organización en la promesa: de día, un naranja vivo que
     sigue dando contraste sobre el papel; de noche, el núcleo de la ola. */
  --bf-door-hl-end: #e0701f;
  --bf-door-halo-strength: 0.45;
  --bf-door-core-width: 3;
  --bf-door-core-opacity: 1;
  --bf-door-strand-over: 0.45;
  --bf-door-strand-under: 0.58;
  --bf-door-bloom-inner: 0.08;
  --bf-door-bloom-middle: 0.025;
  --bf-door-grain: 0;
  --bf-door-card-shadow: 0 0.75rem 2rem -1.25rem rgba(72, 48, 26, 0.24);
  /* El seleccionado de los segmentados de la puerta, el de Latanur: en claro coincide
     con el de la casa; en oscuro va al 11 % (el de la casa, al 20 %). */
  --bf-door-seg-selected: var(--bf-selected);
  /* La constelación, un punto transparente —es fondo—. Y el logotipo, SIN brillo de día: sobre
     papel un resplandor no es luz, es una mancha. */
  --bf-door-orbit-opacity: 0.85;
  --bf-door-wave-opacity: 0.85;
  --bf-door-logo-glow: none;
  /* El cristal de la tarjeta y de sus píldoras: de día con más cuerpo —sobre papel, un
     velo del 18 % no se ve—, y la tarjeta proyecta una sombra cálida hacia la izquierda. */
  --bf-door-panel-veil: 40%;
  /* El TINTE del cristal de la columna del acceso y de la caja: de día, la superficie. */
  --bf-door-glass-tint: var(--bf-surface-bg);
  --bf-door-pill-veil: 60%;
  /* El destello de los botones de terceros al apuntarlos (chore 90.991): el de «Cerrar sesión» del centro de control,
     más disimulado. De día, su barrido cálido del acento (allí al 22 %) a poco más de la mitad. */
  --bf-door-idp-glint: color-mix(in srgb, var(--bf-accent) 12%, transparent);
  --bf-door-panel-drop: -1.5rem 0 3.5rem -2rem rgba(72, 48, 26, 0.22);
  /* El relleno de los campos de la puerta: de día un HUECO un punto más oscuro que el
     cristal (uno más claro se fundía con él); de noche, el panel que tienen en Hugin. */
  --bf-door-field-bg: rgba(41, 63, 79, 0.07);
  /* COLORES QUE ESTABAN ESCRITOS EN bifrost.css (revisión de las chores de interfaz del 2026-10-13). Cada bloque de
     bifrost.css lee aquí su token y ya no detecta el tema por su cuenta. */
  /* El pulgar del interruptor simple, con la gota de los conmutadores (chore 90.858). */
  --bf-switch-thumb-top: rgba(255, 255, 255, 0.6);
  --bf-switch-thumb-bottom: rgba(255, 255, 255, 0.54);
  --bf-switch-thumb-hi: rgba(255, 255, 255, 0.55);
  --bf-switch-thumb-line: rgba(29, 48, 64, 0.16);
  --bf-switch-thumb-drop: rgba(29, 48, 64, 0.12);
  /* El carril de los conmutadores: su filete (chore 90.845) y su canto de cristal, que en oscuro es más transparente
     que el del resto del cristal (chore 90.863). */
  --bf-seg-line: rgba(29, 48, 64, 0.17);
  --bf-seg-rim: var(--bf-glass-rim);
  /* La gota de los conmutadores, más disimulada (chore 90.943). */
  --bf-seg-gota-top: rgba(255, 255, 255, 0.26);
  --bf-seg-gota-bottom: rgba(255, 255, 255, 0.22);
  --bf-seg-gota-hi: rgba(255, 255, 255, 0.34);
  --bf-seg-gota-spec-soft: rgba(255, 255, 255, 0.1);
  --bf-seg-gota-line: rgba(29, 48, 64, 0.08);
  --bf-seg-gota-lift: 1.02;
  /* Auditoría (chore 90.874): los dos verbos que el sistema no tiene en su paleta, los dos tonos de iniciales que
     tampoco, y la opción puesta del desplegable de periodo con su sombra de contacto. */
  --bf-aud-update: #2f7fd8;
  --bf-aud-view: #7a6ae0;
  --bf-aud-hue-pink: #d9709a;
  --bf-aud-hue-teal: #2ba59a;
  --bf-aud-pressed-bg: rgba(255, 255, 255, 0.6);
  --bf-aud-pressed-drop: rgba(0, 0, 0, 0.12);
  /* La caída muy suave del modal (chore 90.905): cálida en claro y negra en oscuro. */
  --bf-modal-soft-drop: 0 24px 60px -28px rgba(29, 48, 64, 0.13), 0 4px 12px -8px rgba(29, 48, 64, 0.06);
  /* Los dos filetes del CTA al apuntarlo, en un naranja claro (chore 90.919). */
  --bf-cta-led: color-mix(in srgb, #ffe0b8 70%, var(--bf-accent));
  /* La caída de la barra de acciones fija del teléfono (chore 90.938). */
  --bf-sticky-bar-drop: 0 0.75rem 1.75rem -0.9rem rgba(8, 15, 20, 0.4);
  /* El brillo de arriba de la marca «sí» del catálogo de permisos (chore 90.988). */
  --bf-catmark-hi: rgba(255, 255, 255, 0.08);
}

/* ── Métrica que ENSANCHA con el ancho (MF-2/MF-3) ────────────────────────────
   El único token que hoy depende del corte. Va aparte de los bloques de tema a
   propósito: NO es color, y los dos bloques oscuros de abajo son planos y se
   comparan entre sí (guardarraíl D10). Desde el corte compacto↔medio la métrica
   es la de ancho completo, sin excepción. */
@media (min-width: 620px) {
  :root {
    --bf-panel-padding: 1.125rem;
  }
}

/* ── Tema oscuro · interruptor 1: preferencia explícita del usuario ───────────
   El shell refleja data-theme en <html> al conmutar. Sólo COLORES (M-7): la
   métrica y la tipografía son comunes a los dos temas y viven arriba.
   Este bloque y el de la media query de abajo son el MISMO juego de valores; la
   suite del Host los compara para que no diverjan (D10).
   El color-scheme de este bloque NO entra en esa comparación (sólo compara los
   --bf-*) y no hace falta en el de abajo: allí «light dark» ya resuelve oscuro. */
/* Los controles NATIVOS —el icono y el calendario de un campo de fecha, las barras
   de desplazamiento, la lista de un <select>, el autocompletado— los pinta el
   navegador según color-scheme, y «light dark» de :root elige por el SISTEMA. Con
   un tema elegido a mano salían en el esquema contrario al de la página (chore
   90.298): se fija al tema elegido. Sin atributo se queda «light dark». */
:root[data-theme="light"] {
  color-scheme: light;
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bf-accent: #ee7b24;
  --bf-accent-text: #f5a061;
  --bf-accent-fg: #ffffff;
  --bf-accent-soft: #2b2018;
  --bf-cta: #e58449; /* 90.908: un poquito más naranja (era #e3864f) */
  --bf-cta-hover: #ec9258; /* 90.908 (era #ec9661) */
  --bf-on-cta: #1a1a1a;
  --bf-cta-edge: var(--bf-cta);

  --bf-sidebar-bg: #10181f;
  --bf-sidebar-fg: #e8eef3;
  --bf-sidebar-hover-bg: #1d2831;
  --bf-sidebar-muted: #8395a2;
  --bf-sidebar-line: rgba(255, 255, 255, 0.07);

  /* La escalera de superficies (contrato §2.2). En oscuro la tarjeta NO se separa
     del panel por relleno —son el mismo color— sino por canto y bisel: quien
     dibuja el borde de la pieza es el cristal (chore 90.251), no el fondo. */
  /* La noche, un poco más honda (chores 90.707 y 90.715): un punto más que la de 90.707 (#0e1216 /
     #161b1f), ya a la altura de la del inicio de sesión (#0c1014 / #14191d); panel y tarjeta no cambian.
     AA del texto sobre la mesa: surface-fg 17.9:1, muted 9.5:1, accent-text 8.6:1; sobre lo más claro de
     la luz de la vista (#20262d): 15.1, 8.0 y 7.3:1. */
  --bf-canvas: #0c1014;
  --bf-app-bg: #13171b;
  --bf-surface-bg: #2c3239;
  /* La tarjeta, más oscura (chore 90.730) y SIN tono azul (90.749): un gris neutro, apenas cálido. Desde la 90.746,
     del color de fondo de los desplegables (--bf-drop-base: aquí el lienzo, #0c1014); el velo y el desenfoque, igual.
     Desde la 90.751, con los tonos del modo oscuro (a petición del usuario): la lámina maciza del lateral oscuro. */
  --bf-card-bg: #1e2327;
  --bf-edge-line: rgba(174, 182, 191, 0.16);
  --bf-rim-hi: rgba(255, 255, 255, 0.22); --bf-rim-mid: rgba(255, 255, 255, 0.07); --bf-rim-lo: rgba(255, 255, 255, 0.1);
  /* Los modales, EXACTAMENTE las ventanas del modo sencillo de Hugin (chore 90.732): #15191C al 88 %. */
  --bf-modal-tint: var(--bf-head-glass); /* 90.769: el de la cabecera al rodar */
  --bf-modal-solid: #15191c;
  --bf-card-veil-mix: 30%;
  --bf-edge-hi-a: 0.07;
  --bf-edge-lo-a: 0.6;
  --bf-panel-bg: rgba(11, 14, 16, 0.5);
  --bf-elevated-bg: #383f47;
  --bf-control-bg: #2b2f33;
  --bf-control-hover-bg: #373c40;
  --bf-select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B4BDC7' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  --bf-number-carets: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B4BDC7' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.5 9.5 12 5l4.5 4.5M7.5 14.5 12 19l4.5-4.5'/%3E%3C/svg%3E");
  --bf-surface-fg: #fefefe;
  --bf-muted: #b4bdc7;

  --bf-border: rgba(174, 182, 191, 0.16);
  --bf-border-soft: #212c35;
  /* Mismo valor que antes de la 90.146: el separador de M-5 no se toca. */
  --bf-border-strong: rgba(214, 224, 232, 0.27);

  /* M-6 al revés: aquí el pozo se consigue con un velo CLARO sobre la tarjeta, no
     bajando el campo por debajo de ella. El filete de control sube de 0.45 a 0.64
     (contrato §2.2): medido sobre los planos nuevos da 4.02:1 sobre la mesa,
     3.54:1 sobre panel y tarjeta, 3.63:1 sobre el desplegable opaco, 4.10:1 sobre
     el hundido, 3.23:1 sobre el propio campo y 3.10:1 sobre la superficie
     elevada, que es el peor caso. Todos por encima de los 3:1 que SC 1.4.11 pide
     al límite de un control. */
  --bf-field-bg: rgba(174, 182, 191, 0.07);
  --bf-field-line: rgba(174, 182, 191, 0.64);

  /* En oscuro el pomo NO necesita refuerzo y por eso se queda con el filete de
     campo: el relleno del pomo ya se separa del naranja de sobra. Y no podría
     reforzarse aunque se quisiera — la tinta de este tema es CLARA, y el naranja
     queda entre ella y el pomo: al 100 % de opacidad llegaría a 2.09:1, nunca a
     3:1. Subirlo sería ruido, no accesibilidad (chore 90.159). */
  --bf-switch-knob-line-on: var(--bf-field-line);
  --bf-switch-track: #444d57;
  --bf-switch-thumb: #171b1e;

  /* El botón conserva la escala que le dio la 90.136/90.214 hasta que la chore
     90.254 lo rehaga con el mando de Hugin: sus ratios están medidos contra los
     planos ANTERIORES y por tanto ya no valen. */
  --bf-button-bg: var(--bf-field-bg);
  --bf-button-bg-legacy: #232e37;
  --bf-button-line: var(--bf-field-line);
  --bf-button-hover-bg: #2c3843;
  --bf-button-press-bg: #33404c;

  --bf-success: #68f09d;
  --bf-success-text: #68f09d;
  --bf-warning: #eed85b;
  --bf-warning-text: #eed85b;
  --bf-danger: #ff5252;
  --bf-danger-solid: var(--bf-danger);
  --bf-on-danger: #1a1a1a;
  --bf-danger-text: #ff9b9b;
  --bf-info: var(--bf-muted);
  --bf-info-text: var(--bf-surface-fg);
  --bf-off: #808080;

  --bf-data-1: #3e86d6;
  --bf-data-2: #d96c2a;
  --bf-data-3: #3fa34d;

  --bf-hover: rgba(174, 182, 191, 0.11);
  /* Al 0.2 y no al 0.11 del hover (chore 90.256): valiendo lo mismo, «lo puesto»
     y «lo apuntado» eran el mismo fondo, y el segmentado —el primero que lo gasta—
     no distinguía su opción elegida de la que tenía el ratón encima. Es el valor
     que el sistema de origen corrigió en su hoja (contrato de diseño §8.1, punto
     4); la tabla de §2.2 aún cita el de antes. */
  --bf-selected: rgba(174, 182, 191, 0.2);
  /* Con el velo al 0.2 el fondo de lo seleccionado se ACLARA, y el naranja
     legible del oscuro (--bf-accent-text) se queda en 3.69:1 sobre la tarjeta,
     por debajo de 4,5:1. Sube un 35 % hacia la tinta del texto (#f8c198): sigue
     leyéndose naranja y da 4.78:1 en el peor plano (tarjeta), 5.70:1 en la mesa.
     En claro, --bf-accent-text da 6.24:1 sobre el mismo velo. */
  --bf-selected-ink: color-mix(in srgb, var(--bf-accent-text), var(--bf-surface-fg) 35%);
  --bf-bevel-hi: rgba(255, 255, 255, 0.11);
  --bf-bevel-lo: rgba(255, 255, 255, 0.05);

  /* Cristal y sombra (contrato §2.4 · chore 90.251). Sólo lo que NO puede
     calcularse: el velo, el canto y el bisel siguen al tema desde :root con var().
     Aquí la sombra de lo elevado lleva un aro de 1 px, y no es adorno: sobre fondo
     negro una sombra negra no separa nada, así que lo que despega la capa es que
     sea más clara que el lienzo y lleve el borde iluminado. */
  --bf-elevated-edge: rgba(183, 190, 199, 0.3);
  --bf-glass-drop: 0 10px 26px rgba(0, 0, 0, 0.32);
  --bf-tip-glass: rgba(56, 63, 71, 0.36); /* la pista, como la del carril (chore 90.973) */
  --bf-tip-solid: #383f47;
  --bf-tip-filter: blur(24px) saturate(1.6) brightness(0.78);
  --bf-tip-fg: #f6f7f8;
  --bf-tip-drop: 0 18px 45px rgba(0, 0, 0, 0.55);
  --bf-tip-bevel: inset 1.5px 1.5px 0 -0.5px rgba(255, 255, 255, 0.11), inset -1.5px -1.5px 0 -0.5px rgba(255, 255, 255, 0.05);
  --bf-glass-white: 0%;
  --bf-glass-alpha: 74%;
  --bf-elevated-shadow: 0 18px 45px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(174, 182, 191, 0.07);
  --bf-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 12px -2px rgba(0, 0, 0, 0.45);
  --bf-relief-floor: var(--bf-shadow-ink);
  --bf-panel-color: #2c3239;
  --bf-lit-tint: #8f8a85;

  /* Campos como los de Hugin: SIN CANTO también de noche (chore 90.399; ver el
     bloque claro). Y el leve resplandor de arriba a la izquierda de las vistas, que
     en claro no existe. */
  --bf-field-edge: transparent;
  --bf-disabled-ink: 0.45;
  /* La luz de arriba a la izquierda, con la noche más honda (90.707, 90.715): un poco más clara que la mesa que la
     recibe de lo que era, para que el degradado se siga leyendo igual. */
  --bf-view-glow: #20262d;
  --bf-view-blob: rgba(238, 123, 36, 0.16);
  --bf-view-blob2: rgba(90, 150, 210, 0.12);
  /* EL FONDO DE LA VISTA, ELABORADO (chore 90.765): una aurora quieta en capas, de arriba abajo (sin grano desde la 90.768, por rendimiento): el grano
     finísimo que le quita lo digital; la brasa del acento arriba a la derecha con su halo ámbar desplazado hacia
     el centro; un velo iris que la enlaza en diagonal; el azul acero abajo a la izquierda; y el resplandor de
     siempre arriba a la izquierda. Fija al hacer scroll, para que el cristal de encima la deje ver al moverse. */
  /* LA IMAGEN QUIETA (chore 90.783), de noche: «Eclipse tras el lateral» con la configuración que eligió el usuario
     (intensidad 0,8, tinta 1, viñeta 0,8, desenfoque 28 px) sobre el lienzo de siempre. */
  /* Chore 90.969: encima de la imagen, otra vez el resplandor de siempre arriba a la izquierda —el que enmarca el rastro de
     migas—, que se perdió al pasar el fondo a imagen quieta. */
  --bf-view-art: url("/img/vista/eclipse-oscuro.webp") left center / cover no-repeat, var(--bf-canvas);
  /* EL LATERAL SE FUNDE CON LA VISTA (chore 90.997, «el sidebar en modo oscuro debe asimilarse más al color de fondo de
     las vistas»). Su velo era el de las tarjetas (--bf-card-bg al 30 %), más claro que el lienzo: medido, el lateral
     #191F24 junto a una vista #101319. Ahora el velo es el LIENZO de la vista al 30 %: el cristal deja ver el mismo
     fondo, desenfocado y apenas oscurecido, y lo separa su canto. El grano y el desenfoque, los de la 90.917. */
  --bf-side-glass: var(--bf-grain), var(--bf-side-veil); /* 90.917; el velo, 90.997 */
  --bf-side-veil: color-mix(in srgb, var(--bf-canvas) 30%, transparent); /* 90.997 */
  --bf-pane-mix: 44%; /* 90.911 */
  --bf-side-filter: blur(16px) saturate(1.3); /* 90.917 */
  --bf-side-dim: transparent; /* sin el oscurecido de noche del marco: el lateral del mockup elegido */
  --bf-card-glass-filter: none;
  --bf-card-glass-bevel: var(--bf-lg-edge);
  --bf-card-glass-drop: 0 0 0 transparent;
  /* El cristal de General (90.708), de noche: velo más fino y sin filete. */
  --bf-head-glass: var(--bf-pane-glass); /* 90.911 (era rgba(28, 32, 36, 0.56)) */
  --bf-lg-fill: var(--bf-card-veil); /* 90.755: el mismo velo que las tarjetas del Menú de Instancias */
  --bf-lg-fill-hover: var(--bf-card-veil-hover);
  --bf-lg-line: transparent;
  --bf-select-hover-veil: rgba(174, 182, 191, 0.21);
  --bf-field-search-focus-bg: #30373e;

  /* La puerta de noche: escenario casi negro —una luz sólo se ve si hay noche
     alrededor—, halo cálido y viñeta que devuelve las esquinas a negro. El
     núcleo es el MÁS CLARO de los tres: así se lee como luz y no como pintura. */
  --bf-door-stage: #0c1014;
  --bf-door-halo: radial-gradient(66rem 28rem at 58% 76%, color-mix(in srgb, var(--bf-accent) 8%, transparent), transparent 72%);
  --bf-door-vignette: radial-gradient(120% 88% at 50% 42%, transparent 38%, rgba(0, 0, 0, 0.6));
  --bf-door-ember: #933c0c;
  --bf-door-flame: var(--bf-accent);
  --bf-door-core: #ffd9ac;
  --bf-door-mark: var(--bf-accent);
  --bf-door-hl-end: #ffd9ac;
  --bf-door-halo-strength: 0.7;
  --bf-door-core-width: 2.8;
  --bf-door-core-opacity: 1;
  --bf-door-strand-over: 0.52;
  --bf-door-strand-under: 0.68;
  --bf-door-bloom-inner: 0.18;
  --bf-door-bloom-middle: 0.06;
  --bf-door-grain: 0.045;
  --bf-door-card-shadow: 0 1rem 2.25rem -1.25rem rgba(0, 0, 0, 0.38);
  --bf-door-seg-selected: rgba(174, 182, 191, 0.11);
  /* De noche la constelación, más transparente, y el brillo del logotipo, algo más contenido: sobre
     el escenario casi negro, el mismo resplandor que de día se leía como un foco. */
  --bf-door-orbit-opacity: 0.82;
  --bf-door-wave-opacity: 0.82;
  --bf-door-logo-glow: drop-shadow(0 0 0.95rem color-mix(in srgb, var(--bf-accent) 36%, transparent));
  /* De noche el cristal va más abierto: sobre el escenario casi negro, el 40 % del día
     se leía como un panel macizo y tapaba la ola. */
  --bf-door-panel-veil: 25%;
  /* De noche el cristal se tiñe del color de la VISTA (la mesa), no del panel: con el
     del panel la columna se leía como una pieza más clara pegada encima del escenario. */
  --bf-door-glass-tint: var(--bf-app-bg);
  --bf-door-pill-veil: 22%;
  /* De noche, el destello blanco de «Cerrar sesión» (allí al 9 %) a poco más de la mitad (chore 90.991). */
  --bf-door-idp-glint: rgba(255, 255, 255, 0.05);
  --bf-door-panel-drop: 0 0 0 transparent;
  --bf-door-field-bg: color-mix(in srgb, var(--bf-surface-bg) 75%, transparent);
  /* Colores que estaban escritos en bifrost.css (revisión del 2026-10-13): ver el bloque claro. */
  --bf-switch-thumb-top: rgba(255, 255, 255, 0.05);
  --bf-switch-thumb-bottom: rgba(255, 255, 255, 0.04);
  --bf-switch-thumb-hi: rgba(255, 255, 255, 0.06);
  --bf-switch-thumb-line: rgba(255, 255, 255, 0.1);
  --bf-switch-thumb-drop: rgba(0, 0, 0, 0.25);
  --bf-seg-line: rgba(174, 182, 191, 0.14);
  --bf-seg-rim: linear-gradient(135deg, rgba(255, 255, 255, 0.145) 0%, rgba(255, 255, 255, 0.045) 18%, transparent 42%,
    transparent 64%, rgba(255, 255, 255, 0.065) 86%, rgba(255, 255, 255, 0.045) 100%);
  --bf-seg-gota-top: rgba(255, 255, 255, 0.045);
  --bf-seg-gota-bottom: rgba(255, 255, 255, 0.035);
  --bf-seg-gota-hi: rgba(255, 255, 255, 0.035);
  --bf-seg-gota-spec-soft: rgba(255, 255, 255, 0.02);
  --bf-seg-gota-line: rgba(255, 255, 255, 0.065);
  --bf-seg-gota-lift: 1.03;
  --bf-aud-update: #5aa9ff;
  --bf-aud-view: #a99cff;
  --bf-aud-pressed-bg: rgba(255, 255, 255, 0.08);
  --bf-modal-soft-drop: 0 24px 60px -28px rgba(0, 0, 0, 0.4), 0 4px 12px -8px rgba(0, 0, 0, 0.18);
}

/* ── Tema oscuro · interruptor 2: preferencia del sistema ─────────────────────
   Salvo override explícito a "light". Mismos valores que el bloque de arriba. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bf-accent: #ee7b24;
    --bf-accent-text: #f5a061;
    --bf-accent-fg: #ffffff;
    --bf-accent-soft: #2b2018;
    --bf-cta: #e58449; /* 90.908: un poquito más naranja (era #e3864f) */
    --bf-cta-hover: #ec9258; /* 90.908 (era #ec9661) */
    --bf-on-cta: #1a1a1a;
    --bf-cta-edge: var(--bf-cta);

    --bf-sidebar-bg: #10181f;
    --bf-sidebar-fg: #e8eef3;
    --bf-sidebar-hover-bg: #1d2831;
    --bf-sidebar-muted: #8395a2;
    --bf-sidebar-line: rgba(255, 255, 255, 0.07);

    /* La escalera de superficies (contrato §2.2). En oscuro la tarjeta NO se separa
       del panel por relleno —son el mismo color— sino por canto y bisel: quien
       dibuja el borde de la pieza es el cristal (chore 90.251), no el fondo. */
    /* La noche, un poco más honda (chores 90.707 y 90.715): un punto más que la de 90.707 (#0e1216 /
       #161b1f), ya a la altura de la del inicio de sesión (#0c1014 / #14191d); panel y tarjeta no cambian.
       AA del texto sobre la mesa: surface-fg 17.9:1, muted 9.5:1, accent-text 8.6:1; sobre lo más claro de
       la luz de la vista (#20262d): 15.1, 8.0 y 7.3:1. */
    --bf-canvas: #0c1014;
    --bf-app-bg: #13171b;
    --bf-surface-bg: #2c3239;
    --bf-card-bg: #1e2327; /* 90.751: los tonos del modo oscuro (la lámina del lateral) */
    --bf-edge-line: rgba(174, 182, 191, 0.16);
    --bf-rim-hi: rgba(255, 255, 255, 0.22); --bf-rim-mid: rgba(255, 255, 255, 0.07); --bf-rim-lo: rgba(255, 255, 255, 0.1);
    --bf-modal-tint: var(--bf-head-glass); /* 90.769: el de la cabecera al rodar */
    --bf-modal-solid: #15191c;
    --bf-card-veil-mix: 30%;
    --bf-edge-hi-a: 0.07;
    --bf-edge-lo-a: 0.6;
    --bf-panel-bg: rgba(11, 14, 16, 0.5);
    --bf-elevated-bg: #383f47;
    --bf-control-bg: #2b2f33;
    --bf-control-hover-bg: #373c40;
    --bf-select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B4BDC7' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    --bf-number-carets: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B4BDC7' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.5 9.5 12 5l4.5 4.5M7.5 14.5 12 19l4.5-4.5'/%3E%3C/svg%3E");
    --bf-surface-fg: #fefefe;
    --bf-muted: #b4bdc7;

    --bf-border: rgba(174, 182, 191, 0.16);
    --bf-border-soft: #212c35;
    /* Mismo valor que antes de la 90.146: el separador de M-5 no se toca. */
    --bf-border-strong: rgba(214, 224, 232, 0.27);

    /* M-6 al revés: aquí el pozo se consigue con un velo CLARO sobre la tarjeta, no
       bajando el campo por debajo de ella. El filete de control sube de 0.45 a 0.64
       (contrato §2.2): medido sobre los planos nuevos da 4.02:1 sobre la mesa,
       3.54:1 sobre panel y tarjeta, 3.63:1 sobre el desplegable opaco, 4.10:1 sobre
       el hundido, 3.23:1 sobre el propio campo y 3.10:1 sobre la superficie
       elevada, que es el peor caso. Todos por encima de los 3:1 que SC 1.4.11 pide
       al límite de un control. */
    --bf-field-bg: rgba(174, 182, 191, 0.07);
    --bf-field-line: rgba(174, 182, 191, 0.64);

    /* En oscuro el pomo NO necesita refuerzo y por eso se queda con el filete de
       campo: el relleno del pomo ya se separa del naranja de sobra. Y no podría
       reforzarse aunque se quisiera — la tinta de este tema es CLARA, y el naranja
       queda entre ella y el pomo: al 100 % de opacidad llegaría a 2.09:1, nunca a
       3:1. Subirlo sería ruido, no accesibilidad (chore 90.159). */
    --bf-switch-knob-line-on: var(--bf-field-line);
    --bf-switch-track: #444d57;
    --bf-switch-thumb: #171b1e;

    /* El botón conserva la escala que le dio la 90.136/90.214 hasta que la chore
       90.254 lo rehaga con el mando de Hugin: sus ratios están medidos contra los
       planos ANTERIORES y por tanto ya no valen. */
    --bf-button-bg: var(--bf-field-bg);
    --bf-button-bg-legacy: #232e37;
    --bf-button-line: var(--bf-field-line);
    --bf-button-hover-bg: #2c3843;
    --bf-button-press-bg: #33404c;

    --bf-success: #68f09d;
    --bf-success-text: #68f09d;
    --bf-warning: #eed85b;
    --bf-warning-text: #eed85b;
    --bf-danger: #ff5252;
    --bf-danger-solid: var(--bf-danger);
    --bf-on-danger: #1a1a1a;
    --bf-danger-text: #ff9b9b;
    --bf-info: var(--bf-muted);
    --bf-info-text: var(--bf-surface-fg);
    --bf-off: #808080;

    --bf-data-1: #3e86d6;
    --bf-data-2: #d96c2a;
    --bf-data-3: #3fa34d;

    --bf-hover: rgba(174, 182, 191, 0.11);
    --bf-selected: rgba(174, 182, 191, 0.2);
    --bf-selected-ink: color-mix(in srgb, var(--bf-accent-text), var(--bf-surface-fg) 35%);
    --bf-bevel-hi: rgba(255, 255, 255, 0.11);
    --bf-bevel-lo: rgba(255, 255, 255, 0.05);

    /* Cristal y sombra (contrato §2.4 · chore 90.251). Sólo lo que NO puede
       calcularse: el velo, el canto y el bisel siguen al tema desde :root con var().
       Aquí la sombra de lo elevado lleva un aro de 1 px, y no es adorno: sobre fondo
       negro una sombra negra no separa nada, así que lo que despega la capa es que
       sea más clara que el lienzo y lleve el borde iluminado. */
    --bf-elevated-edge: rgba(183, 190, 199, 0.3);
    --bf-glass-drop: 0 10px 26px rgba(0, 0, 0, 0.32);
    --bf-tip-glass: rgba(56, 63, 71, 0.36); /* la pista, como la del carril (chore 90.973) */
    --bf-tip-solid: #383f47;
    --bf-tip-filter: blur(24px) saturate(1.6) brightness(0.78);
    --bf-tip-fg: #f6f7f8;
    --bf-tip-drop: 0 18px 45px rgba(0, 0, 0, 0.55);
    --bf-tip-bevel: inset 1.5px 1.5px 0 -0.5px rgba(255, 255, 255, 0.11), inset -1.5px -1.5px 0 -0.5px rgba(255, 255, 255, 0.05);
    --bf-glass-white: 0%;
    --bf-glass-alpha: 74%;
    --bf-elevated-shadow: 0 18px 45px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(174, 182, 191, 0.07);
    --bf-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 12px -2px rgba(0, 0, 0, 0.45);
    --bf-relief-floor: var(--bf-shadow-ink);
    --bf-panel-color: #2c3239;
    --bf-lit-tint: #8f8a85;

    /* Campos como los de Hugin: SIN CANTO también de noche (chore 90.399; ver el
       bloque claro). Y el leve resplandor de arriba a la izquierda de las vistas, que
       en claro no existe. */
    --bf-field-edge: transparent;
    --bf-disabled-ink: 0.45;
    /* La luz de arriba a la izquierda, con la noche más honda (90.707, 90.715): un poco más clara que la mesa que la
       recibe de lo que era, para que el degradado se siga leyendo igual. */
    --bf-view-glow: #20262d;
    --bf-view-blob: rgba(238, 123, 36, 0.16);
    --bf-view-blob2: rgba(90, 150, 210, 0.12);
    /* EL FONDO DE LA VISTA, ELABORADO (chore 90.765): una aurora quieta en capas, de arriba abajo (sin grano desde la 90.768, por rendimiento): el grano
       finísimo que le quita lo digital; la brasa del acento arriba a la derecha con su halo ámbar desplazado hacia
       el centro; un velo iris que la enlaza en diagonal; el azul acero abajo a la izquierda; y el resplandor de
       siempre arriba a la izquierda. Fija al hacer scroll, para que el cristal de encima la deje ver al moverse. */
    /* LA IMAGEN QUIETA (chore 90.783), de noche: «Eclipse tras el lateral» con la configuración que eligió el usuario
       (intensidad 0,8, tinta 1, viñeta 0,8, desenfoque 28 px) sobre el lienzo de siempre. */
    /* Chore 90.969: encima de la imagen, otra vez el resplandor de siempre arriba a la izquierda —el que enmarca el rastro de
       migas—, que se perdió al pasar el fondo a imagen quieta. */
    --bf-view-art: url("/img/vista/eclipse-oscuro.webp") left center / cover no-repeat, var(--bf-canvas);
    --bf-side-glass: var(--bf-grain), var(--bf-side-veil); /* 90.917; el velo, 90.997 */
    --bf-side-veil: color-mix(in srgb, var(--bf-canvas) 30%, transparent); /* 90.997 */
    --bf-pane-mix: 44%; /* 90.911 */
    --bf-side-filter: blur(16px) saturate(1.3); /* 90.917 */
    --bf-side-dim: transparent; /* sin el oscurecido de noche del marco: el lateral del mockup elegido */
    --bf-card-glass-filter: none;
    --bf-card-glass-bevel: var(--bf-lg-edge);
    --bf-card-glass-drop: 0 0 0 transparent;
    /* El cristal de General (90.708), de noche: velo más fino y sin filete. */
    --bf-head-glass: var(--bf-pane-glass); /* 90.911 (era rgba(28, 32, 36, 0.56)) */
    --bf-lg-fill: var(--bf-card-veil); /* 90.755: el mismo velo que las tarjetas del Menú de Instancias */
    --bf-lg-fill-hover: var(--bf-card-veil-hover);
    --bf-lg-line: transparent;
    --bf-select-hover-veil: rgba(174, 182, 191, 0.21);
    --bf-field-search-focus-bg: #30373e;

    /* La puerta de noche: escenario casi negro —una luz sólo se ve si hay noche
       alrededor—, halo cálido y viñeta que devuelve las esquinas a negro. El
       núcleo es el MÁS CLARO de los tres: así se lee como luz y no como pintura. */
    --bf-door-stage: #0c1014;
    --bf-door-halo: radial-gradient(66rem 28rem at 58% 76%, color-mix(in srgb, var(--bf-accent) 8%, transparent), transparent 72%);
    --bf-door-vignette: radial-gradient(120% 88% at 50% 42%, transparent 38%, rgba(0, 0, 0, 0.6));
    --bf-door-ember: #933c0c;
    --bf-door-flame: var(--bf-accent);
    --bf-door-core: #ffd9ac;
    --bf-door-mark: var(--bf-accent);
    --bf-door-hl-end: #ffd9ac;
    --bf-door-halo-strength: 0.7;
    --bf-door-core-width: 2.8;
    --bf-door-core-opacity: 1;
    --bf-door-strand-over: 0.52;
    --bf-door-strand-under: 0.68;
    --bf-door-bloom-inner: 0.18;
    --bf-door-bloom-middle: 0.06;
    --bf-door-grain: 0.045;
    --bf-door-card-shadow: 0 1rem 2.25rem -1.25rem rgba(0, 0, 0, 0.38);
    --bf-door-seg-selected: rgba(174, 182, 191, 0.11);
    /* De noche la constelación, más transparente, y el brillo del logotipo, algo más contenido: sobre
       el escenario casi negro, el mismo resplandor que de día se leía como un foco. */
    --bf-door-orbit-opacity: 0.82;
    --bf-door-wave-opacity: 0.82;
    --bf-door-logo-glow: drop-shadow(0 0 0.95rem color-mix(in srgb, var(--bf-accent) 36%, transparent));
    --bf-door-panel-veil: 25%;
    --bf-door-glass-tint: var(--bf-app-bg);
    --bf-door-pill-veil: 22%;
    --bf-door-idp-glint: rgba(255, 255, 255, 0.05);
    --bf-door-panel-drop: 0 0 0 transparent;
    --bf-door-field-bg: color-mix(in srgb, var(--bf-surface-bg) 75%, transparent);
    /* Colores que estaban escritos en bifrost.css (revisión del 2026-10-13): ver el bloque claro. */
    --bf-switch-thumb-top: rgba(255, 255, 255, 0.05);
    --bf-switch-thumb-bottom: rgba(255, 255, 255, 0.04);
    --bf-switch-thumb-hi: rgba(255, 255, 255, 0.06);
    --bf-switch-thumb-line: rgba(255, 255, 255, 0.1);
    --bf-switch-thumb-drop: rgba(0, 0, 0, 0.25);
    --bf-seg-line: rgba(174, 182, 191, 0.14);
    --bf-seg-rim: linear-gradient(135deg, rgba(255, 255, 255, 0.145) 0%, rgba(255, 255, 255, 0.045) 18%, transparent 42%,
      transparent 64%, rgba(255, 255, 255, 0.065) 86%, rgba(255, 255, 255, 0.045) 100%);
    --bf-seg-gota-top: rgba(255, 255, 255, 0.045);
    --bf-seg-gota-bottom: rgba(255, 255, 255, 0.035);
    --bf-seg-gota-hi: rgba(255, 255, 255, 0.035);
    --bf-seg-gota-spec-soft: rgba(255, 255, 255, 0.02);
    --bf-seg-gota-line: rgba(255, 255, 255, 0.065);
    --bf-seg-gota-lift: 1.03;
    --bf-aud-update: #5aa9ff;
    --bf-aud-view: #a99cff;
    --bf-aud-pressed-bg: rgba(255, 255, 255, 0.08);
    --bf-modal-soft-drop: 0 24px 60px -28px rgba(0, 0, 0, 0.4), 0 4px 12px -8px rgba(0, 0, 0, 0.18);
  }
}

/* ── Sin cristal: lo que flota pasa a OPACO ───────────────────────────────────
   Un marco translúcido SIN desenfoque no es elegancia: es texto encima de texto.
   Y pasa de verdad —un cliente ligero de planta, un navegador empotrado viejo, un
   escritorio remoto con la composición apagada—: lo que se ve entonces son cajas
   transparentes ilegibles, justo donde hay que leer números pequeños.

   LA REGLA ES UNA: --bf-glass-surface pasa a la superficie flotante opaca y
   --bf-glass-filter a none (y lo mismo la cabecera pegajosa de la tabla, que es
   cristal sobre la tarjeta: pasa a la tarjeta maciza, chore 90.259). El bisel y
   la sombra se quedan —sobre opaco siguen dando relieve— y las piezas no se
   enteran de nada. Lo que cambia es QUIÉN la dispara, y son tres vías que CSS no
   deja unir en una sola regla:
     1. el navegador no sabe hacer backdrop-filter;
     2. la persona ha pedido menos transparencia en su sistema;
     3. el producto pasa reduce="transparency" al shell (contrato 00.004 §5.1).
   La tercera queda ESCRITA pero todavía no llega sola: el atributo vive en
   <bifrost-shell> y quien lo refleje en <html> como data-reduce es la chore 90.269.
   El selector está aquí para que aquello sea una línea y no otra ronda de CSS.

   Van al FINAL de la hoja a propósito: las dos primeras pesan lo mismo que el :root
   de arriba (0,1,0), así que entre ellas manda el orden. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root {
    --bf-glass-surface: var(--bf-elevated-bg);
    --bf-head-glass: var(--bf-elevated-bg);
    --bf-head-filter: none;
    --bf-glass-filter: none;
    --bf-table-head-veil: var(--bf-card-bg);
    --bf-table-head-filter: none;
    --bf-card-glass-filter: none;
    --bf-modal-bg: var(--bf-modal-solid); /* 90.732 */
    --bf-modal-filter: none;
    --bf-tip-glass: var(--bf-tip-solid);
    --bf-tip-filter: none;
  }
}

@media (prefers-reduced-transparency: reduce) {
  :root {
    --bf-view-blob: transparent;
    --bf-view-blob2: transparent;
    --bf-view-art: var(--bf-view-halo);
    --bf-glass-surface: var(--bf-elevated-bg);
    --bf-head-glass: var(--bf-elevated-bg);
    --bf-head-filter: none;
    --bf-glass-filter: none;
    --bf-table-head-veil: var(--bf-card-bg);
    --bf-table-head-filter: none;
    --bf-card-glass-filter: none;
    --bf-modal-bg: var(--bf-modal-solid); /* 90.732 */
    --bf-modal-filter: none;
    --bf-tip-glass: var(--bf-tip-solid);
    --bf-tip-filter: none;
  }
}

:root[data-reduce~="transparency"] {
  --bf-view-blob: transparent;
  --bf-view-blob2: transparent;
  --bf-view-art: var(--bf-view-halo);
  --bf-glass-surface: var(--bf-elevated-bg);
  --bf-head-glass: var(--bf-elevated-bg);
  --bf-head-filter: none;
  --bf-glass-filter: none;
  --bf-table-head-veil: var(--bf-card-bg);
  --bf-table-head-filter: none;
  --bf-card-glass-filter: none;
  --bf-modal-bg: var(--bf-modal-solid); /* 90.732 */
  --bf-modal-filter: none;
  --bf-tip-glass: var(--bf-tip-solid);
  --bf-tip-filter: none;
}

/* ── Sin movimiento: lo que se mueve, deja de moverse ─────────────────────────
   Mismo patrón que el apagado del cristal, y por el mismo motivo: la regla se
   escribe UNA vez —las cuatro duraciones a un milisegundo— y ninguna pieza tiene
   que acordarse de consultar la preferencia. Un milisegundo y no cero a propósito:
   así los eventos de fin de transición siguen disparándose y no se queda a medias
   quien espera uno para ordenar lo siguiente.

   Las curvas NO se tocan: sin duración no hay recorrido que curvar.

   Dos vías, otra vez: la preferencia del sistema y el atributo reduce="motion" del
   contrato 00.004 §5.1, que cablea la chore 90.269. Lo que esto NO alcanza es una
   animación con `animation` y valores escritos a mano, y ya hay una: el aro del
   botón ocupado (chore 90.254). Su duración NO sale de estos tokens a propósito
   —ponerlos a un milisegundo haría girar el aro como un ventilador en vez de
   pararlo—, así que lleva su propia regla, junto a la pieza, en bifrost.css §2.
   El barrido del esqueleto (chore 90.259) es el otro bucle y va igual: su duración
   es token (--bf-dur-sweep) pero NO está en esta lista, y la pieza se para sola
   por las dos vías, en bifrost.css §3. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --bf-dur-fast: 1ms;
    --bf-dur-base: 1ms;
    --bf-dur-side: 1ms;
    --bf-dur-slow: 1ms;
    --bf-dur-thumb: 1ms;
    --bf-dur-glide: 1ms;
    --bf-dur-toast: 1ms;
    --bf-dur-pop: 1ms;
    --bf-dur-pop-move: 1ms;
    --bf-dur-mega: 1ms;
    --bf-dur-mega-out: 1ms;
    --bf-dur-theme: 1ms;
    --bf-dur-entry: 1ms;
    --bf-dur-entry-side: 1ms;
  }
}

:root[data-reduce~="motion"] {
  --bf-dur-fast: 1ms;
  --bf-dur-base: 1ms;
  --bf-dur-side: 1ms;
  --bf-dur-slow: 1ms;
  --bf-dur-thumb: 1ms;
  --bf-dur-glide: 1ms;
  --bf-dur-toast: 1ms;
  --bf-dur-pop: 1ms;
  --bf-dur-pop-move: 1ms;
  --bf-dur-mega: 1ms;
  --bf-dur-mega-out: 1ms;
  --bf-dur-theme: 1ms;
  --bf-dur-entry: 1ms;
  --bf-dur-entry-side: 1ms;
}

/* EL ACENTO DEL TITULAR (el nombre del saludo del Inicio y la palabra destacada de la promesa del login) SALE DE LA
   MARCA: los dos extremos de su degradado se derivan de --bf-accent en OKLCH, para que un color de marca distinto
   llegue también ahí. Las diferencias están medidas para que, con el naranja de la casa (#ee7b24), salgan los mismos
   tonos de antes: #c25a0e y #e0701f en claro, #ffd9ac al final en oscuro (en oscuro el principio ya era
   --bf-accent). Va tras @supports porque la sintaxis de color relativo no la tienen todos los navegadores que
   admitimos (Firefox 101, Safari 16.4): en esos se quedan los hex de arriba. */
@supports (color: oklch(from red l c h)) {
  :root {
    --bf-door-mark: oklch(from var(--bf-accent) calc(l - 0.1147) calc(c * 0.912) calc(h - 3.03));
    --bf-door-hl-end: oklch(from var(--bf-accent) calc(l - 0.0367) calc(c * 0.9715) calc(h - 1.36));
  }

  :root[data-theme="dark"] {
    --bf-door-mark: var(--bf-accent);
    --bf-door-hl-end: oklch(from var(--bf-accent) 0.9066 calc(c * 0.4292) calc(h + 19.76));
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --bf-door-mark: var(--bf-accent);
      --bf-door-hl-end: oklch(from var(--bf-accent) 0.9066 calc(c * 0.4292) calc(h + 19.76));
    }
  }
}
