/* =============================================================================
   openu5.css — design system "The Port", extracted to production CSS
   -----------------------------------------------------------------------------
   SOURCE OF TRUTH
     design_handoff_openu5_site/README.md          §"Design tokens", §"Components"
     design_handoff_openu5_site/OpenU5 Design System.dc.html
     (values cross-checked against the 6 page files in the same bundle)

   The .dc.html files express layout with inline styles because of the authoring
   environment. The README says explicitly: "Do not carry that over." This file
   is that extraction.

   READ THIS BEFORE USING
     ✅ SIGNED-OFF as of phase 1 (2026-08-06). Everything that used to be marked
     ✚ ADDED — "proposal awaiting sign-off" — has been decided: five decisions
     (D1 font · D2 text/quiet · D3 focus ring · D4 border/control · D5 the two
     invisible controls) plus a real responsive spec for the home. Blocks that
     implement a decision are marked  ✅ D<n>  and every number in them is
     reproducible with  python3 docs/publicacion/web/ds/decisiones.py.

     Blocks marked  ⚠ CONFLICT  are places where README and .dc.html disagree, or
     where the pages use a value the token list never names. The value chosen is
     the one the 6 page files actually use, with the count.

     Full rationale, counts and adjudications: ds/README.md.

   No drop shadows. One accent. Hairlines. Nothing moves on scroll.
   ========================================================================== */


/* =============================================================================
   0. WEBFONTS — ✅ D1: ONE SELF-HOSTED FACE, SYSTEM MONO, ZERO THIRD PARTIES
   -----------------------------------------------------------------------------
   DECIDED: the sans (Space Grotesk) is self-hosted; the mono is the SYSTEM
   stack, which is what the live site uses today. JetBrains Mono is NOT shipped.

   Why not the <link> to fonts.googleapis.com the prototypes use: it hands every
   visitor's IP and User-Agent to Google on every load. That is exactly the leak
   this site's §03 ("what leaves your device") says it does not have. It is not
   an option with a trade-off; it is a defect.

   Why only the sans: the redesign's voice lives in the headlines. The mono
   carries labels, licences and figures, where a system face is indistinguishable
   at 11px and costs nothing — and this is a site whose headline argument is
   "an hour of play fits in less than 10 kB" (CIFRAS-WEB W2). Two font
   downloads to say that would be funny.

   LICENCE, VERIFIED HERE, NOT TAKEN ON TRUST (2026-08-06):
     source      github.com/floriankarsten/space-grotesk, release 2.0.0
                 (NOT fonts.google.com, which serves cut-down subsets)
     zip         sha256 53b4155…cabffd   697.233 B
     licence     OFL-1.1 (GitHub licence API: spdx_id = OFL-1.1, path = OFL.txt)
     RFN         NONE. "Reserved Font Name" appears once in OFL.txt, on line 33,
                 which is the definition inside the licence body. The copyright
                 notice (line 1) is "Copyright 2020 The Space Grotesk Project
                 Authors (…)" with no reservation ⇒ even a subset keeps the name.
     file        woff2/SpaceGrotesk[wght].woff2 → ds/fonts/space-grotesk-var.woff2
                 49.256 B   sha256 8e085aa…0bbb
     axes        fvar wght 300–700, DEFAULT 300 (fontTools). The default instance
                 is Light: body copy must state font-weight: 400 explicitly, or
                 it renders 300. That is why `body` below sets it.
     notice      OFL.txt + AUTHORS.txt travel next to the file. The licence
                 requires the notice to travel; shipping the woff2 alone is a
                 licence breach, not an oversight.

   DEPLOY (not done by this branch — see ds/README.md §"Falta para desplegar"):
   build-demo-publica.sh must copy ds/fonts/ to $DEST/fonts/. Until it does, the
   @font-face below 404s and the page falls back to the system stack — which is
   ugly-but-correct, not broken.
   ========================================================================== */

@font-face {
  font-family: "Space Grotesk";
  src: url("/fonts/space-grotesk-var.woff2") format("woff2");
  font-weight: 300 700;          /* system uses 400/500/600/700 */
  font-style: normal;
  font-display: swap;
}


/* =============================================================================
   1. TOKENS
   ========================================================================== */

