/* ==========================================================================
   base.css : typographie, mise en page, socle d'accessibilité
   ========================================================================== */

/* --- La police, servie depuis le dépôt ---------------------------------
   Copiée en local et non chargée depuis Google Fonts, pour deux raisons :
   le site doit fonctionner sans connexion pendant la démonstration, et
   aucune requête n'est envoyée à un tiers.
   Sous-ensemble latin : couvre é è à ç ù î ô ë ï ü œ, donc tout le français. */

@font-face {
  font-family: 'Inter';
  src: url('../assets/fonts/inter-latin-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  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;
}

/* --- Remise à zéro minimale -------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;   /* empêche iOS de gonfler le texte en paysage */
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { max-width: 100%; height: auto; display: block; }

/* --- Typographie -------------------------------------------------------- */

h1, h2, h3 {
  line-height: var(--lh-tight);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 var(--sp-3);
  text-wrap: balance;   /* ignoré par les vieux navigateurs, sans conséquence */
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); font-weight: 700; letter-spacing: -0.01em; }

p { margin: 0 0 var(--sp-3); max-width: var(--measure); }

strong { font-weight: 700; }

.lead      { font-size: clamp(1.25rem, 4vw, 1.6rem); line-height: 1.4; }
.caption   { font-size: var(--fs-caption); color: var(--ink-soft); }
.small     { font-size: var(--fs-small); }

a { color: var(--c-impulse-text); text-underline-offset: 0.2em; }

/* --- Mise en page ------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: 60rem;
  margin-inline: auto;
  padding-inline: var(--sp-3);
}

.section {
  padding-block: var(--sp-6);
  scroll-margin-top: var(--sp-4);
}

@media (min-width: 40em) {
  .wrap    { padding-inline: var(--sp-5); }
  .section { padding-block: var(--sp-7); }
}

/* --- Accessibilité : le socle ------------------------------------------ */

/* Réservé aux lecteurs d'écran. Sert notamment aux alternatives textuelles
   des schémas animés. C'est l'équivalent web de l'audiodescription. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Le lien d'évitement : première chose atteinte au clavier, invisible sinon. */
.skip-link {
  position: absolute;
  top: 0; left: 0;
  transform: translateY(-120%);
  background: var(--ink);
  color: var(--paper);
  padding: var(--sp-2) var(--sp-3);
  z-index: 100;
  text-decoration: none;
}
.skip-link:focus { transform: translateY(0); }

/* Un anneau de focus visible et épais. Ne jamais supprimer cet outline
   sans le remplacer : c'est ce qui rend le site navigable au clavier. */
