/* Generated by apps/www/scripts/build.mjs from system/dist/tokens.css. */
:root {
  color-scheme: light dark;
  --ground-default: oklch(97.3% 0.007 88);
  --ground-raised: oklch(99.6% 0.003 88);
  --ground-sunken: oklch(94.4% 0.009 88);
  --fg-default: oklch(21.5% 0.035 268);
  --fg-muted: oklch(42% 0.024 268);
  --fg-faint: oklch(54% 0.018 268);
  --line-hairline: oklch(89.8% 0.010 84);
  --line-default: oklch(83% 0.013 84);
  --line-strong: oklch(48% 0.02 268);
  --line-focus: oklch(58.5% 0.21 272);
  --tech-line: oklch(21.5% 0.035 268);
  --tech-label: oklch(44% 0.022 268);
  --tech-mark: oklch(58.5% 0.21 272);
  --selection-bg: oklch(90% 0.0482 272);
  --world-travail-voice: oklch(67% 0.205 36);
  --world-travail-line: oklch(58.5% 0.2003 36);
  --world-projets-voice: oklch(83.5% 0.17 96);
  --world-projets-line: oklch(58.5% 0.1203 96);
  --world-vision-voice: oklch(50% 0.2 272);
  --world-vision-line: oklch(58.5% 0.21 272);
  --world-outils-voice: oklch(58.5% 0.1246 164);
  --world-outils-line: oklch(58.5% 0.1246 164);
  --world-journal-voice: oklch(75.5% 0.133 322);
  --world-journal-line: oklch(58.5% 0.147 322);
  --world-vie-voice: oklch(75.5% 0.1895 344);
  --world-vie-line: oklch(58.5% 0.21 344);
  --motion-hover: 104ms;
  --ease-hover: cubic-bezier(0.2, 0, 0, 1);
  --motion-enter: 417ms;
  --ease-enter: cubic-bezier(0.2, 0, 0, 1);
  --motion-exit: 208ms;
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --motion-stagger: 208ms;
  --ease-stagger: cubic-bezier(0, 0, 1, 1);
  --motion-portal: 833ms;
  --ease-portal: cubic-bezier(0.2, 0, 0, 1);
  --motion-assemblage: 1667ms;
  --ease-assemblage: cubic-bezier(0.2, 0, 0, 1);
  --motion-snap: 417ms;
  --ease-snap: cubic-bezier(0.7, 0, 0.1, 1);
  --spring-arrive: linear(0, 0.0272, 0.0971, 0.1943, 0.3062, 0.423, 0.5374, 0.6441, 0.7397, 0.8223, 0.8913, 0.9469, 0.9898, 1.0215, 1.0433, 1.0568, 1.0637, 1.0654, 1.0632, 1.0584, 1.0519, 1.0445, 1.0369, 1.0294, 1.0225, 1.0164, 1.0111, 1.0067, 1.0031, 1.0004, 0.9984, 0.997, 0.9962, 0.9958, 0.9957, 0.9959, 0.9962, 0.9967, 0.9971, 0.9976, 1);
  --spring-arrive-ms: 680ms;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;
  --space-32: 128px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground-default: oklch(19% 0.034 268);
    --ground-raised: oklch(25.5% 0.032 268);
    --ground-sunken: oklch(15% 0.03 268);
    --fg-default: oklch(97.2% 0.005 92);
    --fg-muted: oklch(83.5% 0.011 268);
    --fg-faint: oklch(58.5% 0.017 268);
    --line-hairline: oklch(33% 0.028 268);
    --line-default: oklch(41.5% 0.024 268);
    --line-strong: oklch(67% 0.015 268);
    --line-focus: oklch(75.5% 0.1252 272);
    --tech-line: oklch(97.2% 0.005 92);
    --tech-label: oklch(83.5% 0.011 268);
    --tech-mark: oklch(75.5% 0.1252 272);
    --selection-bg: oklch(41.5% 0.175 272);
    --world-travail-voice: oklch(67% 0.205 36);
    --world-travail-line: oklch(67% 0.205 36);
    --world-projets-voice: oklch(83.5% 0.17 96);
    --world-projets-line: oklch(83.5% 0.17 96);
    --world-vision-voice: oklch(67% 0.1744 272);
    --world-vision-line: oklch(67% 0.1744 272);
    --world-outils-voice: oklch(58.5% 0.1246 164);
    --world-outils-line: oklch(67% 0.1427 164);
    --world-journal-voice: oklch(75.5% 0.133 322);
    --world-journal-line: oklch(75.5% 0.133 322);
    --world-vie-voice: oklch(75.5% 0.1895 344);
    --world-vie-line: oklch(75.5% 0.1895 344);
  }
}
/* A theme the reader chose (Réglages, data-theme on <html>) wins over the device's. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground-default: oklch(19% 0.034 268);
  --ground-raised: oklch(25.5% 0.032 268);
  --ground-sunken: oklch(15% 0.03 268);
  --fg-default: oklch(97.2% 0.005 92);
  --fg-muted: oklch(83.5% 0.011 268);
  --fg-faint: oklch(58.5% 0.017 268);
  --line-hairline: oklch(33% 0.028 268);
  --line-default: oklch(41.5% 0.024 268);
  --line-strong: oklch(67% 0.015 268);
  --line-focus: oklch(75.5% 0.1252 272);
  --tech-line: oklch(97.2% 0.005 92);
  --tech-label: oklch(83.5% 0.011 268);
  --tech-mark: oklch(75.5% 0.1252 272);
  --selection-bg: oklch(41.5% 0.175 272);
  --world-travail-voice: oklch(67% 0.205 36);
  --world-travail-line: oklch(67% 0.205 36);
  --world-projets-voice: oklch(83.5% 0.17 96);
  --world-projets-line: oklch(83.5% 0.17 96);
  --world-vision-voice: oklch(67% 0.1744 272);
  --world-vision-line: oklch(67% 0.1744 272);
  --world-outils-voice: oklch(58.5% 0.1246 164);
  --world-outils-line: oklch(67% 0.1427 164);
  --world-journal-voice: oklch(75.5% 0.133 322);
  --world-journal-line: oklch(75.5% 0.133 322);
  --world-vie-voice: oklch(75.5% 0.1895 344);
  --world-vie-line: oklch(75.5% 0.1895 344);
}

/* Composition 0.1.0 — fonts, self-hosted. Albert Sans, Jost, Martian Mono: SIL Open Font License 1.1 (assets/fonts/OFL-*.txt). */
@font-face { font-family: 'Albert Sans'; font-style: normal; font-weight: 300 700; font-display: swap; src: url("/fonts/albert-sans-latin-ext.78b26c5d5d.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Albert Sans'; font-style: normal; font-weight: 300 700; font-display: swap; src: url("/fonts/albert-sans-latin.c2fb6fda41.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Jost'; font-style: normal; font-weight: 300 700; font-display: swap; src: url("/fonts/jost-latin-ext.312ef2b80c.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Jost'; font-style: normal; font-weight: 300 700; font-display: swap; src: url("/fonts/jost-latin.7726a5cd6f.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Martian Mono'; font-style: normal; font-weight: 300 600; font-stretch: 75% 112.5%; font-display: swap; src: url("/fonts/martian-mono-latin-ext.21fce7e290.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Martian Mono'; font-style: normal; font-weight: 300 600; font-stretch: 75% 112.5%; font-display: swap; src: url("/fonts/martian-mono-latin.f9c1655c2c.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

/* www.atecher.org — the arrival HUD. Zen instrument: hairlines, particle surfaces, one moving reticle.
   Colour carries meaning only: each path speaks in its tone, the instrument stays ink. */

@layer base, hud, motion, transitions;

@layer base {
  /* The two scales every content rule draws from (the style pass, 2026-11-05). Spacing steps double from a
     4 px unit. Type has seven sizes: labels and meta (12, 13), card text and captions (15), body (17), a
     heading (22), a section title (32) and a figure (56). Display sizes (page titles, hooks) keep their own
     fluid clamps; `--text-lead` and `--text-title` are the two fluid steps between body and heading and
     between heading and section title, shared so every lead and every title steps together. The welcome
     screen and the landing are frozen: rules they share (the header, the windows, the labels) are
     tokenised only where a value already equals a step. */
  :root {
    --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px;
    --space-6: 32px; --space-7: 48px; --space-8: 64px; --space-9: 96px;
    --text-xs: 12px; --text-sm: 13px; --text-md: 15px; --text-body: 17px; --text-lg: 22px; --text-xl: 32px; --text-2xl: 56px;
    --text-lead: clamp(var(--text-body), 1rem + .45vw, var(--text-lg));
    --text-title: clamp(var(--text-lg), 1rem + 1.2vw, var(--text-xl));
  }
  /* The paths' tones (his choice, 2026-10-30), each a voice and a line (the thin mark that must read on
     its own): a green a little brighter than the system's, a purple a little darker, the system's blue,
     and its gold for the experience still in preparation. Lighter at night. */
  /* On paper the lines carry text (a section's dates, a link's rule), so each is deep enough to read at
     4.5:1, and the gold voice deepens to an ochre that holds its own on cream (the system's yellow nearly
     vanished there). */
  :root {
    --tone-green: oklch(62% 0.16 158); --tone-green-line: oklch(49% 0.13 158);
    --tone-purple: oklch(55% 0.17 310); --tone-purple-line: oklch(48% 0.17 310);
    --tone-blue: var(--world-vision-voice); --tone-blue-line: oklch(50% 0.21 272);
    --tone-gold: oklch(74% 0.155 82); --tone-gold-line: oklch(52% 0.11 82);
    /* Réalisations' own (his ask, 2026-11-05): a coral in the widest gap of the wheel, between the purple and
       the gold, apart from the four colours its cards already wear for their families. */
    --tone-coral: oklch(60% 0.17 32); --tone-coral-line: oklch(51% 0.16 32);
  }
  /* The theme's material, beyond the system's roles.
     - `--sheet`: a card's or a window's ground. On paper a white sheet on the cream, at night a pane of
       the ground. A card lies flat on its hairline in both themes (no shadow: a cast shadow made the paper
       busier, and at night there was none to match).
     - `--construction`: how present the drawing behind the landing screens is. Behind a reading page it is
       gone on paper (`--construction-page`): its dark lines crossed the prose.
     - `--ink-gain`: how much the welcome scene strengthens its ink (bloch.js). The welcome screen stays as
       he left it (« don't modify welcome screen », 2026-10-31), so its value and the drawing's strength
       behind it (`--construction`) are kept, and the hidden centre point (`--construction-point`) spares it.
     - `--glyph-weight`, `--glyph-floor`: a glyph's particle size and least opacity. Small coloured dots on
       paper read as speckle; larger and firmer, drawn in the path's deeper line (`.glyph` below), they read
       as a form.
     - `--glow-lift`, `--glow-max`: how far a rising glyph lightens. At night it glows; on paper it would
       fade into the ground, so it lifts a little and stops well short of it.
     - `--locked`: how far the locked experience's name recedes on the welcome screen.
     - `--field`: a form field's ground. On paper a white sheet to write on, at night a step above the ground. */
  :root {
    --sheet: var(--ground-raised);
    --sheet-shadow: 0 1px 2px oklch(25% 0.03 268 / .06), 0 12px 28px -16px oklch(25% 0.03 268 / .22); /* floating layers only (a term's definition) */
    --construction: .62; --construction-page: 0; --construction-point: 0;
    --ink-gain: 1.45;
    --glyph-weight: 1.35; --glyph-floor: .7;
    --glow-lift: 9; --glow-max: 74;
    --locked: .6;
    --field: var(--ground-raised);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) {
      --tone-green: oklch(74% 0.16 158); --tone-green-line: oklch(74% 0.16 158);
      --tone-purple: oklch(70% 0.15 310); --tone-purple-line: oklch(70% 0.15 310);
      --tone-blue-line: var(--world-vision-line);
      --tone-gold: var(--world-projets-voice); --tone-gold-line: var(--world-projets-line);
      --tone-coral: oklch(72% 0.15 32); --tone-coral-line: oklch(72% 0.15 32);
      --sheet: color-mix(in oklch, var(--ground-raised) 72%, transparent); --sheet-shadow: none;
      --construction: 1; --construction-page: .6; --construction-point: 1; --ink-gain: 1; --glyph-weight: 1; --glyph-floor: .45;
      --glow-lift: 20; --glow-max: 96; --locked: .45; --field: color-mix(in oklch, var(--ground-raised) 60%, var(--ground-default));
    }
    :root:not([data-theme='light']) .glyph { --glyph-ink: var(--path-color); }
  }
  :root[data-theme='dark'] {
    --tone-green: oklch(74% 0.16 158); --tone-green-line: oklch(74% 0.16 158);
    --tone-purple: oklch(70% 0.15 310); --tone-purple-line: oklch(70% 0.15 310);
    --tone-blue-line: var(--world-vision-line);
    --tone-gold: var(--world-projets-voice); --tone-gold-line: var(--world-projets-line);
    --tone-coral: oklch(72% 0.15 32); --tone-coral-line: oklch(72% 0.15 32);
    --sheet: color-mix(in oklch, var(--ground-raised) 72%, transparent); --sheet-shadow: none;
    --construction: 1; --construction-page: .6; --construction-point: 1; --ink-gain: 1; --glyph-weight: 1; --glyph-floor: .45;
    --glow-lift: 20; --glow-max: 96; --locked: .45; --field: color-mix(in oklch, var(--ground-raised) 60%, var(--ground-default));
  }
  :root[data-theme='dark'] .glyph { --glyph-ink: var(--path-color); }
  /* A glyph's ink is its path's line on paper (deep enough to hold as a form), its voice at night. Declared
     on the holder, where the path's tone is known. */
  .glyph { --glyph-ink: var(--path-line); }
  /* The wordmark's one colour is its point (system/STYLE.md §6). Unset, it is the house ultramarine
     (`mascot.body`, the vision world's line in either theme); on a path's page it takes that path's tone,
     its line on paper and its voice at night. The contact page and Présentation, in ink, keep the ultramarine. */
  /* Set on :root, not left to the wordmark's own fallback: the hover's line and particles read it too, and
     with it unset (the landing screens, the contact page) they would draw nothing. */
  :root { --mascot-body: var(--world-vision-line); --brand-point: var(--mascot-body); }
  .hud[data-path]:not([data-path='contact'], [data-path='presentation']) { --brand-point: var(--path-line); }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .hud[data-path]:not([data-path='contact'], [data-path='presentation']) { --brand-point: var(--path-color); }
  }
  :root[data-theme='dark'] .hud[data-path]:not([data-path='contact'], [data-path='presentation']) { --brand-point: var(--path-color); }
  /* Two system icons as masks (externe, flèche droite), each with its one point. */
  :root {
    --icon-out: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75'%3E%3Cpath d='M9.2 14.8 18 6M10 6h8v8'/%3E%3Ccircle cx='6.6' cy='17.4' r='2' fill='%23000' stroke='none'/%3E%3C/svg%3E");
    --icon-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75'%3E%3Cpath d='M8.5 12H19M14 7l5 5-5 5'/%3E%3Ccircle cx='5' cy='12' r='2' fill='%23000' stroke='none'/%3E%3C/svg%3E");
  }
  /* The side a line starts drawing from and the side it withdraws to: the reading direction's. */
  :root { --start: left; --end: right; }
  :root[dir='rtl'] { --start: right; --end: left; }
  *, *::before, *::after { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; }
  body {
    margin: 0;
    min-height: 100svh;
    background: var(--ground-default);
    color: var(--fg-default);
    font: 400 1rem/1.58 'Albert Sans', 'Avenir Next', 'Segoe UI', system-ui, sans-serif;
    -webkit-font-smoothing: antialiased;
  }
  ::selection { background: var(--selection-bg); }
  a { color: inherit; }
  :focus-visible { outline: 2px solid var(--line-focus); outline-offset: 3px; }
  abbr[title] { text-decoration: none; }
  kbd {
    display: inline-grid; place-items: center;
    min-width: 1.6em; height: 1.6em; padding: 0 .35em;
    border: 1px solid var(--line-default);
    font: 400 .6875rem/1 'Martian Mono', ui-monospace, monospace;
    color: var(--tech-label);
  }
}

@layer hud {
  .hud {
    --gutter: clamp(16px, 3.2vw, 44px);
    --corner: clamp(14px, 1.6vw, 22px);
    display: grid;
    grid-template-rows: auto 1fr auto;
    min-height: 100svh;
    padding: calc(var(--gutter) + env(safe-area-inset-top)) calc(var(--gutter) + env(safe-area-inset-right))
      calc(var(--gutter) + env(safe-area-inset-bottom)) calc(var(--gutter) + env(safe-area-inset-left));
    position: relative;
    isolation: isolate;
    overflow-x: clip;
  }

  .hud-label {
    font: 400 .6875rem/1.5 'Martian Mono', ui-monospace, monospace;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--tech-label);
  }
  :where(.hud--page, .hud--stub) .hud-label { font-size: var(--text-xs); font-weight: 500; }

  .skip {
    position: absolute; inset-inline-start: var(--gutter); top: var(--gutter); z-index: 10;
    padding: var(--space-2) var(--space-3); background: var(--ground-raised); border: 1px solid var(--line-strong);
  }
  .skip:not(:focus) { clip-path: inset(50%); width: 1px; height: 1px; overflow: hidden; white-space: nowrap; padding: 0; border: 0; }

  /* The frame: four corner brackets and the construction drawing behind everything. */
  .hud-frame { position: fixed; inset: 0; z-index: -1; pointer-events: none; }
  .hud-frame > i {
    position: absolute; width: var(--corner); height: var(--corner);
    border: 0 solid var(--tech-line); opacity: .55;
  }
  .hud-frame > i:nth-child(1) { top: 10px; left: 10px; border-width: 1.5px 0 0 1.5px; }
  .hud-frame > i:nth-child(2) { top: 10px; right: 10px; border-width: 1.5px 1.5px 0 0; }
  .hud-frame > i:nth-child(3) { bottom: 10px; right: 10px; border-width: 0 1.5px 1.5px 0; }
  .hud-frame > i:nth-child(4) { bottom: 10px; left: 10px; border-width: 0 0 1.5px 1.5px; }
  .hud-construction { position: absolute; inset: 0; width: 100%; height: 100%; }
  .hud-construction * { fill: none; stroke: var(--tech-mark); stroke-width: 1; vector-effect: non-scaling-stroke; }
  .c-orbit { opacity: calc(.16 * var(--construction)); }
  .c-orbit--inner { opacity: calc(.1 * var(--construction)); stroke-dasharray: 2 6; }
  .c-axis { opacity: calc(.14 * var(--construction)); stroke: var(--tech-line); }
  .c-point { fill: var(--fg-default); stroke: none; opacity: calc(.7 * var(--construction) * var(--construction-point)); }
  .hud--gate .c-point { opacity: calc(.7 * var(--construction)); }
  /* Behind a reading page the drawing would cross the prose: gone on paper, faint at night. */
  :is(.hud--page, .hud--stub) .hud-construction { opacity: var(--construction-page); }

  .hud-top, .hud-bottom {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
  }
  .hud-mark { display: inline-flex; padding: 6px 0; text-decoration: none; }
  .hud-mark svg { height: clamp(20px, 2vw, 26px); width: auto; display: block; overflow: visible; }
  /* The wordmark's hover (web/brand-hover.js): the line the point draws under the name (2 px at any size,
     like the header links'), the burst's particles near and far of the ink, and the particles that broke
     free, wandering the window. */
  .v-front line, .v-back line { stroke: var(--brand-point); stroke-linecap: round; }
  .v-back .v-line { stroke-width: 1px; vector-effect: non-scaling-stroke; } /* its gradient rides inline */
  .v-line-stop { stop-color: var(--brand-point); stop-opacity: 1; }
  .v-line-stop--far { stop-opacity: .22; }
  .v-wanderers { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 10; overflow: visible; }

  /* On a content page the header stays: wordmark, the paths, the language. It runs to the screen's
     edges over the ground, tightens once the page scrolls (hud.js `is-scrolled`) and draws a hairline. */
  :is(.hud--page, .hud--stub) {
    --top-pad: clamp(10px, 1.6vw, 20px);
    --sticky-h: calc(44px + 2 * var(--top-pad));
  }
  :is(.hud--page, .hud--stub) .hud-top {
    position: sticky; top: 0; z-index: 5;
    margin: calc(-1 * (var(--gutter) + env(safe-area-inset-top))) calc(-1 * var(--gutter)) 0;
    padding: calc(var(--top-pad) + env(safe-area-inset-top)) var(--gutter) var(--top-pad);
    background: color-mix(in oklch, var(--ground-default) 96%, transparent);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    transition: box-shadow var(--motion-enter) var(--ease-enter);
  }
  .is-scrolled .hud-top { box-shadow: 0 1px 0 var(--line-hairline); }
  /* A phone tucks the header away while reading down (hud.js wireTuck). */
  @media (max-width: 719px) {
    .hud--page .hud-top { transition: transform .32s cubic-bezier(.22, .8, .2, 1), box-shadow var(--motion-enter) var(--ease-enter); }
    .hud--page.is-tucked .hud-top { transform: translateY(-100%); }
  }
  /* The paths: names only. The current one is underlined in its colour; on hover a line draws in
     under the name from the left, in that path's colour, and on leaving it withdraws to the right. */
  .hud-paths { display: flex; gap: var(--space-1); margin-inline: auto; }
  .hud-paths__link {
    position: relative; display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
    color: var(--fg-muted); text-decoration: none; font: 500 var(--text-md)/1 'Albert Sans', system-ui, sans-serif; white-space: nowrap;
    transition: color var(--motion-hover) var(--ease-hover);
  }
  .hud-paths__link::after {
    content: ''; position: absolute; left: 14px; right: 14px; bottom: var(--space-2); height: 2px; background: var(--path-color);
    transform: scaleX(0); transform-origin: var(--end); transition: transform 280ms var(--ease-enter);
  }
  .hud-paths__link[aria-current] { color: var(--fg-default); }
  .hud-paths__link[aria-current]::after { height: 3px; transform: none; }
  @media (hover: hover) {
    .hud-paths__link:hover { color: var(--fg-default); }
    .hud-paths__link:hover::after { transform: scaleX(1); transform-origin: var(--start); }
  }
  /* Chosen from the keyboard (Tab, or ← → from hud.js), a link is marked as a hover marks it. */
  .hud-paths__link:focus-visible { outline: none; color: var(--fg-default); }
  .hud-paths__link:focus-visible::after { transform: scaleX(1); transform-origin: var(--start); }
  /* Écrire, the contact page: the same gesture in ink, beside the language, apart from the paths. */
  .hud-contact { margin-inline-start: auto; }
  .hud-paths + .hud-contact { margin-inline-start: 0; }
  /* A phone: the paths take their own row under the wordmark, Écrire and the language. */
  @media (max-width: 719px) {
    :is(.hud--page, .hud--stub) { --sticky-h: calc(44px + 40px + 2 * var(--top-pad)); }
    :is(.hud--page, .hud--stub) .hud-top { flex-wrap: wrap; row-gap: 0; }
    .hud-paths { order: 3; width: 100%; margin: 0 -10px; justify-content: space-between; gap: 0; }
    /* Four links share the row (Présentation and the three paths): tighter, and a name too long for its
       share clips rather than widening the page. */
    .hud-paths__link { min-width: 0; min-height: 40px; padding: 0 6px; font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; }
    .hud-paths__link::after { left: 6px; right: 6px; bottom: 6px; }
    .hud-paths + .hud-contact { margin-inline-start: auto; }
    .hud-tool { min-width: 38px; padding: 0 var(--space-2); }
    /* Écrire runs long in some languages (« Escreva para mim »): the row tightens, the language tool
       keeps its globe alone, and the label clips rather than pushing the row past the screen. */
    .hud-top { gap: var(--space-2); min-width: 0; }
    .hud-tool__code { display: none; }
    .hud-contact { min-width: 0; padding: 0 6px; overflow: hidden; text-overflow: ellipsis; }
    .hud-contact::after { left: 6px; right: 6px; }
  }
  /* A tablet: the four page links, the contact and the tools no longer fit one row (they ran 195 px past a
     720 px screen), so a content page's links take their own row here too. The welcome screen and the
     home, which have no such links, keep their header. */
  @media (min-width: 720px) and (max-width: 959px) {
    :is(.hud--page, .hud--stub) { --sticky-h: calc(44px + 44px + 2 * var(--top-pad)); }
    :is(.hud--page, .hud--stub) .hud-top { flex-wrap: wrap; row-gap: 0; }
    :is(.hud--page, .hud--stub) .hud-paths { order: 3; width: 100%; margin: 0; justify-content: center; }
    :is(.hud--page, .hud--stub) .hud-paths + .hud-contact { margin-inline-start: auto; }
  }

  /* The header's tools: language (with its code), accessibility, settings, in one hairline box. */
  .hud-tools { display: flex; border: 1px solid var(--line-default); }
  .hud-tool {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    min-width: 42px; min-height: 40px; padding: 0 10px; margin: 0;
    border: 0; background: none; color: var(--fg-muted); cursor: pointer;
    font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .08em;
    transition: color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  .hud-tool + .hud-tool { box-shadow: inset 1px 0 0 var(--line-hairline); }
  .hud-tool__icon { width: 20px; height: 20px; flex: none; }
  .hud-tool:hover, .hud-tool:focus-visible { color: var(--fg-default); background: var(--ground-raised); }
  .hud-tool:focus-visible { outline-offset: -2px; }
  /* A tool whose window is open stands inked. */
  body:has(#modal-lang:popover-open) .hud-tool--lang, body:has(#modal-a11y:popover-open) .hud-tool--a11y,
  body:has(#modal-settings:popover-open) .hud-tool--settings { color: var(--ground-default); background: var(--fg-default); }
  .hud-tool__code { color: inherit; }
  html:not(.js) :is(.hud-tool--a11y, .hud-tool--settings) { display: none; }

  /* The windows (popovers in the top layer): a raised sheet with the reticle's corners, its title and
     key, then its content. Escape or a click outside closes it. */
  .modal {
    position: fixed; inset: 0; margin: auto;
    width: min(500px, calc(100vw - 32px)); height: fit-content; max-height: calc(100svh - 32px); overflow: auto;
    padding: clamp(20px, 3vw, 30px); border: 1px solid var(--line-default);
    background: var(--ground-raised); color: var(--fg-default);
    box-shadow: 0 24px 64px -24px color-mix(in oklch, var(--fg-default) 30%, transparent);
  }
  .modal::backdrop { background: color-mix(in oklch, var(--ground-default) 62%, transparent); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
  .modal__corners { position: absolute; inset: 6px; pointer-events: none; }
  .modal__corners > i { position: absolute; width: 12px; height: 12px; border: 0 solid var(--tech-mark); }
  .modal__corners > i:nth-child(1) { top: 0; left: 0; border-width: 1.5px 0 0 1.5px; }
  .modal__corners > i:nth-child(2) { top: 0; right: 0; border-width: 1.5px 1.5px 0 0; }
  .modal__corners > i:nth-child(3) { bottom: 0; right: 0; border-width: 0 1.5px 1.5px 0; }
  .modal__corners > i:nth-child(4) { bottom: 0; left: 0; border-width: 0 0 1.5px 1.5px; }
  .modal__head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: 22px; }
  .modal__title { margin: 0; font: 450 clamp(1.5rem, 1.3rem + .8vw, 2rem)/1.1 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.01em; }
  .modal__close {
    display: grid; place-items: center; width: 40px; height: 40px; margin: -8px -8px -8px auto; padding: 0;
    border: 0; background: none; color: var(--fg-muted); cursor: pointer;
  }
  .modal__close:hover { color: var(--fg-default); }
  .modal__close .hud-tool__icon { width: 18px; height: 18px; }
  .modal__note, .pref__hint { margin: var(--space-4) 0 0; color: var(--fg-muted); font-size: .875rem; line-height: 1.5; }

  /* Language: the ten most spoken, each in its own name; only the site's own two can be chosen. */
  .langs { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
  .langs__opt {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    min-height: 44px; padding: 0 14px; border: 1px solid var(--line-hairline);
    color: var(--fg-default); text-decoration: none; font-size: 1rem;
    transition: border-color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  .langs__code { font: 400 .6875rem/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .08em; color: var(--tech-label); }
  a.langs__opt:hover, a.langs__opt:focus-visible { border-color: var(--line-strong); background: var(--ground-default); }
  .langs__opt[aria-current] { border-color: var(--fg-default); box-shadow: inset 0 0 0 1px var(--fg-default); }
  .langs__opt[aria-current] .langs__code::before { content: ''; display: inline-block; width: 7px; height: 7px; margin-inline-end: var(--space-2); border-radius: 50%; background: var(--fg-default); vertical-align: .05em; }
  .langs__opt[aria-disabled] { opacity: .38; cursor: not-allowed; }

  /* Accessibility and settings: one row of choices per preference, the chosen one inked. */
  .prefs { display: grid; gap: 20px; }
  .pref { margin: 0; padding: 0; border: 0; min-width: 0; }
  .pref__name { margin: 0 0 10px; padding: 0; font: 400 .6875rem/1.5 'Martian Mono', ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; color: var(--tech-label); }
  .pref__opts { display: flex; flex-wrap: wrap; gap: 6px; }
  .pref__opt { position: relative; }
  .pref__opt input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
  .pref__opt span {
    display: inline-flex; align-items: center; min-height: 40px; padding: 0 14px;
    border: 1px solid var(--line-hairline); font-size: var(--text-md);
    transition: border-color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  .pref__opt:hover span { border-color: var(--line-strong); }
  .pref__opt input:checked + span { background: var(--fg-default); color: var(--ground-default); border-color: var(--fg-default); }
  .pref__opt input:focus-visible + span { outline: 2px solid var(--line-focus); outline-offset: 2px; }
  .prefs + .pref__name { margin-top: 26px; }
  .keys { display: grid; gap: var(--space-2); margin: 0; }
  .keys > div { display: flex; align-items: center; gap: 14px; }
  .keys dt { display: flex; align-items: center; min-width: 9.5rem; }
  .keys dt kbd + kbd { margin-inline-start: -1px; }
  .keys dd { margin: 0; color: var(--fg-muted); font-size: var(--text-md); }
  .modal__actions { margin: var(--space-5) 0 0; }

  .hud-main {
    align-self: center;
    width: min(100%, 1120px);
    margin: 0 auto;
    padding: clamp(var(--space-6), 7vh, 88px) 0;
  }
  .hud-kicker { margin: 0 0 var(--space-4); display: flex; align-items: center; gap: var(--space-3); }
  .hud-kicker::before { content: ''; width: 28px; height: 1px; background: var(--tech-line); opacity: .6; }
  .hud-title {
    margin: 0;
    font: 450 clamp(2.5rem, 1.6rem + 4.2vw, 5.4rem)/.95 'Jost', 'Futura', 'Avenir Next', system-ui, sans-serif;
    letter-spacing: -.022em;
    text-wrap: balance;
  }
  .hud-role {
    margin: 20px 0 0; max-width: 36rem;
    font-size: clamp(1rem, .95rem + .25vw, 1.125rem);
    color: var(--fg-muted); text-wrap: pretty;
  }

  /* The four paths: a row on wide screens, two by two in between, a column on phones. */
  .paths { position: relative; margin-top: clamp(32px, 6vh, 64px); }
  .paths__list {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(12px, 1.4vw, 18px);
  }
  /* The classical home: where to start, large and centred, the line under it, then the paths. */
  .hero { display: grid; justify-items: center; text-align: center; }
  .hero__title { font-size: clamp(2.8rem, 1.7rem + 5vw, 6.4rem); line-height: .95; letter-spacing: -.03em; }
  .hero__role { margin: clamp(18px, 2.4vh, 28px) auto 0; max-width: 38rem; }
  .hero__name { justify-content: center; }
  .hud--landing .paths { margin-top: clamp(40px, 7vh, 80px); }

  /* The welcome screen: the sentence, then the choice on one axis: the classical pole's name above the
     Bloch sphere (web/bloch.js), the quantum pole's below, dimmed and locked. */
  .gate { display: grid; justify-items: center; gap: clamp(14px, 2.4vh, 28px); text-align: center; }
  .gate__title { max-width: 22ch; font-size: clamp(2rem, 1.2rem + 2.8vw, 3.9rem); line-height: 1.04; }
  .gate__ask { display: block; margin-top: .35em; font-size: .52em; letter-spacing: -.01em; color: var(--fg-muted); }
  .gate__choice { display: grid; justify-items: center; gap: 0; }
  .gate__sphere { position: relative; width: min(34vh, 360px); aspect-ratio: 1; }
  /* The sphere and the floor under it (web/bloch.js): one canvas, the whole screen, behind the page; the
     holder places the sphere and takes the pointer. Without script or WebGL it shows a still point. The full
     experience stands on the floor at the foot of the screen, under the throat. */
  .gate__floor { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }
  .js .hud--gate .gate__pole--quantum { position: fixed; left: 50%; bottom: max(clamp(12px, 4svh, 48px), env(safe-area-inset-bottom, 0px)); translate: -50% 0; width: max-content; max-width: calc(100vw - 32px); }
  :is(html:not(.js) .gate__sphere, .gate__sphere.is-still) { background: radial-gradient(circle, var(--fg-default) 0 3px, transparent 4px); }
  .gate__pole { display: grid; justify-items: center; gap: 4px; padding: 8px 18px; color: var(--fg-default); text-decoration: none; }
  .gate__name { position: relative; font: 450 clamp(1.6rem, 1.2rem + 1.2vw, 2.4rem)/1.2 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.01em; }
  /* The two names lettered like their hemispheres (web/bloch.js): a canvas over the real text, which
     stays for screen readers and selection and turns transparent once the lettering is drawn. Without
     script the text simply shows. */
  .gate__word { position: absolute; inset: -.45em -.35em; width: calc(100% + .7em); height: calc(100% + .9em); pointer-events: none; }
  .gate__name.is-lettered { color: transparent; }
  /* The classical pole underlines as the header's links do: a line drawn in from the left. */
  .gate__pole--classical .gate__name::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 2px; background: var(--fg-default);
    transform: scaleX(0); transform-origin: var(--end); transition: transform 280ms var(--ease-enter);
  }
  .gate__pole--classical:focus-visible .gate__name::after { transform: scaleX(1); transform-origin: var(--start); }
  @media (hover: hover) { .gate__pole--classical:hover .gate__name::after { transform: scaleX(1); transform-origin: var(--start); } }
  /* Dimmed, not erased: the name recedes, and its status stays legible (it is the one thing to read here). */
  .gate__pole--quantum > .gate__name { opacity: var(--locked); }
  .gate__status { margin-top: 6px; padding: 3px 8px; border: 1px dashed var(--line-strong); }
  /* What the experience will need, said where it would not run well: a phone, a small or touch screen. */
  .gate__device { display: none; max-width: 32ch; margin: 10px 0 0; font-size: .875rem; line-height: 1.45; color: var(--fg-muted); text-wrap: balance; }
  @media (max-width: 1023px), (pointer: coarse) { .gate__device { display: block; } }
  @media (max-width: 760px) { .gate__sphere { width: min(78vw, 320px); } }
  .paths__item:nth-child(2) { --i: 1; }
  .paths__item:nth-child(3) { --i: 2; }
  .paths__item:nth-child(4) { --i: 3; }
  .path {
    position: relative;
    display: grid; grid-template-rows: auto 1fr; gap: 28px; align-content: start;
    height: 100%; min-height: 248px;
    padding: clamp(20px, 2vw, 28px);
    border: 1px solid var(--line-hairline);
    background: var(--sheet);
    text-decoration: none;
    transition: border-color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  /* The card under the pointer or the keys stays where it is (his remark, 2026-11-01: it used to lift a
     hair, which read as the card slipping); its hairline firms instead. */
  .path:focus-visible { outline-offset: 6px; }
  /* On the classical home the reticle is the focus mark: the arrows, A, D and Tab move its corners onto the
     card, so no outline doubles them. Where the reticle is hidden (touch screens), the outline stays. */
  @media (hover: hover) { .path:focus-visible { outline: none; } }
  .paths[data-active] .path.is-active { border-color: var(--line-default); }
  .path:active { border-color: var(--path-line); }
  @media (hover: hover) { .path:hover { border-color: var(--line-default); } }
  .path__glyph { display: block; width: clamp(96px, 8vw, 120px); aspect-ratio: 1; margin: -8px 0 0 -8px; }

  /* The glyph: a holder hud.js fills with its path's particle surface on a canvas (web/surfaces.js).
     Without script, a still point in the path's colour stands in. */
  .glyph { position: relative; display: block; width: 100%; height: 100%; }
  /* The canvas reaches a fifth past the holder on every side (hud.js ROOM), room for rising particles. */
  .glyph__canvas { position: absolute; inset: -20%; width: 140%; height: 140%; pointer-events: none; }
  html:not(.js) .glyph { background: radial-gradient(circle, var(--path-color) 0 18%, transparent 19%); }
  /* A touch screen has no hover to aim with: the reticle stays away. */
  @media (hover: none) { .reticle { display: none; } }
  /* A small marker is a particle in its section's colour. */
  .mark, .cv-section__title::before, .page-index a::before, .cv-essay__tag::before, .cv-cta__link::before, .cv-head__meta > * + *::before {
    border-radius: 50%;
  }

  .path__body { display: grid; align-content: start; gap: 6px; }
  .path__name {
    font: 450 clamp(1.5rem, 1.2rem + 1vw, 2.1rem)/1.12 'Jost', 'Futura', system-ui, sans-serif;
    letter-spacing: -.01em;
  }
  .path__text { color: var(--fg-muted); font-size: .9375rem; line-height: 1.5; max-width: 30ch; text-wrap: pretty; }

  /* The reticle locks onto the active path; its brackets take that path's voice. */
  .reticle {
    position: absolute; left: 0; top: 0; width: 0; height: 0;
    pointer-events: none;
    --rc: var(--tech-mark);
    transition: translate var(--motion-snap) var(--ease-snap), width var(--motion-snap) var(--ease-snap),
      height var(--motion-snap) var(--ease-snap), opacity var(--motion-hover) linear;
    opacity: 0;
  }
  .reticle.is-placed { opacity: 1; }
  /* Nothing is preselected for the reader: the reticle shows once they aim, with the pointer over the
     cards or a key (arrows, A, D, Tab move focus onto a card). */
  .paths:not(:hover, :focus-within) .reticle { opacity: 0; }
  .paths[data-active]:not(:hover, :focus-within) .path.is-active { border-color: var(--line-hairline); }
  .reticle.is-instant { transition: none; }
  .reticle > i { position: absolute; width: 16px; height: 16px; border: 0 solid var(--rc);
    transition: border-color var(--motion-enter) var(--ease-enter); }
  .reticle > i:nth-child(1) { top: -7px; left: -7px; border-width: 2px 0 0 2px; }
  .reticle > i:nth-child(2) { top: -7px; right: -7px; border-width: 2px 2px 0 0; }
  .reticle > i:nth-child(3) { bottom: -7px; right: -7px; border-width: 0 2px 2px 0; }
  .reticle > i:nth-child(4) { bottom: -7px; left: -7px; border-width: 0 0 2px 2px; }
  .paths[data-active='presentation'] .reticle { --rc: var(--tone-blue-line); }
  .paths[data-active='work'] .reticle { --rc: var(--tone-purple-line); }
  .paths[data-active='together'] .reticle { --rc: var(--tone-green-line); }

  .hud-bottom { flex-wrap: wrap; row-gap: 8px; }
  .hud-bottom { justify-content: flex-end; }
  .hud-keys { margin: 0; display: flex; flex-wrap: wrap; gap: 8px 22px; align-items: center; }
  .hud-keys { font: 400 .75rem/1.5 'Martian Mono', ui-monospace, monospace; color: var(--fg-muted); }
  .hud-keys > span { display: inline-flex; align-items: center; gap: 4px; }
  html:not(.js) .hud-keys, html[data-keys='off'] .hud-keys { display: none; }
  .hud-keys kbd + kbd { margin-inline-start: -1px; }
  .hud-keys__or { margin: 0 3px; opacity: .6; }

  /* Stub pages: one form, its status, the way back. */
  .stub__glyph { display: block; width: clamp(72px, 9vw, 112px); aspect-ratio: 1; margin-bottom: var(--space-6); }
  .stub__status {
    display: inline-block; margin: var(--space-5) 0 0; padding: var(--space-1) var(--space-2);
    border: 1px solid var(--path-line); color: var(--fg-default);
  }
  .stub__actions { margin: var(--space-5) 0 0; }
  .hud--stub .hud-main { width: min(100%, 980px); }

  /* Path pages (Ingénierie, Enseignement, Réalisations): the path's form, its name, one lead. */
  .page { width: min(100%, 980px); align-self: start; }
  .page-head { padding-bottom: clamp(var(--space-5), 4vh, var(--space-7)); }
  .page-head__glyph { width: clamp(96px, 8vw, 120px); margin: 0 0 var(--space-4) -10px; }
  .page-head__lead {
    margin: var(--space-5) 0 0; max-width: 62ch;
    font: 400 var(--text-lead)/1.55 'Jost', 'Futura', system-ui, sans-serif; text-wrap: pretty;
  }
  /* A section whose title the page head already says keeps it for assistive technology only. */
  .cv-section--untitled > .cv-section__title { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .cv-section--untitled { padding-top: 0; border-top: 0; }
  /* A product's real screen tops its card. */
  .cv-essay__shot { display: block; width: calc(100% + 2 * var(--space-5)); max-width: none; height: auto; aspect-ratio: 16 / 9; object-fit: cover; object-position: top left; margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-5)) var(--space-4); border-bottom: 1px solid var(--line-hairline); }

  /* CV: one column of record under the HUD; section labels ride a left rail, dates a second one. */
  .cv-head { padding-bottom: clamp(var(--space-6), 5vh, var(--space-7)); border-bottom: 1px solid var(--line-hairline); }
  /* Présentation's head beside his portrait: the text in a column, the portrait standing at its side, its
     top on the name. A phone stacks it under the name. */
  .cv-head--portrait { display: grid; grid-template-columns: minmax(0, 1fr) clamp(200px, 22vw, 290px); column-gap: clamp(var(--space-5), 4vw, var(--space-8)); align-items: start; }
  .cv-head--portrait > * { grid-column: 1; }
  .cv-head--portrait > .cv-portrait { grid-column: 2; grid-row: 1 / span 20; }
  /* His portrait: painted, so nothing offers to save it, drag it or long-press it. At rest a little
     toward ink, held a touch close. Awake (hovered, or once in view on a touch screen: hud.js `is-on`;
     his choice of 2026-10-31) its colour comes back as it settles into its frame, as on a breath out, and
     a hairline frame in the path's tone draws itself round it from the top left corner. Nothing is drawn
     over the face. */
  .cv-portrait { position: relative; margin: 0; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
  /* The window clips and borders; the photo inside it settles by a GPU scale (animating background-size
     repainted it in whole pixels) and ends on neutral filter values, not `none` (dropping the filter at
     the end redrew it differently): both made it jump as it came to rest. */
  .cv-portrait__window { display: block; aspect-ratio: 4 / 5; overflow: hidden; border: 1px solid var(--line-hairline); background-color: var(--ground-raised); }
  .cv-portrait__photo {
    display: block; width: 100%; height: 100%;
    background-position: 50% 40%; background-size: 100%; background-repeat: no-repeat;
    scale: 1.015; filter: saturate(.78) contrast(.985); will-change: scale, filter;
    transition: filter 600ms var(--ease-enter), scale 900ms var(--ease-enter);
  }
  /* The frame: four hairline sides, all drawn at once, each from its corner clockwise (top from the left,
     right from the top, bottom from the right, left from the bottom; his ask of 2026-10-31); on leaving,
     they withdraw together. */
  .cv-portrait__frame { position: absolute; inset: -7px; pointer-events: none; }
  .cv-portrait__frame > i { position: absolute; background: var(--path-line); transition: scale 280ms var(--ease-enter); }
  .cv-portrait__frame > i:nth-child(1) { top: 0; left: 0; right: 0; height: 1px; transform-origin: left; scale: 0 1; }
  .cv-portrait__frame > i:nth-child(2) { top: 0; right: 0; bottom: 0; width: 1px; transform-origin: top; scale: 1 0; }
  .cv-portrait__frame > i:nth-child(3) { bottom: 0; left: 0; right: 0; height: 1px; transform-origin: right; scale: 0 1; }
  .cv-portrait__frame > i:nth-child(4) { top: 0; left: 0; bottom: 0; width: 1px; transform-origin: bottom; scale: 1 0; }
  .cv-portrait.is-on .cv-portrait__photo { filter: saturate(1) contrast(1); scale: 1; }
  .cv-portrait.is-on .cv-portrait__frame > i { scale: 1; transition-duration: 600ms; transition-delay: 60ms; }
  @media (hover: hover) {
    .cv-portrait:hover .cv-portrait__photo { filter: saturate(1) contrast(1); scale: 1; }
    .cv-portrait:hover .cv-portrait__frame > i { scale: 1; transition-duration: 600ms; transition-delay: 60ms; }
  }
  @media (max-width: 760px) {
    .cv-head--portrait { grid-template-columns: minmax(0, 1fr); }
    .cv-head--portrait > .cv-portrait { grid-column: 1; grid-row: auto; width: min(240px, 64vw); margin: var(--space-5) 0 var(--space-1); }
  }
  .cv-head__glyph { width: clamp(96px, 8vw, 120px); margin: 0 0 var(--space-4) -10px; }
  .cv-head__name { font-size: clamp(2.4rem, 1.7rem + 3.2vw, 4.4rem); }
  .cv-head__role {
    margin: var(--space-4) 0 0; max-width: 34ch;
    font: 400 var(--text-lead)/1.25 'Jost', 'Futura', system-ui, sans-serif;
  }
  .cv-head__motto { margin-top: var(--space-3); max-width: 34ch; color: var(--fg-default); font-size: var(--text-body); line-height: 1.45; text-wrap: balance; }
  .cv-head__meta { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin: var(--space-5) 0 0; }
  .cv-head__email {
    text-transform: none; letter-spacing: .02em; color: var(--fg-default); text-decoration: none;
    background: linear-gradient(var(--path-line), var(--path-line)) 0 100% / 100% 1px no-repeat;
  }
  .cv-head__email:hover { background-size: 100% 2px; }
  .cv-head__meta > * + *::before { content: ''; display: inline-block; width: 7px; height: 7px; margin-inline-end: var(--space-4); background: var(--path-color); vertical-align: .05em; }
  /* The record's address is for paper only. */
  .cv-record__url { display: none; }
  .cv-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: var(--space-5) 0 0; }
  .cv-record__summary { margin: var(--space-4) 0 0; max-width: 64ch; font-size: var(--text-body); line-height: 1.6; text-wrap: pretty; }

  .cv-section {
    display: grid; grid-template-columns: 168px minmax(0, 1fr); gap: 0 var(--space-6);
    padding: clamp(var(--space-5), 4vh, var(--space-6)) 0; border-bottom: 1px solid var(--line-hairline);
  }
  .cv-section__title { align-self: start; margin: var(--space-1) 0 var(--space-4); display: flex; align-items: center; gap: var(--space-2); color: var(--fg-default); }
  .cv-section__title::before { content: ''; width: 10px; height: 10px; background: var(--sec-voice, var(--fg-default)); flex: none; }
  /* A section in a world colour carries it on its marker, its rules, its lead and its dates. */
  .cv-section[style] { border-top: 2px solid var(--sec); margin-top: -1px; }
  .cv-section[style] .cv-entry__when { color: var(--sec); }
  .cv-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-5); }
  .cv-entry { display: grid; grid-template-columns: 116px minmax(0, 1fr); gap: 0 var(--space-5); break-inside: avoid; }
  .cv-entry__when { margin: var(--space-1) 0 0; font: 500 var(--text-xs)/1.5 'Martian Mono', ui-monospace, monospace; color: var(--tech-label); font-variant-numeric: tabular-nums; }
  .cv-entry__role { margin: 0; font: 400 var(--text-lead)/1.3 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.005em; }
  .cv-entry__org { margin: 2px 0 0; font-size: var(--text-md); color: var(--fg-muted); }
  .cv-entry__text { margin: var(--space-2) 0 0; max-width: 62ch; font-size: var(--text-md); line-height: 1.55; text-wrap: pretty; }
  .cv-head__summary { margin: var(--space-5) 0 0; max-width: 64ch; display: grid; gap: var(--space-3); }
  .cv-head__summary p { margin: 0; font-size: var(--text-body); line-height: 1.65; text-wrap: pretty; }
  .cv-entry__text + .cv-entry__text { margin-top: var(--space-1); }

  /* Essays: interests and expertise, a heading and a paragraph each, set for reading. */
  .cv-essays { display: grid; gap: var(--space-5) var(--space-6); }
  .cv-essays--axes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cv-essays--axes .cv-essay:first-child { grid-column: 1 / -1; max-width: 72ch; }
  .cv-essays--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cv-essay { break-inside: avoid; }
  .cv-essay--wide { grid-column: 1 / -1; max-width: 80ch; }
  .cv-essay__text + .cv-essay__text { margin-top: var(--space-3); }
  .cv-essay__name { margin: 0; font: 400 var(--text-lead)/1.3 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.005em; }
  .cv-essay__name::after { content: ''; display: block; width: 24px; height: 1.5px; margin-top: var(--space-2); background: var(--sec, var(--path-line)); }
  /* Ragged right, no hyphenation: a broken word costs a dyslexic reader (and a phone reader) more than
     an uneven edge. */
  .cv-essay__text { margin: var(--space-2) 0 0; font-size: var(--text-md); line-height: 1.65; text-wrap: pretty; }
  .cv-prose { margin: 0; max-width: 72ch; font-size: var(--text-md); line-height: 1.65; text-wrap: pretty; }
  .cv-prose + .cv-prose { margin-top: var(--space-3); }
  .cv-section--direction > .cv-section__body > .cv-prose { max-width: 76ch; font-size: var(--text-body); }

  /* The two directions are the CV's pillars: a full-width heading, a lead in display type, essays. */
  .cv-section--direction { grid-template-columns: 1fr; padding-top: clamp(var(--space-6), 6vh, var(--space-8)); }
  .cv-section--direction > .cv-section__title {
    margin: 0 0 var(--space-4); font: 450 var(--text-title)/1.1 'Jost', 'Futura', system-ui, sans-serif;
    letter-spacing: -.015em; text-transform: none; gap: var(--space-3);
  }
  .cv-section--direction > .cv-section__title::before { width: 18px; height: 18px; }
  .cv-lead {
    margin: 0 0 var(--space-6); max-width: 64ch; padding-inline-start: var(--space-4); border-inline-start: 3px solid var(--sec, var(--line-strong));
    font: 400 var(--text-lead)/1.55 'Jost', 'Futura', system-ui, sans-serif; text-wrap: pretty;
  }
  .cv-lead--small { margin-bottom: var(--space-5); font-size: var(--text-lead); }
  .cv-section--direction .cv-essays--grid { gap: var(--space-6) var(--space-7); }

  /* Reading pages (Présentation, Ingénierie, Enseignement, Réalisations) share one grid (his remark, 2026-10-31:
     « layout is hard to make sense of »). The printable record keeps its rails.
     - Every section opens the same way: a rule, its marker and its title in the display face. The
       crafts are titled largest, the others a step below; no section speaks in small capitals.
     - All prose runs in one size at one measure (`--measure`), headings step down clearly (no stub
       under them), and essays stack in one column (side-by-side pairs left gaps beside short blocks).
     - Product cards keep their grid. Two passages that carry a section (the families one, the mission
       told end to end) take the lead's treatment. */
  :is(.hud--presentation, .hud--work, .hud--together) { --measure: 38rem; }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section { padding: clamp(var(--space-7), 7vh, var(--space-8)) 0 clamp(var(--space-6), 5vh, var(--space-7)); border-bottom: 0; border-top: 1px solid var(--line-hairline); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section[style] { border-top: 2px solid var(--sec); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section:not(.cv-section--direction) { grid-template-columns: minmax(0, 1fr); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section:not(.cv-section--direction) > .cv-section__title {
    margin: 0 0 var(--space-5); gap: var(--space-3); text-transform: none; letter-spacing: -.01em;
    font: 450 var(--text-title)/1.15 'Jost', 'Futura', system-ui, sans-serif;
  }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section:not(.cv-section--direction) > .cv-section__title::before { width: 12px; height: 12px; }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section--direction > .cv-section__title { margin-bottom: var(--space-5); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-lead { max-width: 44rem; margin-bottom: var(--space-7); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essays--grid:not(:has(.cv-essay--tagged)) { grid-template-columns: minmax(0, 1fr); gap: var(--space-7); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay:not(.cv-essay--tagged) { max-width: var(--measure); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay__name { font-size: var(--text-lead); line-height: 1.25; }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay--tagged .cv-essay__name { font-size: var(--text-lead); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay__name::after { display: none; }
  :is(.hud--presentation, .hud--work, .hud--together) :is(.cv-essay__text, .cv-prose, .cv-entry__text, .cv-coda) { max-width: var(--measure); font-size: var(--text-body); line-height: 1.68; }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay--tagged .cv-essay__text { font-size: var(--text-md); line-height: 1.62; }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay__text { margin-top: var(--space-3); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-entry__text { margin-top: var(--space-2); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-essay__figure { max-width: var(--measure); margin-bottom: var(--space-4); }
  :is(#cv-ped-families, #cv-offer-case) { max-width: calc(var(--measure) + 24px); padding-inline-start: var(--space-5); border-inline-start: 3px solid var(--sec); }
  :is(#cv-ped-families, #cv-offer-case) .cv-essay__text { font: 400 var(--text-lead)/1.6 'Jost', 'Futura', system-ui, sans-serif; }
  /* An anchor lands below the sticky header (and Présentation's index row, `--index-h`). */
  .cv-section, .cv-essay { scroll-margin-top: calc(var(--sticky-h, 0px) + var(--index-h, 0px) + 20px); }
  .cv-essay:target { outline: 2px solid var(--sec-voice, var(--line-focus)); outline-offset: 10px; }

  /* Présentation's index (src/pages.mjs pageIndex): each section in its world's form; hud.js marks the
     one in view (`aria-current`). Narrow: a row of chips that sticks under the header. */
  .cv-section__title { scroll-margin-top: calc(var(--sticky-h, 0px) + var(--index-h, 0px) + 20px); }
  .page-index ol { list-style: none; margin: 0; padding: 0; }
  .page-index a {
    display: flex; align-items: center; gap: var(--space-2); color: var(--fg-muted); text-decoration: none;
    font: 400 var(--text-sm)/1.3 'Albert Sans', system-ui, sans-serif;
    transition: color var(--motion-hover) var(--ease-hover), border-color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  .page-index a::before { content: ''; width: 9px; height: 9px; flex: none; background: var(--sec-voice, var(--fg-default)); opacity: .5; transition: opacity var(--motion-hover) var(--ease-hover); }
  .page-index a[aria-current] { color: var(--fg-default); }
  .page-index a[aria-current]::before { opacity: 1; }
  @media (hover: hover) { .page-index a:hover { color: var(--fg-default); } }
  /* The menu asks for no space of its own (his choice, 2026-11-06: it took too much room and attention).
     Narrow: no row, the header names the current section and opens the list (hud.js wireIndex). Under
     1100 px that label takes the place of the page links while reading, and opening brings them back. */
  .section-now { display: none; }
  @media (max-width: 1399px) {
    .page-index { display: none; }
    .hud--page.is-index-open .page-index {
      display: block; position: fixed; z-index: 6; top: var(--menu-top, var(--sticky-h)); inset-inline: var(--gutter);
      max-height: calc(100dvh - var(--menu-top, var(--sticky-h)) - var(--space-5)); overflow-y: auto; padding: var(--space-2);
      background: var(--ground-raised); border: 1px solid var(--line-default);
    }
    .page-index a { min-height: 44px; padding: 0 var(--space-3); }
    .page-index a[aria-current] { background: color-mix(in oklab, var(--fg-default) 6%, transparent); }
    .hud--page.has-section .section-now {
      display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; min-height: 40px; margin: 0; padding: 0 var(--space-2);
      background: none; border: 0; color: var(--fg-default); cursor: pointer; font: 400 var(--text-sm)/1.2 'Albert Sans', system-ui, sans-serif;
    }
    .section-now span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .section-now::after { content: ''; flex: none; width: 7px; height: 7px; margin-top: -3px; border: solid currentColor; border-width: 0 1.5px 1.5px 0; rotate: 45deg; transition: rotate var(--motion-hover) var(--ease-hover); }
    .is-index-open .section-now::after { margin-top: 3px; rotate: 225deg; }
  }
  @media (min-width: 1100px) and (max-width: 1399px) {
    .hud--page.is-index-open .page-index { inset-inline: auto var(--gutter); width: 20rem; }
  }
  /* Opened, the page links come back as the menu's first row, over the page: the header keeps its height,
     so nothing below it moves. */
  @media (max-width: 1099px) {
    .hud--page.has-section:not(.is-index-open) .hud-paths { display: none; }
    .hud--page.has-section .section-now { margin-inline: auto; }
    .hud--page.is-index-open .hud-top .hud-paths {
      position: absolute; z-index: 7; top: 100%; inset-inline: var(--gutter); width: auto; min-height: 46px; margin: 0; padding: 0 var(--space-2);
      justify-content: space-between; background: var(--ground-raised); border: 1px solid var(--line-default); border-bottom: 0;
    }
    .hud--page.is-index-open .page-index { top: calc(var(--menu-top, var(--sticky-h)) + 46px); max-height: calc(100dvh - var(--menu-top, var(--sticky-h)) - 46px - var(--space-5)); }
  }
  @media (max-width: 959px) {
    .hud--page.has-section .section-now { order: 3; width: 100%; margin: 0; padding-inline: 0; justify-content: flex-start; }
  }
  /* As tall as the links it replaces on a tablet, so the header does not move when they swap. */
  @media (min-width: 720px) and (max-width: 959px) {
    .hud--page.has-section .section-now { min-height: 44px; justify-content: center; }
  }
  /* Wide: in the left margin, only the current section's name, quiet, against the thread. Pointing at the
     margin or tabbing into it opens every name; the thread's dots go to the sections too. */
  @media (min-width: 1400px) {
    :is(.hud--presentation, .hud--work, .hud--together) .page { position: relative; }
    :is(.hud--presentation, .hud--work, .hud--together) .page > .page-index {
      position: absolute; z-index: 2; top: 0; bottom: 0; inset-inline-end: calc(100% + clamp(24px, 2.4vw, 44px));
      width: clamp(120px, calc((100vw - var(--page-wide, 980px)) / 2 - 80px), 200px);
    }
    .page-index ol { position: sticky; top: calc(var(--sticky-h) + 40px); }
    .page-index li { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows .28s cubic-bezier(.22, .8, .2, 1), opacity .2s ease; }
    .page-index li:has(a[aria-current]), .page-index:is(:hover, :focus-within) li { grid-template-rows: 1fr; opacity: 1; }
    .page-index a { min-height: 0; overflow: hidden; justify-content: flex-end; padding-inline: 0 var(--space-3); text-align: end; font-size: var(--text-sm); }
    .page-index a::before { content: none; }
    .page-index a[aria-current] { color: var(--fg-muted); }
    .page-index:is(:hover, :focus-within) a { padding-block: var(--space-1); }
    .page-index:is(:hover, :focus-within) a[aria-current] { color: var(--fg-default); }
  }
  /* Without script nothing marks a section: the list simply shows. */
  html:not(.js) .page-index { display: block; }
  html:not(.js) .page-index li { grid-template-rows: 1fr; opacity: 1; }
  /* Leaving the site: the system's « externe » icon; going further on it: « flèche droite ». */
  .cv-essay__link a::after {
    content: ''; display: inline-block; flex: none; width: 1em; height: 1em; margin-inline-start: .3em; vertical-align: -.12em;
    background: currentColor; -webkit-mask: var(--icon-out) center / contain no-repeat; mask: var(--icon-out) center / contain no-repeat;
    transition: translate var(--motion-hover) var(--ease-hover);
  }
  .cv-essay__link a:hover::after { translate: 2px -2px; }
  /* A screen inside a running essay: framed, at reading width. */
  .cv-essay__figure { display: block; width: 100%; max-width: 640px; height: auto; aspect-ratio: 16 / 9; object-fit: cover; object-position: top left; margin: 0 0 var(--space-3); border: 1px solid var(--line-default); }
  .cv-essay__link { margin: var(--space-3) 0 0; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); }
  .cv-essay__link a {
    font: 500 var(--text-md)/1.3 'Albert Sans', system-ui, sans-serif; color: var(--fg-default); text-decoration: none;
    background: linear-gradient(var(--sec-voice), var(--sec-voice)) 0 100% / 100% 1px no-repeat; padding-bottom: 2px;
  }
  .cv-coda { margin: var(--space-5) 0 0; max-width: 64ch; font-size: var(--text-body); line-height: 1.6; text-wrap: pretty; }

  /* Products: a tagged essay is a card, its audience and status read first. */
  .cv-essays--grid .cv-essay--tagged { display: flex; flex-direction: column; padding: var(--space-4) var(--space-5) var(--space-5); background: var(--ground-raised); border: 1px solid var(--line-hairline); border-top: 2px solid var(--sec); }
  .cv-essays--grid:has(.cv-essay--tagged) { align-items: start; }
  /* A world is named by its form and neutral text, never by text in its warm voice (STYLE.md). */
  .cv-essay__tag { margin: 0 0 var(--space-2); display: flex; align-items: baseline; gap: var(--space-2); color: var(--fg-muted); }
  .cv-essay__tag.hud-label { font: 400 var(--text-sm)/1.4 'Albert Sans', system-ui, sans-serif; letter-spacing: 0; text-transform: none; }
  .cv-essay__tag::before { content: ''; width: 9px; height: 9px; align-self: center; background: var(--sec-voice); flex: none; }
  .cv-section--direction .cv-essays--grid:has(.cv-essay--tagged) { gap: var(--space-4); }
  /* Three essays (the offer) stand side by side instead of leaving a hole in a pair grid. */
  .cv-essays--grid:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* Every action that stays on the site has one form and one hover (his remark, 2026-10-30: a solid slab
     beside an outline beside an underline read as three kinds of thing, and the slab turned white at
     night). Ink on a hairline, never a world's warm voice as a fill (STYLE.md: no vermilion buttons): the
     section's colour rides on the particle before the label, and on hover it takes the border and a faint
     wash while the arrow reaches further. Links that leave the site stay text, with their « externe ». */
  .cv-cta { margin: var(--space-6) 0 0; display: flex; flex-wrap: wrap; gap: var(--space-3); }
  .cv-cta__link {
    --cta: var(--sec-voice, var(--path-color)); --cta-line: var(--sec, var(--path-line));
    display: inline-flex; align-items: center; gap: var(--space-3); min-height: 48px; padding: 0 20px; margin: 0;
    border: 1px solid var(--line-strong); background: transparent; color: var(--fg-default); text-decoration: none;
    font: 500 1rem/1 'Albert Sans', system-ui, sans-serif; cursor: pointer;
    transition: border-color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  .cv-cta__link::before { content: ''; width: 10px; height: 10px; background: var(--cta); flex: none; }
  .cv-cta__link::after { content: ''; width: 16px; height: 1.5px; background: currentColor; transition: width var(--motion-hover) var(--ease-hover); }
  .cv-cta__link:is(:hover, :focus-visible) { border-color: var(--cta-line); background: color-mix(in oklch, var(--cta) 9%, transparent); }
  .cv-cta__link:is(:hover, :focus-visible)::after { width: 24px; }
  .cv-cta__link:disabled { opacity: .6; cursor: progress; }
  .cv-cta__link[hidden] { display: none; }
  .cv-cta--essay { margin: var(--space-4) 0 0; }

  /* Contact: the kind of request is chosen first, and only its own fields show (CSS :has, no script). */
  .contact { display: grid; gap: var(--space-5); max-width: 760px; }
  .contact fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
  .contact__kinds { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
  @media (max-width: 760px) { .contact__kinds { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .contact__kinds legend { margin-bottom: var(--space-3); }
  /* « Vous êtes » is the contact page's first question, so it reads as one, not as a label. */
  .hud--contact .contact__kinds legend {
    margin-bottom: var(--space-4); padding: 0; color: var(--fg-default); text-transform: none; letter-spacing: -.01em;
    font: 450 var(--text-title)/1.15 'Jost', 'Futura', system-ui, sans-serif;
  }
  .hud--contact .cv-section--contact { padding-top: 0; border-bottom: 0; }
  /* His profiles: outward links in the page's ink, at the foot of the contact page and under the CV's
     head (where print shows their addresses). */
  .socials__links { --sec-voice: var(--path-color); }
  .socials { margin: var(--space-2) 0 clamp(var(--space-6), 6vw, var(--space-8)); padding-top: var(--space-5); border-top: 1px solid var(--line-hairline); }
  .socials__label { margin: 0; }
  .cv-head__socials { margin-top: var(--space-3); }
  .contact__kind { position: relative; }
  .contact__kind input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
  .contact__kind span {
    display: flex; align-items: center; gap: var(--space-2); min-height: 48px; padding: var(--space-2) var(--space-3);
    border: 1px solid var(--line-default); font: 500 var(--text-md)/1.25 'Albert Sans', system-ui, sans-serif;
    transition: border-color var(--motion-hover) var(--ease-hover), background-color var(--motion-hover) var(--ease-hover);
  }
  .contact__kind span::before { content: ''; width: 10px; height: 10px; border: 1.5px solid var(--line-strong); border-radius: 50%; flex: none; }
  .contact__kind:hover span { border-color: var(--line-strong); }
  .contact__kind input:checked + span { border-color: var(--path-color); background: var(--ground-raised); }
  .contact__kind input:checked + span::before { background: var(--path-color); border-color: var(--path-color); }
  .contact__kind input:focus-visible + span { outline: 2px solid var(--line-focus); outline-offset: 3px; }
  .contact__group { display: none; gap: var(--space-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact__group .contact__hint, .contact__group .contact__checks { grid-column: 1 / -1; }
  .contact:has(input[name='kind'][value='parent']:checked) .contact__group[data-kind='parent'],
  .contact:has(input[name='kind'][value='student']:checked) .contact__group[data-kind='student'],
  .contact:has(input[name='kind'][value='researcher']:checked) .contact__group[data-kind='researcher'],
  .contact:has(input[name='kind'][value='mission']:checked) .contact__group[data-kind='mission'],
  .contact:has(input[name='kind'][value='coaching']:checked) .contact__group[data-kind='coaching'],
  .contact:has(input[name='kind'][value='partner']:checked) .contact__group[data-kind='partner'],
  .contact:has(input[name='kind'][value='other']:checked) .contact__group[data-kind='other'] { display: grid; }
  @supports not selector(:has(*)) { .contact__group { display: grid; } }
  .contact__hint { margin: 0; max-width: 64ch; color: var(--fg-muted); font-size: var(--text-md); line-height: 1.55; text-wrap: pretty; }
  .contact__common { display: grid; gap: var(--space-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact__field { display: grid; gap: var(--space-1); min-width: 0; }
  .contact__field--wide { grid-column: 1 / -1; }
  .contact__label, .contact__checks legend { font: 500 var(--text-sm)/1.3 'Albert Sans', system-ui, sans-serif; color: var(--fg-default); }
  .contact :is(input[type='text'], input[type='email'], select, textarea) {
    width: 100%; min-height: 44px; padding: var(--space-2) var(--space-3); border: 1px solid var(--line-default); border-radius: 0;
    background: var(--field); color: var(--fg-default); font: 400 var(--text-body)/1.4 'Albert Sans', system-ui, sans-serif;
    transition: border-color var(--motion-hover) var(--ease-hover);
  }
  .contact textarea { resize: vertical; min-height: 9rem; }
  .contact :is(input, select, textarea):hover { border-color: var(--line-strong); }
  .contact :is(input, select, textarea):focus-visible { outline: 2px solid var(--line-focus); outline-offset: 1px; border-color: var(--line-strong); }
  .contact__checks { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
  .contact__checks legend { margin-bottom: var(--space-2); }
  .contact__check { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 32px; font-size: var(--text-md); cursor: pointer; }
  .contact__check input { width: 16px; height: 16px; margin: 0; accent-color: var(--path-color); }
  /* Out of sight without leaving the page's box (pushed off to one side, it would widen a page read
     right to left). */
  .contact__trap { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .contact__privacy { margin: 0; max-width: 64ch; font-size: var(--text-sm); line-height: 1.5; color: var(--fg-muted); }
  .contact__actions { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); }
  .contact__status { font-size: var(--text-md); }
  .contact__status[data-state='sent'] { color: var(--fg-default); }
  .contact__status[data-state='error'] { color: var(--fg-default); font-weight: 500; }
  .contact.is-sent :is(.contact__kinds, .contact__group, .contact__common, .contact__privacy, button) { display: none; }
  .contact.is-sent .contact__status { font: 400 var(--text-lead)/1.5 'Jost', 'Futura', system-ui, sans-serif; }

  .hud--page .hud-main { padding-bottom: var(--space-7); }
  :where(.hud--page, .hud--stub) .cv-cta__link { font-size: var(--text-md); }

  .portal { position: fixed; inset: 0; z-index: 20; pointer-events: none; background: var(--portal-color, transparent); visibility: hidden; }
  /* Open while leaving, and on arrival until hud.js closes it into the page's glyph. */
  .portal.is-open, [data-portal-in] .portal { visibility: visible; }

  @media (max-width: 760px) {
    .paths__list { grid-template-columns: 1fr; gap: 10px; }
    .path { min-height: 0; grid-template-rows: none; grid-template-columns: auto 1fr; align-items: center; gap: 18px; padding: 16px 18px; }
    .path__glyph { width: 68px; margin: -6px 0; }
    .path__body { align-content: center; gap: 2px; }
    .path__name { font-size: 1.375rem; }
    .path__text { font-size: .875rem; }
    .hud-keys { display: none; }
    .hud-frame > i { opacity: .4; }
    .hud-main { padding: 28px 0; }
    .cv-section { grid-template-columns: 1fr; }
    .cv-entry { grid-template-columns: 1fr; }
    .cv-entry__when { margin: 0 0 2px; }
    .cv-essays--axes, .cv-essays--grid, .cv-essays--grid:has(> :nth-child(3):last-child) { grid-template-columns: 1fr; }
    .contact__group, .contact__common { grid-template-columns: 1fr; }
  }
  @media (hover: none) { .hud-keys { display: none; } }

  /* The two landing screens hold in one screen, never scrolled (his rule, 2026-10-30): their sizes
     follow the screen's height (svh), and on a short phone the cards drop their line of description. */
  :is(.hud--gate, .hud--landing) .hud-main { padding-block: clamp(6px, 2.4svh, 32px); }
  .hud--gate .gate { gap: clamp(8px, 2svh, 26px); }
  .hud--gate .gate__title { font-size: clamp(1.6rem, 1rem + 3.4svh, 3.9rem); }
  .hud--gate .gate__sphere { width: min(30svh, 360px); }
  .hud--gate .gate__pole { padding-block: clamp(2px, .8svh, 8px); }
  .hud--gate .gate__name { font-size: clamp(1.4rem, .7rem + 2.4svh, 2.4rem); }
  .hud--landing .hero__title { font-size: clamp(2rem, .8rem + 5.6svh, 5.6rem); }
  .hud--landing .hero__role { margin-top: clamp(6px, 1.6svh, 22px); }
  .hud--landing .paths { margin-top: clamp(14px, 4svh, 64px); }
  .hud--landing .path { min-height: clamp(150px, 27svh, 248px); gap: clamp(10px, 2.4svh, 28px); padding: clamp(14px, 2.4svh, 28px); }
  .hud--landing .path__glyph { width: clamp(60px, 11svh, 120px); }
  @media (max-width: 760px) {
    .hud--gate .gate__title { font-size: clamp(1.5rem, .6rem + 3.2svh, 2.4rem); }
    .hud--gate .gate__sphere { width: min(78vw, 34svh); }
    .hud--gate .gate__device { margin-top: 6px; font-size: .8125rem; }
    .hud--landing .hero__title { font-size: clamp(1.7rem, .5rem + 4svh, 2.6rem); }
    .hud--landing .hero__role { font-size: .9375rem; line-height: 1.45; }
    .hud--landing .path { min-height: 0; padding: clamp(8px, 1.4svh, 16px) 14px; gap: 14px; }
    .hud--landing .path__glyph { width: clamp(44px, 7svh, 64px); margin: 0; }
    .hud--landing .path__name { font-size: 1.2rem; }
    .hud--landing .path__text { font-size: .8125rem; line-height: 1.4; }
    .hud--landing .paths__list { gap: 8px; }
  }
  @media (max-width: 760px) and (max-height: 760px) {
    .hud--landing .path__text { display: none; }
    /* A short phone gives the sentence the room: longer languages (Russian) wrap less. */
    .hud--gate .gate__title { max-width: none; font-size: clamp(1.4rem, .5rem + 3svh, 2.2rem); }
    .hud--gate .gate__sphere { width: min(78vw, 28svh); }
  }

  /* Print: the record alone, ink on paper, on A4. */
  @media print {
    @page { size: A4; margin: 12mm 15mm; }
    /* Paper's ink is set at the end of this file, outside the layers (see there). */
    body { background: #fff; font-size: 9.5pt; }
    .hud { display: block; min-height: 0; padding: 0; }
    .hud-frame, .hud-top, .hud-bottom, .cv-actions, .page-index, .cv-cta, .cv-essay__link, .cv-section--contact, .cv-head__email, .page-head__glyph, .cv-essay__shot, .skip, .portal, .cv-head__glyph, .hud-kicker { display: none !important; }
    /* His profiles stay on paper, as their addresses. */
    .cv-essay__link.cv-head__socials { display: flex !important; margin-top: 4pt; gap: 2pt 14pt; }
    .cv-head__socials a { font-size: 8.5pt; background: none; padding: 0; }
    .cv-head__socials a::after { display: none; }
    .cv-coda { display: none; }
    /* The short closing sections (volunteering, career, languages) carry their title as a run-in label. */
    .cv-section:has(> .cv-section__body > .cv-prose:first-child) > .cv-section__title { float: left; margin: 1.5pt 8pt 0 0; }
    .cv-essays--grid .cv-essay--tagged { padding: 0; background: none; border: 0; }
    .cv-essay__tag { margin: 0 0 1pt; font-size: 7pt; color: var(--fg-muted); }
    .hud-main, .page, .hud--page .hud-main { width: auto; padding: 0; }
    .cv-head { padding-bottom: 10pt; }
    .cv-head__name { font-size: 24pt; }
    .cv-head__role { margin-top: 6pt; font-size: 13pt; max-width: none; }
    .cv-head__motto { margin-top: 2pt; font-size: 10pt; }
    .cv-head__meta { margin-top: 8pt; }
    .cv-record__url { display: inline; }
    /* Block flow, not a one-column grid: Chromium then breaks pages between essays, not before a section. */
    .cv-section { display: block; padding: 5pt 0; }
    .cv-section__title { margin: 0 0 5pt; font-size: 7.5pt; break-after: avoid; }
    /* The education list runs in two columns on paper, like the essays, to hold four pages. */
    .cv-list { display: block; columns: 2; column-gap: 7mm; }
    .cv-entry { grid-template-columns: 20mm 1fr; gap: 0 2.5mm; margin-bottom: 4pt; }
    .cv-entry__when { margin-top: 1pt; font-size: 7.5pt; white-space: nowrap; }
    .cv-entry__role { font-size: 9.5pt; }
    .cv-entry__org { font-size: 8.5pt; }
    .cv-entry__text, .cv-essay__text, .cv-prose { margin-top: 2pt; max-width: none; font-size: 8.4pt; line-height: 1.33; }
    .cv-prose + .cv-prose { margin-top: 4pt; }
    .cv-lead { padding-inline-start: 6pt; border-inline-start-width: 2pt; }
    .cv-section[style], .cv-section[style] .cv-lead { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
    .cv-head__summary { margin-top: 6pt; max-width: none; gap: 4pt; }
    .cv-head__summary p { font-size: 9.25pt; line-height: 1.45; }
    .cv-essays { gap: 7pt 7mm; }
    .cv-essays--axes .cv-essay:first-child { max-width: none; }
    .cv-essay__name { font-size: 10pt; }
    .cv-essay__name::after { display: none; }
    .cv-section--direction { padding-top: 10pt; }
    .cv-section--direction > .cv-section__title { margin-bottom: 5pt; font-size: 15pt; }
    .cv-section--direction > .cv-section__title::before { width: 8pt; height: 8pt; }
    .cv-lead { margin-bottom: 7pt; max-width: none; font-size: 9.5pt; line-height: 1.4; }
    .cv-lead--small { margin-bottom: 5pt; font-size: 8.4pt; line-height: 1.36; }
    /* Columns, not a grid, on paper: Chromium moves a whole essay grid (or a column set with full-width
       spanners) to the next page rather than splitting it, which left half-empty pages. So every
       essay flows in the two columns, and only short ones refuse to split. */
    .cv-section--direction .cv-essays--grid, .cv-essays--grid:has(> :nth-child(3):last-child) { display: block; columns: 2; column-gap: 7mm; }
    .cv-essays--grid .cv-essay { margin-bottom: 5pt; }
    .cv-essays--grid .cv-essay--wide { max-width: none; break-inside: auto; }
    .cv-essays--grid:has(> :nth-child(3):last-child) { columns: 3; column-gap: 6mm; }
    .cv-section__title::before, .cv-head__meta > * + *::before { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  }
}

/* Arrival: the three forms begin as one point and unfold onto their cards (point, line, plane).
   Gated on .js so the prerendered page is whole without it; reduced motion skips straight to rest. */
@layer motion {
  @media (prefers-reduced-motion: no-preference) {
    .arrive .hud:not(.is-arrived) .hud-frame > i { translate: var(--fx, 0) var(--fy, 0); opacity: 0; }
    .arrive .hud:not(.is-arrived) .hud-frame > i:nth-child(1) { --fx: -10px; --fy: -10px; }
    .arrive .hud:not(.is-arrived) .hud-frame > i:nth-child(2) { --fx: 10px; --fy: -10px; }
    .arrive .hud:not(.is-arrived) .hud-frame > i:nth-child(3) { --fx: 10px; --fy: 10px; }
    .arrive .hud:not(.is-arrived) .hud-frame > i:nth-child(4) { --fx: -10px; --fy: 10px; }
    .arrive .hud:not(.is-arrived) .hud-construction { opacity: 0; }
    .arrive .hud:not(.is-arrived) :is(.hud-kicker, .hud-title, .hud-role, .hud-bottom > *, .gate__choice) { opacity: 0; translate: 0 12px; }
    /* The header arrives on the welcome screen only; past it, it stays put and only the content comes in. */
    .arrive .hud--gate:not(.is-arrived) .hud-top > * { opacity: 0; translate: 0 12px; }
    /* The cards build themselves on the classical home. */
    .arrive .hud--landing:not(.is-arrived) .path { border-color: transparent; background-color: transparent; }
    .arrive .hud--landing:not(.is-arrived) .path__body { opacity: 0; translate: 0 8px; }
    .arrive .hud--landing:not(.is-arrived) .path__glyph { translate: var(--dx, 0) var(--dy, 0); scale: .14; }
    .arrive :is(.hud--stub, .hud--page):not(.is-arrived) .stub__glyph { scale: .14; }
    .arrive .hud--page:not(.is-arrived) :is(.cv-head__role, .cv-portrait, .cv-head__meta, .page-index, .cv-actions, .page-head__lead, .cv-section) { opacity: 0; translate: 0 12px; }
    .hud--page.is-arrived :is(.cv-head__role, .cv-portrait, .cv-head__meta, .page-index, .cv-actions, .page-head__lead, .cv-section) {
      transition: opacity var(--motion-enter) var(--ease-enter), translate var(--motion-enter) var(--ease-enter);
      transition-delay: calc(180ms + var(--i, 0) * var(--motion-stagger) / 2);
    }
    .hud--page .cv-section:nth-of-type(1) { --i: 2; }
    .hud--page .cv-section:nth-of-type(2) { --i: 3; }
    .hud--page .cv-section:nth-of-type(3) { --i: 4; }
    .hud--page .cv-section:nth-of-type(4) { --i: 5; }
    .hud--page .cv-section:nth-of-type(5) { --i: 6; }
    .hud--page .cv-section:nth-of-type(n+6) { --i: 7; }
    /* The reticle locks on last, once the cards stand. */
    .arrive .hud:not(.is-arrived) .reticle.is-placed { opacity: 0; }
    .hud.is-arrived:not(.is-settled) .reticle { transition-delay: 0ms, 0ms, 0ms, 1000ms; }

    .hud.is-arrived .hud-frame > i { transition: translate var(--motion-enter) var(--ease-enter), opacity var(--motion-enter) var(--ease-enter); }
    .hud.is-arrived .hud-construction { transition: opacity var(--motion-assemblage) var(--ease-enter) 400ms; }
    .hud.is-arrived :is(.hud-kicker, .hud-title, .hud-role, .hud-top > *, .hud-bottom > *, .gate__choice) {
      transition: opacity var(--motion-enter) var(--ease-enter), translate var(--motion-enter) var(--ease-enter);
    }
    .hud.is-arrived .hud-title { transition-delay: 60ms; }
    .hud.is-arrived .hud-role { transition-delay: 120ms; }
    .hud.is-arrived .gate__choice { transition-delay: 320ms; }
    .hud.is-arrived .hud-bottom > * { transition-delay: 900ms; }
    .hud.is-arrived :is(.path__glyph, .stub__glyph) {
      transition: translate var(--spring-arrive-ms) var(--spring-arrive), scale var(--spring-arrive-ms) var(--spring-arrive);
      transition-delay: calc(320ms + var(--i, 0) * var(--motion-stagger));
    }
    .hud.is-arrived .path {
      transition: border-color var(--motion-enter) var(--ease-enter), background-color var(--motion-enter) var(--ease-enter);
      transition-delay: calc(560ms + var(--i, 0) * var(--motion-stagger));
    }
    .hud.is-arrived .path__body {
      transition: opacity var(--motion-enter) var(--ease-enter), translate var(--motion-enter) var(--ease-enter);
      transition-delay: calc(640ms + var(--i, 0) * var(--motion-stagger));
    }
    /* After arrival the per-element delays must not slow hover and focus. */
    .hud.is-settled :is(.path, .path__body, .path__glyph) { transition-delay: 0ms; }
    .hud.is-settled .path { transition: border-color var(--motion-hover) var(--ease-hover); }


    [data-i18n].is-swapping { opacity: 0; translate: 0 -4px; }
    .is-swap-in { transition: opacity var(--motion-enter) var(--ease-enter), translate var(--motion-enter) var(--ease-enter); }

    /* A window rises a little as it opens and sinks as it closes; its backdrop fades with it. */
    .modal, .modal::backdrop {
      transition: opacity var(--motion-enter) var(--ease-enter), translate var(--motion-enter) var(--ease-enter),
        overlay var(--motion-enter) allow-discrete, display var(--motion-enter) allow-discrete;
      opacity: 0;
    }
    .modal { translate: 0 10px; }
    .modal:popover-open, .modal:popover-open::backdrop { opacity: 1; translate: 0 0; }
    @starting-style {
      .modal:popover-open, .modal:popover-open::backdrop { opacity: 0; }
      .modal:popover-open { translate: 0 10px; }
    }
  }
}

/* The reader's preferences (Accessibilité, Réglages), set on <html> by the boot script. Outside the
   layers, so they win over every rule of the page and over the generated tokens. */
:root[data-text='115'] { font-size: 115%; }
:root[data-text='130'] { font-size: 130%; }
:root[data-contrast='more'] {
  --fg-muted: var(--fg-default); --tech-label: var(--fg-default);
  --line-hairline: var(--line-strong); --line-default: var(--line-strong);
}
/* Arabic and Urdu join their letters: any letter spacing would break the words apart. */
:lang(ar), :lang(ur) { letter-spacing: normal !important; }
/* WCAG 1.4.12's spacing: letters .12em, words .16em, lines 1.5 and more. */
:root[data-spacing='wide'] :is(p, li, dd, label, legend, .path__text, .path__name, .cv-essay__name, .cv-entry__role) {
  letter-spacing: .12em; word-spacing: .16em; line-height: 1.7;
}
:root[data-motion='reduce'] *, :root[data-motion='reduce'] *::before, :root[data-motion='reduce'] *::after,
:root[data-motion='reduce'] ::backdrop {
  transition-duration: 0s !important; animation-duration: 0s !important; scroll-behavior: auto !important;
}

/* The glossary (src/pages.mjs gloss, web/terms.js): a technical term reads as text, lightly dotted in
   its section's tone, and its definition opens in a small sheet beside it. No motion beyond a fade. */
@layer hud {
  .term {
    display: inline; margin: 0; padding: 0; border: 0; background: none; color: inherit; font: inherit;
    letter-spacing: inherit; text-align: inherit; cursor: help;
    text-decoration: underline dotted color-mix(in oklch, var(--sec, var(--fg-muted)) 70%, transparent);
    text-decoration-thickness: 1.5px; text-underline-offset: .22em;
    -webkit-tap-highlight-color: transparent;
  }
  .term:hover, .term[data-open] { text-decoration-color: var(--sec, var(--fg-default)); text-decoration-style: solid; }
  .term:focus-visible { outline: 1.5px solid var(--sec, var(--tech-mark)); outline-offset: 2px; border-radius: 2px; }
  .term-tip {
    position: fixed; top: 0; left: 0; z-index: 50; translate: 0 0;
    width: max-content; max-width: min(340px, calc(100vw - 32px));
    padding: var(--space-2) var(--space-3) var(--space-3); border: 1px solid var(--line-default); border-top: 2px solid var(--term-tone, var(--tech-mark));
    background: var(--ground-raised); color: var(--fg-default); box-shadow: var(--sheet-shadow);
    font-size: var(--text-sm); line-height: 1.5; pointer-events: none;
    opacity: 0; visibility: hidden; transition: opacity .14s ease, visibility 0s linear .14s;
  }
  .term-tip.is-open { opacity: 1; visibility: visible; transition: opacity .14s ease, visibility 0s; }
  @media print {
    .term { text-decoration: none; cursor: auto; }
    .term-tip { display: none; }
  }
}

/* « Ce que je fais », the projects hub and the project pages (his brief, 2026-11-01): the skill set as
   groups of plain tags, every project as a card (picture, icon, status, name, a line, tags), and a page
   per project. A family keeps one colour on its tiles and icons (`--fam`); the text stays in ink. */
@layer hud {
  /* One colour, one meaning (his choice, 2026-11-05): the page tones colour the site's chrome, the four
     families colour the cards and their scenes, and no family shares a page tone's hue. Each sits at
     least 25° of hue from every tone and as far from them in OKLab as the two closest tones are from
     each other (purple and blue), each a neighbour of the hue it had before; all read above 6:1 on a
     card. The scenes paint with these four, never with the page tones (scenes/kit.js). */
  :root {
    --fam-products: oklch(80% 0.12 0); --fam-games: oklch(80% 0.14 60);
    --fam-infra: oklch(72% 0.14 232); --fam-tools: oklch(80% 0.135 192);
  }
  [data-family='products'] { --fam: var(--fam-products); }
  [data-family='games'] { --fam: var(--fam-games); }
  [data-family='infra'] { --fam: var(--fam-infra); }
  [data-family='tools'] { --fam: var(--fam-tools); }
  .proj-icon { flex: none; width: 24px; height: 24px; }

  /* The way back (to the hub, or from it to « Ce que je fais »), above a page's head. */
  .crumb { margin: 0 0 var(--space-4); font-size: var(--text-md); }
  .crumb a { color: var(--fg-muted); text-decoration: none; border-bottom: 1px solid var(--line-default); }
  .crumb a:is(:hover, :focus-visible) { color: var(--fg-default); border-color: var(--path-line); }

  /* The skill set: a group per domain, its argument, its tools as plain tags (no logos, no meters). */
  /* Capped relative to its column: a fixed cap would size the auto-filled grid at three columns on a phone. */
  .stack { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); gap: var(--space-6) var(--space-6); max-width: min(100%, 62rem); }
  .stack__name { margin: 0; font: 400 var(--text-lead)/1.3 'Jost', system-ui, sans-serif; }
  :is(.stack__tools, .proj-tags) { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: var(--space-3) 0 0; padding: 0; list-style: none; }
  :is(.stack__tools, .proj-tags) li { padding: 2px var(--space-2); border: 1px solid var(--line-default); border-radius: 999px; font-size: var(--text-sm); line-height: 1.5; color: var(--fg-muted); }

  /* The hub's choices (who it is for, grid or chronology): radio buttons read by CSS (:has), so they work
     without script; the chosen one inked. */
  .hub-controls { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-3) var(--space-6); margin: 0 0 clamp(var(--space-5), 4vh, var(--space-6)); }
  /* The hub is a catalogue: its head is compact, the form beside the title, so the first row of cards
     shows whole in the first screen at 1440 × 900 (audit 2026-11-05: it showed only their top). */
  .hud--hub .page-head { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; column-gap: clamp(var(--space-4), 2vw, var(--space-5)); padding-bottom: clamp(var(--space-4), 2.5vh, var(--space-5)); }
  .hud--hub .page-head__glyph { grid-row: 1; width: clamp(56px, 5vw, 76px); margin: 0 0 0 -6px; }
  .hud--hub .page-head__title { grid-row: 1; margin: 0; font-size: clamp(2.5rem, 1.9rem + 2.2vw, 3.75rem); }
  .hud--hub .page-head__lead { grid-column: 1 / -1; max-width: 64ch; margin-top: var(--space-3); }
  .hud--hub .hub-controls { margin-bottom: clamp(var(--space-3), 2vh, var(--space-4)); }
  .hud--hub .hub-section:first-child { padding-top: clamp(var(--space-4), 2.5vh, var(--space-5)); }
  .hub-filter { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; margin: 0; padding: 0; border: 0; }
  .hub-filter legend { float: inline-start; margin-inline-end: var(--space-3); padding: var(--space-1) 0; }
  .hub-filter__option { position: relative; }
  .hub-filter__option input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
  .hub-filter__option span { display: inline-block; padding: var(--space-1) var(--space-3); border: 1px solid var(--line-default); border-radius: 999px; font-size: var(--text-md); }
  .hub-filter__option input:checked + span { background: var(--fg-default); border-color: var(--fg-default); color: var(--ground-default); }
  .hub-filter__option input:focus-visible + span { outline: 2px solid var(--line-focus); outline-offset: 2px; }
  /* Who it is for: a project that does not serve the chosen readers leaves every view, and a kind's section
     with it once none of its projects is left. */
  .hub:has(input[name='pour'][value='ecole']:checked) [data-for]:not([data-for~='ecole']),
  .hub:has(input[name='pour'][value='entreprise']:checked) [data-for]:not([data-for~='entreprise']),
  .hub:has(input[name='pour'][value='classe']:checked) [data-for]:not([data-for~='classe']),
  .hub:has(input[name='pour'][value='recherche']:checked) [data-for]:not([data-for~='recherche']),
  .hub:has(input[name='pour'][value='ecole']:checked) .hub-section[data-family]:not(:has([data-for~='ecole'])),
  .hub:has(input[name='pour'][value='entreprise']:checked) .hub-section[data-family]:not(:has([data-for~='entreprise'])),
  .hub:has(input[name='pour'][value='classe']:checked) .hub-section[data-family]:not(:has([data-for~='classe'])),
  .hub:has(input[name='pour'][value='recherche']:checked) .hub-section[data-family]:not(:has([data-for~='recherche'])) { display: none; }
  .hub:has(input[name='view'][value='grid']:checked) .hub-view--timeline,
  .hub:has(input[name='view'][value='timeline']:checked) .hub-view--grid { display: none; }
  .hub-section { padding: clamp(var(--space-5), 4vh, var(--space-6)) 0; border-top: 1px solid var(--line-hairline); }
  .hub-section__title { margin: 0 0 var(--space-5); font: 450 var(--text-title)/1.2 'Jost', system-ui, sans-serif; }

  /* One card per project, every card alike: its scene on its kind's ground, then its state, period, name
     and line; the border takes the kind's colour on hover or focus. */
  .hub-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); gap: var(--space-4); margin: 0; padding: 0; list-style: none; }
  .hub-card__link {
    display: flex; flex-direction: column; height: 100%; overflow: hidden; color: inherit; text-decoration: none;
    background: var(--ground-raised); border: 1px solid var(--line-hairline); border-radius: 2px; transition: border-color .2s ease;
  }
  .hub-card__link:is(:hover, :focus-visible) { border-color: var(--fam); }
  :is(.hub-card__visual, .proj-head__thumb) { display: block; width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover; object-position: top; background: var(--ground-sunken); }
  :is(.hub-card__visual--tile, .proj-head__thumb--tile) { display: grid; place-items: center; color: var(--fam); background: color-mix(in oklab, var(--fam) 10%, var(--ground-default)); }
  .hub-card__visual--tile .proj-icon { width: 56px; height: 56px; }
  .hub-card__body { display: flex; flex: 1; flex-direction: column; gap: var(--space-2); padding: var(--space-3) var(--space-4) var(--space-4); }
  .hub-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); color: var(--fam); }
  :is(.hub-card__years, .chrono__years, .proj-head__years) { font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .04em; color: var(--fg-muted); white-space: nowrap; }
  .hub-card__years { margin-inline-start: auto; }
  .hub-card__name { margin: 2px 0 0; font: 400 var(--text-lead)/1.25 'Jost', system-ui, sans-serif; }
  .hub-card__line { font-size: var(--text-md); line-height: 1.5; color: var(--fg-muted); }

  /* The lifecycle: a mark and its word, then the finer context. The mark says it by shape, so no state
     borrows a family's or a page's hue: a lit dot in service (green means only that), a half-filled one
     under construction, a ring once finished. */
  .proj-state { display: inline-flex; align-items: center; gap: var(--space-2); font: 500 var(--text-xs)/1.2 'Martian Mono', ui-monospace, monospace; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-default); }
  .proj-state__dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--fg-muted); }
  .proj-state[data-state='live'] .proj-state__dot { background: var(--tone-green); box-shadow: 0 0 0 3px color-mix(in oklab, var(--tone-green) 22%, transparent); }
  .proj-state[data-state='building'] .proj-state__dot { box-sizing: border-box; border: 1.5px solid var(--fg-default); background: linear-gradient(90deg, var(--fg-default) 50%, transparent 50%); }
  .proj-state[data-state='done'] .proj-state__dot { box-sizing: border-box; background: transparent; border: 1.5px solid var(--fg-muted); }
  .proj-state__context { color: var(--fg-muted); }
  .proj-state__context::before { content: '·'; margin-inline-end: var(--space-2); }


  /* The chronology: his path's eras as bands, the years, then the projects from their first year, those
     begun this year gathered in its column. On a narrow screen it reads down, era by era. */
  .chrono { display: grid; gap: var(--space-2) var(--space-1); margin: 0; padding: 0; list-style: none; }
  .chrono__era { padding: var(--space-2) var(--space-3); border-top: 2px solid var(--line-strong); background: color-mix(in oklab, var(--fg-default) 3%, transparent); font: 500 var(--text-xs)/1.25 'Martian Mono', ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-muted); }
  .chrono__era[data-era='craft'] { border-top-color: var(--path-line); color: var(--fg-default); }
  .chrono__year { padding: var(--space-1) 0 0 var(--space-1); border-inline-start: 1px solid var(--line-hairline); font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; color: var(--fg-muted); }
  .chrono__mark { display: grid; align-content: start; gap: var(--space-2); padding-inline-start: var(--space-2); border-inline-start: 2px solid var(--line-default); font-size: var(--text-sm); color: var(--fg-muted); }
  /* One dated step: its year above its words, which may wrap within the era's width. */
  .chrono__step { display: grid; gap: var(--space-1); line-height: 1.35; }
  .chrono__item a { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); min-width: 0; padding: var(--space-2) var(--space-2); color: inherit; text-decoration: none; font-size: var(--text-sm); line-height: 1.3; background: var(--ground-raised); border: 1px solid var(--line-hairline); border-radius: 2px; transition: border-color .2s ease; }
  .chrono__item a:is(:hover, :focus-visible) { border-color: var(--fam); }
  .chrono__item .proj-icon { width: 18px; height: 18px; color: var(--fam); }
  .chrono__item--span a { background: color-mix(in oklab, var(--fam) 14%, var(--ground-raised)); }
  .chrono__item--span.is-open a { border-inline-end-color: transparent; background: linear-gradient(to right, color-mix(in oklab, var(--fam) 14%, var(--ground-raised)) 70%, transparent); }
  .chrono__item a > span:first-of-type { flex: 1 1 7rem; min-width: 0; }
  .chrono__item .chrono__years { margin-inline-start: auto; }
  .chrono__lanes { align-self: start; }
  .chrono__lanes > ol { display: grid; grid-template-columns: subgrid; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
  .chrono__recent { align-self: start; }
  .chrono__recent > .chrono__years { display: none; }
  .chrono__recent ul { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
  /* A narrow screen reads it down: the eras on one line, then the years' bars, then this year's projects. */
  @media (max-width: 760px) {
    .chrono { display: flex; flex-wrap: wrap; gap: var(--space-2); }
    .chrono__year { display: none; }
    .chrono__era { flex: 1 1 auto; }
    .chrono__mark, .chrono__lanes, .chrono__recent { flex: 1 1 100%; }
    .chrono__lanes > ol { display: grid; grid-template-columns: 1fr; }
    .chrono__lanes .chrono__item { grid-column: 1 !important; grid-row: auto !important; }
    .chrono__recent > .chrono__years { display: block; margin: var(--space-3) 0 var(--space-2); }
  }

  /* A project's page: its picture beside its name, family, status, line, tags and public addresses, then
     what it is, how it works, why it exists, and the projects on either side. */
  .proj-head { display: grid; gap: clamp(var(--space-4), 3vw, var(--space-6)); align-items: center; padding-bottom: clamp(var(--space-5), 4vh, var(--space-7)); }
  @media (min-width: 900px) { .proj-head { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); } }
  .proj-head__thumb { aspect-ratio: 16 / 10; border: 1px solid var(--line-hairline); border-radius: 2px; }
  .proj-head__thumb--tile .proj-icon { width: 96px; height: 96px; }
  .proj-head__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; color: var(--fam); }
  .proj-head__meta .hud-label { color: var(--fg-muted); }
  .proj-head__meta .hud-label + .hud-label::before { content: '·'; margin-inline-end: 10px; }
  .proj-head__name { margin: var(--space-2) 0 0; font-size: clamp(2.2rem, 1.4rem + 2.6vw, 3.6rem); line-height: 1.02; }
  .proj-head .page-head__lead { margin-top: var(--space-3); }
  .proj-head .proj-state { margin-top: var(--space-4); }

  /* The case study: its three parts, and its facts in a column beside them (sticky on a wide screen, first
     on a narrow one), then the call to its reader. */
  .proj-body { display: grid; gap: clamp(var(--space-5), 4vw, var(--space-7)); }
  .proj-facts { order: -1; --sec-voice: var(--path-color); }
  @media (min-width: 1000px) {
    .proj-body { grid-template-columns: minmax(0, 1fr) minmax(15rem, 19rem); align-items: start; }
    .proj-facts { order: 0; position: sticky; top: calc(var(--sticky-h, 72px) + 24px); margin-top: clamp(var(--space-5), 4vh, var(--space-6)); }
  }
  .proj-facts dl { display: grid; gap: var(--space-4); margin: 0; padding: var(--space-4) var(--space-5); background: var(--ground-raised); border: 1px solid var(--line-hairline); border-radius: 2px; }
  .proj-fact dt { margin: 0 0 var(--space-1); font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-muted); }
  .proj-fact dd { margin: 0; font-size: var(--text-md); line-height: 1.45; }
  .proj-fact__list { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); }
  /* In the narrow rail the state reads on one line and its context under it (it broke into two columns,
     « EN / CHANTIER · PILOTE EN / PRÉPARATION », audit 2026-11-05). */
  .proj-fact .proj-state { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: var(--space-1) var(--space-2); }
  .proj-fact .proj-state > span:not([class]) { white-space: nowrap; }
  .proj-fact .proj-state__context { grid-column: 2; text-transform: none; letter-spacing: 0; font: 400 var(--text-sm)/1.35 'Albert Sans', system-ui, sans-serif; }
  .proj-fact .proj-state__context::before { content: none; }
  .proj-fact__list a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--line-default); }
  .proj-fact__list a:is(:hover, :focus-visible) { border-color: var(--path-line); }
  .proj-fact .proj-tags { margin: 0; }
  .proj-fact__links { flex-direction: column; gap: var(--space-1); margin: 0; }
  .proj-call { margin: clamp(var(--space-5), 4vh, var(--space-7)) 0 clamp(var(--space-4), 3vh, var(--space-6)); }

  /* A signature scene: its canvas fills the holder over the family's ground; until it draws (and without
     script) the holder shows the project's picture or its icon. */
  .scene { position: relative; display: block; overflow: hidden; background: color-mix(in oklab, var(--fam) 7%, var(--ground-default)); }
  .proj-head__thumb.scene, .hub-card__visual.scene { aspect-ratio: 16 / 10; }
  .scene__fallback { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }
  .scene__fallback--tile { display: grid; place-items: center; color: var(--fam); }
  .scene__fallback--tile .proj-icon { width: 88px; height: 88px; }
  .scene__canvas { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: pan-y; }
  .scene.is-live .scene__fallback { visibility: hidden; }
  .scene.is-live { cursor: crosshair; }
  .proj-figure { margin: var(--space-5) 0 0; }
  .proj-figure img { display: block; width: 100%; max-width: 640px; height: auto; aspect-ratio: 16 / 9; object-fit: cover; object-position: top left; border: 1px solid var(--line-default); }
  .proj-figure figcaption { max-width: 640px; margin-top: var(--space-2); font-size: var(--text-sm); line-height: 1.5; color: var(--fg-muted); }
  /* A demonstration film: the column's full width (its phone stays legible), the page's coral as its accent. */
  .proj-video { margin: var(--space-5) 0 0; }
  .proj-video video { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; background: var(--ground-sunken); border: 1px solid var(--line-default); accent-color: var(--path-color); }
  .proj-video video:focus-visible { outline: 2px solid var(--path-line); outline-offset: 3px; }
  .proj-video figcaption { max-width: 640px; margin-top: var(--space-2); padding-left: var(--space-3); border-left: 2px solid var(--path-line); font-size: var(--text-sm); line-height: 1.5; color: var(--fg-muted); }

  /* The home's door to the projects, under the three cards. */
  .home-projects { margin: clamp(18px, 3vh, 28px) 0 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 28px; }
  .home-projects__link { color: var(--fg-muted); text-decoration: none; border-bottom: 1px solid var(--line-default); font-size: .9375rem; }
  .home-projects__link:is(:hover, :focus-visible) { color: var(--fg-default); border-color: var(--fg-default); }
  .proj-nav { display: grid; grid-template-columns: 1fr auto 1fr; align-items: end; gap: var(--space-4); padding: clamp(var(--space-5), 4vh, var(--space-7)) 0; border-top: 1px solid var(--line-hairline); }
  /* Where the reader is among the projects, in the hub's order (« 4 / 21 »). */
  .proj-nav__at { padding-bottom: var(--space-1); color: var(--fg-muted); font-variant-numeric: tabular-nums; }
  .proj-nav__link { display: grid; gap: var(--space-1); color: inherit; text-decoration: none; }
  .proj-nav__link--next { text-align: end; }
  .proj-nav__link span:last-child { font: 400 var(--text-lead)/1.3 'Jost', system-ui, sans-serif; border-bottom: 1px solid transparent; justify-self: start; }
  .proj-nav__link--next span:last-child { justify-self: end; }
  .proj-nav__link:is(:hover, :focus-visible) span:last-child { border-color: var(--path-line); }
}

/* The section pages' rhythm (his remark, 2026-11-04: « less flat and repetitive … I don't see why someone
   would keep scrolling »; src/pages.mjs SITE `form`). Each section opens on its title, where the thread
   branches in, then one large sentence (the hook) saying why to read on. The sections take turns: words beside a live project
   scene (sides alternating), a raised band across the screen, a band of numbers, the teaching scale, the
   proof as the hub's own cards, two short sections side by side. No rule separates them any more: space, the
   thread and the change of surface do. Long essays fold after their first paragraph. */
@layer hud {
  :is(.hud--presentation, .hud--work, .hud--together) { --page-wide: 1080px; --thread-x: calc(-1 * clamp(14px, 2.4vw, 32px)); }
  :is(.hud--presentation, .hud--work, .hud--together) .page { position: relative; width: min(100%, var(--page-wide)); }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section {
    border-top: 0; padding: clamp(var(--space-8), 10vh, var(--space-9)) 0 clamp(var(--space-5), 4vh, var(--space-6));
  }
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section[style] { border-top: 0; }
  /* No number and no marker (his note, 2026-11-04): the thread's elbow and dot mark each title. */
  /* Only where the thread runs: the project pages (also `hud--work`) keep their markers. */
  .page:has(> .thread) .cv-section > .cv-section__title::before,
  .page:has(> .thread) .cv-section:not(.cv-section--direction) > .cv-section__title::before { content: none; }

  /* The thread (src/pages.mjs `thread`, hud.js wireThread): a hairline in the page's margin, its track faint,
     its ink in the path's tone down to the reading line; a dot at each title, lit once reached; the pen, a
     point of light, at the tip. */
  /* Above the raised bands, which are positioned and come later in the page. */
  .thread { position: absolute; z-index: 1; top: 0; left: var(--thread-x); width: calc(-1 * var(--thread-x)); height: 100%; pointer-events: none; }
  .thread__svg { position: absolute; top: 0; left: 0; width: 100%; overflow: visible; }
  .thread__svg path { fill: none; stroke-linejoin: round; }
  .thread__line { stroke: var(--line-hairline); stroke-width: 1; }
  .thread__ink { stroke: var(--path-line); stroke-width: 1.5; }
  .thread__ink--thick { stroke-width: 2; }
  /* What the thread reaches answers it (hud.js wireThread): cards and actions take their colour as on
     hover, the eras and the teaching stops light as the ink passes, the next page's arrow reaches on. */
  .hub-card__link.is-reached { border-color: var(--fam); transition-delay: var(--reach-delay, 0ms); }
  .cv-cta__link.is-reached { border-color: var(--cta-line); transition-delay: var(--reach-delay, 0ms); }
  .eras__era, .eras__years { transition: color .3s ease var(--reach-delay, 0ms); }
  .eras__era.is-passed, .eras__era.is-passed .eras__years { color: var(--fg-default); }
  .ladder li::before { transition: background-color .3s ease var(--reach-delay, 0ms); }
  .ladder li.is-passed::before { background: color-mix(in oklab, var(--sec-voice) 45%, var(--ground-default)); }
  .page-foot__next.is-reached .page-foot__name::after { transform: translateX(6px); transition-delay: var(--reach-delay, 0ms); }
  .thread__dot { fill: var(--ground-default); stroke: var(--line-default); stroke-width: 1.25; transition: fill var(--motion-hover) var(--ease-hover), stroke var(--motion-hover) var(--ease-hover); }
  .thread__dot.is-on { fill: var(--path-color); stroke: var(--path-color); }
  /* A dot leads to its section: a larger target around it, the only part of the thread that takes the pointer. */
  .thread__hit { fill: transparent; pointer-events: all; cursor: pointer; }
  .thread__dot:has(+ .thread__hit:hover) { fill: var(--path-color); stroke: var(--path-color); }
  /* The pen: a point of light, moved by transform only (hud.js), so the compositor carries it at any refresh
     rate; its halo is a still shadow, never animated. At a title it opens into a ring. */
  /* `left` is geometry, not spacing: the thread's x (0.5 in the svg), so the point sits on the line. */
  .thread__pen {
    position: absolute; top: 0; left: .5px; width: 9px; height: 9px; border-radius: 50%;
    background: var(--path-color); border: 1.5px solid var(--path-color);
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--path-color) 16%, transparent), 0 0 16px 2px color-mix(in oklab, var(--path-color) 40%, transparent);
    opacity: 0; will-change: transform; transform-origin: 50% 50%;
    transition: opacity 400ms var(--ease-enter), background-color 180ms var(--ease-hover);
  }
  .thread__pen.is-ring { background-color: var(--ground-default); }
  .thread.is-on .thread__pen { opacity: 1; }
  html:not(.js) .thread { display: none; }
  @media (max-width: 760px) {
    :is(.hud--presentation, .hud--work, .hud--together) { --thread-x: -8px; }
  }
  @media print { .thread { display: none; } }
  /* With a hook, the title steps down to a label-sized heading and the hook carries the display size. */
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section:has(> .cv-section__body > .cv-hook) > .cv-section__title {
    margin-bottom: var(--space-4); font: 400 var(--text-lead)/1.2 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: 0;
  }
  .cv-hook {
    margin: 0 0 clamp(var(--space-6), 5vh, var(--space-7)); max-width: 40ch; color: var(--fg-default); text-wrap: balance;
    font: 450 clamp(1.75rem, 1.2rem + 2vw, 3rem)/1.12 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.02em;
  }
  /* Large only at the page's top (his choice, 2026-11-05: « if said loudly the sentences should be
     beautiful »). After the first section the title is the heading again and the hook its standfirst. */
  :is(.hud--presentation, .hud--work, .hud--together) .cv-section ~ .cv-section:has(> .cv-section__body > .cv-hook) > .cv-section__title {
    margin-bottom: var(--space-3); font: 450 var(--text-title)/1.15 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.01em;
  }
  .verse { display: block; text-wrap: balance; }
  .cv-section ~ .cv-section .cv-hook .verse { display: inline; }
  .cv-section ~ .cv-section .cv-hook {
    margin-bottom: clamp(var(--space-5), 4vh, var(--space-6)); max-width: 52ch; color: var(--fg-muted); text-wrap: pretty;
    font: 400 var(--text-lead)/1.45 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: 0;
  }

  /* Split: the words beside the scene, which stays in view while they scroll past it. */
  .cv-split { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: clamp(var(--space-6), 5vw, var(--space-8)); align-items: start; }
  .cv-section--flip .cv-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); }
  .cv-section--flip .cv-scene { order: -1; }
  .cv-scene { position: sticky; top: calc(var(--sticky-h, 72px) + var(--index-h, 0px) + 28px); margin: 0; }
  .cv-scene__view { aspect-ratio: 16 / 11; border: 1px solid var(--line-hairline); border-radius: 2px; }
  .cv-scene figcaption { margin-top: var(--space-2); display: flex; justify-content: flex-end; }
  .cv-scene figcaption a {
    display: inline-flex; align-items: center; gap: var(--space-2); color: var(--fg-muted); text-decoration: none;
    font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .06em; text-transform: uppercase;
  }
  .cv-scene figcaption a::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--fam); }
  .cv-scene figcaption a::after { content: ''; width: 14px; height: 1px; background: currentColor; transition: width var(--motion-hover) var(--ease-hover); }
  .cv-scene figcaption a:is(:hover, :focus-visible) { color: var(--fg-default); }
  .cv-scene figcaption a:is(:hover, :focus-visible)::after { width: 22px; }
  /* Beside a scene the essays stack, whatever their own grid says. */
  .cv-split .cv-essays--grid, .cv-split .cv-essays--grid:has(> :nth-child(3):last-child) { grid-template-columns: minmax(0, 1fr); }

  /* Band: a raised surface across the whole screen (the shadow paints past the page, the clip keeps it to
     the band's height; neither widens the page), with a hairline in the section's tone on top. */
  .cv-section--band, .cv-section--statement {
    position: relative; background: var(--ground-raised); box-shadow: 0 0 0 100vmax var(--ground-raised); clip-path: inset(0 -100vmax);
    margin-block: clamp(var(--space-7), 8vh, var(--space-9)) 0; padding-bottom: clamp(var(--space-7), 9vh, var(--space-9)) !important;
  }
  /* `border-image-outset` paints the hairline past the page without widening it. */
  .cv-section--band, .cv-section--statement { border-top: 1px solid; border-image: linear-gradient(var(--sec, var(--path-line)), var(--sec, var(--path-line))) 1 / 1px 0 0 / 0 100vmax; }
  .cv-section--band + .cv-section, .cv-section--statement + .cv-section { padding-top: clamp(var(--space-8), 12vh, var(--space-9)); }
  .cv-section--band .stack { grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); max-width: none; gap: var(--space-6) var(--space-6); }

  /* Statement: the first paragraph set large across the width, the rest in two columns under it, the
     path's form drifting faint at the side. */
  .cv-section--statement .cv-section__body { position: relative; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4) clamp(var(--space-5), 4vw, var(--space-7)); }
  .cv-section--statement .cv-section__body > .cv-prose { margin: 0; }
  .cv-section--statement .cv-section__body > .cv-prose:first-of-type {
    grid-column: 1 / -1; max-width: 34ch; margin-bottom: clamp(var(--space-5), 4vh, var(--space-7)); color: var(--fg-default); text-wrap: balance;
    font: 450 clamp(1.75rem, 1.2rem + 2vw, 3rem)/1.12 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.02em;
  }
  /* Two short sections side by side (`pair`), so a page does not end on two thin columns of text. */
  .cv-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(var(--space-6), 5vw, var(--space-8)); }
  .cv-pair > .cv-section { align-content: start; }
  .cv-pair > .cv-section :is(.cv-prose, .cv-essay__text) { max-width: none; }
  @media (max-width: 900px) { .cv-pair { grid-template-columns: minmax(0, 1fr); } }
  /* Beside the large line, ending above the paragraphs so it never runs behind the text. */
  .cv-section__backdrop { position: absolute; top: 0; right: 0; width: min(190px, 22%); aspect-ratio: 1; opacity: .55; pointer-events: none; }

  /* Figures: numbers on the page's ground, between two hairlines that run across the screen. */
  .figures {
    margin: clamp(var(--space-7), 9vh, var(--space-9)) 0 0; padding: clamp(var(--space-6), 5vh, var(--space-7)) 0;
    border-block: 1px solid; border-image: linear-gradient(var(--line-default), var(--line-default)) 1 / 1px 0 / 0 100vmax;
  }
  .figures ul { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-5) clamp(var(--space-4), 3vw, var(--space-6)); margin: 0; padding: 0; list-style: none; }
  .figures li { display: grid; align-content: start; gap: var(--space-2); padding-inline-start: var(--space-4); border-inline-start: 1px solid var(--path-line); }
  /* Dates (« Septembre 2026 » holds together): sized to fit a quarter of the page, or half on a phone. */
  .figures__value { font: 450 var(--text-title)/1.1 'Jost', 'Futura', system-ui, sans-serif; letter-spacing: -.015em; color: var(--fg-default); text-wrap: balance; overflow-wrap: anywhere; }
  .figures__label { font-size: var(--text-sm); line-height: 1.45; color: var(--fg-muted); text-wrap: pretty; }

  /* Ladder: the publics on one line, each stop a way to its essay. */
  .ladder { position: relative; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); margin: 0 0 clamp(var(--space-6), 6vh, var(--space-7)); padding: 0; list-style: none; }
  /* Geometry, not spacing: the line runs through the centres of the 11 px stops. */
  .ladder::before { content: ''; position: absolute; top: 5px; left: 0; right: 0; height: 1px; background: linear-gradient(to right, color-mix(in oklab, var(--sec) 35%, transparent), var(--sec)); }
  .ladder li { position: relative; padding-top: var(--space-5); }
  .ladder li::before { content: ''; position: absolute; top: 0; left: 0; width: 11px; height: 11px; border-radius: 50%; background: var(--ground-default); border: 1.5px solid var(--sec-voice); }
  /* The chosen stop is lit and inked; the others wait in muted ink (pages.mjs ladderTabs). */
  .ladder li:has(input:checked)::before { background: var(--sec-voice); box-shadow: 0 0 0 4px color-mix(in oklab, var(--sec-voice) 22%, transparent); }
  .ladder__stop { position: relative; display: inline-block; cursor: pointer; }
  .ladder__stop input { position: absolute; inset: -6px; margin: 0; opacity: 0; cursor: pointer; }
  .ladder__stop span { color: var(--fg-muted); font: 400 var(--text-lead)/1.25 'Jost', 'Futura', system-ui, sans-serif; border-bottom: 1px solid transparent; transition: color var(--motion-hover) var(--ease-hover), border-color var(--motion-hover) var(--ease-hover); }
  .ladder__stop:hover span, .ladder__stop input:focus-visible + span { color: var(--fg-default); border-color: var(--sec); }
  .ladder__stop input:checked + span { color: var(--fg-default); border-color: var(--sec-voice); }
  .ladder__stop input:focus-visible + span { outline: 2px solid var(--line-focus); outline-offset: 4px; }
  .ladder__panel { display: none; }
  .ladder-tabs:has(li:nth-child(1) input:checked) .ladder__panel:nth-child(1),
  .ladder-tabs:has(li:nth-child(2) input:checked) .ladder__panel:nth-child(2),
  .ladder-tabs:has(li:nth-child(3) input:checked) .ladder__panel:nth-child(3),
  .ladder-tabs:has(li:nth-child(4) input:checked) .ladder__panel:nth-child(4) { display: block; }
  .ladder-tabs { margin-bottom: clamp(var(--space-6), 7vh, var(--space-8)); }
  @media print { .ladder__panel { display: block !important; } }

  /* Proof: the hub's own cards, three across, then a quiet way to all of them. */
  .proof { margin-top: clamp(var(--space-6), 6vh, var(--space-7)); }
  .hub-grid--proof { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .hub-grid--proof.hub-grid--n2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* One proof is a row across the width: its scene beside its words. */
  .hub-grid--proof.hub-grid--n1 { grid-template-columns: minmax(0, 1fr); }
  .hub-grid--n1 .hub-card__link { display: grid; grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); }
  .hub-grid--n1 .hub-card__visual { height: 100%; }
  .hub-grid--n1 .hub-card__body { justify-content: center; padding: clamp(var(--space-4), 2.5vw, var(--space-6)); }
  .proof__all { margin: var(--space-4) 0 0; text-align: end; }
  .proof__all a { color: var(--fg-muted); text-decoration: none; font-size: var(--text-md); border-bottom: 1px solid var(--line-default); }
  .proof__all a:is(:hover, :focus-visible) { color: var(--fg-default); border-color: var(--sec, var(--path-line)); }
  .proof + .cv-cta { margin-top: var(--space-5); }

  /* A folded essay: its first paragraph, then « Lire la suite », which opens the rest in place. */
  .cv-more > summary {
    display: inline-flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); cursor: pointer; list-style: none;
    font: 500 var(--text-md)/1.3 'Albert Sans', system-ui, sans-serif; color: var(--fg-default);
  }
  .cv-more > summary::-webkit-details-marker { display: none; }
  .cv-more > summary::before { content: '+'; display: grid; place-items: center; width: 20px; height: 20px; border: 1px solid var(--sec, var(--line-strong)); border-radius: 50%; font: 500 var(--text-sm)/1 'Martian Mono', ui-monospace, monospace; color: var(--sec-voice, var(--fg-default)); }
  .cv-more > summary:is(:hover, :focus-visible) { text-decoration: underline; text-decoration-color: var(--sec); text-underline-offset: 4px; }
  /* Open, the fold keeps its control (« Réduire », the + turned to −), and the rest of the essay slides open
     and shut where the browser can animate a fold's height; elsewhere it opens at once, as before. */
  .cv-more__label--less, .cv-more[open] .cv-more__label:not(.cv-more__label--less) { display: none; }
  .cv-more[open] .cv-more__label--less { display: inline; }
  /* The edit build opens every fold (web/edit.js), so both labels stay in reach to be edited. */
  html.is-editing .cv-more[open] > summary > .cv-more__label { display: inline; }
  html.is-editing .cv-more .cv-more__label + .cv-more__label::before { content: '/'; margin: 0 8px; color: var(--fg-muted); }
  .cv-more[open] > summary::before { content: '−'; }
  .cv-more { interpolate-size: allow-keywords; }
  .cv-more::details-content { block-size: 0; overflow: clip; opacity: 0; transition: block-size .42s cubic-bezier(.22, .8, .2, 1), opacity .3s ease, content-visibility .42s allow-discrete; }
  .cv-more[open]::details-content { block-size: auto; opacity: 1; }
  .cv-more > .cv-essay__text:first-of-type { margin-top: var(--space-3); }

  /* His path's eras, on one strip under « Parcours ». */
  .eras { position: relative; height: 64px; margin: 0 0 clamp(var(--space-5), 4vh, var(--space-6)); padding: 0; list-style: none; }
  .eras__era { position: absolute; top: 0; left: var(--from); width: calc(var(--to) - var(--from)); display: grid; gap: var(--space-1); padding: var(--space-2) var(--space-3) 0; border-top: 2px solid var(--line-strong); font-size: var(--text-sm); color: var(--fg-muted); }
  .eras__era + .eras__era { border-inline-start: 1px solid var(--line-hairline); }
  .eras__era[data-era='craft'] { border-top-color: var(--path-line); color: var(--fg-default); }
  .eras__years { font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .06em; color: var(--fg-muted); }
  /* The dated steps (his research works, his first lessons) in a row under the eras, a year each. */
  .eras + .eras__marks, .eras__marks { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin: -12px 0 clamp(var(--space-5), 4vh, var(--space-6)); padding: 0; list-style: none; }
  .eras__marks li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-2); padding-inline-start: var(--space-2); border-inline-start: 2px solid var(--path-line); font-size: var(--text-sm); color: var(--fg-muted); }
  :is(.eras__marks, .chrono__mark) a { color: var(--fg-default); text-decoration: none; border-bottom: 1px solid var(--line-default); }
  :is(.eras__marks, .chrono__mark) a:is(:hover, :focus-visible) { border-color: var(--path-line); }

  /* Narrow screens: every form stacks (hook, scene, words, cards), the cards swipe sideways. */
  @media (max-width: 900px) {
    .cv-split, .cv-section--flip .cv-split { grid-template-columns: minmax(0, 1fr); }
    .cv-scene, .cv-section--flip .cv-scene { position: static; order: -1; }
    .figures ul { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cv-section--statement .cv-section__body { grid-template-columns: minmax(0, 1fr); }
    .cv-section__backdrop { display: none; }
  }
  @media (max-width: 760px) {
    .hub-grid--proof, .hub-grid--proof.hub-grid--n2 { display: grid; grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: min(80%, 18rem); overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; contain: inline-size; padding-bottom: var(--space-1); }
    .hub-grid--proof > li { scroll-snap-align: start; }
    /* One proof has nothing to scroll to: it is a plain card. */
    .hub-grid--proof.hub-grid--n1 { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); overflow: visible; }
    .hub-grid--n1 .hub-card__link { display: flex; }
    .ladder { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .ladder::before { top: 0; bottom: 0; left: 5px; right: auto; width: 1px; height: auto; background: var(--sec); }
    .ladder li { padding: var(--space-1) 0 var(--space-3) var(--space-5); }
    .ladder li::before { top: 9px; }
    .eras { height: auto; display: grid; gap: var(--space-2); }
    .eras__era { position: static; width: auto; }
    .eras__era + .eras__era { border-inline-start: 0; }
    .eras__marks { margin-top: 0; }
  }

  @media print {
    .cv-scene, .proof, .figures, .ladder, .eras, .eras__marks, .cv-section__backdrop, .cv-hook, .cv-more > summary { display: none !important; }
    .cv-split { display: block; }
    .cv-more::details-content { content-visibility: visible; display: contents; }
    .cv-section--band, .cv-section--statement { background: none; box-shadow: none; clip-path: none; margin: 0; padding-bottom: 0 !important; }
    .cv-section--band, .cv-section--statement { border-top: 0; border-image: none; }
  }
}