:root {
  color-scheme: dark;

  /* --- 1.1 COLOR · surfaces -------------------------------------------------
     README §Color, verbatim. Separation is 1px border + a 2–4 point lightness
     step; depth is never a shadow. */
  --u5-surface-base:    #0C0D10;   /* page background                        */
  --u5-surface-band:    #101319;   /* full-bleed emphasis section            */
  --u5-surface-card:    #111318;   /* cards, panels, media wells             */
  --u5-surface-inset:   #12141A;   /* screenshot frame, code blocks          */

  /* --- 1.2 COLOR · text ---------------------------------------------------- */
  --u5-text-primary:    #E9EAEC;
  --u5-text-secondary:  #A5A9B1;
  --u5-text-tertiary:   #8B9099;
  /* ✅ D2 — was #6F747D (4.14 base · 3.96 band · 3.96 card · 3.92 inset: FAILS
     WCAG AA 1.4.3 on all four, and nothing that uses it reaches 24px so nothing
     claims the large-text exception). Raised to the value below: 5.10 · 4.88 ·
     4.88 · 4.83. The declared cost is that it stops being a fourth text weight:
     against text/tertiary #8B9099 it measures 1.19:1, i.e. the two are no longer
     tellable apart. THE SYSTEM HAS THREE TEXT WEIGHTS, NOT FOUR.
     Corollary rule: never use quiet and tertiary in the same block to signal a
     hierarchy step — there is no step left between them. Use one or the other.
     The collapse is not an artefact of picking a generous value: the LOWEST
     value on this ramp that passes AA on all four is #797E87 (4.51 on inset),
     and that one sits at 1.27:1 from tertiary. Any AA-passing quiet is glued to
     tertiary on these surfaces. #7E838C is chosen over the 4.51 minimum because
     4.51 has no headroom: one surface tweak and it is back under. */
  --u5-text-quiet:      #7E838C;

  /* --- 1.3 COLOR · borders ------------------------------------------------- */
  --u5-border-subtle:   #1C1E23;   /* divides                                */
  --u5-border-strong:   #24272E;   /* outlines static objects                */

  /* ⚠ CONFLICT — the token list has ONE strong border; the pages have TWO.
     #2A2E36 appears 45× as a border across the bundle (35 solid + 8
     border-bottom + 2 dashed) and every element carrying it is an interactive
     control: "Watch", "Copy link", "Download", "Erase my files", "How it
     works", "Try it anyway", "Load", "Export", "Back to home", "Privacy &
     data", "Report this build", "See the full list", "Upload a .zip instead".
     #24272E (106× as a border) carries the static objects: taxonomy tag,
     segmented well, toggle track, aside panel, pill.
     The README assigns the secondary button to border/strong (#24272E) and is
     contradicted by its own page files 45 times. Naming the second value:  */
  --u5-border-control:  #2A2E36;   /* ✅ D4 — the NAME is new, the VALUE is what
                                      the pages already draw 45 times. Carries
                                      every interactive outline: secondary
                                      button, quiet-link underline, drop-zone
                                      dash, window-chrome dot. Anyone who follows
                                      the README's token list instead draws every
                                      secondary button on the site one step
                                      darker than it was designed. */

  /* --- 1.4 COLOR · accent -------------------------------------------------- */
  --u5-accent:          #4F8CFF;   /* primary action, counters, figures,
                                      one word of emphasis. Also the POSITIVE
                                      state — there is no separate green. */
  --u5-accent-ink:      #04070F;   /* text on accent                          */
  --u5-accent-muted:    #2B2F38;   /* segmented-control active cell           */
  --u5-accent-hover:    #7DABFF;   /* ⚠ named only in README prose
                                      ("hover lightens to #7DABFF"), absent
                                      from the token block. 4× in the bundle. */
  --u5-accent-press:    #3E7BEE;   /* ✅ SIGNED — README says "press darkens" and
                                      gives no value. One step down; keeps the
                                      ink contrast at 5.06:1, above AA. */

  /* --- 1.5 COLOR · status --------------------------------------------------
     Glyph first (●◐○), hue second, so tables read in greyscale.
     No red, no success green. */
  --u5-status-verified: var(--u5-accent);   /* ● */
  --u5-status-partial:  #E0A03A;            /* ◐ glyph and label ONLY — never a
                                               border, a fill or a numeral     */
  --u5-status-missing:  var(--u5-text-quiet); /* ○ */

  /* --- 1.6 COLOR · component-local greys -----------------------------------
     ⚠ Used by the pages, named by nothing. Counts are occurrences across the
     whole bundle. Without names these get re-typed by hand and drift. */
  --u5-surface-well:    #16181D;   /* 10× — segmented-control well             */
  --u5-surface-pill:    #14161B;   /*  7× — status pill background             */
  --u5-text-strong:     #C8CBD1;   /* 176×, and all 176 are `color:`. In no token
                                      list. Sits between primary and secondary:
                                      captions, secondary-button label,
                                      permission-row label, step-card body,
                                      taxonomy tag ADDED/FIXED. 11.43:1 on card.
                                      ⚠ the ordering claim that used to sit here
                                      ("third most-used, after quiet 319× and
                                      tertiary 262×") is NOT re-measured: the 319
                                      is the figure AREA-WEB-ARRANQUE.md §7.6
                                      flags as carrying no command. The 176 does
                                      carry one. Ranking retired, count kept. */
  --u5-text-nav:        #9A9EA6;   /* 15× — inactive nav link, pill label      */

  /* --- 1.7 SURFACE · sticky nav -------------------------------------------
     ⚠ CONFLICT: README and the 5 page files say #0C0D10F2 (95% opaque);
     "OpenU5 Design System.dc.html" and "Home Redesign.dc.html" say #0C0D10CC
     (80%). Taking F2 — it is what the shipping pages use and what the README
     text states; the DS document is the stale one. */
  --u5-surface-nav:     #0C0D10F2;
  --u5-nav-blur:        blur(12px);
  --u5-nav-height:      84px;

  /* --- 1.8 TYPE · families ------------------------------------------------- */
  --u5-font-sans: "Space Grotesk", system-ui, -apple-system, "Segoe UI",
                  Roboto, sans-serif;
  /* ✅ D1 — the mono is the SYSTEM stack. JetBrains Mono is deliberately NOT
     shipped: at 11px, under 0.14em tracking and uppercase, it buys nothing that
     justifies a second font download on a page whose thesis is a bound of
     10 kB an hour (CIFRAS-WEB W2). This is
     also what the live site uses today, so the mono does not change at all. */
  --u5-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* --- 1.9 TYPE · scale ----------------------------------------------------
     Sizes as README's table. Sans for voice, mono for evidence: anything
     factual (licences, file paths, captions, section counters, figures) is
     mono, uppercase, wide-tracked. */
  --u5-size-display:    68px;
  --u5-size-h1:         52px;   /* ⚠ CORREGIDO. Este comentario decía «README
                                   gives a 52–60 range; 52 is what the pages
                                   use. 60 is unused in the bundle». FALSO, y
                                   la corrección RECUPERA una regla:
                                   re-medido 2026-08-06 sobre las 9 páginas,
                                   filtrando por la FIRMA del rol (peso 700 +
                                   tracking -0.035em), no por el tamaño suelto:
                                     52px → «Ready, Avatar?», el CTA de cierre
                                            (Home, Home Redesign, y la muestra
                                            del propio documento de sistema)
                                     60px ×5 → el H1 de /play, /status,
                                            /verification, /improvements y
                                            /differences: UN título de página
                                            por página
                                   El «52–60» del README NO era vaguedad: su
                                   propia columna de rol lo dice —«closing CTA,
                                   page titles»— y son DOS roles con DOS
                                   tamaños. La extracción los colapsó en uno y
                                   declaró el otro muerto.
                                   (Las otras 4 apariciones de 52px del bundle
                                   NO son H1: llevan lh 1.12 / tracking -0.015em
                                   y son los titulares de sección de la
                                   dirección 1b «The Archive», que es otra
                                   dirección con su propia escala. Contar «52px»
                                   a secas mezcla tres cosas.) */
  /* 🔴 EL COMENTARIO DE ABAJO ERA CIERTO Y CADUCÓ, Y ESO ES LO QUE HIZO EL DAÑO.
     Decía «Fase 1 es sólo la home y NO LO USA» — verdad al escribirlo, y por eso
     este token nació con un px PELADO mientras el h1 responsive vivía sólo en la
     CLASE `.u5-h1` (que usa OTRO token, `--u5-size-h1`). En cuanto aparecieron
     páginas interiores, el token se convirtió en la vía real y el clamp se quedó
     en la vía muerta. MEDIDO en el sitio construido a 360px: h1 = 60px, cuatro
     líneas y ~500px de alto antes del primer párrafo.
     Consumidores CENSADOS: 7, y NO son sólo los de fase2 — `verificacion.html` y
     `404.html` son MÍOS y tenían el mismo defecto. Por eso el arreglo va aquí y no
     en cada página: el clamp entra en EL TOKEN y lo heredan los siete sin tocarlos.
     🔴 Se puede clampar este token y NO `--u5-size-h1` porque ninguna clase lo
     envuelve; `--u5-size-h1` sí (`.u5-h1`), y clamparlo daría un clamp anidado.
     Si algún día una clase envuelve a éste, quita uno de los dos clamps. */
  --u5-size-h1-page:    clamp(32px, 7vw, 60px);
  --u5-size-h2:         44px;
  --u5-size-h3:         22px;
  --u5-size-body-lg:    18px;
  --u5-size-body:       15.5px;
  --u5-size-mono-label: 11px;
  --u5-size-mono-meta:  11.5px;
  --u5-size-mono-fig:   28px;
  --u5-size-tag:        10px;   /* ⚠ taxonomy tag, README §Components; absent
                                   from the type table                        */
  /* ✅ SIGNED — sizes the pages use that the 9-role table does not cover.
     Measured in "OpenU5 Design System.dc.html": 40px ×7 (section headline —
     NOT the H2 44 the table implies), 17px ×8 (section intro — between
     body-lg 18 and body 15.5), 20px ×4 (panel header), 16px ×12, 14.5px ×14,
     13.5px ×10. Naming the two structural ones; the rest are one-offs that
     should be normalised to the scale during build. */
  /* Mismo caso que `--u5-size-h1-page` y con los mismos 3 generadores leyéndolo
     por token: el clamp entra AQUÍ. La clase `.u5-h2--sub` lo envolvía en su
     propio clamp, así que se simplifica abajo — dos clamps anidados no rompen
     nada, pero el de fuera manda y el de dentro se lee como si aún gobernara. */
  --u5-size-h2-sub:     clamp(26px, 5.2vw, 40px);
  --u5-size-body-intro: 17px;   /* section intro paragraph                    */

  --u5-lh-display:      1.05;
  --u5-lh-h1:           1.10;
  --u5-lh-h2:           1.12;
  --u5-lh-h3:           1.30;
  --u5-lh-body-lg:      1.70;
  --u5-lh-body:         1.65;
  --u5-lh-mono:         1;      /* ✅ SIGNED — table leaves it "—"             */

  --u5-track-display:  -0.035em;
  --u5-track-h1:       -0.035em;
  --u5-track-h2:       -0.03em;
  --u5-track-h3:       -0.02em;
  --u5-track-mono-label: 0.14em;
  --u5-track-mono-meta:  0.16em;
  --u5-track-seg:        0.06em;   /* segmented control                       */
  --u5-track-pill:       0.08em;   /* status pill                             */
  --u5-track-tag:        0.10em;   /* taxonomy tag                            */

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

  --u5-measure: 66ch;   /* prose caps at ~66ch */

  /* --- 1.10 SPACING --------------------------------------------------------
     README calls this "base 4". ⚠ It is not: 14 and 26 are not multiples of 4.
     It is a base-2 set of nine named steps. Named, not indexed, because the
     handoff names them by role. */
  --u5-space-8:   8px;    /* icon gap, inline pairs      */
  --u5-space-14: 14px;    /* button gap                  */
  --u5-space-20: 20px;    /* grid gutter                 */
  --u5-space-26: 26px;    /* card padding                */
  --u5-space-28: 28px;    /* feature-card padding        */
  --u5-space-44: 44px;    /* headline → content          */
  --u5-space-56: 56px;    /* page gutter                 */
  --u5-space-64: 64px;    /* column gutter               */
  --u5-space-96: 96px;    /* section rhythm              */

  /* --- 1.11 RADIUS ---------------------------------------------------------
     Nested radii step down by 6: a 12 card holds a 6 image. */
  --u5-radius-4:    4px;    /* ⚠ segmented active cell, taxonomy tag —
                               used by the pages, absent from README's list */
  --u5-radius-6:    6px;    /* screenshots, code blocks, segmented well     */
  --u5-radius-7:    7px;    /* compact nav button                           */
  --u5-radius-8:    8px;    /* buttons                                      */
  --u5-radius-12:  12px;    /* cards, panels                                */
  --u5-radius-pill: 999px;  /* pills, toggles                               */

  /* --- 1.12 LAYOUT --------------------------------------------------------- */
  --u5-canvas:      1440px;
  --u5-columns:     12;
  --u5-grid-gap:    var(--u5-space-20);
  --u5-gutter:      var(--u5-space-56);

  /* --- 1.13 MOTION ---------------------------------------------------------
     Nothing moves on scroll: no reveals, no parallax, no counting numbers.
     The only thing animating on the page is the game itself. */
  --u5-ease:            cubic-bezier(.2, .6, .2, 1);
  --u5-dur-hover:       120ms;
  --u5-dur-state:       180ms;
  --u5-transition-hover: var(--u5-dur-hover) var(--u5-ease);
  --u5-transition-state: var(--u5-dur-state) var(--u5-ease);

  /* --- 1.14 FOCUS ----------------------------------------------------------
     ✅ D3 — DECIDED IN FULL. The handoff specifies no focus state for anything:
     zero occurrences of `:focus` or `outline` across its 9 files, including the
     language segmented control (which is navigation) and the permission toggle
     (which is the privacy control of a site whose argument is privacy).

     ONE ring for everything interactive, accent, OUTSIDE the control:
       vs base 6.04 · band 5.78 · card 5.78 · inset 5.72 · border/subtle 5.18
     all above the 3.00 that 1.4.11 asks of a focus indicator.

     🔴 THE RULE THIS FORCES — PROHIBIDO: never put a button, or any focusable
     control, on an accent-filled surface. The ring is accent, so on accent it
     measures 1.00:1 and DISAPPEARS. Nothing in CSS goes red the day someone
     does it; the only thing standing between the site and an invisible focus
     ring is this sentence. The system already forbids accent as a background
     for text blocks (README §Color, "never a background for text blocks"), so
     this is an extension of a rule that exists, not a new constraint.

     Escape hatch, measured in case it is ever needed: accent/ink #04070F over
     accent reaches 6.26:1, so an INNER ring on an accent surface is possible.
     It is not used, because a per-context focus ring is the kind of rule nobody
     remembers. If it is ever adopted, this comment stops being true.
     (This corrects the audit, which said no palette colour works over accent —
     it enumerated accent 1.00 and text/primary 2.67 and skipped accent/ink.) */
     ── /play NO ENTRA AQUÍ, Y ES UNA EXCLUSIÓN RAZONADA, NO UNA DEUDA ────────
     El recorte que detectó el usuario («sólo la home tiene el DS») queda cerrado en
     14 de 15 páginas. La 15ª es `/play`, y se queda fuera A PROPÓSITO: su paleta es
     CONTENIDO —los 16 colores EGA del juego de 1988—, no decoración, y la
     intersección medida entre esa paleta y la del sistema es CERO. Repintarla no
     sería aplicar el sistema: sería falsificar el objeto que el sitio dice reproducir.
     Que no herede el sistema NO la deja sin foco visible:
       · `game/index.html` (regla `.touch-btn:focus-visible`) pinta anillo propio,
         `2px solid #ffe9a8` con offset 2px, en la misma familia ámbar del deck.
       · Contraste MEDIDO de ese anillo — 17,47:1 sobre el negro del juego, que es
         el fondo que toca de verdad (el offset lo saca FUERA del botón); 13,89:1 si
         llegara a solaparlo en reposo (rgba(44,35,19,.82) compuesto = #241D10) y
         5,60:1 sobre `:active` #6B5A2A. Los tres por encima del 3,00 de 1.4.11.
     🔴 LO QUE NO ESTÁ CERTIFICADO, y se dice en vez de callarlo: ese anillo NO se ha
     visto con los ojos en el sitio publicado, y NO se puede. `/play` servido sin los
     ficheros del usuario tiene CERO elementos focusables en el DOM —sólo el aviso
     «¿Has ejecutado npm run extract?»— porque el deck se construye al arrancar el
     juego. Es la misma razón por la que `/play` va en `Disallow:` del robots. Medido
     con navegador headless a 390×844 con táctil: 0 en DOM, 0 visibles, 0 `.touch-btn`.
     Certificarlo exige una sesión CON datos extraídos, que es material de EA y no
     entra aquí. Las cifras de arriba salen de los LITERALES, no de una captura. */
  --u5-focus-color:  var(--u5-accent);
  --u5-focus-width:  2px;
  --u5-focus-offset: 2px;
}


/* =============================================================================
   2. BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--u5-surface-base);
  color: var(--u5-text-primary);
  font-family: var(--u5-font-sans);
  font-weight: var(--u5-weight-regular);   /* ✅ D1: the variable font's DEFAULT
                                              instance is 300 (fvar wght
                                              300/300/700). Without this line
                                              every unstyled paragraph renders
                                              Light and the page looks washed
                                              out — and it looks CORRECT in the
                                              system-stack fallback, so nobody
                                              catches it until deploy. */
  font-size: var(--u5-size-body);
  line-height: var(--u5-lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

::selection { background: var(--u5-accent); color: var(--u5-accent-ink); }

a { color: var(--u5-accent); text-decoration: none; }
a:hover { color: var(--u5-accent-hover); }

/* ✅ D3 — one focus rule for everything focusable. :focus-visible only, so
   pointer users never see it. Never remove the outline without replacing it. */
:focus-visible {
  outline: var(--u5-focus-width) solid var(--u5-focus-color);
  outline-offset: var(--u5-focus-offset);
  border-radius: inherit;
}

/* ✅ SIGNED — the system says "nothing moves on scroll"; honour the OS setting
   for the little that does move (hover, state change). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* =============================================================================
   3. TYPOGRAPHY
   ----------------------------------------------------------------------------
   ✚ The clamp() lower bounds are ADDED. The handoff contains ZERO media
   queries and six fixed 1440px canvases; 68px display text overflows a 360px
   viewport. These are a floor so nothing breaks, NOT a responsive spec — the
   handoff still owes real breakpoints (audit §3.6).
   ========================================================================== */

.u5-display {
  font-size: clamp(40px, 4.7vw, var(--u5-size-display));
  line-height: var(--u5-lh-display);
  letter-spacing: var(--u5-track-display);
  font-weight: var(--u5-weight-bold);
  text-wrap: balance;
  margin: 0;
}

.u5-h1 {
  font-size: clamp(34px, 3.6vw, var(--u5-size-h1));
  line-height: var(--u5-lh-h1);
  letter-spacing: var(--u5-track-h1);
  font-weight: var(--u5-weight-bold);
  text-wrap: balance;
  margin: 0;
}

.u5-h2 {
  font-size: clamp(30px, 3.05vw, var(--u5-size-h2));
  line-height: var(--u5-lh-h2);
  letter-spacing: var(--u5-track-h2);
  font-weight: var(--u5-weight-semibold);
  text-wrap: balance;
  margin: 0;
}

/* El clamp vive en el TOKEN (ver §tipografía): aquí sólo se lee, para que la clase
   y la vía-por-token den EXACTAMENTE el mismo tamaño en vez de dos escalas. */
.u5-h2--sub { font-size: var(--u5-size-h2-sub); }

.u5-h3 {
  font-size: var(--u5-size-h3);
  line-height: var(--u5-lh-h3);
  letter-spacing: var(--u5-track-h3);
  font-weight: var(--u5-weight-semibold);
  margin: 0;
}

.u5-body-lg {
  font-size: var(--u5-size-body-lg);
  line-height: var(--u5-lh-body-lg);
  color: var(--u5-text-secondary);
  max-width: var(--u5-measure);
  text-wrap: pretty;
  margin: 0;
}

.u5-body {
  font-size: var(--u5-size-body);
  line-height: var(--u5-lh-body);
  color: var(--u5-text-tertiary);
  max-width: var(--u5-measure);
  text-wrap: pretty;
  margin: 0;
}

.u5-body-intro {
  font-size: var(--u5-size-body-intro);
  line-height: var(--u5-lh-body);
  color: var(--u5-text-secondary);
  max-width: var(--u5-measure);
  text-wrap: pretty;
  margin: 0;
}

/* Mono = evidence. Uppercase is applied in CSS, so the DOM keeps the readable
   string for screen readers and for copy-paste. */
.u5-mono-label {
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-label);
  line-height: var(--u5-lh-mono);
  letter-spacing: var(--u5-track-mono-label);
  text-transform: uppercase;
  font-weight: var(--u5-weight-regular);
  color: var(--u5-text-quiet);
}
.u5-mono-label--accent { color: var(--u5-accent); }

.u5-mono-meta {
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-meta);
  line-height: var(--u5-lh-mono);
  letter-spacing: var(--u5-track-mono-meta);
  text-transform: uppercase;
  font-weight: var(--u5-weight-regular);
  color: var(--u5-text-quiet);
}

.u5-mono-figure {
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-fig);
  font-weight: var(--u5-weight-regular);
  color: var(--u5-accent);
  font-variant-numeric: tabular-nums;   /* ✅ SIGNED: figures sit in a stat bar
                                           and must align column to column   */
}