:focus-visible {
  outline: 3px solid var(--c-impulse);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Toute cible tactile respecte le minimum de 44x44 px. */
button, [role="button"], summary, .tap {
  min-height: var(--tap-min);
  min-width: var(--tap-min);
}

button {
  font: inherit;
  font-size: var(--fs-small);
  color: var(--ink);
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  padding: var(--sp-2) var(--sp-3);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
button:hover { background: var(--ink); color: var(--paper); }

/* L'alternative textuelle d'un schéma, dépliable par tout le monde. */
.alt-text {
  margin-top: var(--sp-3);
  font-size: var(--fs-small);
  border-left: var(--stroke) solid var(--ink-faint);
  padding-left: var(--sp-3);
}
.alt-text summary {
  cursor: pointer;
  color: var(--ink-soft);
  display: flex;
  align-items: center;
}

/* --- Mouvement réduit ---------------------------------------------------
   Deux chemins mènent ici : le réglage système du visiteur, ou la classe
   .reduce-motion posée sur <html> par js/a11y.js (bouton manuel, ou appareil
   détecté comme trop lent). Le contenu reste entier, seul le mouvement part. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

html.reduce-motion { scroll-behavior: auto; }
html.reduce-motion *,
html.reduce-motion *::before,
html.reduce-motion *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
}

/* --- Sans JavaScript ----------------------------------------------------
   Le site ne doit JAMAIS afficher une page blanche. Par défaut tout est
   visible ; c'est le JavaScript qui, une fois chargé, prend la main pour
   animer les apparitions. */

.temps, .temps-parcours, .defiler { opacity: 1; }
html.js .temps, html.js .temps-parcours, html.js .defiler { opacity: 0; }
html.js.reduce-motion .temps, html.js.reduce-motion .temps-parcours { opacity: 1; }

/* Le filet du mode réduit : la classe js est posée avant le premier rendu,
   donc si js/main.js échoue au chargement, tout le contenu resterait
   invisible. La balise <script> de index.html retire alors la classe js
   elle-même, sur son événement error. La protection couvre ainsi les DEUX
   cas, JavaScript désactivé et JavaScript cassé. */

/* ==========================================================================
   Composants
   ========================================================================== */

/* --- La barre du haut --------------------------------------------------
   Volontairement discrète : elle ne contient que le réglage d'animation,
   qui doit rester atteignable à tout moment (WCAG 2.2.2). */

.barre {
  /* Fixe, et pas simplement en haut du document. Deux raisons, et la première
     est une exigence, pas un confort :

     1. WCAG 2.2.2 et le critère 3.2.6 « Consistent Help » demandent que ce
        réglage soit atteignable À TOUT MOMENT. En flux normal, la barre
        sortait de l'écran dès le premier écran de défilement : il fallait
        remonter tout en haut de la page pour couper les animations, ce qui
        est exactement ce que ne peut pas faire quelqu'un que le mouvement
        indispose.
     2. Hors du flux, la scène collée de l'ouverture commence à zéro et
        occupe l'écran exactement, au lieu d'être décalée de la hauteur de la
        barre et de déborder d'autant sous le pli.

     Le bouton garde son fond papier et son trait : il reste lisible quand du
     texte défile dessous. */
  position: fixed;
  top: 0;
  right: 0;
  z-index: 20;
  display: flex;
  justify-content: flex-end;
  padding: var(--sp-2) var(--sp-3);
}
.barre button { font-size: var(--fs-caption); }

/* --- Titre de schéma ----------------------------------------------------
   Tout schéma porte un titre visible. Un dessin sans titre oblige le lecteur
   à deviner ce qu'il regarde, ce qui coûte cher aux personnes qui ont du mal
   à tenir plusieurs informations en tête à la fois (recommandation COGA). */

.figure-titre {
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 var(--sp-2);
}

/* --- L'ouverture : le texte s'empile dans une scène collée --------------
   Le texte ne se remplace plus, il s'ajoute. Chaque temps arrive sous les
   précédents, qui restent à l'écran, et le visiteur voit son paragraphe se
   construire au défilement.

   LE POINT IMPORTANT, et c'est ce qui rend le tout gratuit pour un vieux
   téléphone : les temps encore invisibles OCCUPENT DÉJÀ LEUR PLACE. Seule
   leur opacité change. Rien ne pousse rien, aucune ligne ne se recalcule,
   le texte déjà lu ne bouge pas d'un pixel quand le suivant arrive. On
   n'anime que transform et opacity, comme l'exige la règle du projet. */

.recit { padding-block: 0; }

/* LA SCÈNE. Collée en haut de l'écran du premier écran jusqu'à la fin du
   parcours, elle contient tout ce qui se voit : le texte de l'ouverture, et
   LE neurone, qui est le même du début à la fin.

   Elle est volontairement transparente et ne bloque pas la souris : sous sa
   moitié basse, le texte du parcours défile et doit rester sélectionnable.
   Seuls le neurone et son fond papier sont opaques. */
.recit-scene {
  position: sticky;
  top: 0;
  height: 100svh;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  pointer-events: none;

  /* La place du bouton flottant. Sans elle, le titre de l'ouverture passait
     dessous et devenait illisible : un bouton posé par dessus du texte n'est
     pas un réglage discret, c'est un défaut. */
  padding-top: 3.6rem;
}
@supports not (height: 100svh) {
  .recit-scene { height: 100vh; }
}
.recit-scene .pile,
.recit-scene .porte-neurone { pointer-events: auto; }

/* Une fois la bascule engagée, le texte de l'ouverture est effacé : il ne
   doit plus intercepter la souris à la place du texte du parcours qui défile
   dessous. */
.recit-scene.bascule .pile { pointer-events: none; }

/* LES RAILS. Ils n'affichent rien : leur seule hauteur donne au défilement la
   distance nécessaire pour faire arriver un temps après l'autre, puis pour la
   bascule vers le parcours. Un écran par temps, un pour lire le paragraphe
   entier, un pour la bascule. */
.rails { height: 700svh; }
@supports not (height: 100svh) {
  .rails { height: 700vh; }
}

/* Les deux blocs de texte de l'ouverture, de part et d'autre du neurone. */
.pile > * { margin-bottom: var(--sp-4); }
.pile > *:last-child { margin-bottom: 0; }

.temps { margin-top: 0; }

.recit h1.temps { max-width: 16ch; }

/* LE PORTE-NEURONE. En pleine largeur, hors du .wrap, pour que son fond
   papier couvre tout l'écran quand le texte du parcours défile dessous.

   Les marges hautes et basses sont réservées EN PERMANENCE, même pendant
   l'ouverture où le titre et les boutons sont invisibles : sans ça, leur
   arrivée ferait sauter toute la mise en page au moment précis de la
   bascule. Ils sont positionnés en absolu, donc ils ne coûtent aucune
   hauteur de leur côté. */
.porte-neurone {
  position: relative;
  z-index: 0;
  background: var(--paper);
  padding-top: 2rem;          /* la place du titre, réservée en permanence */
  margin-bottom: var(--sp-4);
}

/* Le fond papier déborde vers le HAUT, jusqu'au bord de l'écran.
   Une fois le dessin remonté, il reste au dessus de lui la marge réservée au
   bouton flottant, qui n'appartient à personne : le texte du parcours y
   défilait à découvert et on voyait passer des bouts de phrases au ras du
   titre. Ce débordement bouche le trou.
   Pendant l'ouverture, il se déploie derrière le titre et la première phrase,
   sans rien changer : c'est du papier sur du papier. */
.porte-neurone::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  /* Deux pixels DANS le bloc, et non affleurant. Bord à bord, il restait
     une jointure d'un pixel, anti-crénelée, entre le débordement et le fond
     du bloc, et le texte du parcours y transparaissait en un liseré de
     hauts de lettres au dessus du titre. Trouvé en peignant le débordement
     en rouge et la scène en jaune : le jaune apparaissait sur une ligne. */
  bottom: calc(100% - 2px);
  height: 60vh;
  background: var(--paper);
  pointer-events: none;
}

/* Le texte de l'ouverture passe DEVANT ce débordement. Sans cette pile
   d'ordre, le fond du porte-neurone, qui vient plus loin dans le document,
   recouvrirait le titre. */
.recit-scene .pile { position: relative; z-index: 1; }
.porte-neurone .figure-titre {
  position: absolute;
  top: 0; left: 0; right: 0;
  padding-inline: var(--sp-3);
  margin: 0;
}
@media (min-width: 40em) {
  .porte-neurone .figure-titre { padding-inline: var(--sp-5); }
}

/* Les boutons du parcours sont accrochés SOUS le dessin, et pas réservés
   dans sa hauteur. Sur un téléphone ils tiennent sur deux rangs, soit plus de
   cent pixels : les réserver en permanence aurait mangé la moitié du neurone
   d'introduction, alors qu'ils y sont invisibles. Accrochés en dessous, ils
   ne coûtent rien tant qu'ils ne servent pas. */
.porte-neurone .etapes {
  position: absolute;
  top: 100%; left: 0; right: 0;
  margin: 0;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  background: var(--paper);
}
@media (min-width: 40em) {
  .porte-neurone .etapes { padding-inline: var(--sp-5); }
}

/* Le texte du parcours passait sous le dessin en étant tranché net au milieu
   d'une ligne, ce qui ressemblait à un défaut d'affichage. Un fondu court le
   fait disparaître progressivement. Un simple dégradé, sans filtre ni flou :
   rien qui coûte cher à un vieux processeur graphique. */
.porte-neurone .etapes::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: 100%;
  height: var(--sp-5);
  background: linear-gradient(var(--paper), rgba(255, 255, 255, 0));
  pointer-events: none;
}