/* Paper's ink, outside the layers: the theme tokens are unlayered and pinned on <html> (pages.mjs
   THEME), so inside a layer this lost to them and the printed CV came out in the dark theme's near-white
   ink on white. */
@media print {
  :root, :root[data-theme] { color-scheme: light; --fg-default: #000; --fg-muted: #333; --tech-label: #444; --line-hairline: #ccc; --ground-default: #fff; }
}

/* Between pages (his choice, 2026-11-05: « card becomes page »). A cross-document view transition: the
   header stays where it is, a project's scene and name travel from its card to its page and back, a
   section page's form and title slide into the next one's, and the rest cross-fades. Browsers without
   view transitions keep the plain page load; so does reduced motion (here, and hud.js skips it for the
   site's own motion setting). The names are set inline by the renderer (pages.mjs projectVisual,
   hubCard, project), one per project and per page. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) { @view-transition { navigation: none; } }
@layer transitions {
  .hud-top { view-transition-name: site-header; }
  :is(.page-head, .cv-head) .glyph { view-transition-name: page-glyph; }
  :is(.page-head__title, .cv-head__name) { view-transition-name: page-title; }
  ::view-transition-group(*) { animation-duration: .52s; animation-timing-function: cubic-bezier(.22, .8, .2, 1); }
  ::view-transition-old(root) { animation: .22s cubic-bezier(.4, 0, 1, 1) both vt-out; }
  ::view-transition-new(root) { animation: .38s cubic-bezier(0, 0, .2, 1) .08s both vt-in; }
  /* The header does not move or fade: only its current link changes. */
  ::view-transition-group(site-header) { animation: none; }
  ::view-transition-old(site-header) { display: none; }
  ::view-transition-new(site-header) { animation: none; }
  /* A scene keeps its proportion while it grows (card and page share 16 / 10), the old and new frames
     cross-fading inside it. */
  ::view-transition-old(*.proj-scene), ::view-transition-new(*.proj-scene) { height: 100%; object-fit: cover; }
  /* A name grows as one line of text from its top left corner: the card's small one leaves at once and
     the page's large one comes in (a cross-fade of the two at once read as « Atlas Atlas »). */
  ::view-transition-old(*.proj-name), ::view-transition-new(*.proj-name) { width: auto; height: 100%; }
  ::view-transition-old(*.proj-name) { animation: .12s linear both vt-out; }
  ::view-transition-new(*.proj-name) { animation: .3s cubic-bezier(0, 0, .2, 1) .1s both vt-fade; }
}
@keyframes vt-fade { from { opacity: 0; } }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(10px); } }