/* =============================================================================
   4. COMPONENT · BUTTONS  (4 variants + disabled state)
   ----------------------------------------------------------------------------
   One primary per view, always paired with a mono caption stating the
   requirement. Hover lightens the accent; press darkens it. No scale, no
   shadow.  Disabled is a STATE, not a fifth variant — and the README requires
   the reason to be rendered beside it, never a silently dead button.
   ========================================================================== */

.u5-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--u5-space-8);
  font-family: var(--u5-font-sans);
  font-size: var(--u5-size-body);
  font-weight: var(--u5-weight-semibold);
  line-height: 1.2;
  border: 1px solid transparent;
  border-radius: var(--u5-radius-8);
  background: none;
  cursor: pointer;
  text-decoration: none;
  transition:
    background-color var(--u5-transition-hover),
    border-color     var(--u5-transition-hover),
    color            var(--u5-transition-hover);
}

/* --- primary --- */
.u5-btn--primary {
  background: var(--u5-accent);
  color: var(--u5-accent-ink);
  padding: 14px 26px;
}
.u5-btn--primary:hover  { background: var(--u5-accent-hover); }
.u5-btn--primary:active { background: var(--u5-accent-press); }   /* ✅ */

/* --- secondary --- */
.u5-btn--secondary {
  border-color: var(--u5-border-control);   /* ⚠ #2A2E36, not border/strong —
                                               see token §1.3 */
  color: var(--u5-text-strong);
  padding: 14px 24px;
}
.u5-btn--secondary:hover {
  border-color: var(--u5-accent);
  color: var(--u5-text-primary);
}
.u5-btn--secondary:active { border-color: var(--u5-accent-press); }  /* ✅ */