.question {
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
}

.figure-courbe { margin-inline: 0; }
.figure-courbe .courbe { width: 100%; max-width: 22rem; height: auto; }
.figure-courbe figcaption { margin-top: var(--sp-2); max-width: var(--measure); }

/* L'invite à défiler, en bas de la scène. Elle s'efface dès le premier
   geste : elle a dit ce qu'elle avait à dire. */
.defiler {
  position: absolute;
  left: 0; right: 0;
  bottom: var(--sp-4);
  margin: 0;
  padding-inline: var(--sp-3);
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
@media (min-width: 40em) {
  .defiler { padding-inline: var(--sp-5); }
}
.defiler::after { content: ''; width: 1px; height: 2rem; background: var(--ink-faint); }

/* --- Les deux replis : sans JavaScript, et en mouvement réduit ----------
   Quand tout doit être visible d'un coup, la scène collée n'a plus de sens :
   elle enfermerait six temps et un dessin dans la hauteur d'un écran, et tout
   se chevaucherait. Les boutons du parcours tombaient sur la courbe, l'invite
   à défiler sur le texte, et le titre sous le bouton flottant. Le contenu
   était bien là, mais illisible, ce qui ne vaut guère mieux qu'absent.

   Elle redevient donc un simple bloc de page, qui coule normalement. Deux
   chemins mènent ici et demandent exactement la même chose : le visiteur sans
   JavaScript, et le visiteur en mouvement réduit. */

html.no-js .recit-scene,
html.reduce-motion .recit-scene {
  position: static;
  height: auto;
  padding-bottom: var(--sp-5);
}

html.no-js .porte-neurone,
html.reduce-motion .porte-neurone { padding-top: 0; }

html.no-js .porte-neurone .figure-titre,
html.reduce-motion .porte-neurone .figure-titre,
html.no-js .porte-neurone .etapes,
html.reduce-motion .porte-neurone .etapes {
  position: static;
  margin-bottom: var(--sp-3);
}

/* Le débordement de papier et le fondu ne servent qu'au dessin collé. */
html.no-js .porte-neurone::before,
html.reduce-motion .porte-neurone::before,
html.no-js .porte-neurone .etapes::after,
html.reduce-motion .porte-neurone .etapes::after { display: none; }

/* Plus rien à révéler, donc plus rien à annoncer, et plus besoin de distance
   de défilement. */
html.no-js .defiler,
html.reduce-motion .defiler,
html.no-js .rails,
html.reduce-motion .rails { display: none; }

/* --- Le parcours : le MÊME neurone, arrivé pendant l'ouverture -----------
   Il n'y a rien à recréer ici. Le dessin est déjà à l'écran, dans la scène
   collée, et la caméra se contente de se déplacer vers la partie dont parle
   le bloc de texte en cours. */

.scene { display: flex; justify-content: center; }

/* La taille du neurone est posée en pixels par js/recit.js, parce qu'elle
   change de façon continue pendant la bascule : il grandit de sa taille
   d'introduction à celle du parcours au fil du défilement. Les valeurs ici ne
   servent qu'AVANT que le JavaScript n'ait la main, et au visiteur qui n'en a
   pas du tout. Elles sont exprimées en vh et non en svh, parce que ce repli
   doit précisément fonctionner sur les navigateurs qui ignorent svh. */
.neurone {
  display: block;
  width: auto;
  max-width: 100%;
}
/* Le repli seulement, quand aucun script ne pose de taille. Ces deux valeurs
   ne doivent JAMAIS s'appliquer quand le script tourne : un max-height posé
   ici plafonnait en silence la hauteur écrite en ligne par js/recit.js, et
   le neurone ne grandissait plus à la bascule, 340 px avant comme après. */
html.no-js .neurone {
  height: 34vh;
  max-height: 340px;
}
/* Sans script, les flèches du signal afférent sont simplement là : le
   dessin dit d'un bloc que le neurone reçoit. */
html.no-js .neurone .afferents { opacity: 1; }

/* La surbrillance est MOMENTANÉE : on désigne la partie, puis le dessin
   revient à l'encre noire. Rien n'est atténué en permanence.
   La couleur ne porte jamais seule l'information : le trait s'épaissit en
   même temps, donc la surbrillance reste visible en noir et blanc. */
.partie .t, .partie .b {
  transition: stroke var(--dur-base) var(--ease),
              fill var(--dur-base) var(--ease),
              stroke-width var(--dur-base) var(--ease);
}
.partie.surbrillance .t { stroke: var(--c-surbrillance); stroke-width: 4.5; }
.partie.surbrillance .b { fill: var(--c-surbrillance); }

.etapes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.etapes button { flex: 1 1 auto; font-size: var(--fs-caption); }
.etapes button[aria-current="true"] { background: var(--ink); color: var(--paper); }

/* --- Les blocs de texte du parcours -------------------------------------
   Assez hauts pour n'être qu'un seul à la fois dans la zone de lecture,
   sous le dessin collé. */

.parcours-textes { padding-top: var(--sp-4); }

.etape-texte {
  min-height: 74svh;
  display: flex;
  align-items: center;
  /* Le dessin collé ne doit jamais recouvrir un élément qui reçoit le focus
     ni la cible d'une ancre. Exigence WCAG 2.4.11 « Focus Not Obscured ». */
  scroll-margin-top: 52svh;
}
@supports not (height: 100svh) {
  .etape-texte { min-height: 74vh; scroll-margin-top: 52vh; }
}
/* width: 100 %, et ce n'est pas superflu : le contenu est un enfant flex, et
   un enfant flex prend la largeur de sa ligne la plus longue. Sur le bloc
   de la fin, une phrase courte et un bouton, le bloc faisait 198 px et le
   bouton « centré » l'était dans ces 198 px, donc collé à gauche. */
.etape-contenu { width: 100%; max-width: var(--measure); }
.etape-contenu h3 { margin-bottom: var(--sp-3); }
.etape-contenu p:last-child { margin-bottom: 0; }

/* Les temps du parcours s'EMPILENT à l'intérieur d'une partie, comme ceux de
   l'ouverture : chacun occupe sa place dès le départ et n'arrive qu'en
   opacité, quand il entre dans la zone de lecture. Les blocs sont donc hauts
   de tout leur contenu, alignés en haut, avec assez d'air pour que deux temps
   n'entrent pas dans la zone ensemble. */
.etape-texte { align-items: flex-start; padding-top: 12svh; }
@supports not (height: 100svh) { .etape-texte { padding-top: 12vh; } }
.temps-parcours { margin-bottom: var(--sp-5); }

/* Le titre d'une partie : encre noire, gros et gras, et le sous-titre en
   encre douce en dessous. Rien d'autre, ni filet ni barre : la hiérarchie se
   fait par la taille, la graisse et le blanc. */
.partie-titre {
  font-size: var(--fs-h2);
  margin-bottom: var(--sp-4);
}
.partie-titre .sous-titre {
  display: block;
  font-size: var(--fs-small);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink-soft);
  margin-top: var(--sp-2);
}