/* A card's scene rests a little dimmed and wakes, brighter and quicker (scenes/kit.js), when the card is
   pointed at or focused. The card itself stays still (his rule, 2026-11-01). */
@layer transitions {
  .hub-card__link .scene__canvas { filter: saturate(.82) brightness(.9); transition: filter .45s ease; }
  .hub-card__link:is(:hover, :focus-visible) .scene__canvas { filter: none; }
  /* The way back to the hub lands on the project's card, clear of the sticky header. */
  .hub-card { scroll-margin-top: calc(var(--sticky-h, 0px) + 24px); }
}

/* The foot of a content page (his choice, 2026-11-05): the next page in the reading order set large, as
   the page's last gesture, then the ways to reach him and the other language, quiet, on one hairline. */
@layer hud {
  .page-foot {
    display: grid; gap: var(--space-5); margin: clamp(var(--space-7), 8vh, var(--space-9)) 0 0; padding: clamp(var(--space-6), 7vh, var(--space-8)) 0 var(--space-6);
    border-top: 1px solid var(--line-hairline);
  }
  .page-foot__next { display: grid; gap: var(--space-2); justify-self: start; color: inherit; text-decoration: none; }
  .page-foot__name { font: 450 clamp(2rem, 4vw, 3.5rem)/1.05 'Jost', system-ui, sans-serif; letter-spacing: -.01em; }
  .page-foot__name::after { content: '\00a0→'; color: var(--path-color); display: inline-block; transition: transform .28s cubic-bezier(.22, .8, .2, 1); }
  .page-foot__next:is(:hover, :focus-visible) .page-foot__name::after { transform: translateX(6px); }
  .page-foot__links { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); font-size: var(--text-md); }
  .page-foot__links a { color: var(--fg-muted); text-decoration: none; border-bottom: 1px solid transparent; }
  .page-foot__links a:is(:hover, :focus-visible) { color: var(--fg-default); border-color: var(--path-line); }
  .page-foot__sign { margin: 0; font: 500 var(--text-xs)/1 'Martian Mono', ui-monospace, monospace; letter-spacing: .04em; color: var(--fg-muted); }
  /* A project page closes on its previous and next already: the foot follows on the same rule. */
  .hud--project .page-foot { margin-top: 0; padding-top: var(--space-2); border-top: 0; }
  @media print { .page-foot { display: none; } }
}