/* --- compact (nav) --- */
.u5-btn--compact {
  background: var(--u5-accent);
  color: var(--u5-accent-ink);
  padding: 9px 18px;
  border-radius: var(--u5-radius-7);
  font-size: 14px;
}
.u5-btn--compact:hover  { background: var(--u5-accent-hover); }
.u5-btn--compact:active { background: var(--u5-accent-press); }      /* ✅ */

/* --- quiet link --- */
.u5-btn--quiet {
  color: var(--u5-text-tertiary);
  border: 0;
  border-bottom: 1px solid var(--u5-border-control);
  border-radius: 0;
  padding: 0 0 2px;
  font-weight: var(--u5-weight-regular);
}
.u5-btn--quiet:hover {
  color: var(--u5-text-primary);          /* ✅ SIGNED — no hover specified */
  border-bottom-color: var(--u5-accent);  /* ✅ */
}

/* --- disabled (state, all variants) ---
   The reason must be rendered; .u5-btn-reason is not optional. */
.u5-btn:disabled,
.u5-btn[aria-disabled="true"],
.u5-btn.is-disabled {
  background: var(--u5-border-subtle);
  border-color: transparent;
  color: var(--u5-text-quiet);
  cursor: not-allowed;
  pointer-events: none;   /* keep aria-disabled links focusable if you swap
                             this for a real <button disabled>               */
}

.u5-btn-caption {   /* the mandatory mono caption under the primary button */
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-meta);
  line-height: 1.5;
  letter-spacing: var(--u5-track-mono-meta);
  text-transform: uppercase;
  color: var(--u5-text-quiet);
  margin-top: var(--u5-space-14);
}

.u5-btn-reason {   /* the reason beside a disabled button */
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--u5-text-tertiary);   /* ⚠ NOT text/quiet: the reason is normal
                                       prose and must clear AA (5.79:1 on
                                       card). Disabled CONTROLS are exempt
                                       from 1.4.3; their explanations are not. */
}

.u5-btn-group { display: inline-flex; gap: var(--u5-space-14); align-items: center; }


/* =============================================================================
   5. COMPONENT · EN/ES SEGMENTED CONTROL
   ----------------------------------------------------------------------------
   Must preserve path and anchor across languages
   (/verification#gaps ↔ /es/verificacion#huecos). Same position on every page.
   Mark up as a list of <a>, not as buttons: it is navigation.
   ========================================================================== */

.u5-seg {
  display: inline-flex;
  background: var(--u5-surface-well);
  border: 1px solid var(--u5-border-strong);
  border-radius: var(--u5-radius-6);
  padding: 2px;
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-label);
  letter-spacing: var(--u5-track-seg);
  line-height: var(--u5-lh-mono);
}