/* Le mot-clé d'une partie : du magenta DERRIÈRE, le texte reste noir.
   7,3:1 en fond sous du noir, alors qu'en couleur de texte il tomberait à
   2,4:1. C'est la règle de la palette, et c'est ce que fait la référence
   citée par l'utilisateur. */
.mot {
  background: var(--accent);
  padding: 0 0.15em;
  border-radius: 2px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* --- Le curseur du seuil, premier moment interactif -----------------------
   Un curseur natif entre deux boutons. Les boutons ne sont pas un doublon :
   ils sont ce qui rend le réglage possible sans aucun glissement. Cibles de
   44 pixels, et le curseur lui même est aussi haut, pour le doigt. */
.interaction { max-width: var(--measure); }
.curseur {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-3) 0 var(--sp-2);
}
.curseur input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--tap-min);
  margin: 0;
  /* La couleur du signal sur le curseur : c'est lui qui fait arriver les
     messages. Du bleu, seule couleur qui tient sur blanc. */
  accent-color: var(--signal);
  cursor: pointer;
}
.curseur-bouton { flex: 0 0 auto; font-size: var(--fs-caption); }

/* Le chronomètre du défi : le temps en gros, tabulaire pour ne pas sauter
   quand les chiffres changent, et la cible en légende. Le succès est dit
   par un mot, jamais par la seule couleur. */
