/* ══════════════════════════════════════════════════════════════════
   GREY97 · TOKENS v2 — FUENTE DE VERDAD CANÓNICA del sistema
   Ningún valor de diseño (color, tipo, espaciado, geometría, preset)
   vive fuera de aquí. CLAUDE.md: assets/tokens.css manda.
   ──────────────────────────────────────────────────────────────────
   UBICACIÓN · assets/tokens.css es el canon. Si un valor cambia,
              cambia AQUÍ (01#36) y de aquí baja a todo lo demás.
              `web/public/assets/tokens.css` es COPIA y debe ser byte a
              byte igual; el verificador de paridad de `npm run check`
              lo comprueba (28§4.15). Ese verificador nace porque el
              2026-08-30 la copia llevaba **cuatro tokens de retraso**
              y el fichero que lo documentaba afirmaba que estaba al día.
   ──────────────────────────────────────────────────────────────────
   ESTADO   · v1.0→v1.2: foco 1c (s8) + estados (s6) + campo-fondo 0.72
            · v1.2→v2.0 (2026-08-30, entrega del design system v2 de
              Claude Design, QA en `partes/parte_qa_ds_v2.md`):
              **cero tokens eliminados**, 3 cambiados y 62 nuevos.
              De los 3 cambiados solo uno cambia de valor de verdad:
              **--radio pasa de 2px a 4px** vía alias al nuevo
              --radio-editorial (01#115), lo que redondea las 10
              llamadas a var(--radio) de la landing de producción.
              Los otros dos (--hover-ms, --estado-ms) pasan a apuntar a
              --t-1 y --t-2 con el MISMO valor. Los cambios sobre v1.2
              van marcados `CAMBIO:` en su sección, con la razón.
            · Radios: escala 4·8·12 con dos excepciones fijas, 0 en
              marca y fotografía y 2 en email. Supersede el 0-2
              universal de 04§6.
            · preset fotográfico v1.0 · CONGELADO 2026-07-12 (01#37):
              contrast 1.05, tinte-op 0.34. Historia y perillas → 04§5.
              **El preset se aplica EN VIVO en web y solo se hornea
              para email (01#114).** El horno único, con su verificador,
              es `tools/hornea_preset.py`, que lee la receta de aquí.
   NO ESTÁ AQUÍ · la carta de calibración del preset (los cinco
              --cal-*) vive en `assets/carta_calibracion.css` y queda
              fuera de la cadena de imports a propósito (01#113): son
              muestras de referencia, no paleta, y --cal-rojo y
              --cal-verde no son un semáforo.
   USO      · <link rel="stylesheet" href="/assets/tokens.css">
              @font-face resuelve ./fonts/ → assets/fonts/ (hermano)
   FUENTES  · IBM Plex self-hosted (repo IBM/plex, licencia OFL-1.1).
              Cinco cortes. El SemiBold 600 está AUTORIZADO para display
              de producto pero su asset NO está generado: hoy
              --w-display-prod no lo consume ningún selector.
   ══════════════════════════════════════════════════════════════════ */

/* GREY97 · v2 · 00 FUENTES — IBM Plex self-hosted (repo IBM/plex, OFL-1.1). Solo estos 5 cortes.
   La itálica NO es recurso libre: solo voz-comentario de Alex (.com).
   SemiBold 600: AUTORIZADO para display de producto, asset PENDIENTE (lo añade implementación);
   hasta entonces el display de producto es 400 con jerarquía por color. */