/* 🔴 SIRVE PARA <a> Y PARA <button>, y el reset NO es cosmético: sostiene una
   cifra FIRMADA. La regla se escribió suponiendo `<a>` (de ahí el
   `text-decoration`) y sin resetear nada, porque un ancla no trae chrome. Con
   `<button>` —que es lo correcto cuando el control CONMUTA el idioma en vez de
   navegar a una URL— el agente de usuario pone su propio fondo y su borde, y
   MEDIDO en Chromium sobre la home de fase 1 la celda inactiva salía
   `background rgb(107,107,107)` con `border 2px` blanco.
   Eso pisa justo el supuesto de la nota de D5(b) de aquí abajo: «la inactiva no
   se toca: text/tertiary sobre el POZO = 5.54». Con el gris del navegador el
   fondo ya no era el pozo, así que la cifra firmada dejaba de describir lo que
   se pintaba — y encima la celda inactiva parecía la seleccionada, que es
   exactamente el defecto que D5(b) existe para arreglar.
   No lo vio la lectura del CSS ni la guarda del ensamblado: lo vio medir el DOM
   en el navegador. */
.u5-seg__opt {
  appearance: none;
  background: none;
  border: 0;
  margin: 0;
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  padding: 4px 9px;
  border-radius: var(--u5-radius-4);
  color: var(--u5-text-tertiary);
  text-decoration: none;
  transition: background-color var(--u5-transition-state),
              color var(--u5-transition-state);
}
.u5-seg__opt:hover { color: var(--u5-text-primary); }   /* ✅ SIGNED */

/* ✅ D5(b) — the active cell used to be told apart by BACKGROUND ALONE:
   accent/muted #2B2F38 vs the well #16181D measures 1.32:1, where 1.4.11 asks
   3.00. For a language switch that means the current language is invisible to
   low vision — and "nothing tells you which language you are in" is blocker #4
   of the handoff's own list, which the redesign fixed in the DOM and not in the
   pixel. Fixed with a 1px accent border, which is a real second channel:
     accent #4F8CFF vs well #16181D = 5.52   vs the cell fill #2B2F38 = 4.17
   The label stays text/primary over #2B2F38 = 11.14. The inactive cell is not
   touched: text/tertiary over the well = 5.54, already AA.
   The 1px border is absorbed by shrinking the padding, so the cell does not
   grow and the control does not reflow when the language changes. */
.u5-seg__opt[aria-current="true"],
.u5-seg__opt.is-active {
  background: var(--u5-accent-muted);
  color: var(--u5-text-primary);
  border: 1px solid var(--u5-accent);
  padding: 3px 8px;
}


/* =============================================================================
   6. COMPONENT · PERMISSION ROW
   ----------------------------------------------------------------------------
   Off state is grey-on-grey by design: the default must look like nothing is
   happening, because nothing is.
   Markup: <input type="checkbox" class="u5-toggle__input"> + <span class="u5-toggle">
   inside a <label>. Never a div — this is a real control.
   ========================================================================== */

.u5-permission {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.u5-permission__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--u5-space-20);
  cursor: pointer;
}

.u5-permission__label {
  font-size: 16px;
  color: var(--u5-text-strong);
}

.u5-permission__note {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--u5-text-tertiary);
  border-top: 1px solid var(--u5-border-subtle);
  padding-top: var(--u5-space-14);
}

/* ✅ D5(a) — the OFF track (#24272E) against the card it sits on measures
   1.24:1, where 1.4.11 asks 3.00. "Grey on grey by design" is a legitimate
   choice for the STATE; it is not a legitimate choice for the BOUNDARY. The
   consequence was concrete: a user who wants to TURN ON analytics has to find
   an interruptor that has no perceivable edge — on the one control this site
   argues its whole privacy case with.
   Fixed with a 1px text/quiet border on the track: #7E838C vs card = 4.88.
   The fill is unchanged, so OFF still reads as "nothing is happening"; what
   changes is that there is now something to aim at. The knob was never the
   problem: #7E838C over the track = 3.92, already above 3.00. */
.u5-toggle {
  position: relative;
  flex: none;
  width: 40px;
  height: 22px;
  border-radius: var(--u5-radius-pill);   /* 11px = pill at this height */
  background: var(--u5-border-strong);
  border: 1px solid var(--u5-text-quiet);
  transition: background-color var(--u5-transition-state),
              border-color var(--u5-transition-state);
}

.u5-toggle::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: var(--u5-radius-pill);
  background: var(--u5-text-quiet);
  transition: transform var(--u5-transition-state),
              background-color var(--u5-transition-state);
}

.u5-toggle__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.u5-toggle__input:checked + .u5-toggle {
  background: var(--u5-accent);
  border-color: var(--u5-accent);   /* ON needs no extra edge: accent vs card
                                       is 5.78, the fill IS the boundary */
}
.u5-toggle__input:checked + .u5-toggle::after {
  transform: translateX(18px);            /* 2 → 20px, as the prototype */
  background: var(--u5-accent-ink);
}

/* ✅ D3 — the handoff gives the toggle no focus and no hover. */
.u5-toggle__input:focus-visible + .u5-toggle {
  outline: var(--u5-focus-width) solid var(--u5-focus-color);
  outline-offset: var(--u5-focus-offset);
}

/* The rest of the 1.4.11 failures in this system (borders at 1.11–1.43 against
   their surfaces) are NOT fixed and are NOT bugs: "separation comes from a 1px
   border plus a 2–4 point lightness step" is the design direction, chosen
   knowingly. D5 fixes exactly the two places where the failure stopped being
   aesthetic and became a control you cannot find: this toggle and the language
   segmented control (§5). */


/* =============================================================================
   7. COMPONENT · FEATURE CARD          Used in threes; never in twos or fours.
   ========================================================================== */

.u5-feature-card {
  background: var(--u5-surface-card);
  border: 1px solid var(--u5-border-subtle);
  border-radius: var(--u5-radius-12);
  padding: var(--u5-space-28);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.u5-feature-card__eyebrow { /* compose with .u5-mono-label .u5-mono-label--accent */ }
.u5-feature-card__title   { /* compose with .u5-h3 */ }
.u5-feature-card__body    { /* compose with .u5-body */ }

.u5-card-grid {   /* the three-up the card is specified for */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--u5-grid-gap);
}


/* =============================================================================
   8. COMPONENT · STEP CARD
   ----------------------------------------------------------------------------
   Numeral in a 26px accent circle + a 1px rule to its right as a progress
   line; the LAST step's rule is omitted. Handled here in CSS via :last-child
   so no author has to remember it.
   ========================================================================== */

.u5-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--u5-grid-gap);
}

.u5-step-card {
  background: var(--u5-surface-card);
  border: 1px solid var(--u5-border-subtle);
  border-radius: var(--u5-radius-12);
  padding: var(--u5-space-26);
  display: flex;
  flex-direction: column;
  gap: var(--u5-space-14);
}

.u5-step-card__head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.u5-step-card__num {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: var(--u5-radius-pill);
  background: var(--u5-accent);
  color: var(--u5-accent-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--u5-font-mono);
  font-size: 13px;
  font-weight: var(--u5-weight-medium);
}

.u5-step-card__rule {
  flex: 1;
  height: 1px;
  background: var(--u5-border-strong);
}

.u5-steps > .u5-step-card:last-child .u5-step-card__rule { display: none; }

.u5-step-card__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--u5-text-strong);
}