.chrono-defi {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin: var(--sp-3) 0 0;
}
.chrono-valeur {
  font-size: var(--fs-h2);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  min-width: 5.2ch;
}
/* Objectif atteint : le temps passe sur fond vert myéline, texte noir, comme
   tout ce qui est coloré sur ce site. Le mot « atteint » est là aussi. */
.defi-atteint .chrono-valeur {
  background: var(--myeline);
  padding: 0 0.15em;
  border-radius: 2px;
}

/* Le bloc invisible ne se laisse pas cliquer ni tabuler : js/recit.js pose
   visibility avec l'opacité. Le repli sans script le laisse visible. */
html.js .interaction { visibility: hidden; }
html.no-js .interaction, html.reduce-motion .interaction { visibility: visible; }

.etape-contenu .figure-courbe { margin: 0 0 var(--sp-5); }

/* La fin : le bouton du premier écran revient, seul, au milieu. */
.fin { display: flex; justify-content: center; padding: var(--sp-3) 0 var(--sp-6); }
/* 15 rem et non 18 : la courbe est un schéma dans le texte, et le texte a
   priorité. Demande de l'utilisateur, 11 septembre 2026. */
.etape-contenu .figure-courbe .courbe { max-width: 15rem; }

.recit .alt-text { padding-bottom: var(--sp-6); }

/* ==========================================================================
   UNE SEULE MISE EN PAGE, CELLE DU TELEPHONE
   ==========================================================================

   Il y avait ici une mise en page a deux colonnes pour les grands ecrans.
   Elle est supprimee, et c'est un choix de calendrier assume : tenir deux
   mises en page, c'est tenir deux fois les memes bugs, et la presentation
   passe avant. Le telephone est la cible declaree du projet depuis le
   depart ; l'ordinateur affiche desormais exactement la meme chose.

   Sur grand ecran, la colonne est simplement centree et bornee. Ce n'est pas
   un pis-aller : une colonne etroite d'encre sur du blanc, c'est la page d'un
   livre, et c'est precisement la direction artistique du site.

   Consequence directe : le dessin et le texte sont toujours l'un au-dessus de
   l'autre, jamais cote a cote. Ils ne peuvent donc plus se recouvrir, ce qui
   supprime par construction toute une famille de defauts.
   ========================================================================== */