@font-face{font-family:'IBM Plex Sans';src:url('./fonts/IBMPlexSans-Regular.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap;}
@font-face{font-family:'IBM Plex Sans';src:url('./fonts/IBMPlexSans-Bold.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap;}
@font-face{font-family:'IBM Plex Sans';src:url('./fonts/IBMPlexSans-Italic.woff2') format('woff2');font-weight:400;font-style:italic;font-display:swap;}
@font-face{font-family:'IBM Plex Mono';src:url('./fonts/IBMPlexMono-Regular.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap;}
@font-face{font-family:'IBM Plex Mono';src:url('./fonts/IBMPlexMono-Medium.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap;}

/* GREY97 · v2 · 01 COLOR — primitivos, escalera fría y alias por registro.
   Sin #FFF ni #000 puros, nunca. Base: tokens v1.2 + escalera firmada del specimen v2. */
:root{
/* 01a · primitivos v1.2 (inalterados) */
--grey-98:#0E1114;      /* abismo    · fondo hondo, chrome de producto, favicon, OG */
--grey-90:#171B1F;      /* antracita · fondo dark editorial · texto sobre claro */
--grey-80:#232930;      /* marengo   · filetes y paneles sobre dark (=hairline) */
--grey-60:#4A525B;      /* plomo     · texto secundario sobre claro · líneas gráficas dark */
--grey-50:#6B747D;      /* medio     · meta grande dark · bordes de campo (3:1) */
--grey-30:#A9B1B8;      /* niebla    · texto secundario sobre dark */
--grey-15:#D3D8DC;      /* bruma     · filetes sobre claro · texto terciario dark */
--grey-05:#EEF1F2;      /* espuma    · texto principal dark · fondo de email · bloque claro */
--payne:#8FA3B8;        /* payne     · links/UI sobre dark · SIEMPRE subrayado */
--payne-tinta:#4E657B;  /* payne tinta · links/UI sobre claro (email) */
--neon-cian:#2EE6D6;    /* neon · 11.0:1 sobre antracita · UNA acción por vista + tinta de estado
                           · nunca texto de lectura · nunca gradiente · nunca segundo acento */

/* 01b · escalera de superficies v2 (specimen 2·SISTEMA, firmada). 4 peldaños nuevos */
--lienzo:#0B0D10;       /* NUEVO v2 · fondo más hondo del sistema (web dark, chrome app) */
--abismo:var(--grey-98);
--sup-1:#12161A;        /* NUEVO v2 · canvas de producto (la lista de datos) */
--antracita:var(--grey-90);
--sup-3:#1D2329;        /* NUEVO v2 · superficies elevadas (toast, menú) */
--marengo:var(--grey-80);
--hairline:var(--grey-80);
--hairline-2:#2E3640;   /* NUEVO v2 · filete de énfasis / borde de superficie elevada */
--espuma:var(--grey-05);
--separador-suave:var(--espuma-05);      /* NUEVO v2 · separador de filas en producto · v3: alias de --espuma-05 */
--edge:rgba(238,241,242,.04);            /* NUEVO v2 · luz superior 1px en tarjetas de producto */
--abismo-72:rgba(14,17,20,0.72);         /* NUEVO v3 · abismo al 72%: el fondo del campo sobre luces de foto */
--espuma-05:rgba(238,241,242,.05);       /* NUEVO v3 · espuma al 5%: el separador de filas en producto */

/* 01c · alias por registro — EDITORIAL (landing, newsletter, email): silencio, foto y filete */
--fondo:var(--grey-90);            /* fondo dark editorial por defecto */
--fondo-hondo:var(--grey-98);
--panel:var(--grey-80);
--campo-fondo:var(--abismo-72);    /* v1.1: anillo de foco legible sobre luces de foto · v3: alias de --abismo-72 */
--campo-borde:var(--grey-50);      /* 3:1 WCAG 1.4.11 */
--campo-borde-foco:var(--payne);
--nl-item-fondo:#F6F8F9;           /* NUEVO v2 · fondo de módulo singular sobre espuma (aviso, invitación, CTA) · it.1: los items de newsletter van SIN caja (filete 1px + aire) */

/* 01d · alias por registro — PRODUCTO (panel, apps HOS): density, superficie y estado */
--prod-canvas:var(--sup-1);        /* el contenido */
--prod-chrome:var(--lienzo);       /* cabeceras y sidebars: el cromo RECEDE un paso */
--cian-tinta:color-mix(in srgb,var(--neon-cian) 11%,var(--sup-1)); /* NUEVO v2 · fondo de selección y estado activo · SOLO estado, jamás decoración */
--plano-hover:color-mix(in srgb,var(--grey-80) 55%,transparent);   /* fila/ítem bajo el cursor */
}

/* GREY97 · v2 · 02 TIPOGRAFÍA — 2 familias, 5 cortes. Voz humana=Sans · dato de máquina=Mono
   · voz-comentario de Alex=Sans itálica (solo .com). */
:root{
--sans:'IBM Plex Sans','Helvetica Neue',Helvetica,Arial,sans-serif; /* @kind font */
--mono:'IBM Plex Mono','Courier New',Courier,monospace; /* @kind font */
--w-statement:700; /* @kind font */ /* Sans — statements y títulos EDITORIALES (aprobado: quedan tal cual) */
--w-cuerpo:400; /* @kind font */ /* Sans — lectura */
--w-mono-marca:500; /* @kind font */ /* Mono — wordmark y kickers */
--w-mono-dato:400; /* @kind font */ /* Mono — metadatos */
--w-titulo-prod:400; /* @kind font */ /* NUEVO v2 · producto titula en 400 con jerarquía por color (espuma vs niebla) */
--w-dato-prod:500; /* @kind font */ /* NUEVO v2 · el dato de producto va en Mono 500 */
--w-display-prod:600; /* @kind font */ /* NUEVO v2 · SemiBold AUTORIZADO para display de producto · ASSET PENDIENTE: cae a 400 */

/* 02a · escala editorial por rol (px · line-height · tracking) — v1.2 inalterada */
--fs-s1:64px;      --lh-s1:1.06; /* @kind font */ --ls-s1:-0.015em; /* @kind font */  /* statement 1 · hero, OG */
--fs-s2:44px;      --lh-s2:1.14; /* @kind font */ --ls-s2:-0.01em; /* @kind font */   /* statement 2 · cierres */
--fs-s3:28px;      --lh-s3:1.25; /* @kind font */ --ls-s3:-0.005em; /* @kind font */  /* statement 3 · hitos, email h1 */
--fs-s2-fluido:min(var(--fs-s2),8.9vw); /* @kind font */  /* 32px a 360, 44 desde 494 */
--fs-s3-fluido:min(var(--fs-s3),6.4vw); /* @kind font */
--fs-campo-movil:16px;                   /* por debajo, iOS hace zoom al enfocar */
--fs-s4:18px;      --lh-s4:1.4; /* @kind font */                      /* título de item */
--fs-cuerpo:15px;  --lh-cuerpo:1.75; /* @kind font */                 /* cuerpo Sans 400 */
--fs-cuerpo-s:14px;--lh-cuerpo-s:1.7; /* @kind font */                /* cuerpo compacto */
--fs-dato:13px;    --lh-dato:1.7; /* @kind font */                    /* dato Mono 400 */
--fs-kicker:11px;  --lh-kicker:1.2; /* @kind font */ --ls-caps:0.14em; /* @kind font */ /* kicker Mono 500 VERSALITAS */
--fs-meta:10px;    --lh-meta:1.7; /* @kind font */ --ls-meta:0.10em; /* @kind font */ /* meta Mono 400 */
--fs-micro:8.5px;  --ls-micro:0.08em; /* @kind font */                /* marcadores [ PLACEHOLDER ] */
--fs-marca:15px;   --ls-marca:0.30em; /* @kind font */                /* wordmark GREY97 · Mono 500 */

/* 02b · escala UI de producto — NUEVO v2 (firmada): 30 cifra · 14 fila · 13 dato
   · 12.5 secundario · 11 kicker · 10 meta */
--fs-cifra:30px;     --lh-cifra:1.1; /* @kind font */   /* la cifra grande del panel · Mono 400 */
--fs-fila:14px;      --lh-fila:1.4; /* @kind font */    /* título de fila · Sans, peso --w-titulo-prod */
--fs-dato-prod:13px;                   /* dato en fila · Mono */
--fs-secundario:12.5px; --lh-secundario:1.6; /* @kind font */
}

/* GREY97 · v2 · 03 ESPACIADO — rejilla base 8px. v1.2 inalterado + constantes de panel (§10). */
:root{
--u:8px;
--sp-2:16px; --sp-3:24px; --sp-4:32px; --sp-6:48px;
--sp-8:64px; --sp-12:96px; --sp-16:128px;
--gutter:48px;          /* margen de página */
--gutter-movil:24px;    /* 24 por debajo de 900, 48 por encima */
--seccion:96px;         /* respiración vertical de sección */
--ancho-max:1280px;     /* medida de contenido editorial */
}

/* GREY97 · v2 · 04 GEOMETRÍA — escala única de radios 4·8·12 (firmada).
   CAMBIO: v1.2 declaraba --radio:2px como máximo del sistema y SIN SOMBRAS en todo;
   v2 sube el editorial a 4 (veredicto de Alex, specimen 2·SISTEMA), abre 8/12 al producto
   y autoriza sombra SOLO en producto (ver §09 producto.css). El 2px sobrevive únicamente
   en email (--radio-email) porque Outlook lo degrada con dignidad.
   REGLA (it.1, firmada): escala 4·8·12 con DOS excepciones fijas — 0 en marca y
   fotografía, 2 en email por ingeniería de clientes de correo. No hay más valores. */
:root{
--radio-0:0px;          /* marca, swatches, fotografía */
--radio-editorial:4px;  /* CAMBIO v2 (2px→4px) · cajas, campos y paneles de texto editoriales */
--radio-control:8px;    /* NUEVO v2 · controles de producto (botones, tabs, campos, menús-item) */
--radio-tarjeta:12px;   /* NUEVO v2 · tarjetas y superficies de producto (cajón, toast, menú) */
--radio-badge:4px;      /* NUEVO v2 · badges y chips de producto */
--radio-email:2px;      /* email · máximo que Outlook degrada con dignidad */
--radio:var(--radio-editorial); /* alias de compatibilidad v1.2 → apunta al editorial */
--filete:1px;           /* todo filete es 1px */
}

/* GREY97 · v2 · 05 MARCA — horizonte con corte (proporciones, no px fijos)
   largo : corto ≈ 3 : 1 · hueco ≈ 0.6 × corto · grosor 2px a cuerpo 15.
   El neon vive SOLO en el segmento corto y SOLO en estado activo.
   IMPLEMENTACIÓN: usar SIEMPRE los SVG de assets/brand/ (la marca no se redibuja).
   IT.3 · datos firmados v1.0 (doc: sistema.html §1.9 y §5.1):
   · Wordmark SIEMPRE «GREY97», una palabra, mayúsculas, Mono 500, tracking .30em. Nunca «Grey 97».
   · Zona de respeto = la altura de la G en todo el perímetro. NO va horneada en los SVG: se aplica al colocar.
   · Lockup estándar cuerpo 15 · mínimo absoluto cuerpo 11 / 96px de ancho · por debajo, monograma 97.
     La marca sin wordmark existe SOLO en favicon/avatar.
   · DECISIÓN v1.0: el 97 acompaña al corte en TODOS los tamaños del monograma (la línea sola se lee
     como guión). A 16px: dígitos a 9px sin tracking, corte 8+1+3 sobre rejilla de píxel. El favicon se
     dibuja a mano en bitmap 16×16 y 32×32, no se escala. Neon mínimo legible: 3×2px.
   · El neon es señal, no luz: NO irradia (sin glow, sombra ni gradiente sobre la marca). */
:root{
--marca-largo:18px; --marca-corto:7px; --marca-hueco:4px; --marca-grosor:2px;
--marca-cuerpo-estandar:15px; --marca-cuerpo-min:11px; --marca-ancho-min:96px; /* IT.3 · mínimos firmados */
}

/* GREY97 · v2 · 06 FOTOGRAFÍA — preset frío v1.0 · CONGELADO 2026-07-12 · INTOCABLE.
   Receta por capas sobre cualquier foto (elección: niebla, bruma, nublado, aéreas de costa;
   cielos reventados se descartan, no se corrige la receta):
   img    → filter: var(--foto-filtro)
   capa 1 → background:var(--foto-tinte);  mix-blend-mode:color;   opacity:var(--foto-tinte-op)
   capa 2 → background:var(--foto-negros); mix-blend-mode:lighten  (negros levantados a abismo)
   capa 3 → background:var(--foto-luces);  mix-blend-mode:darken   (luces contenidas en espuma)
   capa 4 → background-image:var(--grain); opacity:var(--foto-grano-op)
   En email la foto llega YA HORNEADA (misma receta aplicada en el asset). */
:root{
--foto-filtro:grayscale(1) contrast(1.05) brightness(0.97); /* @kind other */
--foto-tinte:#8FA3B8; --foto-tinte-op:0.34; /* @kind other */
--foto-negros:#0E1114;
--foto-luces:#EEF1F2;
--foto-grano-op:0.07; /* @kind other */
--grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E"); /* @kind other */
--scrim-hero:linear-gradient(180deg,rgba(14,17,20,.30) 0%,rgba(14,17,20,.62) 30%,rgba(14,17,20,.82) 62%,rgba(14,17,20,.90) 100%); /* @kind other */ /* CAMBIO v2 (antes .20/0 · .62/55 · .86/100): stops medios ≥.62 medidos worst-case · IT.3: sincronizado con tokens/fotografia.css (el drop-in iba retrasado) */
--scrim-lateral:linear-gradient(90deg,rgba(11,13,16,.96) 0%,rgba(11,13,16,.95) 45%,rgba(11,13,16,.30) 75%,rgba(14,17,20,.06) 100%); /* @kind other */ /* NUEVO v2 · columna de lectura del hero: opacidad ≥.95 hasta fx .45 */
--placa-foto:rgba(11,13,16,.95); /* NUEVO v2 · placa de legibilidad sobre foto: niebla ≥4.9:1 y espuma ≥9:1 worst-case (el preset limita las luces a espuma) */
--scrim-top:linear-gradient(180deg,rgba(11,13,16,.82) 0%,rgba(11,13,16,.55) 60%,rgba(11,13,16,0) 100%); /* @kind other */ /* NUEVO v2 · banda superior para el chrome del hero sobre luces de foto */
/* IT.3 · carta de calibración (sistema.html §1.6): colores de ENTRADA de demo. NO son color de UI. */
/* Carta de calibración: los cinco --cal-* NO viven aquí (01#113).
   No son paleta y no están en la cadena de imports del sistema, para que
   no puedan usarse por accidente como estado. Fuente: assets/carta_calibracion.css
   (copia en la raíz de la entrega). La salida se comprueba con
   `python3 tools/hornea_preset.py carta`. */
}
/* IT.3 · EQUIVALENCIA DE REVELADO (aprox.): saturación −100 · negro 0→14 · blanco 255→238 · altas luces −25 ·
   sombras +10 · contraste +5 · grano 12 fino · virado frío hacia payne #8FA3B8 al 34%.
   LAS DOS REGLAS: 1) el frío lo pone el TINTE, no el contraste (tope ~1.05; --foto-tinte-op es la perilla
   de temperatura) · 2) la atmósfera es elección de FOTO, no del preset (jamás tocar la receta). */

/* GREY97 · v2 · 07 MOVIMIENTO — mínimo, funcional. 120/200/320 ms.
   Se mueven cajones, menús, toasts y lifts de 1px; nada parpadea;
   prefers-reduced-motion apaga todo (regla en base.css).
   Hover/active/focus = payne sobre dark, payne-tinta sobre espuma; el neon jamás es estado.
   Press: entra 0ms / sale 120ms · anillo de foco: 0ms sin fade · error: entra 200ms / sale 0ms. */
:root{
--t-1:120ms; /* @kind other */ --hover-ms:var(--t-1); /* @kind other */
--t-2:200ms; /* @kind other */ --estado-ms:var(--t-2); /* @kind other */
--t-3:320ms; /* @kind other */ /* NUEVO v2 · cajones y velos */
--ease-out:cubic-bezier(0.165,0.84,0.44,1); /* @kind other */
--ease-inout:cubic-bezier(0.645,0.045,0.355,1); /* @kind other */
--lift:translateY(-1px); /* @kind other */
--press:scale(.985); /* @kind other */
}

/* GREY97 · v2 · 08 FOCO — dos registros (CAMBIO: v1.2 tenía un solo anillo payne 1px).
   EDITORIAL: filete perimetral 1px payne PEGADO (offset 0), solo :focus-visible.
     Campos editoriales: SIN filete — el foco es el cambio de borde a payne.
   PRODUCTO (NUEVO v2, firmado): anillo 2px rgba(neon,.55) con offset 1px.
   Prohibido outline:none sin sustituto equivalente. Hit-target táctil ≥44px. */
:root{
--anillo-foco:var(--payne);              /* editorial sobre dark / foto */
--anillo-grosor:1px;
--anillo-offset:0; /* @kind other */
--neon-cian-55:rgba(46,230,214,.55);     /* NUEVO v3 · neon al 55%: el anillo de foco de producto */
--anillo-foco-prod:var(--neon-cian-55);  /* NUEVO v2 · producto · en claro lo reasigna la capa a tinta */
--anillo-grosor-prod:2px;
--anillo-offset-prod:1px;
}

/* GREY97 · v2 · 09 PRODUCTO — perillas exclusivas del registro producto (panel + apps HOS).
   CAMBIO: v1.2 prohibía toda sombra; v2 autoriza estas dos SOLO en producto (specimen firmado).
   El cromo recede: chrome=lienzo, canvas=sup-1, hover/abierto=marengo. Ningún componente
   depende del wordmark GREY97 para funcionar. */
:root{
--sombra-card:0 2px 4px rgba(0,0,0,.35);    /* NUEVO v2 · tarjeta de producto en reposo */
--sombra-hover:0 6px 20px rgba(0,0,0,.45);  /* NUEVO v2 · hover, menú, cajón, toast */
--inset-hairline:inset 0 0 0 1px var(--hairline); /* NUEVO v2 · borde interior de tarjeta */
--edge-luz:inset 0 1px 0 var(--edge);              /* NUEVO v2 · luz superior 1px */

/* 09b · constantes de layout del panel (panel_produccion §00 · no son tokens de marca:
   son la medida de una tabla de datos, que no es la medida de la prosa) */
--panel-max:1440px;      /* la lista respira más que --ancho-max (1280) */
--panel-x:24px;          /* margen lateral · 8px×3 */
--top-h:56px;            /* alto de cabecera · .cab se pega debajo */
--cajon-w:560px;
--z-top:30; /* @kind other */ --z-barra:40; /* @kind other */ --z-velo:50; /* @kind other */ --z-cajon:60; /* @kind other */ --z-toast:70; /* @kind other */
--cols:minmax(220px,1.45fr) minmax(200px,1.55fr) 184px 116px 76px minmax(174px,188px) minmax(0,150px); /* @kind other */
}

/* ══════════════════════════════════════════════════════════════════
   GREY97 · CAPA SEMÁNTICA Y LOS DOS TEMAS · propuesta 2026-09-21
   ──────────────────────────────────────────────────────────────────
   Va DENTRO de assets/tokens.css, al final, después de la paleta. No es
   una hoja aparte: verifica-tokens.mjs falla toda redefinición de un
   token del canon fuera del canon.

   ARQUITECTURA · tres capas, y solo la 2 se reasigna:
     1 · PRIMITIVOS   la paleta y la escalera. Nadie los usa fuera de aquí
     2 · SEMÁNTICOS   el rol. Es lo único que cambia entre temas
     3 · COMPONENTE   solo donde un componente se desvía, con razón escrita

   TRES INVARIANTES, y las tres son puertas:
     · un semántico NUNCA lleva un #: se define con var() contra un primitivo
     · una superficie NUNCA lee un primitivo
     · ningún componente sabe en qué tema está: nadie lee [data-tema]

   DOS REGLAS DE ESTE FICHERO, y salen de haberlo roto primero:
     · :root SOLO declara nombres nuevos. Ningún token que ya existe en el
       canon cambia de valor en oscuro, porque el design system lo lee
       (redefinir --fondo movía el body de toda la landing del DS).
     · ningún nombre nuevo choca con los que base.css reasigna por ámbito
       (--accion-bg, --accion-fg, --filete-fila, --tinta-sel, --sombra-1…):
       declararlos en el canon haría fallar la regla 2 del verificador.

   CADA SUPERFICIE TIENE UN TEMA POR DEFECTO, Y EL USUARIO MANDA ENCIMA.
   El defecto es identidad (la landing nace oscura, la biblioteca clara);
   el interruptor es del usuario y su elección persiste y gana. Por eso
   TODA superficie con interruptor tiene que estar bien en los DOS temas,
   no solo en el suyo. Dos excepciones, y son estructurales:
     · el correo no tiene interruptor. Es claro siempre: un cliente de
       correo no ejecuta nuestro JS y el tema se hornea al enviar.
     · el panel del funnel se queda oscuro mientras no se decida otra cosa.
   Decidido por Alex el 2026-09-21. Antes de eso esta capa asumía lo
   contrario ("el tema es de la superficie, no del usuario"), y la
   diferencia no es de CSS: es de alcance de QA.

   EN CLARO NO SE DIFERENCIA CON RELLENO. Entre el papel (grey-05,
   #EEF1F2) y la bruma (grey-15, #D3D8DC) hay 1.26:1 EN TOTAL. Partir ese
   hueco da dos escalones de 1.12:1, que no los ve nadie. Medido, y por eso
   no hay un gris intermedio: en papel lo que separa es el FILETE y la
   TINTA, no el relleno. Si algo tiene que leerse como marcado en claro,
   lleva --linea-activa o --linea-control, no un fondo más oscuro.

   EL ACENTO EN CLARO NO ES PAYNE-TINTA. payne-tinta es el color de ENLACE
   y de HOVER del sistema, validado en el correo. Usarlo también de acento
   lo convierte en "un azul cualquiera" y el claro deja de ser la marca.
   En papel el acento es TINTA, y si hace falta un escalón más, se usan
   los grises. Firmado por Alex el 2026-09-21.
   ══════════════════════════════════════════════════════════════════ */

/* ── TEMA OSCURO · el por defecto. Solo nombres nuevos ─────────────── */
:root {
  color-scheme: dark;   /* NO es un token: es el tema. Los controles nativos, la barra de
                           scroll y el autorrelleno siguen al tema. Sin esto, un <select> abre
                           su desplegable en el esquema del sistema operativo, no en el de la página. */
  accent-color: var(--enlace);  /* La casilla y el radio nativos. Tampoco es un token: es una
                           propiedad que solo acepta un color. Hoy solo el panel la pone, así que
                           /ficha, /votar y /proponer pintan sus casillas en el AZUL DEL NAVEGADOR,
                           en los dos temas. Se cazó abriendo casos/ficha_claro.html. Aquí, una vez,
                           para todo. */
  --texto:                  var(--grey-05);               /* la tinta principal */
  --texto-2:                var(--grey-30);               /* tinta secundaria */
  --texto-apagado:          var(--grey-50);               /* ≥24px o marcas · 3.64:1 aquí, 4.19:1 en claro */
  --texto-nota:              var(--grey-15);               /* la bruma · notas y el filete de la fila de la Máquina */
  --texto-hover:            var(--payne);                 /* tinta de hover, texto y borde */
  --enlace:                 var(--payne);
  --enlace-hover:           var(--grey-05);

  /* superficie */
  --fondo-extremo:          var(--lienzo);                /* el más hondo: body de landing, modal y 404 */
  --superficie:             var(--sup-1);                 /* tarjeta sobre el fondo */
  --superficie-alta:        var(--sup-3);                 /* tarjeta sobre una sección levantada */
  --bloque-neutro:          var(--grey-80);               /* pista de barra, esqueleto, fila abierta */
  --velo-base:              var(--grey-98);               /* base del color-mix del velo */

  /* línea */
  --linea:                  var(--grey-80);               /* el filete estándar · sustituye a --filete-dark */
  --linea-control:          var(--grey-50);               /* borde de control y de campo, el 3:1 firmado */
  --linea-activa:           var(--grey-05);               /* el borde que repite la tinta */
  --linea-marca:            var(--grey-60);               /* el corte de la marca y del kicker */
  --linea-alta:             var(--hairline-2);            /* NUEVO v3 · el filete de las superficies altas: menú, toast, aviso, badge */

  /* estado */
  --fila-seleccionada:      var(--cian-tinta);
  --fila-hover:             var(--plano-hover);
  --seleccion-fondo:        var(--payne);                 /* ::selection */
  --seleccion-texto:        var(--grey-98);               /* ::selection */

  /* acción · no se llaman --accion-* a propósito: base.css ya usa ese nombre por ámbito */
  --boton-fondo:            var(--grey-05);
  --boton-texto:            var(--grey-98);
  --boton-fondo-hover:      var(--payne);
  --boton-texto-hover:      var(--payne-tinta);
  --boton-fondo-inactivo:   var(--grey-30);

  /* acento · el cian es el acento del tema oscuro */
  --acento:                 var(--neon-cian);
  --acento-suave:           var(--payne);                 /* el mismo trazo, más callado */
  --marca-trazo:            var(--grey-30);               /* segmento largo de la marca */

  /* dato */
  --dato:                   var(--grey-05);
  --dato-linea:             var(--grey-50);               /* CAMBIO: era --grey-60, 2.29:1 sobre el canvas de producto */
  --dato-destacado:         var(--payne);
}

/* ── TEMA CLARO · la biblioteca, /ranking y /ficha ─────────────────────
   Reasigna SOLO semánticos, los nuevos y los que ya existían. La paleta no
   se toca: reasignarla daría 109 textos blancos sobre blanco, porque en
   este sistema espuma es el color del texto.
   Los valores salen del correo, que lleva meses validado en clientes
   reales y es el tema claro que el sistema ya tenía sin saberlo.       */
[data-tema="claro"] {
  color-scheme: light;   /* NO es un token: es el tema. sin esto, el desplegable de un <select> sale oscuro sobre papel */
  accent-color: var(--enlace);
  --texto:                  var(--grey-90);               /* 15.25:1 sobre el fondo · AAA */
  --texto-2:                var(--grey-60);               /* 6.98:1 */
  --texto-apagado:          var(--grey-50);               /* 4.19:1 · ≥24px o marcas */
  --texto-nota:              var(--grey-80);
  --texto-hover:            var(--payne-tinta);
  --enlace:                 var(--payne-tinta);           /* 5.33:1 */
  --enlace-hover:           var(--grey-90);

  /* superficie */
  --fondo:                  var(--grey-05);               /* el papel */
  --fondo-hondo:            var(--nl-item-fondo);                       /* en papel el cromo recede palideciendo, no oscureciendo */
  --fondo-extremo:          var(--nl-item-fondo);
  --superficie:             var(--nl-item-fondo);         /* el módulo del correo, ya validado */
  --superficie-alta:        var(--nl-item-fondo);         /* v3 · lo elevado es lo más claro que hay; en espuma el menú y el toast recedían */
  --bloque-neutro:          var(--grey-15);               /* la bruma. Ver hallazgos.md A1: un escalón intermedio no cabe */
  --velo-base:              var(--grey-98);               /* el velo sigue siendo oscuro sobre papel */
  --campo-fondo:            var(--nl-item-fondo);
  --panel:                  var(--grey-15);
  --prod-canvas:            var(--nl-item-fondo);         /* el panel se queda en oscuro; esto es red de seguridad */
  --prod-chrome:            var(--grey-05);               /* v3 · un paso más oscuro que el canvas, como en oscuro; la bruma era un bloque y escondía el relleno del ítem activo */
  /* elevación en claro (v3): las sombras del canon son negro al 35% y 45%, para superficie oscura; sobre papel son un borrón */
  --sombra-card:            0 2px 4px rgba(14,17,20,.08);   /* misma forma, tinta abismo al 8% */
  --sombra-hover:           0 6px 20px rgba(14,17,20,.14);  /* misma forma, tinta abismo al 14% */
  --inset-hairline:         inset 0 0 0 1px var(--grey-30);  /* el canto del hover es un filete de niebla, no una línea oscura; --edge-luz se hereda (espuma al 4%, invisible en papel) */

  /* línea */
  --linea:                  var(--grey-15);               /* el filete del correo */
  --linea-control:          var(--grey-50);               /* 4.19:1 sobre el papel · por encima del 3:1 */
  --linea-activa:           var(--grey-90);
  --linea-marca:            var(--grey-60);
  --linea-alta:             var(--grey-30);               /* NUEVO v3 · en papel el filete de énfasis es niebla */
  --separador-suave:        var(--grey-15);               /* ver hallazgos.md A1: en claro no hay un segundo escalón */
  --campo-borde:            var(--grey-50);
  --campo-borde-foco:       var(--payne-tinta);

  /* estado */
  --fila-seleccionada:      var(--grey-15);
  --fila-hover:             var(--grey-15);               /* el roce. En claro lo que marca no es el relleno, es el filete */
  --seleccion-fondo:        var(--payne-tinta);
  --seleccion-texto:        var(--grey-05);

  /* acción */
  --boton-fondo:            var(--grey-90);               /* en papel la acción primaria es un bloque oscuro */
  --boton-texto:            var(--grey-05);
  --boton-fondo-hover:      var(--payne-tinta);
  --boton-texto-hover:      var(--payne);
  --boton-fondo-inactivo:   var(--grey-60);                           /* espuma encima da 6.98:1 · con grey-50 se quedaba en 4.19 */

  /* acento · el cian da 1.38:1 sobre espuma: en claro no existe. Y NO se traduce
     a payne-tinta, porque payne-tinta es el color de ENLACE y de HOVER del sistema
     (validado en el correo) y usarlo de acento lo convierte en "un azul cualquiera".
     En papel el acento es TINTA: un trazo grueso en antracita pesa más que un azul
     flojo, y es lo que hace la prensa desde siempre. */
  --acento:                 var(--grey-90);               /* 15.25:1 · el trazo que marca */
  --acento-suave:           var(--grey-60);               /* 6.98:1 · el mismo trazo, más callado */
  --marca-trazo:            var(--grey-60);

  /* dato */
  --dato:                   var(--grey-90);
  --dato-linea:             var(--grey-60);               /* 6.98:1 */
  --dato-destacado:         var(--grey-90);               /* la cifra viva va en tinta, no en azul */

  /* foco · mata --anillo-foco-claro */
  --anillo-foco:            var(--payne-tinta);           /* 5.33:1 */
  --anillo-foco-prod:        var(--grey-90);               /* NUEVO v3 · el cian al 55% da 1.38:1 sobre papel: en claro el anillo de producto es tinta */
}

/* ── Los tres que mueren al hacer el merge ─────────────────────────────
   --filete-claro, --filete-dark y --anillo-foco-claro llevaban el tema en
   el nombre, o sea que obligaban al consumidor a elegirlo. Los sustituyen
   --linea y --anillo-foco, reasignados por ámbito. Se borran del canon.
   Y el desambiguado: --filete vale 1px y es un GROSOR; el color se llama
   --linea. Igual que --tinta-sel es un FONDO: por eso el texto no se
   llama --tinta-* en ninguna parte de esta capa.                       */

/* ── TEMA OSCURO EXPLÍCITO Y ANIDABLE ─────────────────────────────────
   El mismo oscuro de :root, escrito como ámbito para poder ANIDARLO. Es lo
   que hace un bloque invertido: dentro de una página clara, un
   <div data-tema="oscuro"> recupera el tema oscuro entero, y con él el
   neon, que es el único sitio donde el cian vive en claro (§1 del encargo).
   Sin esto el bloque invertido se quedaba sin acento, y no se ve hasta que
   lo renderizas: lo cazó el specimen.
   Un componente sigue sin saber en qué tema está. Y un tercer tema es un
   bloque más de estos, no un cambio en ningún componente.               */
[data-tema="oscuro"] {
  color-scheme: dark;   /* NO es un token: es el tema. y al revés dentro de una isla oscura */
  accent-color: var(--enlace);
  --texto:                    var(--grey-05);
  --texto-2:                  var(--grey-30);
  --texto-apagado:            var(--grey-50);
  --texto-nota:               var(--grey-15);
  --texto-hover:              var(--payne);
  --enlace:                   var(--payne);
  --enlace-hover:             var(--grey-05);
  --fondo:                    var(--grey-90);
  --fondo-hondo:              var(--grey-98);
  --fondo-extremo:            var(--lienzo);
  --superficie:               var(--sup-1);
  --superficie-alta:          var(--sup-3);
  --bloque-neutro:            var(--grey-80);
  --velo-base:                var(--grey-98);
  --campo-fondo:              var(--abismo-72);      /* v3: era un literal; ahora es alias del primitivo con alfa */
  --panel:                    var(--grey-80);
  --prod-canvas:              var(--sup-1);
  --prod-chrome:              var(--lienzo);
  --sombra-card:              0 2px 4px rgba(0,0,0,.35);
  --sombra-hover:             0 6px 20px rgba(0,0,0,.45);
  --inset-hairline:           inset 0 0 0 1px var(--hairline);
  --linea:                    var(--grey-80);
  --linea-control:            var(--grey-50);
  --linea-activa:             var(--grey-05);
  --linea-marca:              var(--grey-60);
  --linea-alta:               var(--hairline-2);
  --separador-suave:          var(--espuma-05);      /* v3: era un literal; ahora es alias del primitivo con alfa */
  --campo-borde:              var(--grey-50);
  --campo-borde-foco:         var(--payne);
  --fila-seleccionada:        var(--cian-tinta);
  --fila-hover:               var(--plano-hover);
  --seleccion-fondo:          var(--payne);
  --seleccion-texto:          var(--grey-98);
  --boton-fondo:              var(--grey-05);
  --boton-texto:              var(--grey-98);
  --boton-fondo-hover:        var(--payne);
  --boton-texto-hover:        var(--payne-tinta);
  --boton-fondo-inactivo:     var(--grey-30);
  --acento:                   var(--neon-cian);
  --acento-suave:             var(--payne);
  --marca-trazo:              var(--grey-30);
  --dato:                     var(--grey-05);
  --dato-linea:               var(--grey-50);
  --dato-destacado:           var(--payne);
  --anillo-foco:              var(--payne);
  --anillo-foco-prod:         var(--neon-cian-55);
}

/* ── LA INVERSIÓN NO ES UN TOKEN, ES UN ÁMBITO ANIDADO ────────────────
   Aquí hubo dos tokens, --superficie-invertida y --texto-invertido, y se
   quitaron al ver el specimen: dentro de [data-tema="oscuro"] la
   superficie invertida es espuma, así que un bloque oscuro dentro de una
   página clara se pintaba de blanco. Y el nombre tenía la misma
   enfermedad que --filete-claro, que significa lo contrario en cada tema.
   El bloque invertido lleva data-tema del tema contrario y pinta con
   --fondo y --texto, como cualquier otra superficie. Un mecanismo, no dos. */