/* =============================================================================
   9. COMPONENT · ASIDE PANEL
   ----------------------------------------------------------------------------
   Darker than the page, with border/strong. For the caveat next to a claim.
   Footnote line last, above a border/subtle top border.
   ========================================================================== */

.u5-aside {
  background: var(--u5-surface-base);
  border: 1px solid var(--u5-border-strong);
  border-radius: var(--u5-radius-12);
  padding: var(--u5-space-28);
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.u5-aside__eyebrow { /* compose with .u5-mono-label */ }

.u5-aside__lead {
  font-size: 21px;
  line-height: 1.45;
  font-weight: var(--u5-weight-medium);
  letter-spacing: -0.015em;
  color: var(--u5-text-primary);
  margin: 0;
}

.u5-aside__footnote {
  border-top: 1px solid var(--u5-border-subtle);
  padding-top: 16px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--u5-text-tertiary);
  margin: 0;
}

/* On a band the aside is base-on-band; on base it needs the card surface
   instead, or it disappears. */
.u5-band .u5-aside { background: var(--u5-surface-base); }


/* =============================================================================
   10. COMPONENT · SECTION HEADER
   ----------------------------------------------------------------------------
   Mono counter (02 / WHY YOUR COPY) → 24px gap → H2. Second clause of the
   headline greyed to text/tertiary to carry the turn in the sentence.
   ========================================================================== */

.u5-section-header { display: block; }

.u5-section-header__counter { /* compose with .u5-mono-meta */ }

.u5-section-header__title {
  margin-top: 24px;
  font-size: clamp(30px, 3.05vw, var(--u5-size-h2));
  line-height: var(--u5-lh-h2);
  letter-spacing: var(--u5-track-h2);
  font-weight: var(--u5-weight-semibold);
  text-wrap: balance;
}

.u5-turn { color: var(--u5-text-tertiary); }   /* the greyed second clause */

.u5-section {          /* section rhythm: open at 96, close at 0 */
  padding-top: var(--u5-space-96);
  padding-bottom: 0;
}

.u5-band {             /* full-bleed emphasis section */
  background: var(--u5-surface-band);
}


/* =============================================================================
   11. COMPONENT · STAT BAR
   ----------------------------------------------------------------------------
   Equal flex cells, 1px border/subtle BETWEEN, no outer border.
   ========================================================================== */

.u5-statbar {
  display: flex;
  border-top: 1px solid var(--u5-border-subtle);
}

.u5-statbar__cell {
  flex: 1;
  padding: 24px 28px 0;
  border-right: 1px solid var(--u5-border-subtle);
}
.u5-statbar__cell:last-child { border-right: 0; }

.u5-statbar__figure { /* compose with .u5-mono-figure */ }

.u5-statbar__label {
  font-size: 14.5px;
  color: var(--u5-text-tertiary);
  margin-top: var(--u5-space-8);
}


/* =============================================================================
   12. COMPONENT · MEDIA FRAME
   ----------------------------------------------------------------------------
   surface/card, 1px border/subtle, radius 12; caption in the footer strip
   above a top border. Hero media gets the window-chrome treatment (two 9px
   dots + a mono filename); the rest do not.
   image-rendering: pixelated on EVERY screenshot, without exception.
   Never crop, never overlay text on a screenshot, caption everything.
   ========================================================================== */

.u5-media {
  background: var(--u5-surface-card);
  border: 1px solid var(--u5-border-subtle);
  border-radius: var(--u5-radius-12);
  overflow: hidden;
}

.u5-media img,
.u5-media video {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

.u5-media__caption {
  padding: 16px 20px;
  border-top: 1px solid var(--u5-border-subtle);
  font-size: var(--u5-size-body);
  color: var(--u5-text-strong);
}

/* --- window-chrome variant: HERO ONLY --- */
.u5-media--chrome .u5-media__bar {
  display: flex;
  align-items: center;
  gap: var(--u5-space-8);
  padding: 14px 16px 12px;
}

.u5-media--chrome .u5-media__dot {
  width: 9px;
  height: 9px;
  border-radius: var(--u5-radius-pill);
  background: var(--u5-border-control);
}

.u5-media--chrome .u5-media__name {
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-label);
  color: var(--u5-text-quiet);
  margin-left: var(--u5-space-8);
  text-transform: none;
}

.u5-media--chrome .u5-media__well { padding: 0 10px 10px; }
.u5-media--chrome .u5-media__well img {
  border-radius: var(--u5-radius-6);    /* nested radius: 12 card holds a 6 */
}

/* Video: muted, looping, playsinline, no controls — set on the element. */


/* =============================================================================
   13. COMPONENT · TAXONOMY TAG
   ----------------------------------------------------------------------------
   Mono 10px / 0.1em, 1px border, radius 4, padding 3px 7px.
   FIDELITY accent · ADDED text/primary · DIVERGENCE status/partial ·
   REMOVED text/quiet.
   ⚠ README says ADDED is text/primary (#E9EAEC); the page files draw it in
   #C8CBD1 (--u5-text-strong) every time. Taking the pages.
   ⚠ The bundle also uses PRESERVED / FIXED / NOT YET REPRODUCED / LEVEL n /
   NOT BUILT / MOST RECENT with the same three colour classes — the taxonomy
   is wider than the four names the README lists.
   ========================================================================== */

.u5-tag {
  display: inline-block;
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-tag);
  line-height: var(--u5-lh-mono);
  letter-spacing: var(--u5-track-tag);
  text-transform: uppercase;
  border: 1px solid var(--u5-border-strong);
  border-radius: var(--u5-radius-4);
  padding: 3px 7px;
  color: var(--u5-text-quiet);
  white-space: nowrap;
}

/* accent family: FIDELITY, PRESERVED, MOST RECENT — border takes the hue too */
.u5-tag--fidelity,
.u5-tag--preserved {
  color: var(--u5-accent);
  border-color: var(--u5-accent);
}

/* neutral-strong family: ADDED, FIXED */
.u5-tag--added,
.u5-tag--fixed { color: var(--u5-text-strong); }

/* partial family: DIVERGENCE, NOT YET REPRODUCED, LEVEL n.
   status/partial is glyph-and-label ONLY — the border stays border/strong. */
.u5-tag--divergence,
.u5-tag--not-reproduced { color: var(--u5-status-partial); }

/* quiet family: REMOVED, NOT BUILT */
.u5-tag--removed,
.u5-tag--not-built { color: var(--u5-text-quiet); }


/* =============================================================================
   14. SUPPORTING PATTERNS the components above depend on
   ----------------------------------------------------------------------------
   Not in the README's component list, but the pages cannot be built without
   them and they are fully specified in the .dc.html files.
   ========================================================================== */

/* --- 14.1 sticky nav — the ONE place a translucent surface is allowed --- */
.u5-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--u5-nav-height);
  padding: 0 var(--u5-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--u5-surface-nav);
  backdrop-filter: var(--u5-nav-blur);
  -webkit-backdrop-filter: var(--u5-nav-blur);
  border-bottom: 1px solid var(--u5-border-subtle);
}

.u5-nav__logo { height: 32px; image-rendering: pixelated; }

.u5-nav__links { display: flex; gap: 28px; font-size: 14px; }

.u5-nav__link {
  color: var(--u5-text-nav);
  text-decoration: none;
  transition: color var(--u5-transition-hover);
}
.u5-nav__link:hover { color: var(--u5-text-primary); }        /* ✅ SIGNED */
.u5-nav__link[aria-current="page"] { color: var(--u5-text-primary); }

/* --- 14.2 status pill --- */
.u5-pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--u5-surface-pill);
  border: 1px solid var(--u5-border-strong);
  border-radius: var(--u5-radius-pill);
  padding: 6px 14px;
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-meta);
  letter-spacing: var(--u5-track-pill);
  text-transform: uppercase;
  color: var(--u5-text-nav);
}