.recit .wrap { max-width: 34rem; }

/* On borne LA SCENE elle-meme, et pas seulement ce qu'elle contient.

   Bornee de l'interieur, chaque element se centrait pour son propre compte :
   le titre du schema et les boutons, qui font leur largeur de contenu,
   restaient colles a gauche pendant que le dessin et le texte se centraient.
   Trois alignements differents sur le meme ecran.

   En bornant la scene, tout ce qu'elle porte s'aligne sur la meme colonne,
   et cette colonne est exactement celle du texte du parcours qui defile
   dessous. Le lecteur n'a jamais qu'une colonne sous les yeux. */
.recit-scene {
  max-width: 34rem;
  margin-inline: auto;
}


/* --- Le bouton d'ouverture ----------------------------------------------
   La première chose du site, et la seule à agir avant le défilement. */

.porte-entree {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
  overflow: hidden;
  max-height: 20rem;
  transition: max-height var(--dur-slow) var(--ease),
              opacity var(--dur-base) var(--ease);
}

/* Le titre du site, au-dessus du premier bouton. Il n'est pas soumis au
   plafond de --fs-h1 : ce plafond protège la scène collée, où le texte et
   le dessin se disputent la hauteur, alors qu'ici le bloc occupe tout
   l'écran et n'a rien d'autre à loger. Encre noire, gras, aucun ornement. */
.titre-site {
  font-size: clamp(2rem, 9vw, 3.4rem);
  max-width: 12ch;
  margin: 0 0 var(--sp-5);
}

.bouton-entree {
  font-size: var(--fs-body);
  font-weight: 700;
  padding: var(--sp-3) var(--sp-5);
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  /* Le magenta en FOND derrière du texte noir, jamais l'inverse : un texte
     coloré serait contraire à la règle de la palette, et moins lisible. */
  background: var(--accent, #FF6EF5);
  color: var(--ink);
}
.bouton-entree:hover { background: var(--ink); color: var(--paper); }
.bouton-entree:active { transform: translateY(1px); }

/* Une fois cliqué, le bouton a fait son travail : il se replie. Ce n'est pas
   qu'une économie de place, même si elle compte sur un écran court. C'est
   aussi juste narrativement : l'instrument s'efface une fois le geste
   accompli, et ne reviendra qu'à la toute fin du récit. */
/* AVANT le clic, le bouton occupe l'ecran entier, centre.
   Trois raisons. C'est la seule chose a faire, donc rien d'autre ne doit
   distraire. Un fond opaque garantit qu'aucun element du recit ne transparait
   derriere, donc aucun chevauchement possible a cette etape. Et le centre est
   la ou l'oeil se pose d'abord. */
/* html.js, et non html seul : sans JavaScript, la classe `entre` n'est jamais
   posée, donc ce plein écran serait définitif et le visiteur ne verrait
   jamais rien d'autre que le bouton. Mesuré : capture sans script, tout le
   récit derrière un cache blanc. Le site doit rester lisible sans script,
   c'est une règle du projet. */
html.js:not(.entre) .porte-entree {
  position: fixed;
  inset: 0;
  z-index: 5;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  text-align: center;
  max-height: none;
  pointer-events: auto;
}

/* LE CACHE D'OUVERTURE DOIT ETRE AU-DESSUS DE TOUT LE RECIT, Y COMPRIS AU CLIC.

   Son z-index de 5 ne suffit pas : il se compte A L'INTERIEUR de la pile du
   haut, qui forme son propre empilement au niveau 1. La pile du bas, au meme
   niveau et plus loin dans le document, se peignait donc par-dessus le cache
   entier. Rien ne se voyait, ses textes etant encore a opacite nulle, mais
   elle interceptait les clics : un vrai clic de souris au centre du bouton
   « Clique » tombait sur elle, et le site ne s'ouvrait jamais.

   Le defaut etait latent. Avec l'ancien titre, la moitie basse du bouton
   etait deja morte ; le titre plus long du 14 septembre 2026, une ligne de
   plus, a fait descendre le bouton de 19 px et mis son centre dans la zone
   morte. Trouve par le controle 13, qui clique avec un vrai bouton de souris.

   Remonter la pile du haut tant qu'on n'est pas entre rend vraie la promesse
   du commentaire ci-dessus : aucun element du recit ne passe devant le cache. */
html.js:not(.entre) .recit-scene .pile-haut { z-index: 2; }

/* Une fois clique, il disparait et laisse la place a la suite.

   `visibility: hidden` n'est pas un doublon d'`opacity: 0`, et la
   difference se mesure : sans elle le bouton reste ATTEIGNABLE. Il etait
   le troisieme arret de tabulation d'un site ou il n'existe plus, un
   lecteur d'ecran continuait d'annoncer le titre du site et la consigne
   « Clique sur ce bouton. », et Entree dessus ne faisait rien puisque le
   gestionnaire sort aussitot sur `if (entre) return`. Constate par les
   chantiers D et G de l'audit technique.

   Le retard de transition sur `visibility` est ce qui permet de garder le
   fondu : la propriete ne bascule qu'une fois l'opacite arrivee a zero.
   En mouvement reduit, toutes les durees valent 0,01 ms, donc elle
   bascule tout de suite, ce qui est exactement ce qu'on veut. */
html.entre .porte-entree {
  max-height: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: max-height var(--dur-slow) var(--ease),
              opacity var(--dur-base) var(--ease),
              visibility 0s linear var(--dur-base);
}

/* --- Les deux visuels, dans la MEME case --------------------------------
   La silhouette et le neurone ne sont jamais montres ensemble : l'une est le
   plan large, l'autre est ce qu'on trouve au bout du zoom. Les empiler dans
   une seule case de grille n'est pas une astuce, c'est le recit lui-meme.

   C'est aussi ce qui garantit que la scene ne deborde pas. Quand les deux
   additionnaient leurs hauteurs, la scene depassait la fenetre, le
   debordement se repartissait en haut et en bas, et le DEBUT du recit
   sortait de l'ecran : la page s'ouvrait sur du vide. */

.visuels { display: grid; }
.visuels > * { grid-area: 1 / 1; }

/* align-items: center, et ce n'est pas cosmetique. Par defaut un conteneur
   flex ETIRE ses enfants sur l'axe secondaire : le SVG voyait sa hauteur
   ecrasee par celle de la case, et la silhouette sortait a 1495 pixels de
   haut au lieu des 520 calcules. La taille posee par le script etait
   simplement ignoree. */
.porte-silhouette .scene {
  display: flex;
  justify-content: center;
  align-items: center;
}

/* Le porte-silhouette a le MÊME fond papier, le même débordement vers le
   haut et le même fondu vers le bas que le porte-neurone. À l'acte 2, le
   neurone est effacé : son fond l'est avec lui, et le texte du parcours
   passait à découvert par dessus la silhouette et jusque sous la barre. Les
   deux dessins partagent une case, ils doivent partager son papier. */
.porte-silhouette {
  position: relative;
  z-index: 0;
  background: var(--paper);
}
.porte-silhouette::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  bottom: calc(100% - 2px);
  height: 60vh;
  background: var(--paper);
  pointer-events: none;
}
.porte-silhouette::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: 100%;
  height: var(--sp-5);
  background: linear-gradient(var(--paper), rgba(255, 255, 255, 0));
  pointer-events: none;
}
html.no-js .porte-silhouette::before, html.reduce-motion .porte-silhouette::before,
html.no-js .porte-silhouette::after,  html.reduce-motion .porte-silhouette::after { display: none; }
.silhouette { display: block; width: auto; max-width: 100%; }

/* Le temps du trajet, inscrit à côté du dessin au temps 4. En encre douce,
   comme une légende : c'est une mesure, pas le sujet. */
.chrono {
  text-align: center;
  margin: var(--sp-2) 0 0;
  font-variant-numeric: tabular-nums;
}

/* Dans les deux replis, plus rien ne s'efface : la silhouette et le neurone
   doivent donc cesser de partager une case, sinon ils se superposent. Ils
   s'empilent, l'un au-dessus de l'autre, dans l'ordre du récit. */
html.no-js .visuels,
html.reduce-motion .visuels { display: block; }
html.no-js .porte-silhouette,
html.reduce-motion .porte-silhouette { margin-bottom: var(--sp-4); }

/* Tant que le verrou n'est pas ouvert, la page ne defile pas.

   Le bloc du bouton couvre tout l'ecran en position fixe, donc la page
   pouvait defiler DERRIERE lui sans que le visiteur s'en apercoive. Il
   arrivait alors sur le bouton en ayant deja bouge, et le clic l'envoyait
   ailleurs. Bloquer le defilement rend la position de depart certaine.

   Pose sur <html> par le CSS, et jamais par le script : si le JavaScript
   echoue, la classe `entre` n'est jamais absente puisqu'elle n'est jamais
   ajoutee... donc on retire le verrou des que le script est absent. */