.u5-pill__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--u5-radius-pill);
  background: var(--u5-text-tertiary);
}
.u5-pill--live .u5-pill__dot { background: var(--u5-accent); }

/* --- 14.3 drop zone (/play step 2) --- */
.u5-dropzone {
  border: 1px dashed var(--u5-border-control);
  border-radius: var(--u5-radius-12);
  background: var(--u5-surface-card);
  padding: 56px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  text-align: center;
  transition: border-color var(--u5-transition-state);
}
.u5-dropzone--compact { padding: 32px 22px; gap: 16px; }
.u5-dropzone.is-dragover { border-color: var(--u5-accent); }   /* ✅ SIGNED */

/* --- 14.4 status glyph — glyph FIRST, hue second, so it reads in greyscale --- */
.u5-status { font-family: var(--u5-font-mono); }
.u5-status--verified { color: var(--u5-status-verified); }
.u5-status--partial  { color: var(--u5-status-partial); }
.u5-status--missing  { color: var(--u5-status-missing); }

/* --- 14.5 layout shell --- */
.u5-canvas {
  max-width: var(--u5-canvas);
  margin-inline: auto;
  padding-inline: var(--u5-gutter);
}

.u5-cols-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--u5-space-64);
}

.u5-inset {   /* code blocks, screenshot wells */
  background: var(--u5-surface-inset);
  border: 1px solid var(--u5-border-subtle);
  border-radius: var(--u5-radius-6);
  font-family: var(--u5-font-mono);
}


/* =============================================================================
   15. ✅ RESPONSIVE — a spec, not a floor.  SCOPE: THE HOME.
   ----------------------------------------------------------------------------
   What was here before was explicitly a FLOOR: it hid .u5-nav__links below
   960px and put nothing in their place, which is not shippable — a navigation
   that disappears is worse than a navigation that wraps.

   The handoff has ZERO media queries (measured: `grep -c '@media'` returns 0
   across all 9 of its files) and six fixed 1440px canvases; its "mobile
   designs" are separate fixed-width artifacts with no reflow order.

   FOUR REGIMES, each one a decision about what the page is for at that width:

     ≥1100   FULL       12 cols, gutter 56, 4-cell stat bar, inline nav.
     880–1099 COMPACT   gutter 32. Everything else holds: three feature cards
                        still fit (≈250px each) and the inline nav still fits.
     600–879  STACKED   Nav collapses to the disclosure menu (§15.1). Cards,
                        steps and 2-col sections go to one column. Stat bar
                        goes 4 → 2×2: four 28px mono figures do not fit on one
                        row under 880 without shrinking the figure, and the
                        figure size is what makes it read as evidence.
     <600     NARROW    Gutter 20. Stat bar 2×2 → 1 column at 420 (see §15.3:
                        "TypeScript" at 28px mono is ~170px and a 2×2 cell at
                        360 is 160px — it is the WORD that sets this
                        breakpoint, not the viewport).

   REFLOW ORDER (what the reader meets, in order, on a phone): hero headline →
   sub → primary CTA → its mono requirement caption → stat bar → sections in
   source order. The CTA never moves below the fold-worth of text, and the
   requirement caption never separates from the button — the README makes that
   caption mandatory, and a caption that reflows away from its button is the
   same defect as not having it.
   ========================================================================== */

/* --- 15.1 the breakpoints, named once ------------------------------------ */
/* CSS custom properties cannot be used in media queries, so the numbers are
   literal below. They are named here so nothing invents a fifth one:
     1100  full → compact
      880  compact → stacked   (nav collapses HERE, and only here)
      600  stacked → narrow
      420  stat bar 2×2 → 1                                                  */

/* --- 15.2 COMPACT --------------------------------------------------------- */
@media (max-width: 1099px) {
  :root { --u5-gutter: 32px; }
  .u5-section { padding-top: var(--u5-space-64); }
}

/* --- 15.3 STACKED --------------------------------------------------------- */
@media (max-width: 879px) {
  :root { --u5-nav-height: 64px; }

  .u5-card-grid,
  .u5-steps  { grid-template-columns: 1fr; }
  .u5-cols-2 { grid-template-columns: 1fr; gap: var(--u5-space-44); }

  /* stat bar 4 → 2×2. The cell keeps its right border only on the left column
     and its bottom border only on the top row, so the grid reads as a grid and
     not as four floating blocks. */
  .u5-statbar { flex-wrap: wrap; }
  .u5-statbar__cell { flex: 1 1 50%; padding-bottom: 24px; }
  .u5-statbar__cell:nth-child(2n)   { border-right: 0; }
  .u5-statbar__cell:nth-child(-n+2) { border-bottom: 1px solid var(--u5-border-subtle); }

  /* THE MOBILE NAV. The inline links and the desktop CTA hide; the disclosure
     button appears. This is the only place in the system where something is
     hidden without a replacement being shown in the same rule — deliberately
     written as one block so the two halves cannot drift apart. */
  .u5-nav__links,
  .u5-nav__cta      { display: none; }
  /* 🔴 `header.u5-nav` NO es decoración: §16 declara después
     `.u5-nav__toggle { display: none }` y, a igual especificidad, gana LA
     ÚLTIMA. Con el selector pelado el botón de menú no aparecía nunca y la
     página quedaba, exactamente, sin navegación bajo 880 — el defecto que este
     bloque existe para arreglar, reintroducido por orden de cascada. Medido:
     `capturar.mjs` lo cazó a 360px con «se esperaba nav movil y el boton no se
     ve». Se sube la especificidad en vez de reordenar el fichero, para que la
     regla siga siendo cierta si alguien mueve las secciones. */
  header.u5-nav .u5-nav__toggle { display: inline-flex; }
  .u5-nav__panel[data-open]     { display: block; }
}

/* --- 15.4 NARROW ---------------------------------------------------------- */
@media (max-width: 599px) {
  :root { --u5-gutter: 20px; }
  .u5-nav__logo  { height: 24px; }        /* README §Assets: 24px mobile */
  .u5-feature-card,
  .u5-aside      { padding: var(--u5-space-20); }
  .u5-step-card  { padding: var(--u5-space-20); }
  .u5-media__caption { padding: 14px 16px; }
  .u5-btn-group  { flex-direction: column; align-items: stretch; width: 100%; }
  .u5-btn-group > .u5-btn { width: 100%; }
}

@media (max-width: 419px) {
  /* 4 → 1. Set by the widest stat FIGURE ("TypeScript", ~170px at 28px mono),
     not by the viewport: two of those do not fit in a 360-wide canvas minus
     2×20 gutters = 320px. */
  .u5-statbar__cell { flex: 1 1 100%; border-right: 0;
                      border-bottom: 1px solid var(--u5-border-subtle); }
  .u5-statbar__cell:last-child { border-bottom: 0; }
  .u5-statbar__cell:nth-child(2n) { border-right: 0; }
}