html.js:not(.entre) { overflow: hidden; }

/* ET RIEN DERRIERE LE CACHE NE SE LAISSE TABULER.

   Le verrou ci-dessus empeche la page de defiler, mais il n'empeche pas le
   clavier d'atteindre ce qui est dessous. Mesure avant correction : quatre
   tabulations depuis la page neuve tombaient sur << Vue entiere >>, et
   Entree envoyait le defilement a 7586 px, au milieu du recit, SANS que la
   classe `entre` soit posee. Le verrou tenait toujours, donc plus rien ne
   defilait ensuite : le visiteur au clavier etait fige au milieu du site,
   sans rien a l'ecran pour lui indiquer la sortie. Retirer les seuls
   boutons du parcours ne reglait rien, le piege se deplacait sur le bouton
   de la fin, puis sur le quiz.

   `visibility: hidden` retire de la tabulation ET de l'arbre
   d'accessibilite, et il est compris partout, ce que `inert` n'est pas sur
   les navigateurs que le projet vise. Il garde aussi la mise en page, donc
   les mesures de hauteur faites au chargement restent justes, ce que
   `display: none` aurait fausse.

   Rien ne change a l'oeil : tout ceci est deja derriere un cache opaque en
   plein ecran. Trouve par le chantier D de l'audit technique. */
html.js:not(.entre) .parcours-textes,
html.js:not(.entre) .recit .alt-text,
html.js:not(.entre) #quiz { visibility: hidden; }


/* ==========================================================================
   L'ACTE 3, LE QUIZ
   ==========================================================================
   Cinq questions en boutons radio natifs, un fieldset par question : le
   clavier les pilote aux flèches sans une ligne de script. Les questions
   s'empilent (js/quiz.js) ; sans script, tout est visible et le quiz est
   une liste de questions-réponses. Aucun score, donc aucun tableau de
   résultats à mettre en page : une question, ses trois choix, sa réponse. */

.quiz .wrap { max-width: 34rem; }
.quiz h2 { margin-bottom: var(--sp-2); }
.quiz-intro { margin-bottom: var(--sp-5); }

.quiz-question {
  border: 0;
  padding: 0;
  margin: 0 0 var(--sp-6);
  min-width: 0;
}
.quiz-question legend {
  padding: 0;
  margin-bottom: var(--sp-3);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.3;
  letter-spacing: -0.01em;
  max-width: var(--measure);
}
.quiz-question .numero { color: var(--ink-soft); }

.choix {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* L'étiquette entière est la cible, 44 px de haut au minimum, cernée
   d'encre comme les boutons du site : c'est elle que le doigt touche, pas
   le petit bouton rond. */
.choix-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  cursor: pointer;
  font-size: var(--fs-small);
  line-height: 1.35;
}
.choix-item input {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
  accent-color: var(--ink);
  cursor: pointer;
}
/* Le choix retenu s'épaissit, sans bouger la mise en page : le trait
   supplémentaire est dessiné en dedans. */
.choix-item.choisi {
  box-shadow: inset 0 0 0 1.5px var(--ink);
  font-weight: 700;
}
/* L'anneau de focus passe sur l'étiquette entière quand le navigateur sait
   le faire ; sinon, celui du bouton rond reste, c'est le filet. */
.choix-item:has(> input:focus-visible) {
  outline: 3px solid var(--c-impulse);
  outline-offset: 3px;
}
.choix-item:has(> input:focus-visible) input { outline: none; }

/* Le verdict : un mot et une icône, jamais la couleur seule. « Bonne
   réponse » reçoit le magenta en fond via .mot, le texte reste noir. */
.reponse { margin-top: var(--sp-3); }
.verdict {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 700;
  margin: 0 0 var(--sp-2);
}
.verdict svg { width: 1.3em; height: 1.3em; flex: 0 0 auto; }
.explication { margin: 0; }

/* Ce qui attend une réponse n'est pas là, et ne se tabule donc pas. La
   classe est posée par le script : sans lui, tout est visible. Le mot
   « Réponse » ne sert qu'à cette liste sans script. */
.quiz-question.attente, .quiz-fin.attente { display: none; }
html.js .sans-js { display: none; }

.quiz-fin .lead { margin-bottom: var(--sp-4); }