/* =============================================================================
   16. ✅ COMPONENT · MOBILE NAV (the thing the floor was missing)
   ----------------------------------------------------------------------------
   MARKUP CONTRACT — the CSS alone is not the component. Required:

     <header class="u5-nav">
       <a class="u5-nav__home" href="/"><img class="u5-nav__logo" …></a>
       <nav class="u5-nav__links" aria-label="Main">…</nav>
       <div class="u5-nav__cta">  <segmented> <a class="u5-btn u5-btn--compact">…  </div>
       <button class="u5-nav__toggle" type="button"
               aria-expanded="false" aria-controls="u5-nav-panel">
         <span class="u5-nav__bars" aria-hidden="true"></span>
         <span class="u5-nav__toggle-label">Menu</span>
       </button>
     </header>
     <div class="u5-nav__panel" id="u5-nav-panel">…links, segmented, CTA…</div>

   Non-negotiables, each one a failure mode that has a name:
     · aria-expanded on the BUTTON, toggled by the script. A disclosure without
       it is a button that says nothing to a screen reader.
     · The button has a TEXT label ("Menu"), not only a glyph. Three bars with
       no accessible name is the single most common a11y bug on the web.
     · 44×44 minimum hit area (WCAG 2.5.5 AAA / 2.5.8 AA asks 24; 44 is the
       platform convention and this is a one-handed page).
     · The panel repeats the EN/ES control and the primary CTA. On a phone the
       CTA is the page: it cannot live only in a menu the reader must open, so
       the hero CTA stays in the flow and this one is the second chance.
     · Escape closes and returns focus to the button.
     · The panel is a SIBLING of the sticky header, not a child: inside a
       backdrop-filter ancestor, a fixed/absolute child is clipped by the
       filter's containing block, and the menu renders inside 64px of header.

   The 12 lines of JS this needs are in the home prototype
   (responsive/home-responsive.html, <script> at the end). It is deliberately
   not a framework component: a disclosure is a boolean.
   ========================================================================== */

.u5-nav__toggle {
  display: none;                 /* shown by §15.3 */
  align-items: center;
  gap: 10px;
  min-width: 44px;
  min-height: 44px;
  padding: 0 12px;
  background: none;
  border: 1px solid var(--u5-border-control);
  border-radius: var(--u5-radius-8);
  color: var(--u5-text-strong);
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-label);
  letter-spacing: var(--u5-track-mono-label);
  text-transform: uppercase;
  cursor: pointer;
}

/* Three bars, drawn in CSS so there is no icon file and no <svg> to mislabel. */
.u5-nav__bars,
.u5-nav__bars::before,
.u5-nav__bars::after {
  display: block;
  width: 16px;
  height: 1.5px;
  background: currentColor;
  transition: transform var(--u5-transition-state),
              opacity   var(--u5-transition-state);
}
.u5-nav__bars { position: relative; }
.u5-nav__bars::before,
.u5-nav__bars::after { content: ""; position: absolute; left: 0; }
.u5-nav__bars::before { top: -5px; }
.u5-nav__bars::after  { top:  5px; }

/* Open state: the bars become an X. The label switches to "Close" in the DOM
   (the script does it), so the state is never carried by the glyph alone. */
.u5-nav__toggle[aria-expanded="true"] .u5-nav__bars { background: transparent; }
.u5-nav__toggle[aria-expanded="true"] .u5-nav__bars::before { transform: translateY(5px)  rotate(45deg); }
.u5-nav__toggle[aria-expanded="true"] .u5-nav__bars::after  { transform: translateY(-5px) rotate(-45deg); }

.u5-nav__panel {
  display: none;                 /* [data-open] shows it, §15.3 */
  position: sticky;
  top: var(--u5-nav-height);
  z-index: 19;
  background: var(--u5-surface-nav);
  backdrop-filter: var(--u5-nav-blur);
  -webkit-backdrop-filter: var(--u5-nav-blur);
  border-bottom: 1px solid var(--u5-border-subtle);
  padding: var(--u5-space-20) var(--u5-gutter) var(--u5-space-26);
}

.u5-nav__panel .u5-nav__links {
  display: flex;                 /* re-shown INSIDE the panel: §15.3 hides the
                                    header copy, not this one */
  flex-direction: column;
  gap: 0;
  font-size: 17px;
}

.u5-nav__panel .u5-nav__link {
  display: block;
  padding: 14px 0;               /* ≥44px row with the 17px line box */
  border-bottom: 1px solid var(--u5-border-subtle);
  color: var(--u5-text-strong);
}
.u5-nav__panel .u5-nav__link:last-child { border-bottom: 0; }

.u5-nav__panel-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--u5-space-14);
  margin-top: var(--u5-space-20);
}
.u5-nav__panel-foot .u5-btn--compact { flex: 1; }

/* Above the collapse point the panel must never appear, whatever the script
   left in the DOM — a stale data-open after a resize is a real bug, not a
   hypothetical one. */
@media (min-width: 880px) {
  .u5-nav__panel,
  .u5-nav__panel[data-open] { display: none; }
}

/* =============================================================================
   §17 · DESPLEGABLE — <details> nativo, para lo que sirve de referencia y no de
   lectura (la lista de 31 nombres de fichero, la tabla de estados del cargador,
   el catálogo de eventos de /privacidad).

   🔴 VIVE AQUÍ Y NO EN CADA GENERADOR. Los cinco generadores de documentación
   duplican su propio bloque `.u5-doc{…}`; una sexta copia de este componente
   habría nacido divergiendo el día que alguien tocara una sola. Todas las
   páginas enlazan este fichero (lo exige la guarda 1a-bis del ensamblador), así
   que aquí llega a las cinco de una vez.

   🔴 SIN JAVASCRIPT, Y ESO ES EL PUNTO. `<details>` abre y cierra por sí solo,
   se puede buscar con Ctrl+F en los navegadores modernos, y el lector de
   pantalla lo anuncia como lo que es. Un acordeón hecho a mano habría que
   cablearlo con aria-expanded y teclado — es decir, reimplementar lo que el
   elemento ya trae.
   ============================================================================= */
.desplegable {
  border: 1px solid var(--u5-border-subtle);
  border-radius: var(--u5-radius-8);
  background: var(--u5-surface-card);
  margin: var(--u5-space-20) 0;
}
.desplegable > summary {
  cursor: pointer;
  padding: var(--u5-space-14) var(--u5-space-20);
  font-family: var(--u5-font-mono);
  font-size: var(--u5-size-mono-label);
  letter-spacing: var(--u5-track-mono-label);
  text-transform: uppercase;
  color: var(--u5-text-secondary);
  /* El marcador propio del navegador se conserva: es la única señal de que
     esto se abre, y sustituirlo por un glifo dibujado obliga a girarlo a mano
     en cada estado. Lo que se ajusta es el hueco, no el marcador. */
  list-style-position: outside;
}
.desplegable > summary:hover { color: var(--u5-text-strong); }
/* 🔴 AQUÍ NO VA UNA REGLA DE FOCO, Y ESO ES UNA DECISIÓN. La primera versión de este
   componente añadía `.desplegable > summary:focus-visible { outline: var(--u5-focus-ring) }`
   y ese token NO EXISTE: la regla habría sido inválida y, al ganar en especificidad a la
   `:focus-visible` global de D3, habría dejado al `summary` —que ES un control— como el
   ÚNICO control del sitio SIN anillo de foco. Es literalmente lo que el comentario de D3
   prohíbe («never remove the outline without replacing it»), cometido al intentar
   respetarlo. Lo cazó buscar el token antes de darlo por bueno.
   ⇒ El foco lo pone la regla GLOBAL, que ya alcanza a `summary` sin ayuda. */
.desplegable[open] > summary {
  color: var(--u5-text-strong);
  border-bottom: 1px solid var(--u5-border-subtle);
}
/* El contenido se separa del borde; los bloques anchos siguen scrolleando
   DENTRO (la regla de .u5-doc .tabla sigue valiendo aquí). */
.desplegable > :not(summary) { margin-left: var(--u5-space-20); margin-right: var(--u5-space-20); }
.desplegable > :not(summary):first-of-type { margin-top: var(--u5-space-14); }
.desplegable > :last-child { margin-bottom: var(--u5-space-14); }
