/* =============================================================================
   MOTEUR DE DECK — le cadre de présentation
   À charger APRÈS ../charte/charte.css, qui reste la seule source.

   Ce fichier ne définit aucune couleur d'identité, aucune taille de police,
   aucune durée, aucune courbe : tout vient des jetons de la charte.
   Il ajoute trois choses, et rien d'autre :
     1. un cadre fixe de 1280 × 720 px, mis à l'échelle de l'écran ;
     2. le rôle des dix marches typographiques en projection ;
     3. les layouts propres à une slide.

   POURQUOI 1280 × 720. La charte plafonne à --fs-1000 (68 px). Sur un cadre
   de 1280 px, 68 px pèsent 5,3 % de la largeur — exactement le poids optique
   d'un titre sur atlas.expectpatient.com. Sur un cadre plus large il aurait
   fallu inventer des tailles hors échelle, ce que la charte interdit.
   Accessoirement, 1280 px à 96 dpi font 13,33 pouces : la largeur exacte
   d'une diapositive 16:9, donc un PDF aux dimensions standard.
   ========================================================================== */

/* =============================================================================
   1. LE CADRE
   ========================================================================== */

html, body {
  height: 100%;
  overflow: hidden;
  /* Le pourtour de la slide : l'encre de la charte, pas un gris inventé. */
  background: var(--ink);
}

/* @famille stage · hors-vocabulaire */
.stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

/* Une grille ne centre pas un enfant plus grand qu'elle : elle le cale au
   début. D'où le centrage explicite. */
/* @famille deck · hors-vocabulaire */
.deck {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1280px;
  height: 720px;
  transform: translate(-50%, -50%) scale(var(--k, 1));
  transform-origin: center center;
  background: var(--paper);
  overflow: hidden;
}

/* =============================================================================
   2. LES MARCHES, EN PROJECTION
   Les dix marches sont intactes. Seul leur RÔLE se décale : ce qui est du
   texte de lecture à 50 cm sur un écran devient une légende à 10 m sur un
   vidéoprojecteur. Équivalences sur un cadre de 13,33 pouces :
     --fs-1000  68 px = 51 pt   titre d'ouverture
     --fs-800   44 px = 33 pt   titre de slide
     --fs-700   34 px = 25 pt   chapô, texte mis en avant
     --fs-600   26 px = 20 pt   texte de lecture      ← plancher de lisibilité
     --fs-500   21 px = 16 pt   libellés de données
     --fs-200   14 px = 11 pt   repères en capitales
     --fs-100   12 px =  9 pt   sources et références
   ========================================================================== */

/* Les clamp() de la charte interpolent sur des vw : dans un cadre fixe, il
   n'y a rien à interpoler et la taille varierait avec la fenêtre. On épingle
   donc chaque titre sur une marche existante. */
/* @famille title-display · hors-vocabulaire */
.deck .title-display { font-size: var(--fs-1000); }
.deck .title-section { font-size: var(--fs-800); }
/* @famille stat-value · hors-vocabulaire */
.deck .stat-value    { font-size: var(--fs-1000); margin: 0; }

.deck .prose       { font-size: var(--fs-600); line-height: 1.5; }
/* @famille lede · hors-vocabulaire */
.deck .lede        { font-size: var(--fs-500); max-width: 62ch; }
/* @famille eyebrow · hors-vocabulaire */
.deck .eyebrow,
/* @famille kicker · hors-vocabulaire */
.deck .kicker,
/* @famille stat-label · hors-vocabulaire */
.deck .stat-label,
/* @famille scope-note · hors-vocabulaire */
.deck .scope-note  { font-size: var(--fs-200); }
.deck .source-note { font-size: var(--fs-100); }

/* Le tableau : en-têtes non collants (il n'y a pas de défilement dans une
   slide), valeurs à la marche des libellés. */
/* @famille data-table · registre nommé */
.deck .data-table th { position: static; padding: 12px 16px; font-size: var(--fs-200); }
.deck .data-table td { padding: 14px 16px; font-size: var(--fs-500); }
.deck .data-table td:first-child { font-size: var(--fs-500); }

/* La charte bascule .split en une colonne sous 980 px et à l'impression.
   Dans un cadre fixe, la fenêtre peut être étroite alors que le cadre fait
   toujours 1280 px : sans ces deux règles, les slides s'effondreraient à
   l'écran comme en PDF. */
/* @famille split · hors-vocabulaire */
.deck .split {
  min-height: 0;
  height: 100%;
  grid-template-columns: minmax(340px, 0.88fr) minmax(480px, 1.12fr);
}
@media (max-width: 980px) {
  .deck .split { grid-template-columns: minmax(340px, 0.88fr) minmax(480px, 1.12fr); }
}
@media print {
  .deck .split { grid-template-columns: minmax(340px, 0.88fr) minmax(480px, 1.12fr); }
}

/* @famille split-copy · hors-vocabulaire */
.deck .split > .split-copy  { gap: var(--space-3); padding: var(--space-8) var(--slide-marge-h); }
/* @famille split-stage · hors-vocabulaire */
.deck .split > .split-stage { padding: var(--space-8); }

/* =============================================================================
   3. LA SLIDE
   ========================================================================== */

/* Les marges de slide. Elles ne sont PAS portées par `.deck` seul : une slide
   est aussi rendue hors du deck — miniatures du rail, vue d'ensemble, établi
   de l'atelier, aperçu agrandi. Hors de portée, `var(--slide-marge-h)` ne
   résout pas, et TOUTE déclaration qui l'emploie tombe : le numéro de slide
   se retrouvait alors calé en haut à gauche, par-dessus le fil rouge.

   Tout conteneur qui accueille une slide clonée doit donc les porter. */
.deck,
.vignette__cadre,
.rail__vue,
.etabli__cadre,
.loupe__cadre {
  --slide-marge-h: 68px;   /* le plafond de --gutter dans la charte */
  --slide-marge-v: 48px;   /* 2 × --space-6                        */
}

/* @famille slide · hors-vocabulaire */
.slide {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  padding: var(--slide-marge-v) var(--slide-marge-h);
  background: var(--paper);

  /* « clip » plutôt que « hidden » : les deux rognent pareil, mais « hidden »
     fait de la slide un conteneur de défilement, qu'on peut faire défiler par
     mégarde — un focus() sur un élément hors cadre suffit, et la slide reste
     décalée sans que rien ne l'indique. Une slide est un cadre fixe : elle ne
     doit jamais pouvoir défiler. */
  overflow: clip;
}
.slide.est-active { display: flex; }



/* Les gabarits en grille doivent surpasser la spécificité de .slide.est-active,
   sinon leur display:grid est écrasé par le flex par défaut et les colonnes
   s'empilent. */
.slide.est-active.couverture,
.slide.est-active.chapitre { display: grid; }

/* Une slide bâtie sur .split ne prend pas les marges : le gabarit les porte. */
.slide[data-gabarit="split"] { padding: 0; }

/* @famille notes · hors-vocabulaire */
.contenu-prive-absent { display: none; }

/* Corps de slide : occupe ce qui reste sous le titre. */
/* @famille corps · hors-vocabulaire */
.corps {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-6);
}
.corps[data-cale="haut"] { justify-content: flex-start; }

/* --- Le chrome : filet tricolore, numéro, aplat ------------------------- */

/* Le bandeau de pied, repris du gabarit PPT. C'est la rampe de marque —
   donc le filet tricolore de la charte, la seule ornementation dégradée
   autorisée, simplement posée en pied plutôt qu'en tête.

   Il fait deux choses avec un seul objet : il signe la slide, et il indique
   l'avancement. La part parcourue est découpée au clip-path et non par une
   largeur : réduire la largeur comprimerait le dégradé et déplacerait les
   couleurs ; le découpage laisse chaque teinte à son abscisse. */
/* LA RAMPE DE MARQUE — un FOND, et non un élément.
   -------------------------------------------------------------------------
   Elle a d'abord été un <div class="bandeau"> en position absolue, calé par
   « bottom: 0 ». Il se plaçait 18 px trop bas, donc hors cadre, donc invisible
   sur toutes les slides — et la position restait fausse même après avoir
   ancré par « top ». La cause est une boucle : la position d'un enfant absolu
   se résout contre la zone de débordement de la slide, que cet enfant agrandit
   en s'y plaçant.

   Deux calques de fond suppriment le problème à la racine : pas d'élément,
   pas de bloc conteneur, pas de débordement. Le premier calque est la part
   parcourue, le second la rampe complète en filigrane. Ça s'imprime, et ça ne
   peut plus se décaler.

   La couverture et la slide finale s'en passent : leur aplat joue déjà ce rôle. */

.deck .slide {
  /* Pas de color-mix() ici : à l'intérieur d'un dégradé il fait tomber TOUTE
     la déclaration, sans le moindre avertissement — la rampe disparaissait.
     Le filigrane est donc un aplat translucide de la charte. */
  background-image:
    linear-gradient(90deg, var(--brand-primary), var(--brand-accent), var(--brand-focus)),
    linear-gradient(90deg, var(--line-strong), var(--line-strong));
  background-size: var(--avancement, 0%) 10px, 100% 10px;
  background-position: left bottom, left bottom;
  background-repeat: no-repeat;
  transition: background-size var(--dur-2) var(--ease-io);
}
.deck .slide.couverture { background-image: none; }

/* --- La référence bibliographique, en pied de slide ----------------------
   Deux objets distincts, qu'il ne faut pas confondre :

   .scope-note  le PÉRIMÈTRE d'un chiffre — champ, période, dénominateur.
                Il appartient au chiffre : il reste collé à lui, avec son
                filet d'accent.
   .source-note la RÉFÉRENCE — d'où vient la donnée, qui l'a publiée.
                C'est de l'appareil : elle se cale au pied de la slide.

   Mécanique : un .source-note enfant DIRECT de la slide se retrouve au pied
   tout seul, parce que le .corps qui le précède absorbe l'espace restant.
   Aucun positionnement absolu — la référence reste donc dans le flux, et le
   garde-fou de débordement la voit. Imbriqué dans .corps ou .split-copy, il
   reste au plus près de ce qu'il source.

   Nuance de lecture : la charte écrit « jamais en note de bas de page ». Elle
   proscrit le RENVOI — l'appel en exposant qui oblige à chercher ailleurs —
   pas la position en pied. Ici la référence est intégralement lisible sur la
   slide qu'elle source, sans renvoi. */
.slide > .source-note {
  margin: 0;
  padding-top: var(--space-3);
  padding-right: 116px;              /* dégage le numéro de slide */
  border-top: 1px solid var(--line);
}
/* Plusieurs références sur une même slide : un <div class="source-note">
   contenant des <p>. C'est le cas courant en conférence. */
.slide > .source-note > p { margin: 0 0 3px; max-width: none; font: inherit; }
.slide > .source-note > p:last-child { margin-bottom: 0; }

.slide[data-fond="navy"] > .source-note { border-top-color: rgba(255,255,255,.26); }
.slide[data-fond="tint"] > .source-note { border-top-color: var(--line-strong); }

/* Sur le gabarit canonique, il n'y a pas de « pied de slide » : la référence
   se cale en bas de la colonne de lecture. La marge automatique l'emporte sur
   le centrage vertical de .split-copy. */
.deck .split > .split-copy > .source-note:last-child {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

/* Le marqueur devant le titre, comme dans le gabarit PPT. Dimensionné en em :
   il suit la marche typographique du titre au lieu d'être une taille de plus. */
/* @famille title-section · hors-vocabulaire */
.title-section[data-marque] {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--space-3);
}
.title-section[data-marque]::before {
  content: "";
  width: .62em;
  height: .62em;
  background: var(--brand-mark-image) center / contain no-repeat;
}

.slide::after {
  content: attr(data-numero);
  position: absolute;
  right: var(--slide-marge-h);
  bottom: var(--slide-marge-v);
  color: var(--muted);
  font-size: var(--fs-100);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
}
.slide[data-nu]::after { content: none; }

/* Aplats francs. Le navy porte le texte blanc à 6,06:1. */
/* « background-color », et NON le raccourci « background » : le raccourci
   remet background-image à zéro, et il effaçait la rampe de marque posée en
   fond par « .deck .slide ». Le défaut ne se voyait que sur les slides
   portant un aplat — c'est-à-dire la plupart. */
.slide[data-fond="navy"] {
  background-color: var(--brand-primary);
  color: var(--paper-bright);
}
.slide[data-fond="navy"] :where(.title-display, .title-section, .stat-value) { color: var(--paper-bright); }
/* Le turquoise ne donne que 3,19:1 sur le navy : admis pour un grand titre
   (>= 24 px), jamais pour un repère de 14 px. Les surtitres restent donc
   blancs, et le turquoise ne sert plus qu'en filet. */
.slide[data-fond="navy"] :where(.eyebrow, .kicker, .lede, .source-note, .scope-note) { color: rgba(255,255,255,.86); }
.slide[data-fond="navy"] .scope-note { border-left-color: var(--brand-focus); }

/* La nuance du titre : « la goutte claire du marqueur », 3,59:1 — conforme
   pour du grand texte, et elle conserve le geste en deux temps de la charte. */
.slide[data-fond="navy"] .title-display span { color: var(--brand-focus-light); }
.slide[data-fond="navy"]::after { color: rgba(255,255,255,.6); }

.slide[data-fond="tint"] { background-color: var(--surface-tint); }
/* =============================================================================
   4. LAYOUTS
   ========================================================================== */

/* --- 4.1 Couverture ------------------------------------------------------
   La structure du gabarit PPT : un aplat porteur du titre, une scène à côté
   pour l'image. Le dégradé d'origine est remplacé par un aplat franc — la
   charte n'autorise qu'une seule ornementation dégradée, le filet tricolore,
   qu'on pose ici sur la couture entre les deux moitiés.                     */
/* @famille couverture · hors-vocabulaire */
.couverture {
  /* Pas de display ici : il est posé par .slide.est-active.couverture, sans
     quoi la couverture resterait affichée sur toutes les autres slides. */
  grid-template-columns: var(--couv-part, 0.86fr) 1.14fr;
  padding: 0;
  gap: 0;
}

.couverture__panneau {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--space-4);
  padding: var(--slide-marge-v) var(--space-8) var(--slide-marge-v) var(--slide-marge-h);
  background: var(--brand-primary);
  color: var(--paper-bright);
}
.couverture__panneau .title-display { color: var(--paper-bright); }
.couverture__panneau .title-display span { color: var(--brand-focus-light); }

/* @famille identite-hote · hors-vocabulaire */
.identite-hote,
.couverture__identite-hote {
  display: block;
  max-width: 100%;
  object-fit: contain;
}
.couverture__panneau .kicker { color: rgba(255,255,255,.86); }

/* Le filet tricolore descend la couture. */
.couverture__panneau::after {
  content: "";
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg,
              var(--brand-focus), var(--brand-accent), var(--brand-primary));
}

.couverture__scene {
  position: relative;
  display: grid;
  place-items: center;
  background: var(--surface-tint);
  overflow: hidden;
}
.couverture__scene > img[data-illustration] {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.couverture__scene .releve a { color: var(--brand-primary); }
/* Sans illustration, la scène porte le marqueur — l'asset de la marque,
   pas un ornement inventé. */
.couverture__scene > .couverture__filigrane {
  width: 46%;
  opacity: .17;
}

.couverture__meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid rgba(255,255,255,.32);
  color: rgba(255,255,255,.86);
  font-size: var(--fs-200);
  line-height: 1.5;
}
.couverture__meta strong { color: var(--paper-bright); font-weight: 700; }

/* Le lockup horizontal. Les deux SVG livrés contiennent le bandeau
   « EXPÉRIENCES ET ATTENTES DE CHAQUE PATIENT », alors que la charte le
   réserve au logo de pied — et à cette taille il devient une bavure turquoise
   illisible, précisément ce qu'elle interdit. On recadre donc le fichier sur
   sa définition : le lockup seul. (Le bandeau démarre à y ≈ 270 des 338
   unités du viewBox.) */
.couverture__marque {
  position: absolute;
  right: var(--slide-marge-h);
  bottom: var(--slide-marge-v);
  z-index: 2;
  display: block;
  width: var(--marque-l, 190px);
  height: calc(var(--marque-l, 190px) * 250 / 1800);
  overflow: hidden;
}
.couverture__marque img { display: block; width: 100%; height: auto; }

/* Le logo de pied, lui, doit être assez grand pour que le bandeau se lise. */
.couverture__pied {
  position: absolute;
  left: var(--slide-marge-h);
  top: var(--slide-marge-v);
  display: block;
  width: 240px;
  height: auto;
}

/* --- 4.2 Ouverture de partie ---------------------------------------------
   Le gabarit PPT posait un grand motif décoratif. On garde l'ampleur mais on
   la confie à la typographie — « la profondeur vient de la hiérarchie
   typographique, pas de l'élévation » — et on rend la slide utile : elle
   montre où l'on en est dans le déroulé. Le plan est construit tout seul à
   partir des ouvertures de partie du deck (voir deck.js).                   */
/* @famille chapitre · hors-vocabulaire */
.chapitre {
  /* idem : le display vient de .slide.est-active.chapitre. */
  grid-template-columns: 1.25fr 0.75fr;
  align-items: center;
  gap: var(--space-8);
}

.chapitre__titre { position: relative; z-index: 2; }
.chapitre__titre .title-section { font-size: var(--fs-900); }

/* Le numéro, en filigrane, entièrement contenu dans le cadre : en pagination
   Chrome supprime ce qui déborde, et le numéro disparaissait du PDF. */
/* L'ATTERRISSAGE emprunte la mise en page de la porte de partie et rien
   d'autre : ni numéro, ni filigrane, ni place dans le compte. Il ferme les
   parties au lieu d'en ouvrir une. */
/* @famille atterrissage · verbe ÉNONCER · registre nommé */
.chapitre.atterrissage .chapitre__numero,
.chapitre.atterrissage .chapitre__filigrane { display: none !important; }

.chapitre__numero {
  position: absolute;
  left: var(--slide-marge-h);
  bottom: var(--slide-marge-v);
  z-index: 1;
  color: rgba(255, 255, 255, .13);
  font-size: 190px;
  font-weight: 600;
  letter-spacing: -0.062em;
  line-height: 0.8;
  pointer-events: none;
  user-select: none;
}

/* --- Le gabarit BANDEAU — la transition de partie ------------------------
   Demandé par Nicolas le 25 août : « j'aimerais qu'il y ait une différence
   plus marquée entre les slides de conclusion de partie et de transition de
   nouvelle partie ; ici les deux sont à fond bleu, ce n'est pas hyper
   lisible ». Repris de son gabarit PowerPoint : fond CLAIR, la goutte de
   marque en grand à gauche, et un bandeau de la rampe qui traverse la slide
   avec le titre en blanc.

   La règle qui en sort, et qui vaut pour tout le deck : LA TRANSITION EST
   CLAIRE, LA CONCLUSION EST SOMBRE. On entre dans une partie sur du papier ;
   on en sort sur du navy plein. Deux gestes opposés, impossibles à confondre
   au fond d'une salle.                                                     */
.chapitre[data-gabarit="bandeau"] {
  grid-template-columns: 1fr;
  align-content: center;
  background-color: var(--paper);
  overflow: hidden;
}
/* La goutte : le marqueur de la charte, en grand, débordant à gauche. */
.chapitre[data-gabarit="bandeau"]::before {
  content: "";
  position: absolute;
  left: -5%;
  top: -22%;
  width: 38%;
  aspect-ratio: 1;
  background-image: var(--brand-mark-image);
  background-repeat: no-repeat;
  background-size: contain;
  opacity: .26;
  z-index: 0;
  pointer-events: none;
}
/* Le bandeau : il traverse toute la slide, marges comprises. */
.chapitre[data-gabarit="bandeau"] .chapitre__titre {
  position: relative;
  z-index: 2;
  margin-inline: calc(var(--slide-marge-h) * -1);
  padding: var(--space-8) var(--slide-marge-h);
  background-image: linear-gradient(90deg,
      var(--brand-primary) 0%, var(--brand-accent) 62%, var(--brand-focus) 100%);
}
.chapitre[data-gabarit="bandeau"] .chapitre__titre .kicker { color: rgba(255, 255, 255, .82); }
.chapitre[data-gabarit="bandeau"] .chapitre__titre .title-section { color: var(--paper-bright); }
/* CE QUE LE MOTEUR REMPLIT — deux emplacements, aucun style.
   `.chapitre__rang` porte « Première », « Deuxième »… et `.chapitre__compte`
   le NOMBRE de portes en toutes lettres. Ils héritent de leur voisinage, et
   c'est voulu : ce sont des MOTS dans une phrase, pas des éléments. Ils ont
   une règle parce que le contrôle exige qu'une classe employée en ait une —
   et parce qu'un lecteur qui les croise dans le balisage doit trouver ici de
   quoi comprendre qu'ils se remplissent seuls (`numeroterChapitres`). */
.chapitre__rang, .chapitre__compte { font: inherit; color: inherit; }

/* LA PROMESSE DE LA PORTE — ce que la partie va ouvrir, en une ligne.
   Posée le 26 août, après le redispatch : la quatrième raison a absorbé le
   travail contraceptif, le remboursement et l'arrêt, et son titre — « je ne
   décide pas seule » — ne les annonçait plus. Une porte qui ne prépare pas ce
   qu'elle ouvre laisse la salle découvrir en cours de route.
   Elle vit DANS le bandeau, sous le titre, en blanc bas : c'est une promesse,
   pas un second titre. */
.chapitre[data-gabarit="bandeau"] .chapitre__promesse {
  position: relative;
  z-index: 2;
  max-width: 54ch;
  margin: var(--space-3) 0 0;
  color: rgba(255, 255, 255, .84);
  font-size: var(--fs-400);
  line-height: 1.4;
}
/* LE NUMÉRO REVIENT — c'est ce qui distingue les quatre portes.
   Elles étaient la MÊME slide quatre fois : même bandeau, même goutte, même
   plan, et pour seule différence la ligne surlignée dans une liste de 14 px.
   Sur la planche de contact, 08, 17, 34 et 40 sont indiscernables — or ce
   sont les quatre repères de navigation du deck.
   Le moteur savait déjà le faire : `.chapitre__numero` existe à 190 px, mais
   ce gabarit l'éteignait. Il revient DANS le bandeau, à droite, en blanc très
   bas — une masse, pas une information, puisque le titre dit déjà laquelle.
   Rien n'est ajouté au vocabulaire : une règle est retirée. */
/* PAS DANS LE BANDEAU : son extrémité droite est turquoise clair, et du blanc
   à 19 % y disparaît — essayé, mesuré à l'écran, écarté. Le numéro se pose sur
   le PAPIER, en bas à droite, où la page est vide et où le navy a du contraste.
   Il équilibre du même coup la composition : le plan tient la gauche. */
.chapitre[data-gabarit="bandeau"] .chapitre__numero {
  display: block;
  left: auto;
  right: var(--slide-marge-h);
  bottom: calc(var(--slide-marge-v) + 22px);   /* au-dessus du folio */
  color: var(--brand-primary);
  opacity: .11;
  font-size: 210px;
  z-index: 1;
}
/* Le plan reprend l'encre du papier : il quitte le navy. */
.chapitre[data-gabarit="bandeau"] .plan {
  position: relative;
  z-index: 2;
  margin-top: var(--space-8);
}
.chapitre[data-gabarit="bandeau"] .plan li { color: var(--muted); }
.chapitre[data-gabarit="bandeau"] .plan li b { color: var(--brand-accent); }
.chapitre[data-gabarit="bandeau"] .plan li[aria-current] { color: var(--ink); }
.chapitre[data-gabarit="bandeau"] .plan li[aria-current] b { color: var(--brand-primary); }
/* LE PLAN ACHEVÉ, sur l'atterrissage. Aucune entrée n'est « en cours » : les
   trois sont derrière. Sans cette règle elles restent toutes à l'opacité des
   « pas encore », ce qui dit exactement l'inverse de la slide. */
.chapitre[data-gabarit="bandeau"] .plan[data-acheve] li { color: var(--ink); }
.chapitre[data-gabarit="bandeau"] .plan[data-acheve] li b { color: var(--brand-primary); }
@media print {
  .chapitre[data-gabarit="bandeau"] .chapitre__titre { print-color-adjust: exact; }
}

/* Variante : le marqueur en filigrane, à la manière du gabarit PPT. */
.chapitre[data-motif="marque"] .chapitre__numero { display: none; }
.chapitre__filigrane {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 46%;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.chapitre__filigrane img {
  position: absolute;
  right: -12%;
  top: 50%;
  width: 118%;
  transform: translateY(-50%);
  opacity: .13;
}

/* Le plan : toutes les parties restent lisibles, seule la courante est
   marquée. On ne les estompe pas sous le seuil de contraste. */
.plan {
  position: relative;
  z-index: 2;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.plan li {
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: var(--space-3);
  align-items: baseline;
  padding: var(--space-2) 0 var(--space-2) var(--space-3);
  border-left: 2px solid transparent;
  color: rgba(255,255,255,.72);
  font-size: var(--fs-200);
  line-height: 1.4;
}
.plan li b {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.plan li[aria-current] {
  border-left-color: var(--brand-focus);
  color: var(--paper-bright);
  font-weight: 700;
}

/* --- 4.2 bis. Énoncé — la slide qui ne porte qu'une phrase ----------------
   .title-display est le titre d'OUVERTURE : interlignage 0,92 et interlettrage
   -0,062 em, taillés pour une ou deux lignes très grandes. Employé pour une
   phrase de quatre lignes, il écrase le bloc. L'énoncé garde le geste en deux
   temps de la charte — l'affirmation puis la nuance — avec une respiration.  */
/* @famille enonce · registre nommé */
.enonce {
  margin: 0 auto;                     /* l'assertion se centre dans sa slide */
  max-width: 26ch;
  color: var(--ink);
  /* Georgia, pas Inter : une assertion est de l'ÉDITORIAL. La passe design
     du 25 août a trouvé trois slides où l'Inter gras criait plus fort que
     les titres — la voix du deck doit être une. */
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-800);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.14;
  text-wrap: balance;
}
.enonce > span {
  display: block;
  margin-top: var(--space-4);
  max-width: 34ch;
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: .5em;
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.32;
}
.enonce[data-taille="grand"] { font-size: var(--fs-900); max-width: 22ch; }

/* ═══ REGISTRE · LA RESPIRATION ══════════════════════════════════════════
   Établi le 26 août, sur une mesure. L'encre des 47 pages du deck, relevée
   bande par bande sur le tirage, tient entre 12 et 20 % — PLAT, de la
   première page à la dernière. Une salle qui reçoit quarante-sept pages de
   même densité n'a aucun moyen de savoir laquelle porte l'argument : tout
   pèse pareil, donc rien ne pèse.

   Ce registre creuse — mais pas en encre, et la mesure l'a corrigé : à 68 px
   une assertion en POSE plus qu'à 44. Ce qu'il retire, c'est le NOMBRE DE
   CHOSES sur la page. Un titre, deux niveaux de texte, une figure, une chute
   et une source deviennent une phrase et une glose. Le vide ne vient pas de
   moins d'encre, il vient de moins d'objets à trier.
   Il ne se donne qu'aux assertions de la colonne vertébrale — quatre dans ce
   deck — et sa rareté EST son sens : s'il servait dix fois, il ne dirait
   plus rien.

   POURQUOI --fs-1000. C'était la seule marche de l'échelle employée UNE fois
   dans tout le deck, sur la couverture. Une marche qu'on n'emploie jamais ne
   fait pas partie de l'échelle — elle la décore. Elle revient ici, et le
   geste est le même qu'à l'ouverture : poser une phrase et laisser la salle
   la recevoir. La charte n'est pas élargie, elle est enfin employée.

   LE FILET DE TURQUOISE ne code rien : c'est le tiret du kicker, présent sur
   chaque page du deck, qui s'allonge. La continuité est déjà là ; on la rend
   visible à l'échelle de la planche de contact, où se voit la colonne
   vertébrale. */
.slide[data-registre="respiration"] .enonce,
.slide[data-registre="respiration"] .enonce[data-taille="grand"] {
  /* SUR LA MARGE COMMUNE, pas centré. `.enonce` se centre par défaut, ce qui
     décalait l'assertion de 120 px vers la droite par rapport au kicker et
     aux titres de TOUTES les autres pages. Sur la planche de contact, la
     respiration paraissait alors être une autre mise en page, au lieu d'être
     la même page qui respire. L'alignement est ce qui fait tenir un deck
     ensemble ; c'est la densité qui doit varier, pas la marge. */
  margin-inline: 0;
  font-size: var(--fs-1000);
  max-width: 19ch;
  line-height: 1.04;
  letter-spacing: -0.035em;
}
/* DEUX MARCHES, ET LA RÈGLE QUI LES SÉPARE : une respiration tient en TROIS
   lignes. À cinq, la phrase remplit le cadre et le registre s'annule lui-même
   — c'est ce qu'a montré « les arrêts et les changements » à 68 px, qui
   n'avait plus un pouce de vide. La longueur de la phrase décide, pas le
   goût : au-delà de ~90 signes, la marche d'en dessous. */
.slide[data-registre="respiration"][data-souffle="long"] .enonce,
.slide[data-registre="respiration"][data-souffle="long"] .enonce[data-taille="grand"] {
  font-size: var(--fs-900);
  max-width: 24ch;
}
.slide[data-registre="respiration"] .enonce::before {
  content: "";
  display: block;
  width: 64px;
  height: 2px;
  margin-bottom: var(--space-6);
  background-color: var(--brand-focus);
}
/* La glose n'est pas DANS la phrase : elle vient après, et plus bas. Collée
   sous l'assertion elle se lisait comme une sous-titre, donc comme une
   seconde phrase — deux voix pour une seule idée. */
.slide[data-registre="respiration"] .enonce > span {
  display: block;
  margin-top: var(--space-8);
  max-width: 46ch;
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-300);
  font-weight: 400;
  font-style: normal;
  letter-spacing: 0;
  line-height: 1.5;
  color: var(--muted);
}
.slide[data-registre="respiration"][data-fond="navy"] .enonce > span {
  color: rgba(255,255,255,.72);
}
.slide[data-registre="respiration"][data-fond="navy"] .enonce::before {
  background-color: var(--brand-focus-light);
}
.couverture .enonce { color: var(--paper-bright); }
.slide[data-fond="navy"] .enonce { color: var(--paper-bright); }
.slide[data-fond="navy"] .enonce > span { color: var(--brand-focus-light); }

/* --- 4.2 ter. Le fil rouge -----------------------------------------------
   Le titre de la partie, répété en tête de chaque slide de contenu. C'est la
   convention des decks de conférence : on sait toujours où l'on est, même
   loin d'un intertitre. Il est posé par deck.js à partir des slides
   .chapitre — donc il ne peut pas diverger du plan.
   Un .eyebrow écrit à la main sur la slide le remplace.                     */
.fil {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-200);
  font-weight: 800;
  letter-spacing: 0.14em;
  line-height: 1.4;
  text-transform: uppercase;
}
.fil::before {
  content: "";
  width: 18px;
  height: 2px;
  flex: none;
  background: var(--brand-focus);
}
/* Le statut de la slide — « Pour aller plus loin », « Repère pour la
   consultation » — se pose au bout du fil. Une seule ligne en tête, deux
   informations : où l'on est, et ce que cette slide est. */
.fil em {
  margin-left: auto;
  padding-left: var(--space-4);
  color: var(--brand-accent);
  font-style: normal;
}
.fil em:has(.fil__preuve) {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.fil__preuve {
  color: var(--muted);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: none;
}
.fil__statut { color: var(--brand-primary); }
.slide[data-fond="navy"] .fil { color: rgba(255,255,255,.72); }
.slide[data-fond="navy"] .fil em { color: var(--brand-focus); }
.slide[data-fond="navy"] .fil__preuve { color: rgba(255,255,255,.72); }
.slide[data-fond="navy"] .fil__statut { color: var(--paper-bright); }
.slide[data-fond="tint"] .fil { color: var(--muted); }

/* --- 4.2 quater. Icônes ---------------------------------------------------
   Pas de librairie : la charte interdit toute dépendance externe et impose
   des schémas en SVG inline. Le jeu est donc injecté par deck.js sous forme
   de <symbol>, et chaque icône est un <use>. Elles héritent de currentColor,
   s'impriment, et pèsent quelques kilo-octets.                              */
/* @famille ico · hors-vocabulaire */
.ico {
  width: 1em;
  height: 1em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.14em;
}
.ico--bloc {
  display: block;
  width: 30px;
  height: 30px;
  margin-bottom: var(--space-3);
  color: var(--froid, var(--brand-accent));
  stroke-width: 1.6;
}
/* @famille volet · verbe JUXTAPOSER · registre nommé */
.volet[data-ton="chaud"] .ico--bloc { color: var(--chaud, var(--brand-caution)); }
.volet[data-ton="retrait"] .ico--bloc { color: var(--muted); }
.slide[data-fond="navy"] .ico--bloc { color: var(--brand-focus); }

/* --- 4.2 quater bis. Le picto composé -------------------------------------
   Deux icônes qui font UN signe : la base porte le domaine, la pastille
   porte l'événement. Réponse au constat de l'audit (« du marqueur au
   lexique ») : l'icône cesse d'être une puce décorative quand la composition
   dit ce qu'aucune des deux ne dit seule. Demandé par Nicolas le 25 août. */
/* @famille compose · hors-vocabulaire */
.compose {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 64px;
  height: 64px;
}
.compose__base { width: 50px; height: 50px; color: var(--froid, var(--data-cool)); stroke-width: 1.5; }
.compose__badge {
  position: absolute;
  right: -6px;
  bottom: -2px;
  box-sizing: border-box;
  width: 30px;
  height: 30px;
  padding: 5px;
  background-color: var(--paper-bright);
  border: 1.5px solid var(--neutre, var(--data-neutral));
  border-radius: 50%;
  color: var(--brand-primary);
  stroke-width: 2;
}
.compose[data-ton="chaud"] .compose__badge { color: var(--chaud, var(--data-warm)); }
.compose[data-ton="retrait"] { opacity: .45; }

/* L'unité légendée. Le couple signe + libellé reste obligatoire — comme au
   lexique, une icône ne remplace jamais le mot. */
.compose-item {
  display: grid;
  justify-items: center;
  gap: var(--space-4);
  text-align: center;
  min-width: 10.5em; /* le libellé est souvent plus large que le signe : sans
                       plancher, les mots de deux unités voisines se touchent */
}
.compose-item > small { font-size: var(--fs-200); font-weight: 650; color: var(--ink); max-width: 20ch; }

/* --- 4.2 quater ter. La une — citer le débat public sans capture ----------
   Le moteur n'embarque aucune image bitmap : quand une slide doit montrer
   « ce que dit la presse », elle le TYPOGRAPHIE. Un filet, un surtitre, la
   phrase en serif, la source. C'est une citation mise en page — aucun logo,
   aucune imitation de la maquette d'un titre réel. */
/* @famille presse · registre nommé */
.presse {
  justify-self: center;
  margin: 0 auto;                  /* le conteneur n'est pas toujours une grille */
  max-width: 58ch;
  padding: var(--space-4) var(--space-6);
  border-block-start: 3px solid var(--ink);
  border-block-end: 1px solid var(--neutre, var(--data-neutral));
  background-color: var(--paper-bright);
}
.presse__kicker {
  font-size: var(--fs-100);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-2);
}
.presse__phrase {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  line-height: 1.25;
  color: var(--ink);
}
.presse__source { margin-top: var(--space-2); font-size: var(--fs-200); color: var(--muted); }

/* --- 4.2 quater ter bis. La pancarte (CITER) ------------------------------
   Quand le débat public répond par un slogan, la slide le CITE : le carton
   redessiné, le slogan lettré, la source en dessous. On ne reproduit pas la
   photographie de presse — le cadrage est l'œuvre du photographe — on cite
   l'ÉNONCÉ, court et attribué, comme un verbatim. */
/* @famille pancarte · registre nommé */
.pancarte { display: block; width: min(500px, 100%); margin: 0 auto; }
.pancarte__carton { fill: var(--paper-bright); stroke: var(--ink); stroke-width: 3; }
.pancarte__main { fill: var(--ink); }
.pancarte__mot {
  font-weight: 900;
  letter-spacing: .02em;
  fill: var(--ink);
}

/* --- 4.2 quater quinquies. La photographie documentaire -------------------
   La doctrine évolue (décision de Nicolas, 25 août 2026) : « aucun bitmap »
   devient « aucun bitmap DÉCORATIF ». La photographie documentaire est
   admise quand elle EST le document du propos — une pancarte de cortège,
   une affiche institutionnelle, une une datée — aux conditions du moteur :
   fichier LOCAL (assets/photos/, jamais une URL distante), crédit
   obligatoire sous l'image, droits vérifiés par l'auteur du deck. Jamais en
   fond de slide, jamais en décor. */
.photo { margin: 0; display: grid; gap: var(--space-2); justify-items: center; }
.photo > img {
  display: block;
  max-width: 100%;
  max-height: var(--photo-hauteur, 400px);
  border: 1px solid var(--line);
}
.photo--sobre > img { filter: grayscale(1); }   /* pour asseoir une photo criarde dans la charte */
.photo__credit { font-size: var(--fs-100); color: var(--muted); text-align: center; }

/* --- 4.2 quater sexies. Le portrait — l'unité humaine ---------------------
   Une silhouette de figures.js posée en <use>. Hors d'un schéma qui remplit
   déjà ses éléments, le portrait prend le froid — c'est un point de donnée
   humain, pas une décoration. */
/* @famille portrait · hors-vocabulaire */
.portrait { fill: var(--froid, var(--data-cool)); }

/* Quatre morphologies NEUTRES combinées aux bustes natifs quand
   l'individualité compte. Elles ne codent aucune donnée démographique ; elles
   empêchent seulement deux personnes de devenir graphiquement
   interchangeables. Un deck peut préciser le genre seulement quand sa source
   le documente — jamais en l'inférant depuis un prénom seul. */
[data-individualite] [data-personne] use {
  transform-box: fill-box;
  transform-origin: center bottom;
  transform: scale(var(--silhouette-largeur, 1), var(--silhouette-hauteur, 1));
}
[data-individualite] [data-personne][data-variante="a"] { --silhouette-largeur: .90; --silhouette-hauteur: 1; }
[data-individualite] [data-personne][data-variante="b"] { --silhouette-largeur: 1.06; --silhouette-hauteur: .96; }
[data-individualite] [data-personne][data-variante="c"] { --silhouette-largeur: .94; --silhouette-hauteur: 1.07; }
[data-individualite] [data-personne][data-variante="d"] { --silhouette-largeur: 1.04; --silhouette-hauteur: 1.03; }

/* La teinte est un renfort REDONDANT d'identification individuelle. Elle
   prélève quatre marches de la palette de données, ne remplace jamais la
   forme et ne code ni groupe, ni genre, ni statut. */
[data-individualite] [data-personne] { fill: var(--silhouette-encre, var(--froid, var(--data-cool))); }
[data-individualite] [data-personne][data-teinte="1"] { --silhouette-encre: var(--seq-4); }
[data-individualite] [data-personne][data-teinte="2"] { --silhouette-encre: var(--seq-3); }
[data-individualite] [data-personne][data-teinte="3"] { --silhouette-encre: var(--data-cool); }
[data-individualite] [data-personne][data-teinte="4"] { --silhouette-encre: var(--seq-2); }

/* --- 4.2 quater quater. L'illustration au trait ---------------------------
   Vectorielle, hors ligne, recolorée à la charte (provenance et licence :
   assets/BIBLIO.md). Une scène là où la forme abstraite ne suffit plus ;
   jamais en décor. */
.illustration { display: grid; justify-items: center; align-content: start; gap: var(--space-4); }
.illustration svg { width: 100%; height: auto; max-height: 290px; }
.illustration figcaption {
  font-size: var(--fs-200);
  color: var(--muted);
  text-align: center;
  max-width: 42ch;
}

/* --- 4.2 quinquies. Paroles — ce que disent les patientes -----------------
   Une bulle par phrase, avec sa queue et la personne qui parle. Pour les
   verbatims courts que l'on veut donner à entendre plutôt qu'à lire.        */
/* @famille paroles · registre nommé */
.paroles {
  display: grid;
  grid-template-columns: repeat(var(--paroles, 2), minmax(0, 1fr));
  gap: var(--space-8) var(--space-6);
}
/* @famille parole · verbe CITER · registre nommé */
.parole { margin: 0; }
/* LE JALON DANS UNE LÉGENDE. Trois bulles, et une seule conséquence à
   désigner — « aucun des dix thèmes ne le porte ». Sans cette règle,
   `data-jalon` était compté par le contrôle et rendu en encre ordinaire :
   la marque existait au code et pas à l'écran. Vu au tirage, le 27 août. */
.parole figcaption [data-jalon] { color: var(--chaud); font-weight: 800; }
.parole figcaption > .ico { flex: none; margin-top: 2px; }
.parole__bulle {
  position: relative;
  padding: var(--space-4) var(--space-6);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--paper-bright);
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.4;
}
/* La queue : deux triangles superposés, pour garder le filet visible. */
.parole__bulle::before,
.parole__bulle::after {
  content: "";
  position: absolute;
  left: 30px;
  width: 0;
  height: 0;
  border: 11px solid transparent;
  border-top-color: var(--line-strong);
  border-bottom: 0;
}
.parole__bulle::before { bottom: -11px; }
.parole__bulle::after  { bottom: -10px; border-top-color: var(--paper-bright); }

/* Une parole portée par une figure : la personne à gauche, ce qu'elle dit à
   droite. La queue de la bulle pointe alors vers elle, plus vers le bas. */
.parole[data-figure] {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-4);
  align-items: center;
}
.parole__personne { display: block; width: 96px; height: 96px; }
.parole[data-figure] .parole__bulle::before,
.parole[data-figure] .parole__bulle::after {
  left: -11px;
  bottom: auto;
  top: 50%;
  margin-top: -11px;
  border: 11px solid transparent;
  border-right-color: var(--line-strong);
  border-left: 0;
}
.parole[data-figure] .parole__bulle::after { left: -10px; border-right-color: var(--paper-bright); }
.parole[data-figure] figcaption { grid-column: 2; margin: var(--space-2) 0 0; }

/* LA LÉGENDE S'ALIGNE EN TÊTE, PAS AU MILIEU, et son texte tient en UN
   morceau. En `center` avec plusieurs enfants, chaque fragment de légende
   devenait un item de flex qui se repliait pour son compte : à trois
   colonnes, « LE RESTE À CHARGE » sortait sur trois lignes en escalier.
   Mesuré au tirage le 27 août. Le picto reste en tête, le texte coule. */
.parole figcaption {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: var(--space-4) 0 0 var(--space-3);
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.parole figcaption .ico { width: 22px; height: 22px; color: var(--brand-accent); }
.parole[data-ton="chaud"] figcaption .ico { color: var(--brand-caution); }

/* --- 4.3 Repères chiffrés ------------------------------------------------
   La charte l'exige : un chiffre ne paraît jamais sans son périmètre ni sa
   source, visibles. Le gabarit les rend structurellement obligatoires.       */
/* @famille chiffres · registre nommé */
.chiffres {
  display: grid;
  grid-template-columns: repeat(var(--colonnes, 3), minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border-block: 1px solid var(--line);
}
/* @famille chiffre · hors-vocabulaire */
.chiffre {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-6);
  background: var(--paper-bright);
}
.chiffre .stat-value { font-size: var(--fs-800); }
.chiffre .stat-label { color: var(--muted); }

/* --- 4.4 Verbatim et citation -------------------------------------------- */
/* @famille verbatim · registre nommé */
.verbatim { justify-content: center; }

.verbatim blockquote {
  max-width: 46ch;
  margin: 0;
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-700);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.32;
}
.verbatim blockquote::before { content: "« "; color: var(--brand-accent); }
.verbatim blockquote::after  { content: " »"; color: var(--brand-accent); }

.verbatim figcaption {
  margin-top: var(--space-6);
  padding-left: var(--space-3);
  border-left: 2px solid var(--brand-primary);
  color: var(--muted);
  font-size: var(--fs-200);
  line-height: 1.45;
}
.verbatim figcaption strong { color: var(--ink); font-weight: 700; }

/* LE VERBATIM LONG — une contribution écrite n'est pas une phrase entendue.
   `.verbatim` est taillé pour une parole courte qui occupe la slide : --fs-700
   sur 46 caractères. Une réponse libre de quatre cent soixante signes y ferait
   dix lignes énormes sur une slide qui porte déjà un titre, un chapô et une
   source. Même forme, même guillemets, une mesure d'écrit. Ajouté le 27 août. */
.verbatim--long blockquote {
  max-width: 68ch;
  font-size: var(--fs-400);
  letter-spacing: 0;
  line-height: 1.5;
}
.verbatim--long figcaption { margin-top: var(--space-4); }

/* --- 4.4 bis. La citation analysée — un contrat, pas un effet local -------
   Bulle sans guillemets manuels, attribution sous la parole, source au pied,
   segment décisif pointé et signe qui illustre l'IDÉE. `data-citation`
   distingue l'analytique du documentaire ; verifier.py contrôle la structure. */
/* @famille citation-pointee · verbe CITER · registre nommé */
.citation-pointee blockquote {
  max-width: 62ch;
  font-size: var(--fs-500);
  line-height: 1.42;
  text-wrap: pretty;
}
.citation-pointee figcaption { margin-top: var(--space-4); }
.citation-pointee .surligneur[data-ligne] { white-space: nowrap; }

.slide.verbatim > .corps > figure,
.corps.verbatim > figure { width: 100%; margin: 0; }
.slide.verbatim blockquote,
.corps.verbatim blockquote {
  hanging-punctuation: first last;
  text-wrap: pretty;
}
.slide.verbatim figcaption,
.corps.verbatim figcaption { font-size: var(--fs-300); line-height: 1.4; }
.slide.verbatim--long blockquote {
  max-width: 64ch;
  font-size: var(--fs-500);
  line-height: 1.46;
}

.paroles .parole { display: flex; flex-direction: column; }
.paroles .parole__bulle { flex: 1; text-wrap: pretty; }
.paroles .parole figcaption {
  font-size: var(--fs-200);
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.35;
  text-transform: none;
}
/* @famille citation-harmonisee · verbe CITER · registre nommé */
.citation-harmonisee .parole { position: relative; margin: 0; }
.citation-harmonisee blockquote.parole__bulle {
  width: 100%;
  margin: 0;
  text-wrap: pretty;
}
.citation-harmonisee blockquote.parole__bulle::before,
.citation-harmonisee blockquote.parole__bulle::after {
  content: "";
  color: transparent;
}
.citation-harmonisee .parole figcaption {
  margin-top: var(--space-3);
  padding-left: 0;
  border-left: 0;
  color: var(--muted);
  font-size: var(--fs-200);
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.35;
  text-transform: none;
}
.citation-harmonisee .parole figcaption strong { color: var(--ink); }

.parole--verbatim { width: min(1060px, 100%); margin-inline: auto !important; }
.parole--verbatim .parole__bulle {
  max-width: none;
  padding: var(--space-6) 104px var(--space-6) var(--space-6);
  font-size: var(--fs-500);
  line-height: 1.44;
}
.parole--verbatim[data-court] { width: min(930px, 100%); }
.parole--verbatim[data-court] .parole__bulle {
  font-size: var(--fs-600);
  line-height: 1.4;
}

/* @famille signe-verbatim · verbe CITER · registre nommé */
.signe-verbatim {
  position: absolute;
  top: 22px;
  right: 24px;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background-color: var(--surface-tint);
  color: var(--brand-primary);
}
.signe-verbatim .ico { width: 30px; height: 30px; }
.paroles .parole--illustree .parole__bulle { padding-right: 76px; }
.paroles .parole--illustree .signe-verbatim {
  top: 18px;
  right: 18px;
  width: 40px;
  height: 40px;
}
.paroles .parole--illustree .signe-verbatim .ico { width: 24px; height: 24px; }

/* @famille micro-verbatim · verbe CITER · registre nommé */
.micro-verbatim {
  position: relative;
  margin: 0;
  padding: var(--space-3) 68px var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background-color: var(--paper-bright);
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  line-height: 1.38;
  text-wrap: pretty;
}
.micro-verbatim::before,
.micro-verbatim::after {
  content: "";
  position: absolute;
  bottom: -10px;
  left: 28px;
  width: 0;
  height: 0;
  border: 10px solid transparent;
  border-top-color: var(--line-strong);
  border-bottom: 0;
}
.micro-verbatim::after { bottom: -9px; border-top-color: var(--paper-bright); }
.micro-verbatim .signe-verbatim {
  top: 50%;
  right: 14px;
  width: 36px;
  height: 36px;
  transform: translateY(-50%);
}
.micro-verbatim .signe-verbatim .ico { width: 21px; height: 21px; }
.micro-verbatim[data-compact] { padding-block: var(--space-2); font-size: var(--fs-300); }
/* @famille constat-isole · verbe ÉNONCER · registre nommé */
.constat-isole .micro-verbatim {
  margin: var(--space-6) 0 var(--space-4);
  font-size: var(--fs-400);
  line-height: 1.35;
}
/* @famille citations-chiffres · verbe DÉNOMBRER · registre nommé */
.citations-chiffres .parole__bulle {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.citations-chiffres .stat-value {
  display: block;
  color: var(--brand-primary);
  font-size: var(--fs-700);
  line-height: 1.05;
}
.citations-chiffres .stat-label { margin: 0; color: var(--muted); }
.citations-chiffres .scope-note { margin-top: var(--space-2); }
.tri__reponse .micro-verbatim { display: block; margin-top: var(--space-3); }
/* @famille verbatim-signe-inline · verbe CITER · registre nommé */
.verbatim-signe-inline {
  display: inline-block;
  width: 24px;
  height: 24px;
  margin-right: var(--space-2);
  color: var(--brand-primary);
  vertical-align: -5px;
}
/* @famille comptes-dissocies · verbe DÉNOMBRER · registre nommé */
.comptes-dissocies__lecture.citation-harmonisee { text-align: left; }
.comptes-dissocies__lecture .micro-verbatim { color: var(--ink); }
.micro-verbatim__source {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: var(--space-3) 0 0 var(--space-3);
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .06em;
}
.micro-verbatim__source .ico {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--brand-accent);
}
/* @famille opposition · registre nommé */
.opposition .micro-verbatim__source {
  margin: var(--space-3) 0 0 var(--space-3);
  color: var(--muted);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .06em;
  line-height: 1.35;
}
.citation-harmonisee .surligneur[data-pose] { background-size: 100% 38%; }

@media print {
  .citation-harmonisee .surligneur { background-size: 100% 38% !important; }
}

/* --- 4.5 bis. L'écho — la parole qui confirme, posée sous une figure ------
   CITER. Récolté le 27 août, à la troisième occurrence : la même exception
   écrite à la main sur trois slides (l'opposition du retrait, les rangs du
   glissement, le relevé du nouveau). Quand la même exception se répète, ce
   n'est plus une exception.

   Ce n'est ni un `.verbatim` (qui occupe la slide) ni une `.chute` (qui est
   la phrase de l'orateur) : c'est une voix du corpus qui vient confirmer ce
   que la figure vient de montrer, et qui doit peser MOINS qu'elle. */
/* @famille echo · registre nommé */
.echo {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  font-style: italic;
  line-height: 1.45;
}
.echo[data-pose="centre"] { max-width: 72ch; margin-left: auto; margin-right: auto; text-align: center; }

/* --- 4.5 Sommaire et « que retenir » ------------------------------------- */
/* @famille releve · registre nommé */
.releve { display: flex; flex-direction: column; }

.releve__item {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: var(--space-6);
  align-items: baseline;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line);
}
.releve__item:last-child { border-bottom: 0; }

.releve__cle {
  margin: 0;
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.12;
}
.releve__texte {
  margin: 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.42;
}
.releve__texte strong { color: var(--brand-primary); font-weight: 700; }

/* LE RELEVÉ DENSE — trois clés, mais des développements qui ÉNUMÈRENT.
   Le préset est calibré sur un développement d'une à deux lignes ; à cinq
   ou six entrées par ligne, il déborde le corps de 39 px, mesurés dans la
   page. Ce n'est pas une réduction d'échelle décorative : c'est la mesure
   d'un relevé qui liste au lieu de développer. Ajouté le 27 août 2026. */
.releve[data-dense] .releve__item { padding: var(--space-3) 0; gap: var(--space-4); }
.releve[data-dense] .releve__cle { font-size: var(--fs-500); }
.releve[data-dense] .releve__texte { font-size: var(--fs-400); }

/* La phrase qui doit rester quand tout le reste est oublié. */
/* @famille chute · registre nommé */
.chute {
  margin: 0;
  padding: var(--space-6);
  background: var(--brand-primary);
  color: var(--paper-bright);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.3;
}

/* --- LE REPÈRE — ce que la salle emporte en consultation ------------------
   Le gabarit existait DÉJÀ, dix fois, mais nulle part : la même chaîne de
   styles recopiée à la main sur chaque slide, au caractère près. Il ferme
   chaque scène des decks JSF — une thèse, puis les gestes qu'elle appelle.

   Il n'invente pas un neuvième verbe : c'est la forme de RÉCAPITULER
   adressée au praticien. `.chute` dit la phrase qu'on retient, le repère dit
   ce qu'on fait — et il occupe la slide entière, sur un aplat navy.

   La mesure du titre se resserre à 24ch sur les titres COURTS, pour les
   casser en deux lignes plutôt qu'en une longue : d'où `data-mesure`. */
/* @famille repere · registre nommé */
.repere .title-section {
  font-size: var(--fs-700);
  max-width: 26ch;
}
.repere[data-mesure="serre"] .title-section { max-width: 24ch; }
.repere .prose {
  color: rgba(255, 255, 255, .9);
  font-size: var(--fs-500);
  max-width: 62ch;
}
.repere .prose strong { color: #fff; }

/* --- Les deux réglages que la prose écrivait à la main --------------------
   Vingt et une slides portaient `style="font-size:var(--fs-500)"` faute d'un
   modificateur, et quatre une chaîne de centrage recopiée. Un texte qu'on ne
   peut pas régler par la charte se règle contre elle. */
/* @famille prose · hors-vocabulaire */
.prose[data-taille="grand"] { font-size: var(--fs-500); }
.prose[data-pose="centre"] {
  text-align: center;
  max-width: 60ch;
  margin: 0 auto;
}
/* Même vocabulaire que le repère : une mesure plus serrée casse la phrase
   là où l'auteur l'a voulu. Trois slides la demandent — normaliser à 60ch
   déplaçait leur texte de 29,6 points, ce qu'un diff de PDF a vu et que
   personne n'aurait vu autrement. */
.prose[data-pose="centre"][data-mesure="serre"] { max-width: 56ch; }

/* --- 4.6 Liens d'intérêts ------------------------------------------------ */
/* @famille interets · verbe RÉCAPITULER · registre nommé */
.interets { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-8); }
.interets__bloc { padding-top: var(--space-3); border-top: 2px solid var(--brand-accent); }
.interets__categorie {
  margin: 0 0 var(--space-3);
  color: var(--muted);
  font-size: var(--fs-200);
  font-weight: 800;
  letter-spacing: 0.08em;
  line-height: 1.35;
  text-transform: uppercase;
}
.interets__liste { margin: 0; padding: 0; list-style: none; }
.interets__liste li {
  padding: var(--space-1) 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.4;
}
.interets__note {
  grid-column: 1 / -1;
  padding: var(--space-4) var(--space-6);
  border-left: 3px solid var(--brand-caution);
  background: var(--paper-bright);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.4;
}
.interets__note strong { color: var(--brand-primary); font-weight: 700; }

/* =============================================================================
   5. ÉCHELLE D'ADHÉSION
   Extension de données, sur le modèle de celle de la charte et pour la même
   raison : une échelle d'adhésion est une POLARITÉ, elle exige une opposition
   chaud / froid, et les couleurs d'identité n'encodent jamais une donnée.

   Deux valeurs viennent de la charte (#2670cb divergent positif,
   #eb6834 pôle chaud) ; #d4d3cd est son « non classable ». Les deux paliers
   tièdes sont dérivés, puis mesurés.

   CIEDE2000 · protanopie Viénot-Brettel-Mollon (1999) ; seuil ΔE >= 15 :
     tout à fait / plutôt d'accord      ΔE 27,8 · protanopie 28,8
     plutôt d'accord / plutôt pas       ΔE 29,6 · protanopie 25,6
     plutôt pas / pas du tout           ΔE 23,0 · protanopie 25,5
   Paire la plus proche : ΔE 23,0. Texte dans le segment : 4,9:1 au pire.

   RÉSERVE MESURÉE : le NSP (#d4d3cd) et « plutôt pas d'accord » (#f2bfae)
   tombent à ΔE 7,9 en protanopie. C'est admis ici parce que le NSP n'est
   JAMAIS dans la barre empilée — dénominateur différent — qu'il en est séparé
   par un filet, et qu'il porte son libellé en toutes lettres : la couleur
   n'est pas le seul véhicule de l'information.
   ========================================================================== */

/* @famille likert · registre nommé */
.likert {
  --adh-4: #2670cb;   /* tout à fait d'accord  — divergent positif, charte  */
  --adh-3: #a9c6e2;   /* plutôt d'accord       — dérivé, mesuré             */
  --adh-2: #f2bfae;   /* plutôt pas d'accord   — dérivé, mesuré             */
  --adh-1: #eb6834;   /* pas du tout d'accord  — pôle chaud, charte         */
  --adh-nsp: #d4d3cd; /* ne se prononce pas    — « non classable », charte  */

  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.likert__ligne { display: grid; gap: var(--space-2); }

.likert__intitule {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.35;
}

.likert__piste {
  display: grid;
  /* Colonne NSP de largeur FIXE : en « auto » elle variait avec le nombre de
     chiffres, les barres n'avaient plus la même longueur d'une ligne à
     l'autre et leur 100 % ne s'alignait plus — les comparer devenait faux. */
  grid-template-columns: 1fr 96px;
  align-items: stretch;
  gap: var(--space-6);
  height: 34px;
}

/* Aucun aplat de la palette ne tient 3:1 contre le fond de scène : le filet
   est ce qui rend la barre perceptible comme objet. */
.likert__barre {
  display: flex;
  height: 100%;
  border: 1px solid var(--line-strong);
}

.likert__seg {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  color: var(--ink);
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}
.likert__seg[data-i="0"] { background: var(--adh-4); color: var(--paper-bright); }
.likert__seg[data-i="1"] { background: var(--adh-3); }
.likert__seg[data-i="2"] { background: var(--adh-2); }
.likert__seg[data-i="3"] { background: var(--adh-1); }
.likert__seg[data-etroit] { font-size: 0; }

/* Le NSP : hors de la barre, séparé, gris, et nommé. */
.likert__nsp {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-3);
  border: 1px solid var(--line-strong);
  background: var(--adh-nsp);
  color: var(--ink);
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}
.likert__nsp::before {
  content: "";
  position: absolute;
  left: -13px;
  top: 0;
  bottom: 0;
  border-left: 1px dotted var(--line-strong);
}
.likert__nsp em {
  font-size: var(--fs-100);
  font-style: normal;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* L'agrégation s'écrit en toutes lettres, jamais « % d'accord » seul. */
.likert__synthese {
  color: var(--muted);
  font-size: var(--fs-100);
  font-variant-numeric: tabular-nums;
  line-height: 1.45;
}
.likert__synthese strong { color: var(--brand-primary); font-weight: 700; }

.legende {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}
.legende__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--muted);
  font-size: var(--fs-100);
}
.legende__pastille { width: 13px; height: 13px; flex: none; border: 1px solid var(--line-strong); }
.legende__pastille[data-i="0"]   { background: var(--adh-4); }
.legende__pastille[data-i="1"]   { background: var(--adh-3); }
.legende__pastille[data-i="2"]   { background: var(--adh-2); }
.legende__pastille[data-i="3"]   { background: var(--adh-1); }
.legende__pastille[data-i="nsp"] { background: var(--adh-nsp); }

/* =============================================================================
   5 bis. SCHÉMAS
   « Les schémas se font en HTML/CSS ou SVG inline, pas avec une librairie :
     accessible, léger, imprimable. »

   Tout schéma hérite de .graphe, qui expose la palette de DONNÉES — jamais
   les couleurs d'identité. La rampe séquentielle est celle de la charte,
   en quatre classes discrètes : une rampe continue ne permet pas de remonter
   d'une couleur vers une valeur, et escamote les seuils.
   ========================================================================== */

/* @famille graphe · hors-vocabulaire */
.graphe {
  --seq-1: #d6e5f2;
  --seq-2: #89b2d6;
  --seq-3: #3f76ac;
  --seq-4: #0d3c69;
  --froid: var(--data-cool);
  --chaud: var(--data-warm);
  --neutre: var(--data-neutral);
  --hors: #d4d3cd;          /* non classable */
}

/* --- 5b.1 Barres horizontales --------------------------------------------
   Le cas le plus fréquent : comparer des grandeurs nommées.               */
/* @famille barres · registre nommé */
.barres { display: flex; flex-direction: column; gap: var(--space-3); }

/* @famille barre · hors-vocabulaire */
.barre {
  display: grid;
  /* Colonne de valeur fixe et assez large : « ≈ 4/10 » y tient sans passer
     à la ligne, et toutes les barres gardent la même longueur. */
  grid-template-columns: minmax(120px, 240px) 1fr 92px;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--fs-200);
}
.barre > span { color: var(--ink); line-height: 1.3; }
.barre > i {
  display: block;
  height: 22px;
  width: var(--v, 0%);
  background: var(--froid);
  border: 1px solid var(--line-strong);
  transform-origin: left center;
}
.barre[data-ton="chaud"]  > i { background: var(--chaud); }
.barre[data-ton="neutre"] > i { background: var(--neutre); }
.barre[data-ton="retrait"] > i { background: var(--seq-1); }
.barre > b {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* --- 5b.2 Parts — la reformulation humaine, rendue visible ---------------
   « 1 demande sur 3 » plutôt que « 33,4 % » : la grille de pastilles montre
   le rapport sans passer par le pourcentage.                              */
/* @famille parts · registre nommé */
.parts { display: flex; flex-direction: column; gap: var(--space-4); }

.parts__grille {
  display: grid;
  grid-template-columns: repeat(var(--par-ligne, 10), 1fr);
  gap: 6px;
  width: min(100%, 420px);
}
.parts__pastille {
  aspect-ratio: 1;
  border-radius: var(--radius-pill);
  background: var(--neutre);
  border: 1px solid var(--line-strong);
}
.parts__pastille[data-active] { background: var(--froid); border-color: var(--froid); }
.parts[data-ton="chaud"] .parts__pastille[data-active] { background: var(--chaud); border-color: var(--chaud); }

.parts__lecture {
  margin: 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  line-height: 1.3;
}
.parts__lecture strong { color: var(--brand-primary); font-weight: 700; }

/* --- 5b.3 Flux — une suite d'étapes ------------------------------------- */
/* @famille flux · registre nommé */
.flux {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: etape;
}
.flux > li {
  counter-increment: etape;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-6) var(--space-4) 0;
  border-top: 3px solid var(--froid);
}
.flux > li + li { padding-left: var(--space-6); }
/* Le lien entre deux étapes : un chevron sur le filet, pas une flèche dessinée. */
.flux > li + li::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--space-4);
  bottom: var(--space-4);
  border-left: 1px solid var(--line);
}
.flux > li > b {
  display: block;
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.1;
}
.flux > li > b::before {
  content: "0" counter(etape);
  display: block;
  margin-bottom: var(--space-2);
  color: var(--muted);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.14em;
}
.flux > li > span {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.4;
}
/* Une étape qui rompt la série — le pôle chaud, réservé aux oppositions. */
.flux > li[data-rupture] { border-top-color: var(--chaud); }
.flux > li[data-rupture] > b { color: var(--chaud); }

/* LE FLUX SUR NAVY. Rien n'était prévu, et ça s'est vu le jour où « Demander
   le parcours » est devenue la conclusion de la deuxième partie : `.flux > li
   > b` est en navy, donc invisible sur navy — DEUX TITRES SUR TROIS avaient
   disparu, et le troisième ne restait que parce qu'il est chaud. Vu au
   tirage ; le code, lui, ne disait rien.

   Le chaud non plus ne tient pas ici : #eb6834 sur #0066a8 mesure 1,89:1,
   sous le plancher même en grand texte. Sur navy, le jalon passe au turquoise
   clair — 3,59:1, ce qui tient pour du GRAND texte et pour lui seul : le titre
   est à --fs-600, la glose dessous reste en blanc. C'est déjà ce que fait la
   synthèse. La règle du chaud n'est pas levée pour autant : elle vaut sur fond
   clair, où il la tient. */
.slide[data-fond="navy"] .flux > li { border-top-color: rgba(255,255,255,.32); }
.slide[data-fond="navy"] .flux > li > b { color: var(--paper-bright); }
.slide[data-fond="navy"] .flux > li > b::before { color: rgba(255,255,255,.64); }
.slide[data-fond="navy"] .flux > li + li::before { border-left-color: rgba(255,255,255,.20); }
.slide[data-fond="navy"] .flux > li > span { color: rgba(255,255,255,.84); }
.slide[data-fond="navy"] .flux > li[data-rupture] { border-top-color: var(--brand-focus); }
.slide[data-fond="navy"] .flux > li[data-rupture] > b { color: var(--brand-focus-light); }
/* La réglette est un aplat clair : sur navy elle noierait le texte. Elle
   s'éclaircit juste assez pour se voir passer. */
.slide[data-fond="navy"] .flux--reglette::before { background-color: rgba(255,255,255,.10); }

/* --- 5b.4 Chronologie ---------------------------------------------------- */
/* @famille chrono · registre nommé */
.chrono {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  margin: 0;
  padding: 0;
  list-style: none;
}
.chrono > li {
  position: relative;
  padding: var(--space-6) var(--space-6) 0 0;
  border-top: 1px solid var(--line-strong);
}
.chrono > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: -6px;
  width: 11px;
  height: 11px;
  border-radius: var(--radius-pill);
  background: var(--froid);
}
.chrono > li[data-jalon]::before { background: var(--chaud); }
.chrono > li > b {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.chrono > li > span {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.35;
}

/* --- 5b.4 bis. RADIAL — un noyau, ce qui l'entoure, et ce qu'il produit ---
   Deux temps, pilotés par des fragments :

     data-etat="alimentation"  les quatre contextes alimentent le noyau ;
     data-etat="influence"     le noyau devient source à son tour, et son
                               faisceau atteint quelqu'un.

   Le déplacement du radial n'est pas un effet : il fait la place au troisième
   terme, et il dit que le social a changé de rôle — d'aboutissement, il
   devient origine.                                                         */

/* Second temps : les quatre contextes se regroupent à GAUCHE du noyau, en
   deux colonnes équilibrées, et le noyau glisse à droite. Les positions sont
   absolues — la disposition d'arrivée est écrite ici, celle de départ dans les
   attributs transform du SVG.

   On ne réduit PAS l'échelle pour resserrer : le texte tomberait sous le
   plancher de 12 px de la charte. On déplace. */
/* @famille radial · verbe RELIER · registre nommé */
.radial__noyau,
.radial__satellite { transition: transform var(--dur-3) var(--ease-io); }

/* Second temps : les quatre se regroupent en ÉVENTAIL sur la gauche du
   noyau — plus rien à sa droite, la flèche y passe. Les branches restent
   courtes : ils sont regroupés, pas éloignés. */
.slide[data-etat="influence"] .radial__noyau { transform: translate(520px, 210px); }
/* LE NOYAU SE DÉPLACE AUSSI — et sa position ci-dessus vient du cadre en
   bande (1160 × 430). Dans le cadre ample il faut la sienne : c'est ce qui
   manquait pour que les branches de l'éventail rejoignent quelque chose.
   Une figure qui change de cadre change TOUTES ses coordonnées, y compris
   celles qu'on avait oubliées parce qu'elles vivent dans une autre règle. */
.slide[data-etat="influence"] .radial--ample .radial__noyau { transform: translate(470px, 250px); }
.slide[data-etat="influence"] .radial__satellite[data-coin="nord"]  { transform: translate(423px,  41px); }
.slide[data-etat="influence"] .radial__satellite[data-coin="ouest"] { transform: translate(341px, 145px); }
.slide[data-etat="influence"] .radial__satellite[data-coin="est"]   { transform: translate(341px, 275px); }
.slide[data-etat="influence"] .radial__satellite[data-coin="sud"]   { transform: translate(411px, 366px); }

/* L'ÉVENTAIL DE LA VERSION AMPLE — boîte 1144 × 520, quatre pairs à égale
   distance. Les valeurs ci-dessus servent la figure en bande (1160 × 430),
   qui vit encore dans deux autres decks : on ne les touche pas. Une figure
   qui change de cadre change de coordonnées, elle ne change pas de sens. */
.slide[data-etat="influence"] .radial--ample .radial__satellite[data-coin="nord"] { transform: translate(332px, 85px); }
.slide[data-etat="influence"] .radial--ample .radial__satellite[data-coin="ouest"] { transform: translate(268px, 176px); }
.slide[data-etat="influence"] .radial--ample .radial__satellite[data-coin="est"] { transform: translate(268px, 324px); }
.slide[data-etat="influence"] .radial--ample .radial__satellite[data-coin="sud"] { transform: translate(332px, 415px); }

/* L'éventail ne peut pas contenir les précisions sans que les libellés se
   chevauchent. Elles s'effacent : elles ont déjà été lues au premier temps. */
.radial__detail { transition: opacity var(--dur-2) var(--ease-io); }
.slide[data-etat="influence"] .radial__detail { opacity: 0; }

/* La pointe de la flèche paraît avec elle. */
/* Le grain qui descend une branche. Les quatre partagent la même durée quelle
   que soit la longueur de leur trajet : offset-distance va de 0 à 100 %, donc
   ils arrivent ENSEMBLE sur le noyau — c'est le point du schéma. */
.radial__grain { fill: var(--brand-primary); opacity: 0; }

@media (prefers-reduced-motion: no-preference) {
  .slide[data-etat="alimentation"] .radial__liens[data-temps="1"] .radial__grain {
    animation: cheminer 820ms var(--ease-io) both;
  }
  /* 520 + 900 = 1420 ms, soit 100 ms de plus que le dernier verbe (1100 + 220).
     MESURÉ SUR LA SLIDE, ce n'est pas un dépassement : `cheminer` atteint
     offset-distance 100 % à 88 % de sa durée, donc le grain ARRIVE à 1312 ms —
     avant le dernier verbe. Les 100 ms restantes sont son estompage, à
     position fixe. Rien ne bouge après que le propos est posé ; le mouvement
     finit, ce qui est précisément ce que la charte demande.
     Raccourcir à 800 ms ferait arriver le grain AVANT les verbes, sans rien
     gagner. Ne pas « réparer » ce calcul : il a déjà été refait une fois. */
  /* LES GRAINS DE L'ÉVENTAIL. Au premier temps ils montent des quatre
     contextes vers le noyau ; au second, ils avaient disparu — les contextes
     se regroupaient et cessaient d'alimenter, alors que le propos dit qu'ils
     continuent. Nicolas l'a signalé trois fois : « il manque toujours les
     petits points ». Les branches de l'éventail sont courtes, donc la course
     est brève : 620 ms, et ils arrivent avant la flèche. */
  .slide[data-etat="influence"] .radial__liens[data-temps="2"] .radial__grain {
    animation: cheminer 620ms var(--ease-io) both;
  }
  .slide[data-etat="influence"] .radial__grain[data-sur="fleche"] {
    animation: cheminer 900ms var(--ease-io) 520ms both;
  }
}
@keyframes cheminer {
  0%        { offset-distance: 0%;   opacity: 0; }
  10%       { opacity: 1; }
  88%       { offset-distance: 100%; opacity: 1; }
  100%      { offset-distance: 100%; opacity: 0; }
}

/* Le contour du noyau se remplit à l'arrivée des quatre grains. */
.radial__anneau {
  fill: none;
  stroke: var(--brand-focus);
  stroke-width: 5;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transform: rotate(-90deg);          /* le remplissage part du haut */
}
/* Une ANIMATION, pas une transition : si l'on arrive sur la slide et qu'on
   appuie aussitôt, le navigateur n'a jamais peint l'état de base — la
   transition n'a alors pas de point de départ et saute à la fin. Une
   animation part toujours de ses propres images-clés. */
.slide[data-etat="alimentation"] .radial__anneau,
.slide[data-etat="influence"] .radial__anneau {
  animation: remplir-anneau 640ms var(--ease-out) 700ms both;
}
@keyframes remplir-anneau { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

/* La pointe est pleine : sans surpasser la règle du groupe (fill: none),
   elle sortirait en triangle creux et pointillé. */
.radial__faisceau .radial__pointe {
  fill: var(--brand-accent);
  stroke: none;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out) 480ms;
}
.slide[data-etat="influence"] .radial__faisceau .radial__pointe { opacity: 1; }

/* Les branches portent le propos — les quatre contextes alimentent le noyau —
   et non le décor. Elles se redessinent vers le centre au premier temps. */
.radial__lien { fill: none; stroke: var(--brand-accent); stroke-width: 3.5; opacity: .8; }

@media (prefers-reduced-motion: no-preference) {
  .slide[data-etat="alimentation"] .radial__liens[data-temps="1"] .radial__lien {
    stroke-dasharray: 1;
    animation: alimenter var(--dur-3) var(--ease-out) both;
  }
  .slide[data-etat="alimentation"] .radial__liens[data-temps="1"] .radial__lien:nth-of-type(2) { animation-delay: 100ms; }
  .slide[data-etat="alimentation"] .radial__liens[data-temps="1"] .radial__lien:nth-of-type(3) { animation-delay: 200ms; }
  .slide[data-etat="alimentation"] .radial__liens[data-temps="1"] .radial__lien:nth-of-type(4) { animation-delay: 300ms; }
}
@keyframes alimenter { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

/* Les quatre restent reliés au noyau dans les deux dispositions — c'est ce
   que dit le croquis. Un tracé SVG ne pouvant pas déplacer ses extrémités en
   CSS, on écrit les deux jeux de liens et on les croise. */
.radial__liens { transition: opacity var(--dur-2) var(--ease-io); }
.radial__liens[data-temps="2"] { opacity: 0; }
.slide[data-etat="influence"] .radial__liens[data-temps="1"] { opacity: 0; }
.slide[data-etat="influence"] .radial__liens[data-temps="2"] { opacity: 1; }

/* Le faisceau : trois filets, pas un dégradé — la charte n'en autorise qu'un,
   et ce n'est pas ici. Ils se dessinent du noyau vers la silhouette. */
.radial__faisceau path {
  fill: none;
  stroke: var(--brand-accent);
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.slide[data-etat="influence"] .radial__faisceau path { stroke-dashoffset: 0; transition: stroke-dashoffset 620ms var(--ease-out); }
.slide[data-etat="influence"] .radial__faisceau path:nth-of-type(2) { transition-delay: 80ms; }
.slide[data-etat="influence"] .radial__faisceau path:nth-of-type(3) { transition-delay: 160ms; }

/* La silhouette : elle n'existe qu'au second temps, et son contour s'allume
   quand le faisceau l'atteint. */
.radial__silhouette {
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="influence"] .radial__silhouette { opacity: 1; transition-delay: 300ms; }

/* @famille halo · hors-vocabulaire */
.radial__silhouette > .halo {
  fill: none;
  stroke: var(--brand-focus);
  stroke-width: 3;
  opacity: 0;
}
.radial__verbes > g { opacity: 0; transition: opacity var(--dur-2) var(--ease-out); }
.slide[data-etat="influence"] .radial__verbes > g { opacity: 1; }
.slide[data-etat="influence"] .radial__verbes > g:nth-of-type(1) { transition-delay: 800ms; }
.slide[data-etat="influence"] .radial__verbes > g:nth-of-type(2) { transition-delay: 950ms; }
.slide[data-etat="influence"] .radial__verbes > g:nth-of-type(3) { transition-delay: 1100ms; }

.slide[data-etat="influence"] .radial__silhouette > .halo {
  opacity: 1;
  transition: opacity var(--dur-2) var(--ease-out) 700ms;
}

/* À l'impression, la slide est finie : le second temps est celui qu'on garde. */
@media print {
  .radial__noyau { transform: translate(520px, 210px) !important; }
  .radial--ample .radial__noyau { transform: translate(470px, 250px) !important; }
  .radial__satellite[data-coin="nord"]  { transform: translate(423px,  41px) !important; }
  .radial__satellite[data-coin="ouest"] { transform: translate(341px, 145px) !important; }
  .radial__satellite[data-coin="est"]   { transform: translate(341px, 275px) !important; }
  .radial__satellite[data-coin="sud"]   { transform: translate(411px, 366px) !important; }
  .radial--ample .radial__satellite[data-coin="nord"] { transform: translate(332px, 85px) !important; }
  .radial--ample .radial__satellite[data-coin="ouest"] { transform: translate(268px, 176px) !important; }
  .radial--ample .radial__satellite[data-coin="est"] { transform: translate(268px, 324px) !important; }
  .radial--ample .radial__satellite[data-coin="sud"] { transform: translate(332px, 415px) !important; }
  .radial__detail { opacity: 0 !important; }
  .radial__anneau { stroke-dashoffset: 0 !important; }
  .radial__grain { opacity: 0 !important; }
  .radial__faisceau .radial__pointe { opacity: 1 !important; }
  .radial__liens[data-temps="1"] { opacity: 0 !important; }
  .radial__liens[data-temps="2"] { opacity: 1 !important; }
  .radial__silhouette,
  .radial__silhouette > .halo,
  .radial__verbes > g { opacity: 1 !important; }
  .radial__faisceau path { stroke-dashoffset: 0 !important; }
}

/* --- 5b.4 ter. BOUCLE — l'influence réciproque ---------------------------
   « Elles influencent la réalité autant qu'elles sont influencées par elle. »
   C'est une boucle, pas deux flèches parallèles : le composant doit le dire. */
/* @famille boucle · registre nommé */
.boucle { display: grid; place-items: center; }
.boucle__terme {
  padding: var(--space-3) var(--space-6);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--paper-bright);
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  line-height: 1.15;
  text-align: center;
}

/* --- 5b.4 quater. MATRICE — deux axes, quatre cases ---------------------- */
/* Des blocs autonomes, et non des cases découpées dans un cadre : une case
   qui n'est pas encore révélée ne doit rien laisser voir. Le cadre au trait
   dessinait un rectangle vide en attendant. */
/* @famille matrice · registre nommé */
.matrice {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}
.matrice__case {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-6);
  border: 1px solid var(--line);
  border-top: 3px solid var(--brand-accent);
  border-radius: var(--radius-md);
  background: var(--paper-bright);
}
.matrice__case[data-ton="chaud"] { border-top-color: var(--brand-caution); }
.matrice__case > b {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: 1.1;
}
.matrice__case > b .ico { width: 22px; height: 22px; }
.matrice__case > span { font-family: Georgia, "Times New Roman", serif;
                        font-size: var(--fs-400); line-height: 1.4; }
.matrice__case[data-ton="chaud"] > b { color: var(--chaud, var(--brand-caution)); }

/* --- 5b.4 quinquies. BALANCE — deux pôles, et les deux façons de rompre ---
   L'équilibre au centre, les ruptures aux extrémités. Le picto porte les
   deux plateaux ; les ruptures sont sur le pôle chaud, parce que ce sont
   des écueils, pas des options.                                            */
/* @famille balance · registre nommé */
.balance { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: var(--space-6); align-items: center; }
.balance__centre { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); }
/* Le picto est INLINÉ dans la slide, pas chargé en <img> : le contenu d'une
   image SVG est isolé du document — ni le CSS ni les variables de la page n'y
   entrent. Tout picto qui doit être recoloré ou animé doit donc être écrit
   dans le HTML. Le fichier assets/pictos/balance.svg reste la source. */
.balance__picto {
  --picto-clair: var(--gris-bleu, #a9c5d6);
  --picto-moyen: var(--brand-accent);
  --picto-fonce: var(--brand-primary);
  display: block; width: 190px; height: auto;
}
.balance__poles { display: flex; gap: var(--space-6); width: 100%; }
.balance__pole {
  flex: 1;
  padding-top: var(--space-2);
  border-top: 2px solid var(--brand-accent);
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  line-height: 1.3;
  text-align: center;
}
.balance__titre {
  margin: 0;
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-700);
  letter-spacing: -.03em;
}
.balance__rupture {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-4) var(--space-6);
  border-top: 3px solid var(--chaud, var(--brand-caution));
  background: var(--paper-bright);
}
.balance__rupture b {
  display: flex; align-items: center; gap: var(--space-2);
  color: var(--chaud, var(--brand-caution));
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600); font-weight: 400; letter-spacing: -.03em; line-height: 1.1;
}
.balance__rupture b .ico { width: 22px; height: 22px; }
.balance__rupture span { font-family: Georgia, "Times New Roman", serif;
                         font-size: var(--fs-400); line-height: 1.4; }

/* Les VISIONS : la même pesée vue de deux places. À distinguer des ruptures
   (qui sont des mises en garde) — les visions sont des positions, pas des
   dangers. data-ton="chaud" marque le côté qui pèse. */
.balance__vision {
  display: flex; flex-direction: column; justify-content: center; gap: var(--space-2);
  min-height: 118px;                  /* les deux cartes d'une pesée s'alignent */
  padding: var(--space-4) var(--space-6);
  border-top: 3px solid var(--brand-accent);
  background: var(--paper-bright);
}
.balance__vision b {
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500); font-weight: 400; letter-spacing: -.02em; line-height: 1.15;
}
/* D'OÙ L'ON REGARDE — demande de Nicolas, 26 août : « mettre en évidence que
   les représentations sociales sont situées, ici perspective patient vs
   perspective PDS ». Les deux volets disaient déjà QUOI chacun met dans la
   balance ; ils ne disaient pas que c'est la POSITION qui fait la différence.
   « De là où elle est » / « de là où vous êtes » nomme le point de vue, et
   la symétrie des deux formules est ce qui fait la démonstration : même
   objet, même balance, deux endroits d'où l'on pèse. C'est la définition
   sociologique d'un savoir situé, sans le mot savant. */
.balance__oeil {
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-style: normal;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.balance__vision[data-ton="chaud"] .balance__oeil { color: var(--chaud, var(--data-warm)); }
.balance__vision span { font-family: Georgia, "Times New Roman", serif;
                        font-size: var(--fs-300); line-height: 1.45; }
.balance__vision[data-ton="chaud"] { border-top-color: var(--chaud, var(--data-warm)); }
.balance__vision[data-ton="chaud"] b { color: var(--chaud, var(--data-warm)); }

/* LA BALANCE PENCHE. « La balance qui penche EST la rupture d'équilibre » —
   l'exemple que la charte donne depuis le début, construit le 25 août 2026.
   Le fléau n'a AUCUN attribut transform : le CSS peut donc le faire tourner
   sans rien écraser. La règle pose la valeur finale ; 900 ms de trajet,
   dans la bande démonstration — le basculement est le propos. */
.balance__fleau {
  transform-box: view-box;
  transform-origin: 100px 47px;        /* le pivot du fléau                  */
}
/* LA BALANCE PENCHE DU CÔTÉ OÙ ARRIVE LE TEXTE (26 août, demande de Nicolas).
   Elle ne penchait qu'une fois, à la fin. Or le propos de la slide est que
   « la même expérience ne pèse pas pareil selon qui la tient » : il faut donc
   la voir pencher DEUX FOIS, d'un côté puis de l'autre, à mesure que chaque
   regard se pose. Le fléau suit celui qui parle.
   Puis elle revient du côté vécu — c'est là qu'elle s'arrête, et c'est ce que
   dit la chute : une douleur minimisée ne disparaît pas. */
.balance__fleau { transition: transform 900ms var(--ease-io); }
/* `penche` A ÉTÉ RETIRÉ — il servait à DEUX slides pour des sens opposés.
   « Ce qui se vit » l'employait pour pencher à gauche, « quand la discrétion
   prime » pour pencher à droite, et la seconde ne pouvait pas gagner sans
   surenchère de spécificité. Un nom d'état est un nom : deux propos
   différents en prennent deux. La discrétion a maintenant les siens
   (tolerance, discretion), et celui-ci reste à la seule slide qui le lisait. */
.slide[data-etat="vecu"] .balance__fleau {
  transform: rotate(-9deg);            /* à gauche : le côté vécu pèse       */
}
.slide[data-etat="evalue"] .balance__fleau {
  transform: rotate(9deg);             /* à droite : c'est l'évaluation qui parle */
}
/* ET ELLE REVIENT AU CENTRE SUR LA CHUTE (demande de Nicolas, 26 août).
   Le fléau a penché des deux côtés : on a vu que le même poids ne pèse pas
   pareil selon la main. Quand arrive « une douleur minimisée ne disparaît
   pas : elle devient une mauvaise expérience », le fléau se remet droit —
   non pas parce que l'équilibre serait rétabli, mais parce que la douleur a
   QUITTÉ LA BALANCE. Elle n'est plus une chose qu'on pèse : elle est devenue
   autre chose. Le retour au centre ferme la pesée, il ne la conclut pas. */
.slide[data-etat="rendue"] .balance__fleau { transform: rotate(0deg); }

/* L'AMBIANCE DE LA BALANCE — une balance n'est jamais parfaitement immobile.
   Registre de l'ambiance (ANIMATIONS.md) : ±0,8°, six secondes, et SEULEMENT
   quand le fléau est au repos — avant le premier regard, et après la chute qui
   le remet droit. Jamais pendant les penchements : l'ambiance occupe le temps
   mort, pas le temps du propos. Le retard de 900 ms laisse la transition finir
   avant que l'oscillation reprenne, sinon le fléau saute.
   Ça ne démontre rien, et c'est sa définition : le retirer ne change rien à
   l'argument. */
@media (prefers-reduced-motion: no-preference) {
  .slide.est-active:not([data-etat]) .balance__fleau,
  .slide.est-active[data-etat="rendue"] .balance__fleau {
    animation: balance-osciller 6s var(--ease-io) 900ms infinite;
  }
}
@keyframes balance-osciller {
  0%, 100% { transform: rotate(-0.8deg); }
  50%      { transform: rotate(0.8deg); }
}
/* DEUX TEMPS, comme sur « ce qui se vit, ce qui s'évalue » — demande de
   Nicolas, 26 août. Le fléau penchait UNE fois, vers la discrétion, et le
   déplacement de l'arbitrage ne se voyait donc pas : on ne voit un
   déplacement que si l'on a vu d'où il part.
   Premier temps, la tolérance l'emporte — c'est le cas ordinaire. Second
   temps, la discrétion : le fléau REPART de l'autre côté, et c'est là que la
   slide dit ce qu'elle a à dire. Les deux états sont portés par les volets
   eux-mêmes, pas par des marqueurs muets : le volet qui paraît est
   exactement ce qui fait pencher. */
.slide[data-etat="tolerance"] .balance__fleau {
  transform: rotate(-9deg);            /* à gauche : le confort l'emporte    */
}
.slide[data-etat="discretion"] .balance__fleau {
  transform: rotate(9deg);             /* à droite : l'arbitrage s'est déplacé */
}
/* ET ELLE REVIENT AU CENTRE, comme le modèle. Nicolas a dû le redire : « la
   balance penche d'un côté quand le texte apparaît et à la fin elle est au
   milieu avec une légère oscillation ». Ma version s'arrêtait penchée, à deux
   temps, quand le modèle en fait TROIS — et le troisième n'est pas décoratif :
   la pesée s'est faite, elle se referme. `rendue` est réemployé tel quel,
   parce que le geste est identique ; c'est le seul cas où partager un nom
   d'état est juste. */
/* ET `penche` RESTE, pour les deux decks qui l'emploient encore sur cette
   même figure — jsf-2026-seance et la copie de Codex. Le retirer aurait
   laissé leur fléau droit sans que rien ne le signale : une règle qu'on
   déplace doit être suivie chez tous ceux qui la lisent. */
.slide[data-etat="penche"] .balance[data-penche="droite"] .balance__fleau {
  transform: rotate(9deg);
}
@media print { .balance__fleau { transition: none !important; animation: none !important; } }

/* --- 5b.4 sexies. VIDÉO --------------------------------------------------
   Fichier local, contrôles natifs. L’affiche documentaire explicite prend
   le relais au tirage ; Chrome n’imprime pas la vidéo.                       */
/* @famille video · hors-vocabulaire */
.video {
  display: block;
  width: 100%;
  max-height: 100%;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--ink);
}

/* L’affiche reprend le cadre du lecteur, sans ajouter de hauteur au PDF. */
.video__cadre { position: relative; min-width: 0; margin: 0; }
.video__cadre > .video,
.video__cadre > .video__affiche { width: 100%; height: 100%; object-fit: contain; }
.video__affiche { display: none; }
@media print {
  .video__cadre > .video { display: none !important; }
  .video__cadre > .video__affiche { display: block !important; }
}

/* --- 5b.5 Volets — n colonnes titrées, sans ordre -------------------------
   Pour des registres, des dimensions, des facettes : ce qui se juxtapose
   sans se succéder. À distinguer de .flux, qui implique une séquence.     */
/* @famille volets · registre nommé */
.volets {
  display: grid;
  grid-template-columns: repeat(var(--volets, 3), minmax(0, 1fr));
  gap: var(--space-6);
}
.volet { padding-top: var(--space-3); border-top: 3px solid var(--froid, var(--brand-accent)); }
/* Quand des titres de colonnes font 1 OU 2 lignes, les corps décrochent :
   l'attribut pose un plancher et toute la rangée retrouve sa ligne de base. */
[data-titres="deux-lignes"] > * > b { display: block; min-height: 2.35em; }

/* --- Volets posés : l'illustration AU-DESSUS du titre --------------------
   Demandé par Nicolas le 21 août (croquis sur la slide 09) : « les
   illustrations pourraient être au-dessus ; et plus design ».

   L'icône passe donc en tête, dans une pastille, et le volet devient une
   carte posée plutôt qu'un filet surmonté de texte. Le regard prend l'image,
   puis le titre, puis le corps — au lieu de buter d'emblée sur du texte.
   -------------------------------------------------------------------------- */

.volets--posees { gap: var(--space-6); align-items: stretch; }
.volets--posees > .volet {
  display: flex;
  flex-direction: column;
  padding: var(--space-6) var(--space-6) var(--space-6);
  border-top-width: 4px;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  background: var(--paper-bright);
}
.volet__vignette {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  margin-bottom: var(--space-4);
  border-radius: var(--radius-pill);
  background: rgba(0, 102, 168, .09);
  color: var(--brand-primary);
}
.volet__vignette .ico { width: 26px; height: 26px; }
.volet[data-ton="chaud"] .volet__vignette {
  background: rgba(235, 104, 52, .12);
  color: var(--data-warm);
}
/* Le repère de registre est chassé en pied : c'est une signature, pas une
   information qu'on lit en premier. */
.volets--posees > .volet > em { margin-top: auto; padding-top: var(--space-4); }
.volet[data-ton="chaud"]  { border-top-color: var(--chaud, var(--brand-caution)); }
.volet[data-ton="retrait"] { border-top-color: var(--line-strong); }
.volet > b {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.1;
}
.volet[data-ton="chaud"] > b { color: var(--chaud, var(--brand-caution)); }
.volet > span {
  display: block;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.4;
}
.volet > em {
  display: block;
  margin-top: var(--space-2);
  color: var(--muted);
  font-size: var(--fs-100);
  font-style: normal;
  line-height: 1.4;
}

/* --- 5b.6 Opposition — deux termes en vis-à-vis ---------------------------
   Le pôle froid et le pôle chaud, séparés par un filet. Pour ce que l'on
   confond et qu'il faut distinguer.                                        */
.opposition {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  gap: var(--space-8);
  align-items: start;
}
/* ⚠ `grid-row: 1` N'EST PAS DÉCORATIF — sans lui le filet n'existe pas.
   Mesuré le 27 août sur la page vivante : le pseudo-élément est le PREMIER
   item de la grille, il se pose en (ligne 1, colonne 2), et le curseur de
   placement passe alors en colonne 3. Les deux `div` demandent les colonnes
   1 et 3 : la colonne 1 est DERRIÈRE le curseur, donc le placement clairsemé
   les renvoie tous les deux à la LIGNE 2. Le filet reste seul en ligne 1,
   dans une ligne vide — hauteur zéro, rien à peindre.

   Il n'a donc jamais été visible, dans aucun deck, depuis qu'il est écrit.
   Ni le code ni l'écran ne le disaient : une règle correcte, un résultat nul.
   Une sonde posée dans la même cellule mesurait 0 px de haut, 158 px avec
   cette ligne. */
.opposition::before {
  content: "";
  grid-column: 2;
  grid-row: 1;
  align-self: stretch;
  background: var(--line-strong);
}
.opposition > div:first-child { grid-column: 1; }
.opposition > div:last-child  { grid-column: 3; }
.opposition b {
  display: block;
  margin-bottom: var(--space-3);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.opposition > div:first-child b { color: var(--froid, var(--brand-accent)); }
.opposition > div:last-child  b { color: var(--chaud, var(--brand-caution)); }
/* LA CATÉGORIE PEINT LE MONDE AVANT QU'ON REGARDE — et c'est le fond qui le
   dit, pas le texte. La demande était « les phrases de droite en saumon » :
   #eb6834 sur le papier mesure 3,02:1, ce qui tient pour un libellé court en
   capitales grasses (la règle du libellé direct, CHARTE 3.2) et PAS pour
   quatre phrases à --fs-500. La couleur descend donc dans l'aplat, à 8 % :
   la colonne est teintée, le texte reste à l'encre, et la démonstration y
   gagne — on voit la catégorie colorer ce qu'elle range, ce qui est
   exactement ce que la ligne du dessous vient défaire.

   POURQUOI LE CHAUD EST À DROITE ET PAS AILLEURS : il marque le côté qu'on
   REMARQUE. La chute dit que le versant positif n'est pas moins puissant,
   seulement plus facile à ne pas voir — le bleu pâle de gauche le montre en
   le faisant. Le chaud n'est donc pas un jugement sur le pôle chimique ; il
   est ce que la salle voit en premier, et c'est le propos.               */
.opposition--categorie > div {
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-md);
  background-color: var(--surface-tint);      /* AU DÉPART, RIEN NE LES DISTINGUE */
  transition: background-color 620ms var(--ease-out);
}
.opposition--categorie p:last-child { margin-bottom: 0; }

/* LE CLASSEMENT ARRIVE PAR-DESSUS UN TEXTE QUI NE BOUGE PAS, et c'est toute la
   démonstration. Les quatre phrases paraissent d'abord sur un fond identique :
   ce sont quatre phrases qu'on entend, rien de plus. Puis les deux fonds
   prennent leur couleur et les étiquettes se posent — le texte n'a pas bougé
   d'un pixel.

   C'est la ligne du dessous, faite au lieu d'être dite : ce n'est pas la
   substance qui classe, c'est la catégorie. Elle est appliquée AU monde, pas
   tirée de lui.

   ET LA CHUTE SE JOUE LÀ AUSSI. Au basculement, le saumon saute aux yeux et le
   bleu pâle s'efface. « Plus facile à ne pas voir » cesse d'être une
   affirmation : la salle vient de l'éprouver deux secondes plus tôt. C'est
   pourquoi les deux aplats n'ont PAS la même force — 10 % à droite, 8 % à
   gauche. L'écart est petit et il est délibéré.

   UNE VARIANTE ÉCARTÉE : faire retomber les couleurs au neutre après la ligne
   de bascule, pour montrer l'analyse défaire le classement. Chrome imprime
   l'état d'arrivée — le PDF sortirait une slide grise qui ne dit plus rien.
   Le geste ne survivrait qu'à l'oral. */
.opposition--categorie > div > b {
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 180ms;
}
/* DEUX TEMPS, ET LE SECOND DÉFAIT LE PREMIER.

   Premier temps : les couleurs arrivent, mais PAS À ÉGALITÉ. Le saumon à 16 %,
   le bleu à 4 % — un rapport de quatre, assez pour que la salle voie un côté
   et pas l'autre. C'est une illusion, et elle est montée exprès.

   Second temps, avec la chute : le bleu MONTE rejoindre le saumon. Les deux
   pèsent le même poids, et le versant positif se révèle être un aplat aussi
   plein que l'autre. La chute cesse d'être une affirmation — la salle vient de
   voir ce qu'elle n'avait pas vu.

   LE BLEU MONTE, LE SAUMON NE DESCEND PAS. Faire baisser le chaud dirait « la
   crainte était surévaluée » ; faire monter le froid dit « le positif était là
   depuis le début, et vous ne l'avez pas vu ». C'est la seconde phrase que la
   chute écrit.

   Le geste est celui de la balance qui penche puis revient au centre : une
   seule opération, jouée deux fois, et c'est le RETOUR qui démontre. */
/* ⚠ UN APLAT FAIBLE SUR UN FOND DÉJÀ TEINTÉ PEUT ÊTRE PLUS CLAIR QUE LUI.
   Première version : le bleu arrivait à 4 %, ce qui compose (240,243,249) —
   plus PÂLE que le `--surface-tint` de départ (237,242,248). Le côté gauche ne
   s'allumait donc pas faiblement : il s'effaçait. Mesuré sur la slide vivante,
   pas devinée.
   D'où le parti pris, plus franc et plus juste : au premier temps, le gauche
   NE PREND PAS DE COULEUR DU TOUT. Seul le saumon s'allume. */
.slide[data-etat="range"] .opposition--categorie > div:last-child,
.slide[data-etat="egalise"] .opposition--categorie > div:last-child { background-color: rgba(235, 104, 52, .18); }
.slide[data-etat="egalise"] .opposition--categorie > div:first-child { background-color: rgba(67, 127, 179, .18); }
.slide[data-etat="range"] .opposition--categorie > div > b,
.slide[data-etat="egalise"] .opposition--categorie > div > b { opacity: 1; }

@media print {
  /* L'ARRIVÉE EST L'ÉGALITÉ, et c'est bien elle qu'il faut imprimer : sur le
     papier, les deux aplats se valent — ce que la slide démontre. Imprimer le
     premier temps figerait l'illusion. */
  .opposition--categorie > div:first-child { background-color: rgba(67, 127, 179, .18) !important; }
  .opposition--categorie > div:last-child  { background-color: rgba(235, 104, 52, .18) !important; }
  .opposition--categorie > div > b { opacity: 1 !important; transition: none !important; }
}

.opposition p {
  margin: 0 0 var(--space-3);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.4;
}
.opposition p:last-child { margin-bottom: 0; }
.opposition strong { color: var(--brand-primary); font-weight: 700; }

/* Une bascule : les deux flèches vont dans le MÊME sens, parce qu'il ne s'agit
   pas d'opposer mais d'enchaîner — ce qu'on vient d'établir, ce qui manque
   encore. Le pôle chaud est alors celui qui ouvre, pas celui qui recule. */
.bascule > [data-sens="retrait"] b { color: var(--chaud); }
.bascule > [data-sens="vers"] b { color: var(--brand-primary); }
.bascule > [data-sens="retrait"] { border-top-color: var(--chaud); }

/* Dans un élan, le pôle chaud n'est pas le second mais celui qui RECULE.
   `.opposition` colore par le rang ; ici c'est le sens qui décide, et ces
   règles doivent donc venir après elle pour l'emporter. */
/* @famille elan · registre nommé */
.elan > [data-sens="retrait"] b { color: var(--chaud); }
.elan > [data-sens="vers"] b    { color: var(--brand-primary); }

/* --- 5b.7 Schéma SVG inline ---------------------------------------------- */
/* Une icône du sprite posée dans un schéma : <use> hérite du viewBox du
   symbole, donc width/height suffisent à la dimensionner. */
.schema__ico {
  fill: none;
  stroke: var(--brand-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* @famille schema · hors-vocabulaire */
.schema { display: block; width: 100%; max-height: 100%; }
.schema text { font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif; }
/* @famille t-serif · hors-vocabulaire */
.schema .t-serif { font-family: Georgia, "Times New Roman", serif; }
/* @famille t-repere · hors-vocabulaire */
.schema .t-repere {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  fill: var(--muted);
}
/* @famille t-titre · hors-vocabulaire */
.schema .t-titre { font-size: 21px; fill: var(--brand-primary); }
/* @famille t-corps · hors-vocabulaire */
/* Un `t-corps` en tspan sous un `t-repere` héritait ses CAPITALES, sa graisse
   et son interlettrage : une glose de soixante-dix signes se posait en
   capitales grasses, contre la charte — « capitales réservées aux repères
   courts ». Le corps de texte réaffirme donc ce qu’il est, où qu’il vive. */
.schema .t-corps {
  font-size: 15px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  fill: var(--ink);
}

/* --- 5b.5 Légende commune ------------------------------------------------ */
.graphe .legende__pastille[data-ton="froid"]  { background: var(--froid); }
.graphe .legende__pastille[data-ton="chaud"]  { background: var(--chaud); }
.graphe .legende__pastille[data-ton="neutre"] { background: var(--neutre); }

/* Les barres poussent depuis leur origine, jamais l'inverse. */
@media (prefers-reduced-motion: no-preference) {
  .barre > i { animation: bar-grow var(--dur-3) var(--ease-out) both; }
  .barre:nth-child(2) > i { animation-delay: 60ms; }
  .barre:nth-child(3) > i { animation-delay: 120ms; }
  .barre:nth-child(4) > i { animation-delay: 180ms; }
}
/* =============================================================================
   5 ter. FRAGMENTS — les apparitions internes à une slide
   La flèche les découvre un à un avant de passer à la slide suivante. C'est
   ce que le PPT appelle une animation d'apparition, sauf qu'ici l'élément
   occupe déjà sa place : rien ne bouge quand il paraît.

   Un fragment peut porter data-etat : deck.js recopie la valeur sur la slide,
   et le CSS y réagit. C'est ainsi qu'une apparition fait pencher une balance
   ou allume une branche d'un schéma, plutôt que de seulement se montrer.
   ========================================================================== */

/* @famille fragment · hors-vocabulaire */
.fragment {
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-out);
}
.fragment.est-revele { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .fragment:not(.est-revele) { transform: translateY(var(--rise)); }
  .fragment.est-revele { transform: none; }
}

/* Un fragment dont la seule fonction est de piloter un état (data-etat) n'a
   rien à montrer : il ne doit pas laisser de trou dans la mise en page. */
.fragment[data-muet] { position: absolute; width: 0; height: 0; overflow: hidden; }

/* =============================================================================
   5 quater. ANIMATIONS QUI PORTENT LE PROPOS
   La charte veut un mouvement « rapide et assuré, jamais rebondissant ni
   joueur », en trois durées. Les deux animations ci-dessous en sortent
   délibérément, et pour une seule raison : elles NE SONT PAS décoratives,
   elles sont le contenu. Le point qui tourne EST le va-et-vient ; la balance
   qui penche EST la rupture d'équilibre. Toute animation qui ne satisferait
   pas ce critère doit rester dans les trois durées.
   ========================================================================== */

/* --- Le point qui parcourt la boucle, et la trace qu'il laisse -----------
   La trace se dessine derrière lui : c'est le geste `draw-line` de la charte,
   stroke-dashoffset sur pathLength="1". Le trajet devient visible, pas
   seulement le mobile. */
.boucle__point { fill: var(--paper-bright); stroke: var(--brand-accent); stroke-width: 3; }
/* ELLE CHANGE DE COULEUR SELON LA PHASE (26 août, demande de Nicolas).
   Un aller et un retour ne disent pas la même chose : « la réalité déplace la
   représentation », puis l'inverse. La boule prenait une seule couleur pour
   les deux, et le circuit se lisait comme un mouvement unique au lieu de deux
   temps qui s'enchaînent. Turquoise à l'aller, bleu de marque au retour — deux
   teintes voisines, jamais le chaud : le chaud est le jalon rare, et une boule
   qui tourne sans fin n'en est pas un. */
@media (prefers-reduced-motion: no-preference) {
  .slide.est-active .schema:not(.boucle--menue) .boucle__point {
    animation: parcourir 5.5s linear 1400ms infinite, teinter-phase 5.5s linear 1400ms infinite;
  }
}
@keyframes teinter-phase {
  0%, 46%   { stroke: var(--brand-primary); }
  50%, 96%  { stroke: var(--chaud, var(--data-warm)); }
  100%      { stroke: var(--brand-primary); }
}
@media print {
  /* Sans animation, le grain se rend en (0,0) — un croissant orphelin rogné
     par le cadre (vu page 9 par la passe design). Une boucle n'a pas d'état
     d'arrivée : sur le papier, le point disparaît. */
  .boucle__point { display: none !important; }
}
/* --- La boucle en petit, sous un titre ------------------------------------
   Réemploi de la boucle de la slide 11 pour porter un cas empirique : chaque
   temps de la chronologie allume l'arc correspondant. La forme est déjà
   connue de la salle — c'est ce qui fait qu'elle se lit sans être réexpliquée.
   -------------------------------------------------------------------------- */

.boucle--menue { margin-bottom: var(--space-8); }
.boucle--menue .boucle__sens {
  fill: var(--muted);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.boucle--menue .boucle__trace { stroke-width: 3; }

/* L'ALLUMAGE EST CUMULATIF ET SUIT LA CHRONOLOGIE. Le récit commence par le
   réel — un cas médiatisé, un déremboursement : c'est l'arc du HAUT, qui va de
   la réalité vers les représentations. Le retour vient avec « et ensuite », et
   le premier reste allumé : la boucle se referme au lieu de se remplacer. */

/* Premier temps — la réalité va vers les représentations. */
.slide[data-etat="aller"]  .boucle--menue .boucle__trace[data-arc="aller"],
.slide[data-etat="retour"] .boucle--menue .boucle__trace[data-arc="aller"] {
  opacity: 1;
  animation: boucle-menue-tracer 640ms var(--ease-io) both;
}
.slide[data-etat="aller"]  .boucle--menue .boucle__sens[data-arc="aller"],
.slide[data-etat="retour"] .boucle--menue .boucle__sens[data-arc="aller"],
.slide[data-etat="aller"]  .boucle--menue .boucle__pointe[data-arc="aller"],
.slide[data-etat="retour"] .boucle--menue .boucle__pointe[data-arc="aller"] { opacity: 1; }

/* Second temps — et les représentations reviennent sur la réalité.
   EN CHAUD : c'est le mouvement que « et ensuite » annonce, et le jalon de la
   chronologie porte déjà cette couleur. Une seule couleur pour un seul fait. */
.slide[data-etat="retour"] .boucle--menue .boucle__trace[data-arc="retour"] {
  opacity: 1;
  animation: boucle-menue-tracer 720ms var(--ease-io) 160ms both;
}
.slide[data-etat="retour"] .boucle--menue .boucle__sens[data-arc="retour"],
.slide[data-etat="retour"] .boucle--menue .boucle__pointe[data-arc="retour"] { opacity: 1; }

/* La couleur du retour, tracé comme pointe. */
.boucle--menue .boucle__trace[data-arc="retour"] { stroke: var(--data-warm); }
.boucle--menue .boucle__sens[data-arc="retour"] { fill: var(--data-warm); }

/* Les pointes de flèche paraissent AVEC leur arc, pas avant : une flèche qui
   attend au bout d'un trait qui n'est pas encore tiré annonce la fin avant le
   geste. */
.boucle__pointe {
  fill: var(--brand-primary);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out) 420ms;
}
.boucle__pointe[data-arc="retour"] { fill: var(--data-warm); }

@keyframes boucle-menue-tracer { to { stroke-dashoffset: 0; } }

@media print {
  .boucle--menue .boucle__trace { opacity: 1 !important; stroke-dashoffset: 0 !important;
                                  animation: none !important; }
  .boucle--menue .boucle__sens { opacity: 1 !important; }
  /* Les pointes aussi : une flèche sans tête n'est plus une flèche. */
  .boucle--menue .boucle__pointe { opacity: 1 !important; transition: none !important; }
}

.boucle__trace {
  fill: none;
  stroke: var(--brand-primary);
  stroke-width: 3.5;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  opacity: 0;
}
/* Le segment qui file, et sa couleur de phase. La menue garde son tracé qui
   pousse : chez elle les arcs s'allument avec les faits et RESTENT allumés. */
.boucle:not(.boucle--menue) .boucle__trace,
.schema:not(.boucle--menue) .boucle__trace { stroke-dasharray: 0.2 0.8; }
.schema:not(.boucle--menue) .boucle__trace[data-arc="aller"]  { stroke: var(--brand-primary); }
.schema:not(.boucle--menue) .boucle__trace[data-arc="retour"] { stroke: var(--chaud, var(--data-warm)); }

@media (prefers-reduced-motion: no-preference) {
  .slide.est-active .boucle__point {
    offset-path: path("M312 108C400 58 460 58 548 108L548 142C460 192 400 192 312 142Z");
    offset-rotate: 0deg;
    animation: parcourir 5.5s linear 1400ms infinite;
  }
  /* Chaque libellé s'allume quand le point passe sur son arc.

     SAUF DANS LA VARIANTE MENUE, qui ne tourne pas : ses arcs s'allument avec
     les faits de la chronologie et RESTENT allumés. Une animation infinie sur
     l'opacité l'emporte sur un « opacity: 1 » statique, quelle que soit la
     spécificité — les libellés restaient donc invisibles à l'écran, alors
     qu'ils s'imprimaient (le bloc print force leur opacité). Un défaut qui ne
     se voyait que sur écran, et jamais sur le PDF qu'on relit. */
  .slide.est-active .boucle:not(.boucle--menue) .boucle__sens,
  .slide.est-active .schema:not(.boucle--menue) > .boucle__sens {
    animation: souligner 5.5s linear 1400ms infinite;
  }
  .slide.est-active .boucle:not(.boucle--menue) .boucle__sens[data-arc="retour"],
  .slide.est-active .schema:not(.boucle--menue) > .boucle__sens[data-arc="retour"] {
    animation-delay: 4.15s;   /* 1 400 ms de départ + la demi-période */
  }

  /* Les deux traces suivent le point, puis s'effacent ensemble à la fin du
     tour — sinon la reprise se verrait. */
  .slide.est-active .schema:not(.boucle--menue) .boucle__trace[data-arc="aller"]  { animation: tracer-aller 5.5s linear 1400ms infinite; }
  .slide.est-active .schema:not(.boucle--menue) .boucle__trace[data-arc="retour"] { animation: tracer-retour 5.5s linear 1400ms infinite; }
}
/* ELLE COLORE EN SE DÉPLAÇANT, ET LE GRIS REVIENT DERRIÈRE (26 août).
   La trace poussait comme une ligne qui s'allonge, tenait allumée jusqu'à la
   fin du tour, puis s'éteignait d'un coup — la reprise se voyait, et le
   circuit paraissait s'accumuler au lieu de circuler.
   C'est maintenant un SEGMENT QUI FILE : un cinquième de l'arc, allumé, qui
   suit la boule. L'arc gris est dessous en permanence ; il réapparaît de
   lui-même dans son sillage. Rien ne s'éteint, la couleur passe. */
@keyframes tracer-aller {
  0%        { stroke-dashoffset: 0.2; opacity: 0; }
  4%        { opacity: 1; }
  46%       { stroke-dashoffset: -0.8; opacity: 1; }
  50%, 100% { stroke-dashoffset: -0.8; opacity: 0; }
}
/* Le cadre s'allume quand la trace l'atteint : l'arrivée se voit, pas
   seulement le trajet. */
@media (prefers-reduced-motion: no-preference) {
  /* Les cadres pulsent au rythme du point — donc PAS dans la variante menue,
     qui ne tourne pas. Sans cette exclusion, les deux termes clignotaient
     derrière une chronologie qui, elle, avance par à-coups : deux horloges
     dans la même image. */
  .slide.est-active .schema:not(.boucle--menue) .boucle__cadre {
    animation: allumer-cadre 5.5s linear 1400ms infinite;
  }
  .slide.est-active .schema:not(.boucle--menue) .boucle__cadre[data-terme="depart"] {
    animation-delay: 4.15s;   /* 1 400 ms de départ + la demi-période */
  }
}
.boucle__cadre { transition: stroke var(--dur-2) var(--ease-io); }
/* Le cadre s'allume dans la couleur de la phase QUI VIENT D'Y ARRIVER : le
   terme d'arrivée reçoit le turquoise de l'aller, le terme de départ reçoit le
   bleu du retour. C'est la même information que la boule, redite là où l'œil
   se pose — et c'est ce qui fait qu'on suit un circuit plutôt qu'un aller. */
@keyframes allumer-cadre {
  0%, 34%   { stroke: var(--line-strong); stroke-width: 1; }
  46%, 62%  { stroke: var(--brand-primary); stroke-width: 3; }
  76%, 100% { stroke: var(--line-strong); stroke-width: 1; }
}
.slide.est-active .schema:not(.boucle--menue) .boucle__cadre[data-terme="depart"] {
  animation-name: allumer-cadre-retour;
}
@keyframes allumer-cadre-retour {
  0%, 34%   { stroke: var(--line-strong); stroke-width: 1; }
  46%, 62%  { stroke: var(--chaud, var(--data-warm)); stroke-width: 3; }
  76%, 100% { stroke: var(--line-strong); stroke-width: 1; }
}

@keyframes tracer-retour {
  0%, 50%   { stroke-dashoffset: 0.2; opacity: 0; }
  54%       { opacity: 1; }
  96%       { stroke-dashoffset: -0.8; opacity: 1; }
  99%, 100% { stroke-dashoffset: -0.8; opacity: 0; }
}
/* 1 400 MS AVANT DE PARTIR. Le circuit démarrait en même temps que le contenu :
   deux mouvements se disputaient l'œil, et on ne lisait ni le titre ni la
   figure. La slide se pose d'abord — `arrive` met 420 ms, échelonné jusqu'à
   180 —, puis le circuit s'ébranle. Toutes les pièces partagent ce départ,
   sinon elles se déphasent. */
@keyframes parcourir { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@keyframes souligner {
  0%, 4%   { fill: var(--muted); }
  10%, 40% { fill: var(--brand-primary); }
  50%, 100%{ fill: var(--muted); }
}

/* --- La balance qui penche ------------------------------------------------
   Le fléau entier pivote sur son axe. Les états viennent des fragments. */
.balance__fleau {
  transform-origin: 100px 47px;
  transition: transform var(--dur-3) var(--ease-io);
}
.slide[data-etat="stigmatisation"] .balance__fleau { transform: rotate(-7deg); }
.slide[data-etat="ethnocentrisme"] .balance__fleau { transform: rotate(7deg); }
.slide[data-etat="equilibre"]      .balance__fleau { transform: rotate(0deg); }

/* La rupture nommée passe au premier plan, l'autre recule — le survol qui
   isole de la charte, déclenché ici par l'apparition. */
.balance__rupture { transition: opacity var(--dur-2) var(--ease-io), filter var(--dur-2) var(--ease-io); }
.slide[data-etat="stigmatisation"] .balance__rupture[data-cote="droite"],
.slide[data-etat="ethnocentrisme"] .balance__rupture[data-cote="gauche"] { opacity: .38; filter: saturate(.6); }
.slide[data-etat="equilibre"] .balance__rupture { opacity: .5; filter: saturate(.6); }

/* =============================================================================
   6. MOUVEMENT
   Les jetons de la charte, rien d'autre. Le contrat prefers-reduced-motion
   de charte.css coupe tout ceci sans qu'on ait à le répéter.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  /* LE PASSAGE D'UNE SLIDE À L'AUTRE — deux manières, pas un catalogue.
     Le moteur n'a jamais fait défiler ni fondre la SLIDE : c'est son contenu
     qui arrive. Trois modèles de fondu de slide ont été essayés et écartés le
     26 août — croisé (deux textes à 0,5 font une soupe illisible), relayé
     (un creux de 30 ms qui fait clignoter du papier en quittant les six
     aplats navy), et sol-net-contenu-relayé (une transition ne démarre pas
     dans un sous-arbre qui sort de `display:none`, piège déjà consigné plus
     bas). Ce qui existait était le bon geste : il manquait de dire ce qu'il
     sert.                                                                   */

  /* 1 · L'ARRIVÉE ORDINAIRE — le propos se construit, donc il s'échelonne :
         le titre, puis le chapô, puis la figure. La cascade EST la lecture. */
  .slide.est-active > *:not(.contenu-prive-absent):not(.chapitre__numero):not(.brand-rule) {
    animation: arrive var(--dur-3) var(--ease-out) both;
  }
  .slide.est-active > *:nth-child(2) { animation-delay: 60ms; }
  .slide.est-active > *:nth-child(3) { animation-delay: 120ms; }
  .slide.est-active > *:nth-child(4) { animation-delay: 180ms; }

  /* 2 · LA RESPIRATION — portes de partie, repères, chute. Ce sont les
         ponctuations du deck, et une ponctuation ne se construit pas : elle
         se pose. Tout arrive ENSEMBLE (aucun décalage) et plus lentement —
         620 ms, le plancher de la démonstration, parce qu'ici le passage EST
         le propos : il dit « on s'arrête ». La cascade dirait l'inverse, elle
         ferait lire une suite là où il y a un point. */
  .slide.est-active:is([data-fond="navy"], .chapitre, .repere)
    > *:not(.contenu-prive-absent):not(.chapitre__numero):not(.brand-rule) {
    animation: arrive 620ms var(--ease-out) both;
    animation-delay: 0ms;
  }

  /* Le viseur `voir.py` photographie la scène demandée, pas l'arrivée
     générique de la slide. La couper évite une première image blanche sans
     toucher aux transformations internes auditionnées. */
  body[data-voir-rendu] .slide.est-active
    > *:not(.contenu-prive-absent):not(.chapitre__numero):not(.brand-rule) {
    animation: none;
  }

  .likert__seg { transform-origin: left center; animation: bar-grow var(--dur-3) var(--ease-out) both; }
  .likert__ligne:nth-child(2) .likert__seg { animation-delay: 80ms; }
  .likert__ligne:nth-child(3) .likert__seg { animation-delay: 160ms; }
}
/* =============================================================================
   7. VUE D'ENSEMBLE (touche O)
   ========================================================================== */

.apercu {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  overflow-y: auto;
  padding: var(--space-6) var(--space-8) var(--space-8);
  background-color: var(--ink);
}
body.mode-apercu .apercu { display: block; }
body.mode-apercu .stage  { display: none; }

.apercu__grille {
  display: grid;
  gap: var(--space-8);
  max-width: 1400px;
  margin: 0 auto;
}
.apercu__tete {
  position: sticky;
  top: calc(-1 * var(--space-6));
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  max-width: 1400px;
  min-height: 64px;
  margin: 0 auto var(--space-6);
  padding: var(--space-3) 0;
  background-color: var(--ink);
  color: var(--paper-bright);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.apercu__tete > div:first-child { display: grid; gap: var(--space-1); }
.apercu__tete strong { font-size: var(--fs-500); }
.apercu__tete span { color: rgba(255,255,255,.62); font-size: var(--fs-100); }
.apercu__actions { display: flex; gap: var(--space-3); }
.apercu__fermer,
.apercu__lecture,
.apercu__telecommande,
.apercu__chercher {
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid rgba(255,255,255,.38);
  border-radius: var(--radius-sm);
  background-color: transparent;
  color: var(--paper-bright);
  font: 700 var(--fs-100)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  cursor: pointer;
}
.apercu__lecture { display: none; }
.apercu__fermer:focus-visible,
.apercu__lecture:focus-visible,
.apercu__telecommande:focus-visible,
.apercu__chercher:focus-visible {
  outline: 3px solid var(--brand-focus);
  outline-offset: 2px;
}
.apercu__mouvement {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-4);
}
.apercu__mouvement h2 {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--brand-focus);
  font: 700 var(--fs-200)/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.apercu__groupe {
  padding: 0;
  border: 0;
  background-color: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
}
.vignette {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 2px solid transparent;
  background: var(--paper);
  cursor: pointer;
  overflow: hidden;
}
.vignette.est-active { border-color: var(--brand-focus); }
.vignette__cadre { width: 1280px; height: 720px; transform-origin: top left; pointer-events: none; }
.vignette__cadre .slide > * { animation: none !important; }
.vignette__num {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 4px 10px;
  background: var(--ink);
  color: var(--paper-bright);
  font-size: var(--fs-100);
  font-weight: 700;
}
/* =============================================================================
   8. MODE PRÉSENTATEUR (touche P — seconde fenêtre)
   ========================================================================== */

body.est-presentateur { overflow: hidden; background-color: var(--ink); }
body.est-presentateur .stage,
body.est-presentateur .apercu { display: none; }

.presentateur {
  display: none;
  width: 100vw;
  height: 100vh;
  min-height: 0;
  overflow: hidden;
  padding: var(--space-6);
  gap: var(--space-6);
  grid-template-columns: 1.5fr 1fr;
  grid-template-rows: auto minmax(0, 1fr);
  color: var(--paper-bright);
}
body.est-presentateur .presentateur { display: grid; }

.pres__barre {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-variant-numeric: tabular-nums;
}
.pres__chrono {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-800);
  letter-spacing: -0.04em;
}
.pres__horloge { color: rgba(255,255,255,.62); font-size: var(--fs-300); }
.pres__position { margin-left: auto; color: rgba(255,255,255,.78); font-size: var(--fs-200); font-weight: 700; }

/* Cible tactile : 44 × 44 px, sans exception. */
.pres__bouton {
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid rgba(255,255,255,.28);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--paper-bright);
  cursor: pointer;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  transition: background var(--dur-1) var(--ease-out);
}
.pres__bouton:hover { background: rgba(255,255,255,.14); }

.pres__scene, .pres__cote {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.pres__etiquette {
  color: var(--brand-focus);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.pres__vue { position: relative; aspect-ratio: 16 / 9; background: var(--paper); overflow: hidden; }
.pres__vue .vignette__cadre { position: absolute; top: 0; left: 0; }
.pres__suivante { flex: none; }
.pres__notes {
  flex: 1;
  padding: var(--space-6);
  border-radius: var(--radius-md);
  background: rgba(255,255,255,.08);
  color: rgba(255,255,255,.92);
  font-family: Georgia, "Times New Roman", serif;
  /* Réglable, et retenue : à un mètre d'un écran de portable, c'est le
     premier geste qu'on cherche. Le --fs-400 de la charte reste le défaut. */
  font-size: var(--pres-notes, var(--fs-400));
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-y: auto;
}
.pres__assistant { position: relative; }
.pres__assistant > summary {
  display: inline-flex;
  align-items: center;
  list-style: none;
}
.pres__assistant > summary::-webkit-details-marker { display: none; }
.pres__assistant-contenu {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: 4;
  display: grid;
  gap: var(--space-3);
  width: min(390px, 80vw);
  padding: var(--space-4);
  border: 1px solid rgba(255,255,255,.28);
  border-radius: var(--radius-sm);
  background-color: var(--ink);
  color: var(--paper-bright);
  font: var(--fs-100)/1.45 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.pres__assistant-contenu p { margin: 0; }
.pres__assistant-contenu span { color: var(--brand-focus); }
.pres__bouton:focus-visible,
.pres__assistant > summary:focus-visible {
  outline: 3px solid var(--brand-focus);
  outline-offset: 2px;
}

@media screen and (max-height: 820px) {
  .presentateur { padding: var(--space-3); gap: var(--space-3); }
  .pres__chrono { font-size: var(--fs-700); }
  .pres__scene, .pres__cote { gap: var(--space-2); }
  .pres__notes { padding: var(--space-4); line-height: 1.45; }
  .pres__amorce { margin-top: 0; font-size: var(--fs-100); }
}
.pres__notes:empty::before {
  content: "Pas de note pour cette slide.";
  color: rgba(255,255,255,.38);
  font-style: italic;
}

/* L'écart au budget — la seule question qui vaille pendant qu'on parle.
   Le retard est chaud, l'avance froide, et « à l'heure » ne crie pas. */
.pres__ecart { display: flex; flex-direction: column; line-height: 1.15; }
.pres__ecart b { font-size: var(--fs-300); font-weight: 800; letter-spacing: -0.01em; }
.pres__ecart span { color: rgba(255,255,255,.55); font-size: var(--fs-100); }
.pres__ecart.est-juste b   { color: rgba(255,255,255,.82); }
.pres__ecart.est-retard b  { color: var(--data-warm); }
.pres__ecart.est-avance b  { color: var(--brand-focus); }

/* L'amorce : la première phrase de ce qui vient, pour enchaîner sans blanc. */
.pres__amorce {
  margin: var(--space-2) 0 0;
  color: rgba(255,255,255,.68);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-200);
  font-style: italic;
  line-height: 1.45;
}

.pres__taille { float: right; display: flex; gap: 4px; }
.pres__bouton--menu { padding: 1px 8px; font-size: var(--fs-100); }

/* Quand la salle est devant un écran noir, le présentateur le DIT — sinon on
   parle par-dessus une image que personne ne voit. */
.pres__noir { display: none; }
body.pres-noir .pres__noir {
  display: inline-block;
  margin-left: var(--space-3);
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  background: var(--data-warm);
  color: #fff;
  letter-spacing: 0.08em;
}
body.pres-noir .pres__scene .pres__vue { opacity: .28; }

/* Le sas de lancement ne recompose pas la console : il rend seulement les
   cinq prérequis observables avant d'ouvrir la seconde fenêtre. */
.controle-lancement {
  position: fixed;
  z-index: 120;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background-color: rgba(15, 32, 46, .78);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.controle-lancement__boite {
  width: min(620px, calc(100vw - 48px));
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  padding: var(--space-8);
  border-radius: var(--radius-md);
  background-color: var(--paper-bright);
  color: var(--ink);
}
.controle-lancement__boite h2 {
  margin: var(--space-2) 0 var(--space-6);
  font: 400 var(--fs-700)/1.08 Georgia, "Times New Roman", serif;
  color: var(--brand-primary);
}
.controle-lancement__liste { display: grid; gap: 1px; margin: 0; padding: 0; list-style: none; }
.controle-lancement__liste li {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-200);
}
.controle-lancement__liste span { color: var(--muted); }
.controle-lancement__noir,
.controle-lancement__annuler,
.controle-lancement__ouvrir {
  min-height: 44px;
  margin-top: var(--space-4);
  padding: 0 var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background-color: var(--paper);
  color: var(--ink);
  cursor: pointer;
  font: 700 var(--fs-100)/1 Inter, sans-serif;
}
.controle-lancement__noir { display: inline-flex; align-items: center; gap: var(--space-3); }
.controle-lancement__echantillon {
  width: 64px;
  height: 28px;
  border: 1px solid var(--line-strong);
  background-color: var(--paper-bright);
}
.controle-lancement__noir.est-teste .controle-lancement__echantillon { background-color: #000; }
.controle-lancement__etat-noir { min-height: 1.5em; margin: var(--space-2) 0 0;
                                 color: var(--muted); font-size: var(--fs-100); }
.controle-lancement__actions { display: flex; justify-content: flex-end; gap: var(--space-3); }
.controle-lancement__ouvrir { border-color: var(--brand-primary);
                               background-color: var(--brand-primary); color: white; }
.controle-lancement button:focus-visible { outline: 3px solid var(--brand-focus); outline-offset: 2px; }

/* --- Le plancher, rapporté à la durée annoncée ----------------------------
   Trois états, et le plus chaud ne dit qu'une chose : le plancher dépasse déjà
   ce qui est annoncé, donc la séance est trop longue. C'est le seul énoncé que
   cette mesure autorise — le chrono reste le seul juge du reste. */
.atelier__alerte {
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.atelier__alerte[data-etat="ok"]    { color: var(--muted);      background: var(--surface-tint); }
.atelier__alerte[data-etat="juste"] { color: var(--brand-primary); background: rgba(0, 102, 168, .1); }
.atelier__alerte[data-etat="trop"]  { color: #fff;              background: var(--data-warm); }

/* --- Le brouillon retrouvé ------------------------------------------------
   Une bande sobre, en tête de l'atelier. Elle ne réclame rien : deux issues
   également faciles, et elle disparaît dès qu'on tranche. */
.atelier__brouillon {
  grid-row: 2;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0 var(--space-6) var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--surface-tint);
  color: var(--ink);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-200);
}
.atelier__brouillon span { flex: 1; }
.atelier__brouillon b { color: var(--brand-primary); }

.atelier__bureau { display: none; }

@media screen and (max-width: 899px) {
  body.mode-atelier .atelier > .atelier__tete,
  body.mode-atelier .atelier > .atelier__brouillon,
  body.mode-atelier .atelier > .atelier__corps,
  body.mode-atelier .atelier > .atelier__zone--etagere { display: none; }

  body.mode-atelier .atelier__bureau {
    grid-row: 1 / -1;
    display: grid;
    place-items: center;
    align-content: center;
    gap: var(--space-6);
    padding: var(--space-8);
    background-color: var(--paper);
    color: var(--ink);
    text-align: center;
  }
  .atelier__bureau p {
    display: grid;
    gap: var(--space-3);
    max-width: 34ch;
    margin: 0;
    font-family: Georgia, "Times New Roman", serif;
    font-size: var(--fs-500);
    line-height: 1.45;
  }
  .atelier__bureau p strong {
    color: var(--brand-primary);
    font-size: var(--fs-700);
    line-height: 1.05;
  }
}

/* --- Le calque : le croquis rendu à sa slide ------------------------------
   Le tracé est en coordonnées de slide (1280 × 720) : superposé, il retombe
   exactement là où il a été fait, sur le contenu réel. C'est la seule façon
   de vérifier qu'une correction respecte le dessin — et de voir ce que Claude
   a vu. Sous l'écran noir, au-dessus de tout le reste. */
.calque {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 19;
  pointer-events: none;
}
/* Jamais à l'impression ni en projection : c'est un outil de relecture. */
@media print { .calque { display: none !important; } }

/* --- Commenter une slide, sans le crayon ---------------------------------
   Six croquis sur huit n'avaient aucun tracé : l'usage réel est de désigner
   et d'écrire. La barre s'ouvre sous la slide, sans toile ni mode de dessin. */
.commentaire {
  position: fixed;
  z-index: 55;
  left: 50%;
  bottom: 3vh;
  transform: translateX(-50%);
  display: none;
  align-items: flex-start;
  gap: var(--space-3);
  width: min(920px, 92vw);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--paper-bright);
  box-shadow: var(--shadow-dialog);
}
body.mode-commentaire .commentaire { display: flex; }
.commentaire__cible {
  flex: none;
  align-self: center;
  max-width: 22ch;
  color: var(--brand-primary);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
}
.commentaire__champ {
  flex: 1;
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  line-height: 1.45;
  resize: vertical;
}
.commentaire__champ:focus-visible { outline: 2px solid var(--brand-focus); outline-offset: 1px; }
.commentaire__etat {
  flex: none;
  align-self: center;
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
}
.commentaire__etat.est-erreur { color: var(--data-warm); }
@media print { .commentaire { display: none !important; } }

/* --- L'écran noir (touche N) ----------------------------------------------
   Rendre la salle à soi le temps d'un échange, ou couvrir une transition. On
   couvre l'image, on ne quitte pas la slide : elle est toujours là dessous, et
   elle revient où on l'avait laissée. Un voile, pas un changement d'état. */
.deck::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 30;
  background: #000;
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity var(--dur-3) var(--ease-io), visibility var(--dur-3);
}
body.mode-noir .deck::after { opacity: 1; visibility: visible; }
/* Jamais à l'impression : un PDF tout noir n'est le souhait de personne. */
@media print { .deck::after { display: none !important; } }

/* --- Aller à une slide au clavier ---------------------------------------
   Les chiffres tapés, montrés le temps qu'on les compose. Sans repère on tape
   à l'aveugle ; avec un repère qui reste, on encombre l'écran. Il s'efface
   donc tout seul au bout de deux secondes. */
.saut {
  position: fixed;
  z-index: 60;
  left: 50%;
  bottom: 8vh;
  transform: translateX(-50%);
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
  background: rgba(15, 23, 42, .92);
  box-shadow: var(--shadow-dialog);
  font-family: Inter, system-ui, sans-serif;
}
body.mode-saut .saut { display: flex; }
.saut b {
  color: #fff;
  font-size: var(--fs-700);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.saut span {
  color: rgba(255,255,255,.6);
  font-size: var(--fs-100);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.saut.est-hors b { color: var(--data-warm); }
@media print { .saut { display: none !important; } }
/* --- Démarrer une présentation publique ---------------------------------
   Une seule commande reste visible hors du cadre. Le panneau ne rivalise
   jamais avec la slide : il disparaît au plein écran, au tirage et sur le
   poste téléphone. */

.commande-publique,
.demarrage-presentation,
.telecommande { display: none; }

@media screen and (min-width: 901px) and (pointer: fine) {
  .commande-publique {
    position: fixed;
    right: var(--space-4);
    bottom: var(--space-4);
    z-index: 45;
    min-width: 116px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--brand-primary);
    border-radius: var(--radius-sm);
    background-color: var(--brand-primary);
    color: var(--paper-bright);
    font: 700 var(--fs-200)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
    cursor: pointer;
  }
  .commande-publique:hover { background-color: var(--ink); border-color: var(--ink); }
  .commande-publique:focus-visible {
    outline: 3px solid var(--brand-focus);
    outline-offset: 3px;
  }
  :fullscreen .commande-publique,
  body.mode-demarrage-presentation .commande-publique { display: none; }
}

.demarrage-presentation {
  position: fixed;
  inset: 0;
  z-index: 90;
  align-items: flex-end;
  justify-content: flex-end;
  padding: var(--space-4);
  background-color: rgba(8, 24, 43, .72);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
body.mode-demarrage-presentation .demarrage-presentation {
  display: flex;
  animation: interface-apparait var(--dur-2) var(--ease-out) both;
}
@keyframes interface-apparait { from { opacity: 0; } to { opacity: 1; } }

.demarrage__panneau {
  width: min(520px, calc(100vw - 2 * var(--space-4)));
  max-height: calc(100vh - 2 * var(--space-4));
  overflow-y: auto;
  padding: var(--space-6);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background-color: var(--paper-bright);
  color: var(--ink);
}
.demarrage__tete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}
.demarrage__tete span,
.demarrage__etape {
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.demarrage__tete h2 {
  margin: var(--space-1) 0 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -.025em;
}
.demarrage__fermer {
  min-width: 44px;
  min-height: 44px;
  border: 0;
  background-color: transparent;
  color: var(--muted);
  font: 400 var(--fs-600)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  cursor: pointer;
}
.demarrage__actions { display: grid; margin-top: var(--space-4); }
.demarrage__action {
  min-height: 74px;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-bottom: 1px solid var(--line);
  background-color: transparent;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.demarrage__action b { font-size: var(--fs-300); }
.demarrage__action span { color: var(--muted); font-size: var(--fs-100); }
.demarrage__action:hover { background-color: var(--surface-tint); }
.demarrage__action.est-primaire b { color: var(--brand-primary); }
.demarrage__outils {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  padding-top: var(--space-4);
}
.demarrage__outils button,
.demarrage__partage button {
  min-height: 44px;
  padding: var(--space-2) 0;
  border: 0;
  border-bottom: 1px solid var(--line-strong);
  background-color: transparent;
  color: var(--brand-primary);
  font: 700 var(--fs-100)/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  cursor: pointer;
}
.demarrage__indisponible,
.demarrage__aide,
.demarrage__etat,
.demarrage__connecte {
  margin: var(--space-3) 0 0;
  color: var(--muted);
  font-size: var(--fs-100);
  line-height: 1.45;
}
.demarrage__etat.est-erreur { color: var(--data-warm); }
.demarrage__association,
.demarrage__session {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line-strong);
}
.demarrage__association[hidden],
.demarrage__session[hidden] { display: none !important; }
.demarrage__association label {
  display: block;
  margin: var(--space-3) 0 var(--space-2);
  color: var(--muted);
  font-size: var(--fs-100);
}
.demarrage__saisie { display: grid; grid-template-columns: 1fr auto; gap: var(--space-2); }
.demarrage__saisie input,
.demarrage__saisie button {
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font: 600 var(--fs-200)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.demarrage__saisie input { background-color: var(--paper-bright); color: var(--ink); }
.demarrage__saisie button {
  background-color: var(--brand-primary);
  color: var(--paper-bright);
  cursor: pointer;
}
.demarrage__session > p { margin: var(--space-2) 0; line-height: 1.45; }
.demarrage__code {
  display: block;
  margin: var(--space-4) 0;
  color: var(--brand-primary);
  font: 400 var(--fs-900)/1 Georgia, "Times New Roman", serif;
  letter-spacing: .14em;
}
.demarrage__partage { display: flex; gap: var(--space-4); }

/* --- Le poste orateur sur téléphone -------------------------------------
   Les notes sont la surface principale. Les trois commandes restent fixes,
   sous le pouce, avec la même hiérarchie dans tous les decks. */

body.mode-telecommande {
  overflow: hidden;
  background-color: var(--paper-bright);
}
body.mode-telecommande .stage,
body.mode-telecommande .nav__mobile,
body.mode-telecommande .commande-publique,
body.mode-telecommande .apercu,
body.mode-telecommande .aide { display: none !important; }
body.mode-telecommande .telecommande {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: block;
  background-color: var(--paper-bright);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.telecommande__association {
  width: min(560px, 100%);
  margin: 0 auto;
  padding: max(var(--space-8), env(safe-area-inset-top, 0px))
           max(var(--space-6), env(safe-area-inset-right, 0px))
           var(--space-8) max(var(--space-6), env(safe-area-inset-left, 0px));
}
.telecommande__association[hidden],
.telecommande__poste[hidden] { display: none !important; }
.telecommande__marque {
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.telecommande__association h1 {
  margin: var(--space-4) 0 var(--space-2);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-800);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -.035em;
}
.telecommande__association > p {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-300);
  line-height: 1.5;
}
.telecommande__formulaire { display: grid; gap: var(--space-3); margin-top: var(--space-8); }
.telecommande__formulaire label {
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 700;
}
.telecommande__formulaire input {
  width: 100%;
  min-height: 64px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background-color: var(--paper-bright);
  color: var(--ink);
  font: 400 var(--fs-700)/1 Georgia, "Times New Roman", serif;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.telecommande__formulaire button {
  min-height: 54px;
  border: 1px solid var(--brand-primary);
  border-radius: var(--radius-sm);
  background-color: var(--brand-primary);
  color: var(--paper-bright);
  font: 700 var(--fs-200)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.telecommande__message { min-height: 1.5em; margin-top: var(--space-3); color: var(--data-warm); }
.telecommande__poste { height: 100%; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
.telecommande__tete {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--space-3);
  min-height: 58px;
  padding: max(var(--space-3), env(safe-area-inset-top, 0px))
           max(var(--space-4), env(safe-area-inset-right, 0px)) var(--space-3)
           max(var(--space-4), env(safe-area-inset-left, 0px));
  border-bottom: 1px solid var(--line);
}
.telecommande__connexion,
.telecommande__chrono,
.telecommande__tete button {
  font-size: var(--fs-100);
  font-weight: 700;
}
.telecommande__connexion { color: var(--muted); }
.telecommande__connexion[data-etat="connecte"] { color: var(--brand-primary); }
.telecommande__chrono { color: var(--ink); font-variant-numeric: tabular-nums; }
.telecommande__tete button {
  min-height: 44px;
  border: 0;
  background-color: transparent;
  color: var(--brand-primary);
}
.telecommande__contenu {
  overflow-y: auto;
  padding: var(--space-6) max(var(--space-6), env(safe-area-inset-right, 0px))
           var(--space-8) max(var(--space-6), env(safe-area-inset-left, 0px));
}
.telecommande__position {
  margin: 0 0 var(--space-3);
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.telecommande__titre {
  margin: 0 0 var(--space-6);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-700);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -.03em;
}
.telecommande__notes {
  white-space: pre-line;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  line-height: 1.6;
}
.telecommande__ensuite {
  margin-top: var(--space-8);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}
.telecommande__ensuite span {
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.telecommande__ensuite p {
  margin: var(--space-2) 0 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  line-height: 1.3;
}
.telecommande__commandes {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-2);
  padding: var(--space-3) max(var(--space-3), env(safe-area-inset-right, 0px))
           calc(var(--space-3) + env(safe-area-inset-bottom, 0px))
           max(var(--space-3), env(safe-area-inset-left, 0px));
  border-top: 1px solid var(--line-strong);
  background-color: var(--paper-bright);
}
.telecommande__commandes button {
  min-width: 44px;
  min-height: 58px;
  display: grid;
  place-items: center;
  gap: var(--space-1);
  padding: var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background-color: var(--paper-bright);
  color: var(--brand-primary);
  font: 700 var(--fs-400)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.telecommande__commandes button span { font-size: var(--fs-100); }
.telecommande__commandes button.est-primaire {
  border-color: var(--brand-primary);
  background-color: var(--brand-primary);
  color: var(--paper-bright);
}
.telecommande__commandes button.est-actif {
  background-color: var(--ink);
  border-color: var(--ink);
  color: var(--paper-bright);
}
.telecommande__commandes button:disabled { opacity: .42; }

@media (max-width: 620px) {
  .demarrage-presentation { padding: 0; align-items: stretch; }
  .demarrage__panneau { width: 100%; max-height: 100%; border: 0; border-radius: 0; }
  .demarrage__saisie { grid-template-columns: 1fr; }
  .telecommande__titre { font-size: var(--fs-600); }
}

@media (prefers-reduced-motion: reduce) {
  body.mode-demarrage-presentation .demarrage-presentation { animation: none; }
}

@media print {
  .commande-publique,
  .demarrage-presentation,
  .telecommande { display: none !important; }
}
/* =============================================================================
   9. AIDE (touche ?)
   ========================================================================== */

.aide {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: none;
  place-items: center;
  background: rgba(15, 23, 42, .92);
}
body.mode-aide .aide { display: grid; }
.aide__carte { max-width: 620px; }
.aide__carte h2 {
  margin: 0 0 var(--space-6);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-700);
  font-weight: 400;
  letter-spacing: -0.045em;
}
.aide__carte dl {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: var(--space-3) var(--space-4);
  margin: 0;
  font-size: var(--fs-200);
}
.aide__carte dt { font-weight: 700; }
.aide__carte dd { margin: 0; color: var(--muted); }
kbd {
  display: inline-block;
  padding: 3px 8px;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--surface-tint);
  color: var(--brand-primary);
  font-family: inherit;
  font-size: var(--fs-100);
  font-weight: 700;
}

/* =============================================================================
   10. IMPRESSION → PDF
   « Tout doit s'imprimer. »
   Chrome : ⌘P, Paysage, marges « Aucune », et cocher
   « Graphiques d'arrière-plan » pour les aplats.
   ========================================================================== */

@page {
  size: 338.7mm 190.5mm;   /* = 1280 × 720 px à 96 dpi = 13,33 × 7,5 pouces */
  margin: 0;
}

@media print {
  html, body { height: auto; overflow: visible; background: #fff; }

  .stage { position: static; display: block; }

  .deck {
    position: static;
    left: auto;
    top: auto;
    width: 1280px;
    height: auto;
    transform: none !important;
  }

  .slide {
    position: relative;
    display: flex !important;
    /* cf. .slide.est-active.couverture — le !important d'impression doit
       laisser les gabarits en grille tranquilles (règle juste en dessous). */
    width: 1280px;
    height: 720px;
    overflow: hidden;
    break-after: page;
    page-break-after: always;
  }
  .slide.couverture,
  .slide.chapitre { display: grid !important; }

  .slide:last-of-type { break-after: auto; page-break-after: auto; }

  .slide > * { animation: none !important; }

  /* CE QUE CHROME NE FAIT PAS AU TIRAGE : jouer les animations.
     Il rend la PREMIÈRE image, et ni raccourcir la durée ni poser un délai
     négatif n'y change quoi que ce soit — les deux ont été essayés et le PDF
     est ressorti identique au pixel près. Un effet qui pousse depuis zéro doit
     donc poser son état d'arrivée EXPLICITEMENT ici. C'est la règle qu'ont
     suivie les rattrapages par schéma plus bas, et elle vaut pour tout schéma
     neuf : ce que le mouvement démontre, l'impression doit l'écrire.

     deck.js pose de son côté ce qui ne dépend pas d'une animation — fragments
     révélés, data-etat final, avancement propre à chaque page. */
  .ecartee > span::after { width: 100% !important; animation: none !important; }
  .chrono__barre         { width: 100% !important; animation: none !important; }

  /* Une slide imprimée est une slide finie : tous ses fragments paraissent. */
  .fragment { opacity: 1 !important; transform: none !important; }
  .fragment[data-muet] { display: none !important; }
  .balance__rupture { opacity: 1 !important; filter: none !important; }

  .apercu, .aide, .presentateur { display: none !important; }

  /* --- LE POLYCOPIÉ — une feuille par slide, notes comprises -------------
     Le tirage ordinaire montre la slide seule. Celui-ci montre aussi ce qui
     se dit par-dessus : c'est le support de répétition, et le seul livrable
     qui porte le cours plutôt que ses appuis. « ?polycopie » à l'ouverture.

     La slide garde ses 1280 × 720 et se réduit par transform : recalculer
     une mise en page à une autre largeur ferait des slides différentes de
     celles qu'on a réglées. */
  body.mode-polycopie .feuille {
    display: block;
    width: 1280px;
    height: 720px;
    overflow: hidden;
    break-after: page;
    page-break-after: always;
  }
  body.mode-polycopie .feuille:last-of-type { break-after: auto; page-break-after: auto; }

  body.mode-polycopie .feuille__cadre {
    width: 1280px;
    height: 382px;              /* 720 × 0,53, la place que la slide occupe */
    overflow: hidden;
  }
  body.mode-polycopie .feuille__cadre .slide {
    transform: scale(.53);
    transform-origin: top center;
    break-after: auto !important;
    page-break-after: auto !important;
    box-shadow: none;
  }

  body.mode-polycopie .feuille__notes {
    height: 338px;
    padding: 18px 64px 0;
    column-count: 2;
    column-gap: 40px;
    border-top: 1px solid var(--line);
    font-family: Georgia, "Times New Roman", serif;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--ink);
    overflow: hidden;
  }
  body.mode-polycopie .feuille__notes p { margin: 0 0 .52em; }

  /* LA FEUILLE NE S'ÉTIRE PAS, ET LE TROP-PLEIN NE SE VOYAIT PAS. Le bloc a
     une hauteur fixe : au-delà, le texte part dans une troisième colonne,
     hors feuille, que `overflow:hidden` efface sans bruit. En répétition, ça
     veut dire une fin de note manquante que rien n'annonce.

     Ces règles vivant sous @media print, aucune mesure JS à l'écran ne dit la
     vérité du tirage : le calibre se décide au montage, sur le nombre de
     signes et d'alinéas (`data-densite`). Deux colonnes dans tous les cas —
     changer le nombre de colonnes d'une feuille à l'autre se voit, pas une
     demi-graduation de corps. */
  body.mode-polycopie .feuille__notes[data-densite="serree"] {
    font-size: 11.5px;
    line-height: 1.44;
  }
  body.mode-polycopie .feuille__notes[data-densite="serree"] p { margin: 0 0 .44em; }
  body.mode-polycopie .feuille__notes[data-densite="dense"] {
    font-size: 10.5px;
    line-height: 1.4;
  }
  body.mode-polycopie .feuille__notes[data-densite="dense"] p { margin: 0 0 .38em; }
  /* Les repères que Nicolas écrit en capitales dans ses notes — PREMIER TEMPS,
     ATTENTION, LA CHUTE — sont ce qu'on cherche des yeux en parlant. */
  body.mode-polycopie .feuille__notes strong,
  body.mode-polycopie .feuille__notes b {
    font-family: Inter, system-ui, sans-serif;
    font-size: 11px;
    letter-spacing: .04em;
    color: var(--brand-primary);
  }
  body.mode-polycopie .feuille__muette { color: var(--muted); font-style: italic; }

  *, *::before, *::after {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
}

/* --- La navigation ---------------------------------------------------------
   Une présentation ouverte en local est d'abord un document qu'on consulte :
   revenir au sommaire, sauter à une slide, retrouver un concept. D'où une
   chrome autour du cadre — barre en haut, rail à gauche, palette au centre.

   Elle disparaît dès qu'on projette. Les trois portes de sortie sont le plein
   écran, le mode présentateur et l'impression : dans les trois cas la slide
   doit occuper l'écran seule.
   -------------------------------------------------------------------------- */

.nav, .rail { display: none; }

body.mode-navigation .nav,
body.mode-navigation .rail { display: flex; }

/* La scène se replie sur ce qui reste. C'est elle qui donne son échelle au
   cadre — cf. ajusterEchelle(), qui mesure la scène et non la fenêtre. */
body.mode-navigation .stage {
  top: 46px;
  left: 244px;
}

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  height: 46px;
  align-items: center;
  gap: 18px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
  background: var(--paper-bright);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.nav__retour {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px;
  border-radius: 7px;
  color: var(--brand-primary);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  min-height: 44px;
}
.nav__retour:hover { background: rgba(0, 102, 168, .08); }
.nav__titre {
  flex: 1 1 210px;
  min-width: 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav__modes {
  display: flex;
  align-self: stretch;
  border-inline: 1px solid var(--line);
}
.nav__mode {
  min-height: 44px;
  padding: 0 10px;
  border: 0;
  border-bottom: 3px solid transparent;
  background-color: transparent;
  color: var(--muted);
  cursor: pointer;
  font: 700 11px/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.nav__mode[aria-pressed="true"] { border-bottom-color: var(--brand-primary); color: var(--brand-primary); }
.nav__mode:focus-visible, .nav__bouton:focus-visible, .nav__retour:focus-visible {
  outline: 3px solid var(--brand-focus);
  outline-offset: 2px;
}
.nav__outils { display: flex; gap: 6px; }
.nav [data-mode-outil] { display: none; }
.nav[data-mode="parcourir"] [data-mode-outil="parcourir"],
.nav[data-mode="construire"] [data-mode-outil="construire"],
.nav[data-mode="repeter"] [data-mode-outil="repeter"],
.nav[data-mode="diffuser"] [data-mode-outil="diffuser"] { display: inline-flex; }
.nav__bouton {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
}
.nav__bouton:hover { border-color: var(--brand-primary); color: var(--brand-primary); }
.nav__bouton kbd {
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--paper-bright);
  font-family: inherit;
  font-size: 10px;
  color: var(--muted);
}

/* --- L'édition en place --------------------------------------------------
   La barre change de visage : les outils s'effacent, l'état et les deux
   décisions prennent leur place. On doit voir d'un coup d'œil qu'on est en
   train d'écrire — c'est ce qui évite de fermer l'onglet sans enregistrer. */

.nav__edition { display: none; }
body.mode-edition .nav__outils { display: none; }
body.mode-edition .nav__modes { display: none; }
body.mode-edition .nav__edition { display: flex; align-items: center; gap: 12px; }
body.mode-edition .nav {
  background: #fff6f1;
  border-bottom-color: var(--data-warm);
}
.nav__etatEdition {
  font-size: 12px;
  color: var(--data-warm);
  font-weight: 600;
  white-space: nowrap;
}
.nav__etatEdition.est-erreur { text-decoration: underline; }

/* Ce qui est modifiable doit se voir sans maquiller la slide : rien au repos,
   un voile au survol, un liseré net quand on y est. */
body.mode-edition [contenteditable] {
  border-radius: 3px;
  outline: none;
  cursor: text;
}
body.mode-edition [contenteditable]:hover { background: rgba(235, 104, 52, .07); }
body.mode-edition [contenteditable]:focus {
  background: rgba(235, 104, 52, .10);
  box-shadow: 0 0 0 2px var(--data-warm);
}

/* --- Le rail des slides -------------------------------------------------- */

.rail {
  position: fixed;
  top: 46px;
  bottom: 0;
  left: 0;
  z-index: 35;
  width: 244px;
  flex-direction: column;
  gap: 1px;
  padding: 10px 8px 24px;
  border-right: 1px solid var(--line);
  background: var(--paper-bright);
  overflow-y: auto;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.rail__groupe { margin-bottom: 4px; }
.rail__mouvement {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 12px 0 6px;
  padding: 4px 8px;
  border-radius: 5px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--brand-accent);
  cursor: pointer;
  list-style: none;
}
.rail__mouvement::-webkit-details-marker { display: none; }
.rail__mouvement:hover { background: rgba(0, 102, 168, .07); }
.rail__groupe:first-child .rail__mouvement { margin-top: 2px; }

/* Un chevron qui dit dans quel sens ça va, et le nombre de slides repliées. */
.rail__nom::before {
  content: "▾ ";
  display: inline-block;
  transition: transform var(--dur-1) var(--ease-out);
}
.rail__groupe:not([open]) .rail__nom::before { content: "▸ "; }
.rail__combien {
  padding: 1px 6px;
  border-radius: 8px;
  background: rgba(0, 102, 168, .1);
  font-size: 9px;
  color: var(--muted);
  letter-spacing: 0;
}

.rail__ligne {
  display: grid;
  grid-template-columns: 22px 1fr;
  grid-template-areas: "num vue" ".   titre";
  gap: 4px 6px;
  width: 100%;
  padding: 6px 6px 8px;
  border: 0;
  border-radius: 7px;
  background: none;
  font-family: inherit;
  font-size: 11px;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
}
.rail__ligne:hover { background: rgba(0, 102, 168, .07); }
.rail__ligne.est-active { background: var(--brand-primary); color: #fff; }

.rail__num {
  grid-area: num;
  padding-top: 3px;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--muted);
}
.rail__ligne.est-active .rail__num { color: rgba(255, 255, 255, .78); }

/* La miniature : une vraie slide clonée, ramenée à l'échelle de la case. */
.rail__vue {
  grid-area: vue;
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--paper);
  overflow: hidden;
}
.rail__ligne.est-active .rail__vue { border-color: rgba(255, 255, 255, .55); }
.rail__vue .vignette__cadre {
  position: absolute;
  top: 0;
  left: 0;
  width: 1280px;
  height: 720px;
  transform-origin: 0 0;
  pointer-events: none;
}

/* Glisser-déposer : la vignette tirée s'efface, et un filet montre où elle
   tombera. Sans ce repère on lâche au jugé. */
.rail__ligne.est-tiree { opacity: .3; }
.rail__ligne[data-depot] { position: relative; }
.rail__ligne[data-depot]::after {
  content: "";
  position: absolute;
  left: 24px;
  right: 4px;
  height: 3px;
  border-radius: 2px;
  background: var(--brand-primary);
}
.rail__ligne[data-depot="avant"]::after { top: -2px; }

/* Le repère peut se poser sur n'importe quelle vignette, y compris depuis un
   intitulé ou le vide : c'est le rail qui décide, pas l'élément survolé. */
.rail__fond { min-height: 72px; }
.rail__ligne[data-depot="apres"]::after { bottom: -2px; }

.rail__titre {
  grid-area: titre;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- La répétition chronométrée ------------------------------------------ */

.nav__chrono, .nav__mesure {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 12px;
  white-space: nowrap;
}
.nav__mesure { color: var(--brand-accent); font-weight: 600; }
.nav__demandes {
  padding: 3px 9px;
  border-radius: 20px;
  background: var(--data-warm);
  color: #fff;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.nav__demandes[hidden] { display: none; }
.nav__chrono { color: var(--data-warm); font-weight: 700; }
body.mode-repetition [data-va="repeter"] {
  background: var(--data-warm);
  border-color: var(--data-warm);
  color: #fff;
}
body.mode-repetition [data-va="repeter"]::after { content: " — terminer"; }

/* La durée mesurée, posée sur la miniature. */
/* Une demande en attente, posée sur la vignette. Elle suit la slide même
   déplacée : c'est la clé stable qui l'attache, pas le numéro. */
.rail__demande {
  position: absolute;
  top: 4px;
  left: 4px;
  z-index: 2;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: grid;
  place-items: center;
  border-radius: 9px;
  background: var(--data-warm);
  color: #fff;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
}

.rail__duree {
  position: absolute;
  right: 4px;
  bottom: 4px;
  z-index: 2;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(255, 255, 255, .93);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 9px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.rail__duree[data-long] { background: var(--data-warm); color: #fff; font-weight: 700; }

/* --- Le croquis ----------------------------------------------------------
   Dessiner sur la slide pour dire ce qui ne va pas. La toile vit DANS le deck,
   donc dans le repère 1280 × 720 : le tracé reste superposable quelle que soit
   la taille de la fenêtre.
   -------------------------------------------------------------------------- */

.croquis { display: none; }
body.mode-croquis .croquis {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: block;
}
.croquis__toile {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  touch-action: none;          /* sinon le geste fait défiler au lieu de tracer */
  background: rgba(235, 104, 52, .04);
}
.croquis__traces path {
  fill: none;
  stroke: var(--data-warm);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* La barre est posée en unités de slide : elle grandit avec le cadre, ce qui
   est voulu — on la lit à la même distance que la slide. */
.croquis__barre {
  position: absolute;
  left: 24px;
  right: 24px;
  bottom: 20px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border: 1px solid var(--data-warm);
  border-radius: 10px;
  background: rgba(255, 255, 255, .96);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
}
.croquis__compte {
  color: var(--data-warm);
  font-weight: 600;
  white-space: nowrap;
}
.croquis__note {
  flex: 1;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
}
.croquis__note:focus { outline: none; border-color: var(--data-warm); }
.croquis__etat { color: var(--muted); font-size: 12px; white-space: nowrap; }

@media print { .croquis { display: none !important; } }

/* --- Le volet de notes ---------------------------------------------------
   Sous la slide, comme dans un logiciel de présentation. Modifiable en
   permanence : c'est du texte qui ne paraît jamais à l'écran.
   -------------------------------------------------------------------------- */

.volet-notes { display: none; }
body.mode-navigation .volet-notes {
  position: fixed;
  left: 244px;
  right: 0;
  bottom: 0;
  z-index: 30;
  display: grid;
  grid-template-rows: auto 1fr;
  height: 168px;
  border-top: 1px solid var(--line);
  background: var(--paper-bright);
}
body.mode-navigation .stage { bottom: 168px; }

/* Replié, il ne garde que sa poignée. */
body.volet-replie.mode-navigation .volet-notes { height: 34px; }
body.volet-replie.mode-navigation .stage { bottom: 34px; }
body.volet-replie .volet__champ { display: none; }

.volet__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--line);
}
.volet__replier {
  padding: 2px 8px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--muted);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
}
.volet__replier:hover { background: rgba(0, 102, 168, .08); color: var(--brand-primary); }
.volet__compte {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  color: var(--muted);
}
.volet__champ {
  width: 100%;
  height: 100%;
  padding: 10px 16px 14px;
  border: 0;
  background: none;
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: 13.5px;
  line-height: 1.5;
  resize: none;
}
.volet__champ:focus { outline: none; background: rgba(0, 102, 168, .03); }

/* Enregistrer ne paraît que s'il y a quelque chose à enregistrer. */
.nav__sauver { display: none; }
body.a-des-modifications .nav__sauver { display: inline-flex; }
body.mode-edition .nav__sauver { display: none; }

/* --- L'historique -------------------------------------------------------- */

.histo { display: none; }
body.mode-historique .histo {
  position: fixed;
  inset: 0;
  z-index: 86;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(15, 34, 52, .46);
}
.histo__boite {
  width: min(620px, 100%);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--paper);
  box-shadow: 0 28px 64px rgba(15, 34, 52, .3);
  overflow: hidden;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.histo__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--paper-bright);
  font-size: 14px;
}
.histo__note { display: block; margin-top: 3px; font-size: 11px; color: var(--muted); }
.histo__liste { max-height: 56vh; overflow-y: auto; }
.histo__ligne {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 14px;
  align-items: center;
  padding: 10px 18px;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
}
.histo__ligne:last-child { border-bottom: 0; }
.histo__quand { color: var(--ink); }

/* La couverture réemploie le panneau de l'historique : même forme, même
   place, un outil de moins à apprendre. */
.couv__ligne { grid-template-columns: 1fr auto auto; }
.couv__titre { color: var(--ink); }
.couv__part { font-variant-numeric: tabular-nums; color: var(--muted); }
.couv__etat {
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .04em;
  background: rgba(0, 102, 168, .1);
  color: var(--brand-primary);
}
.couv__ligne[data-etat="en réserve"] .couv__etat {
  background: var(--data-warm); color: #fff;
}
.couv__ligne[data-etat="effleuré"] .couv__etat {
  background: rgba(235, 104, 52, .16); color: var(--data-warm);
}
.couv__pied { padding: 12px 18px; }
.histo__compte { font-size: 11px; color: var(--muted); }

/* --- L'aperçu agrandi ----------------------------------------------------
   Une miniature de 200 px donne une forme, pas un contenu. Un clic l'agrandit
   pour qu'on décide, un double-clic agit.
   -------------------------------------------------------------------------- */

.loupe { display: none; }
body.mode-loupe .loupe {
  position: fixed;
  inset: 0;
  z-index: 85;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(15, 34, 52, .46);
}
.loupe__boite {
  width: min(1020px, 100%);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--paper);
  box-shadow: 0 28px 64px rgba(15, 34, 52, .3);
  overflow: hidden;
}
.loupe__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--paper-bright);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.loupe__titre { font-size: 14px; color: var(--ink); }
.loupe__source {
  margin-left: 12px;
  font-size: 12px;
  color: var(--muted);
}
.loupe__actions { display: flex; gap: 10px; }
.loupe__agir[hidden] { display: none; }
.loupe__corps { min-width: 0; }
.loupe__scene { position: relative; overflow: hidden; }
.loupe__cadre {
  width: 1280px;
  height: 720px;
  transform-origin: 0 0;
}
/* Dans la loupe, la slide occupe son cadre propre : la miniature clonée ne
   doit plus être réduite une seconde fois. */
.loupe__cadre .vignette__cadre {
  position: absolute;
  inset: 0;
  width: 1280px;
  height: 720px;
  transform: none !important;
}
.loupe--galerie .loupe__boite {
  width: min(1180px, 100%);
  max-height: calc(100vh - 48px);
}
.loupe--galerie .loupe__corps {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  min-height: 0;
}
.loupe--galerie .loupe__scene { align-self: start; }
.loupe__fiche {
  max-height: calc(100vh - 106px);
  overflow-y: auto;
  padding: 16px;
  border-left: 1px solid var(--line);
  background: var(--paper-bright);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.loupe__fiche[hidden] { display: none; }

/* --- Le menu d'une slide, au clic droit ----------------------------------
   Supprimer, dupliquer, déplacer — depuis le rail, sans changer de vue.
   -------------------------------------------------------------------------- */

.menu {
  position: fixed;
  z-index: 80;
  min-width: 196px;
  padding: 6px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--paper);
  box-shadow: 0 12px 32px rgba(15, 34, 52, .18);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.menu[hidden] { display: none; }
.menu__page[hidden] { display: none; }
/* La liste des formes : un nom, et ce que la forme sert à DIRE. Sans la
   seconde ligne, « Repère » et « Respiration » ne se distinguent pas — et on
   choisit au hasard, ce qui est exactement ce qu'on voulait éviter. */
.menu__gabarit {
  display: block; width: 100%; max-width: 268px;
  padding: 7px 10px; margin: 0;
  border: 0; border-radius: 6px; background: none;
  font-family: inherit; text-align: left; cursor: pointer;
}
.menu__gabarit:hover, .menu__gabarit:focus-visible { background: var(--line); }
.menu__gabarit b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }
.menu__gabarit span { display: block; margin-top: 1px; font-size: 11px; line-height: 1.35; color: var(--muted); }
.menu__retour { color: var(--muted) !important; font-size: 11px !important; }

/* L'ajout au pied du rail : discret tant qu'on ne le cherche pas, franc dès
   qu'on le survole. */
.rail__ajout {
  display: block; width: calc(100% - 16px);
  margin: 10px 8px 18px;
  padding: 9px 10px;
  border: 1px dashed var(--line-strong);
  border-radius: 8px;
  background: none;
  color: var(--muted);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 12px; text-align: center; cursor: pointer;
}
.rail__ajout:hover, .rail__ajout:focus-visible {
  border-color: var(--brand-primary); color: var(--brand-primary); border-style: solid;
}
.rail__ajout span { font-size: 14px; font-weight: 700; }
.menu__cible {
  margin: 0 0 4px;
  padding: 6px 10px 8px;
  border-bottom: 1px solid var(--line);
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.menu__geste {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.menu__geste:hover:not([disabled]) { background: rgba(0, 102, 168, .09); }
.menu__geste[disabled] { opacity: .35; cursor: default; }
.menu__geste.est-grave { color: var(--data-warm); }
.menu__geste.est-grave:hover:not([disabled]) { background: rgba(235, 104, 52, .12); }

/* --- La palette ---------------------------------------------------------- */

.palette { display: none; }
body.mode-palette .palette {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: block;
  background: rgba(15, 34, 52, .38);
}
.palette__boite {
  width: min(640px, calc(100vw - 40px));
  margin: 12vh auto 0;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--paper);
  box-shadow: 0 24px 60px rgba(15, 34, 52, .28);
  overflow: hidden;
}
.palette__champ {
  width: 100%;
  padding: 16px 18px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
}
.palette__champ:focus { outline: none; }
.palette__resultats { max-height: 52vh; overflow-y: auto; }

.palette__ligne {
  display: grid;
  grid-template-columns: 74px 1fr auto;
  gap: 12px;
  align-items: baseline;
  width: 100%;
  padding: 10px 18px;
  border: 0;
  background: none;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
}
.palette__ligne.est-visee { background: rgba(0, 102, 168, .09); }
.palette__genre {
  padding: 2px 7px;
  border-radius: 5px;
  background: var(--muted);
  color: #fff;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  text-align: center;
}
.palette__genre[data-genre="concept"] { background: var(--brand-accent); }
.palette__genre[data-genre="séquence"] { background: var(--brand-focus); color: var(--ink); }
.palette__genre[data-genre="deck"]    { background: var(--brand-primary); }
.palette__titre {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.palette__detail { font-size: 11px; color: var(--muted); white-space: nowrap; }
.palette__rien { padding: 18px; font-size: 13px; color: var(--muted); }

/* Les trois portes de sortie : plein écran, présentateur, impression. */
body.est-presentateur .nav,
body.est-presentateur .rail,
body.est-presentateur .volet-notes { display: none; }
body.est-presentateur.mode-navigation .stage { top: 0; left: 0; bottom: 0; }

@media print {
  .nav, .rail, .palette, .volet-notes { display: none !important; }
  body.mode-navigation .stage { top: 0; left: 0; bottom: 0; }
}
/* --- La navigation publique sur écran tactile ----------------------------
   Le clic dans une slide avance déjà. Sur un smartphone public, il n'existait
   aucun geste visible et fiable pour faire l'inverse. Cette barre vit hors du
   cadre 1280 × 720 et la scène lui cède sa hauteur : elle ne recouvre donc
   jamais une source, un numéro ou un fragment. */

.nav__mobile { display: none; }

@media screen and (max-width: 900px), screen and (pointer: coarse) {
  body[data-navigation-mobile] .stage {
    bottom: calc(60px + env(safe-area-inset-bottom, 0px));
  }
  body[data-navigation-mobile] .nav__mobile {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    min-height: calc(60px + env(safe-area-inset-bottom, 0px));
    display: grid;
    grid-template-columns: minmax(44px, 1fr) auto minmax(44px, 1fr);
    align-items: start;
    gap: var(--space-2);
    padding: var(--space-2) max(var(--space-3), env(safe-area-inset-right, 0px))
             calc(var(--space-2) + env(safe-area-inset-bottom, 0px))
             max(var(--space-3), env(safe-area-inset-left, 0px));
    border-top: 1px solid var(--line-strong);
    background-color: var(--paper-bright);
    font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  }
  .nav__mobile-bouton {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background-color: var(--paper-bright);
    color: var(--brand-primary);
    font-family: inherit;
    font-size: var(--fs-200);
    font-weight: 700;
    cursor: pointer;
  }
  .nav__mobile-bouton--suivant {
    border-color: var(--brand-primary);
    background-color: var(--brand-primary);
    color: var(--paper-bright);
  }
  .nav__mobile-bouton:focus-visible {
    outline: 3px solid var(--brand-primary);
    outline-offset: 2px;
    box-shadow: 0 0 0 6px var(--paper-bright);
  }
  .nav__mobile-bouton[disabled] {
    opacity: .38;
    cursor: default;
  }
  .nav__mobile-compteur {
    min-width: 68px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    font-family: inherit;
    font-size: var(--fs-200);
    font-variant-numeric: tabular-nums;
    border: 0;
    border-radius: var(--radius-sm);
    background-color: transparent;
    cursor: pointer;
  }
  .nav__mobile-compteur:focus-visible {
    outline: 3px solid var(--brand-primary);
    outline-offset: 2px;
    box-shadow: 0 0 0 6px var(--paper-bright);
  }

  .apercu { padding: var(--space-3); }
  .apercu__tete {
    top: calc(-1 * var(--space-3));
    align-items: flex-start;
    padding: var(--space-3) 0;
  }
  .apercu__tete > div:first-child span { display: none; }
  .apercu__actions { align-items: stretch; justify-content: flex-end; flex-wrap: wrap; }
  .apercu__lecture { display: inline-flex; align-items: center; }
  .apercu__fermer { width: 44px; padding: 0; font-size: 0; }
  .apercu__fermer::after { content: "×"; font-size: var(--fs-500); }
  .apercu__grille { gap: var(--space-6); }
  .apercu__mouvement {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
  }
  .apercu__mouvement h2 { font-size: var(--fs-100); }
  .apercu__mouvement:not(.est-ouvert) .vignette { display: none; }
  .apercu__groupe {
    width: 100%;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    cursor: pointer;
  }
  .apercu__groupe::after { content: "+"; font-size: var(--fs-300); font-weight: 400; }
  .apercu__mouvement.est-ouvert .apercu__groupe::after { content: "−"; }
  .apercu .vignette { min-width: 0; }
}

/* --- La transcription publique annexe -----------------------------------
   Le cadre projeté ne bouge pas. Cette couche, réservée à l'écran étroit,
   donne un ordre de lecture linéaire au même contenu visible. */
.lecture-mobile { display: none; }

@media screen and (max-width: 900px), screen and (pointer: coarse) {
  body.mode-lecture-mobile { overflow: hidden; }
  body.mode-lecture-mobile .lecture-mobile {
    position: fixed;
    inset: 0;
    z-index: 75;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    background-color: var(--paper-bright);
    color: var(--ink);
    font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  }
  .lecture-mobile__tete,
  .lecture-mobile__nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) max(var(--space-4), env(safe-area-inset-right, 0px))
             var(--space-3) max(var(--space-4), env(safe-area-inset-left, 0px));
    border-color: var(--line);
    background-color: var(--paper-bright);
  }
  .lecture-mobile__tete { border-bottom: 1px solid var(--line); }
  .lecture-mobile__nav {
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--line);
  }
  .lecture-mobile__position {
    color: var(--muted);
    font-size: var(--fs-200);
    font-variant-numeric: tabular-nums;
  }
  .lecture-mobile button {
    min-width: 44px;
    min-height: 44px;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--brand-primary);
    border-radius: var(--radius-sm);
    background-color: var(--paper-bright);
    color: var(--brand-primary);
    font: 700 var(--fs-200)/1 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  }
  .lecture-mobile button:focus-visible {
    outline: 3px solid var(--brand-primary);
    outline-offset: 2px;
  }
  .lecture-mobile button[disabled] { opacity: .38; }
  .lecture-mobile__contenu {
    overflow-y: auto;
    padding: var(--space-8) max(var(--space-6), env(safe-area-inset-right, 0px))
             var(--space-8) max(var(--space-6), env(safe-area-inset-left, 0px));
  }
  .lecture-mobile__kicker {
    margin: 0 0 var(--space-3);
    color: var(--brand-primary);
    font-size: var(--fs-100);
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
  }
  .lecture-mobile__titre {
    margin: 0 0 var(--space-6);
    font-family: Georgia, "Times New Roman", serif;
    font-size: var(--fs-700);
    font-weight: 400;
    line-height: 1.08;
    letter-spacing: -.035em;
  }
  .lecture-mobile__transcription {
    font-family: Georgia, "Times New Roman", serif;
    font-size: var(--fs-400);
    line-height: 1.6;
  }
  .lecture-mobile__transcription :is(p, blockquote, ul, ol, div) {
    margin-top: 0;
    margin-bottom: var(--space-4);
  }
  .lecture-mobile__transcription :is(div, span):empty { display: none; }
  .lecture-mobile__transcription blockquote {
    margin-inline: 0;
    padding-left: var(--space-4);
    border-left: 3px solid var(--brand-accent);
  }
  .lecture-mobile__transcription li + li { margin-top: var(--space-2); }
  .lecture-mobile__transcription mark {
    padding: 0 .08em;
    background-color: rgba(67, 127, 179, .18);
    color: inherit;
  }
  .lecture-mobile__perimetre,
  .lecture-mobile__source {
    margin: var(--space-6) 0 0;
    padding-top: var(--space-3);
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: var(--fs-100);
    line-height: 1.45;
  }
}

@media print {
  .nav__mobile { display: none !important; }
  .lecture-mobile { display: none !important; }
  body[data-navigation-mobile] .stage { bottom: 0 !important; }
}
/* --- L'atelier ------------------------------------------------------------
   Voir la présentation, et travailler dessus. Trois zones :

     le plan      les vignettes, à gauche — retirer, réordonner ;
     l'établi     la slide ouverte, en grand, texte et notes modifiables ;
     l'étagère    les séquences réutilisables, en bas.

   Rien n'est écrit tant qu'on n'enregistre pas — fermer annule, et c'est le
   seul « annuler » nécessaire.

   Le panneau vit hors du cadre 1280 × 720 : il est en unités d'écran, pas en
   unités de slide. C'est délibéré — c'est un outil de travail, pas une slide.
   -------------------------------------------------------------------------- */

.atelier { display: none; }
body.mode-atelier { overflow: hidden; }
body.mode-atelier .atelier {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  background: var(--paper);
}

.atelier__tete {
  grid-row: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 12px 22px;
  border-bottom: 1px solid var(--line);
  background: var(--paper-bright);
}
.atelier__reperes { display: flex; align-items: baseline; gap: 16px; }
.atelier__titre {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--brand-primary);
}
.atelier__compte,
.atelier__budget {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  color: var(--muted);
}
.atelier__budget { color: var(--brand-accent); font-weight: 600; }
.atelier__compte.est-erreur { color: var(--data-warm); font-weight: 600; }

.atelier__actions { display: flex; gap: 10px; }
.atelier__bouton {
  min-height: 44px;
  padding: 8px 16px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  cursor: pointer;
}
.atelier__bouton:hover { border-color: var(--brand-primary); }
.atelier__bouton.est-primaire {
  background: var(--brand-primary);
  border-color: var(--brand-primary);
  color: #fff;
}
.atelier__bouton[disabled] { opacity: .55; cursor: default; }

/* Le plan à gauche, l'établi à droite. */
.atelier__corps {
  grid-row: 3;
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  min-height: 0;
}
/* L'établi et l'intention se relaient dans la même colonne : on travaille sur
   une slide, ou sur le cadre de la séance, jamais sur les deux à la fois. */
.atelier__etabli,
.atelier__intention { grid-column: 2; grid-row: 1; }
.atelier__plan,
.atelier__etabli {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 10px;
  min-height: 0;
  padding: 14px 18px 16px;
  overflow-y: auto;
}
.atelier__plan { border-right: 1px solid var(--line); align-content: start; }
.atelier__etabli { grid-template-rows: auto auto auto auto; align-content: start; }

.atelier__zone--etagere {
  grid-row: 4;
  border-top: 1px solid var(--line);
  background: var(--paper-bright);
  padding: 12px 18px 16px;
  max-height: 30vh;
  overflow-y: auto;
}
.atelier__onglets {
  display: flex;
  gap: 4px;
  margin: -4px 0 10px;
  border-bottom: 1px solid var(--line);
}
.atelier__onglet {
  margin: 0 0 -1px;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-bottom-color: var(--line);
  background: transparent;
  color: var(--muted);
  font: 600 12px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  cursor: pointer;
}
.atelier__onglet[aria-selected="true"] {
  border-color: var(--line);
  border-bottom-color: var(--paper-bright);
  color: var(--brand-primary);
}
.atelier__onglet:focus-visible {
  outline: 2px solid var(--brand-primary);
  outline-offset: 2px;
}
[data-panneau-atelier][hidden] { display: none; }
.atelier__legende {
  margin: 0;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.atelier__grille {
  display: grid;
  gap: 10px;
  align-content: start;
}
.atelier__plan .atelier__grille { grid-template-columns: 1fr; }
.atelier__zone--etagere .atelier__grille {
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  margin-top: 10px;
}

/* L'intitulé d'un mouvement dans le plan de l'atelier, avec ses deux flèches :
   déplacer sept parties slide par slide n'est pas une organisation. */
.atelier__mouvement {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 10px 0 2px;
  padding: 4px 2px;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--brand-accent);
}
.atelier__mouvement > span { flex: 1; min-width: 0; }
.atelier__pousser {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--paper);
  color: var(--ink);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}
.atelier__pousser:hover:not([disabled]) {
  border-color: var(--brand-primary);
  color: var(--brand-primary);
}
.atelier__pousser[disabled] { opacity: .3; cursor: default; }

.atelier__carte {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  overflow: hidden;
  cursor: pointer;
}
.atelier__carte:hover { border-color: var(--brand-accent); }
.atelier__carte:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--paper-bright);
}
.atelier__carte.est-choisie {
  border-color: var(--brand-primary);
  box-shadow: 0 0 0 2px var(--brand-primary);
}
.atelier__carte.est-tiree { opacity: .35; }
.atelier__carte--source.est-posee {
  border-color: var(--brand-focus);
  box-shadow: 0 0 0 2px var(--brand-focus);
}

/* La miniature est une vraie slide clonée, ramenée à l'échelle de la carte. */
.atelier__carte .vignette__cadre {
  position: absolute;
  top: 0;
  left: 0;
  width: 1280px;
  height: 720px;
  transform-origin: 0 0;
  pointer-events: none;
}

.atelier__retirer,
.atelier__ajouter {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-1) var(--ease-out);
}
.atelier__carte:hover .atelier__retirer,
.atelier__carte:hover .atelier__ajouter,
.atelier__carte:focus-within .atelier__retirer,
.atelier__carte:focus-within .atelier__ajouter { opacity: 1; }
.atelier__retirer:focus-visible,
.atelier__ajouter:focus-visible,
.atelier__pousser:focus-visible,
.atelier__bouton:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--paper-bright);
}
.atelier__retirer:hover { background: var(--data-warm); border-color: var(--data-warm); color: #fff; }
.atelier__ajouter:hover { background: var(--brand-primary); border-color: var(--brand-primary); color: #fff; }

.atelier__carte .vignette__num {
  position: absolute;
  left: 6px;
  top: 6px;
  bottom: auto;
  z-index: 2;
  padding: 2px 7px;
  border-radius: 5px;
  background: rgba(255, 255, 255, .92);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  color: var(--muted);
}
.atelier__carte-titre {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 2;
  min-height: 30px;
  padding: var(--space-2) var(--space-3);
  overflow: hidden;
  background-color: rgba(255, 255, 255, .94);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs-100);
  font-weight: 700;
  line-height: 1.2;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
}
.atelier__source {
  position: absolute;
  left: 6px;
  bottom: 6px;
  z-index: 2;
  max-width: calc(100% - 12px);
  padding: 2px 7px;
  border-radius: 5px;
  background: var(--brand-primary);
  color: #fff;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 10px;
  letter-spacing: .03em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.atelier__filtre {
  display: flex;
  align-items: center;
  gap: 14px;
}
.atelier__filtre .atelier__legende { flex: none; }
.atelier__chercher {
  flex: 1;
  max-width: 340px;
  padding: 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 12px;
}
.atelier__chercher:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--paper-bright);
}
.atelier__compteFiltre {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  color: var(--muted);
}
.atelier__carte[hidden] { display: none; }

/* Le second rayon de l'étagère est un catalogue, jamais une palette
   d'insertion. Les cartes sont des boutons de lecture et ne bougent pas. */
.galerie__filtres { flex-wrap: wrap; gap: 8px 12px; }
.galerie__filtres .atelier__legende { margin-right: auto; }
.galerie__champ {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  font: 10px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.galerie__champ .atelier__chercher { width: 180px; }
.galerie__champ select {
  max-width: 150px;
  padding: 5px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
  font: 12px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.galerie__champ select:focus-visible {
  outline: 2px solid var(--brand-primary);
  outline-offset: 1px;
}
.galerie__option {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  font: 11px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  white-space: nowrap;
  cursor: pointer;
}
.galerie__option input {
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--brand-primary);
}
.galerie__option:focus-within {
  outline: 2px solid var(--brand-primary);
  outline-offset: 2px;
}
.galerie__etat {
  margin: 8px 0 0;
  color: var(--muted);
  font: 11px/1.35 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.atelier__zone--etagere .galerie__grille {
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
.galerie__carte {
  min-width: 0;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.galerie__carte:hover { border-color: var(--brand-accent); }
.galerie__carte:focus-visible {
  outline: 2px solid var(--brand-primary);
  outline-offset: 2px;
}
.galerie__carte[hidden] { display: none; }
.galerie__miniature {
  position: relative;
  display: grid;
  width: 100%;
  aspect-ratio: 16 / 9;
  place-items: center;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
  background-color: var(--paper);
  color: var(--muted);
  font: 11px/1.35 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.galerie__miniature .vignette__cadre {
  position: absolute;
  inset: 0 auto auto 0;
  width: 1280px;
  height: 720px;
  transform-origin: 0 0;
  pointer-events: none;
}
.galerie__miniature--reference,
.galerie__miniature--indisponible { padding: 12px; text-align: center; }
.galerie__carte-texte {
  display: block;
  min-height: 72px;
  padding: 8px 10px 9px;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.galerie__carte-texte strong {
  display: block;
  overflow: hidden;
  color: var(--ink);
  font-size: 12px;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.galerie__carte-texte > span {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 10px;
  line-height: 1.3;
}
.galerie__carte-texte .galerie__carte-verbes {
  color: var(--ink);
  font-weight: 600;
  letter-spacing: .02em;
}

/* Une galerie montre des états, jamais leur trajet. Le gel descend jusqu'aux
   pseudo-éléments et aux SVG ; le clone garde donc la valeur finale dictée
   par data-etat, sans transition ni boucle. */
.galerie__cadre[data-galerie-statique],
.galerie__cadre[data-galerie-statique] *,
.galerie__cadre[data-galerie-statique] *::before,
.galerie__cadre[data-galerie-statique] *::after {
  animation: none !important;
  transition: none !important;
  scroll-behavior: auto !important;
}
.galerie__source-indisponible {
  display: grid;
  width: 1280px;
  height: 720px;
  margin: 0;
  padding: 80px;
  place-items: center;
  background-color: var(--paper);
  color: var(--muted);
  font: 24px/1.4 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  text-align: center;
}
.galerie__controles {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.galerie__variante {
  display: grid;
  gap: 5px;
  color: var(--muted);
  font-size: 11px;
}
.galerie__variante select {
  width: 100%;
  padding: 7px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  line-height: 1.2;
}
.galerie__etats { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.galerie__etat-bouton {
  padding: 5px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  font: 11px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  cursor: pointer;
}
.galerie__etat-bouton[aria-pressed="true"] {
  border-color: var(--brand-primary);
  color: var(--brand-primary);
}
.galerie__etat-bouton:focus-visible {
  outline: 2px solid var(--brand-primary);
  outline-offset: 1px;
}
.galerie__etat-source {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 10px;
  line-height: 1.4;
  overflow-wrap: anywhere;
}
.galerie__metadonnees {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 9px 8px;
  margin: 14px 0 0;
  font-size: 10px;
  line-height: 1.4;
}
.galerie__metadonnees dt { color: var(--muted); font-weight: 600; }
.galerie__metadonnees dd { min-width: 0; margin: 0; overflow-wrap: anywhere; }
.galerie__metadonnees ul { margin: 0; padding: 0; list-style: none; }
.galerie__metadonnees li + li { margin-top: 5px; }
.galerie__metadonnees a { color: var(--brand-primary); }
.galerie__metadonnees code {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 8px;
  overflow-wrap: anywhere;
}
.galerie__provenance { display: grid; gap: 3px; }
.galerie__provenance strong { color: var(--ink); font-size: 10px; }
.galerie__banc-recette {
  position: fixed;
  top: 0;
  left: -100000px;
  width: 1280px;
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}
.galerie__banc-recette .galerie__cadre {
  position: relative;
  width: 1280px;
  height: 720px;
  transform: none !important;
}

.atelier__vide {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  color: var(--muted);
}

/* --- L'établi ----------------------------------------------------------- */

.etabli__tete {
  display: flex;
  align-items: baseline;
  gap: 14px;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 12px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.etabli__duree { color: var(--brand-accent); font-weight: 600; }

.etabli__scene {
  position: relative;
  width: 100%;
  max-width: 940px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
}
.etabli__cadre {
  width: 1280px;
  height: 720px;
  transform-origin: 0 0;
}
/* La slide sur l'établi n'est plus positionnée dans un deck : elle occupe
   son cadre propre. */
.etabli__cadre > .slide {
  position: relative;
  inset: auto;
  width: 1280px;
  height: 720px;
}

/* Ce qui est modifiable doit se voir, sans maquiller la slide. */
.etabli__cadre [contenteditable] { border-radius: 3px; outline: none; }
.etabli__cadre [contenteditable]:hover { background: rgba(0, 102, 168, .06); }
.etabli__cadre [contenteditable]:focus {
  background: rgba(0, 102, 168, .09);
  box-shadow: 0 0 0 2px var(--brand-focus);
}

.etabli__notes {
  width: 100%;
  min-height: 132px;
  padding: 12px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--paper-bright);
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: 14px;
  line-height: 1.5;
  resize: vertical;
}
.etabli__notes:focus-visible,
.etabli__champ:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--paper-bright);
}

.atelier__intention {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  gap: 10px;
  align-content: start;
  min-height: 0;
  padding: 14px 18px 16px;
  overflow-y: auto;
}
.atelier__intention[hidden],
.atelier__etabli[hidden] { display: none; }
.atelier__champIntention {
  min-height: 300px;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
}

.etabli__demande { display: grid; gap: 8px; }
.etabli__champ {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  resize: vertical;
}
.etabli__ligne { display: flex; align-items: center; gap: 12px; }
.etabli__etat {
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 12px;
  color: var(--muted);
}
.etabli__etat.est-erreur { color: var(--data-warm); font-weight: 600; }

@media print { .atelier { display: none !important; } }

/* --- Studio de scènes ---------------------------------------------------
   Il agrandit l'étagère seulement pendant l'audition. Les slides gardent
   leur cadre 1280 × 720 ; seule leur miniature change d'échelle. */

.atelier.atelier--scenes { grid-template-rows: auto auto minmax(150px, .45fr) minmax(0, 1fr); }
.atelier.atelier--scenes .atelier__zone--etagere { max-height: none; }
.scenes__filtres { flex-wrap: wrap; gap: 8px 12px; }
.scenes__filtres .atelier__legende { margin-right: auto; }
.scenes__studio {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 14px;
  min-height: 0;
  margin-top: 8px;
}
.scenes__liste {
  display: grid;
  gap: 7px;
  align-content: start;
  max-height: 48vh;
  overflow-y: auto;
  padding: 3px;
}
.scenes__carte {
  min-height: 64px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background-color: var(--paper);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.scenes__carte:hover,
.scenes__carte[aria-selected="true"] { border-color: var(--brand-primary); }
.scenes__carte[aria-selected="true"] { box-shadow: inset 3px 0 0 var(--brand-primary); }
.scenes__carte:focus-visible,
.scenes__variante:focus-visible,
.scenes__pilotage input:focus-visible,
.scenes__reduit:focus-within {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
}
.scenes__carte-verbe,
.scenes__carte strong,
.scenes__carte small,
.scenes__carte-statut { display: block; }
.scenes__carte-verbe {
  color: var(--brand-accent);
  font: 700 9px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: .07em;
}
.scenes__carte strong { margin-top: 3px; font: 700 12px/1.25 Inter, Avenir, "Helvetica Neue", Arial, sans-serif; }
.scenes__carte small { margin-top: 3px; color: var(--muted); font: 10px/1.25 Inter, Avenir, "Helvetica Neue", Arial, sans-serif; }
.scenes__carte-statut {
  margin-top: 5px;
  color: var(--brand-primary);
  font: 800 8px/1.2 Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.scenes__detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 250px;
  gap: 9px 14px;
  min-width: 0;
  max-height: 58vh;
  overflow-y: auto;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background-color: var(--paper);
  color: var(--ink);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
}
.scenes__entete,
.scenes__variantes,
.scenes__comparaison,
.scenes__pilotage,
.scenes__actions { grid-column: 1; }
.scenes__lecture { grid-column: 2; grid-row: 2 / span 4; }
.scenes__entete { display: flex; justify-content: space-between; gap: 16px; }
.scenes__source { color: var(--brand-accent); font-size: 9px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.scenes__badges { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.scenes__badges span {
  padding: 3px 6px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--muted);
  font-size: 8px;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.scenes__badges [data-verdict="exact"] { border-color: var(--brand-primary); color: var(--brand-primary); }
.scenes__badges [data-verdict="adjacent"] { border-color: var(--brand-accent); color: var(--brand-primary); }
.scenes__badges [data-verdict="incompatible"] { border-color: var(--data-warm); color: var(--data-warm); }
.scenes__entete h4 { margin: 3px 0 2px; font: 700 18px/1.2 Georgia, "Times New Roman", serif; }
.scenes__phrase { max-width: 720px; margin: 0; color: var(--muted); font: 12px/1.35 Georgia, "Times New Roman", serif; }
.scenes__syntaxe { display: flex; gap: 10px; margin: 0; }
.scenes__syntaxe div { min-width: 82px; }
.scenes__syntaxe dt,
.scenes__lecture dt { color: var(--muted); font-size: 9px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; }
.scenes__syntaxe dd,
.scenes__lecture dd { margin: 2px 0 0; font-size: 10px; line-height: 1.3; }
.scenes__variantes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; }
.scenes__variante {
  min-height: 54px;
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 7px;
  padding: 7px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background-color: var(--paper-bright);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.scenes__variante[aria-pressed="true"] { border-color: var(--brand-primary); box-shadow: inset 0 -3px 0 var(--brand-primary); }
.scenes__variante > span { grid-row: 1 / span 2; align-self: center; color: var(--brand-primary); font: 700 20px/1 Georgia, serif; }
.scenes__variante strong { font-size: 10px; line-height: 1.2; }
.scenes__variante small { color: var(--muted); font-size: 9px; line-height: 1.2; }
.scenes__comparaison { display: grid; grid-template-columns: minmax(0, 3fr) minmax(150px, 1fr); gap: 8px; }
.scenes__audition,
.scenes__reference { position: relative; min-height: 210px; overflow: hidden; border: 1px solid var(--line); border-radius: 7px; background-color: var(--paper-bright); }
.scenes__cadre,
.scenes__reference [data-role="cadre-reference"] { position: absolute; inset: 0; overflow: hidden; }
.scenes__cadre .vignette__cadre,
.scenes__reference .vignette__cadre { position: absolute; top: 0; left: 0; width: 1280px; height: 720px; transform-origin: 0 0; pointer-events: none; }
.scenes__cadre.est-semantique { background-color: var(--paper-bright); }
.scenes__etat-semantique {
  position: absolute;
  right: 10px;
  bottom: 10px;
  left: 10px;
  z-index: 2;
  padding: 8px 10px;
  border-left: 4px solid var(--brand-primary);
  background-color: rgba(255, 255, 255, .94);
}
.scenes__etat-semantique span { color: var(--brand-accent); font-size: 9px; font-weight: 700; text-transform: uppercase; }
.scenes__etat-semantique p { margin: 2px 0 0; font: 11px/1.3 Georgia, "Times New Roman", serif; }
.scenes__reference > span { position: absolute; z-index: 3; top: 7px; left: 7px; padding: 3px 6px; background-color: var(--brand-primary); color: #fff; font-size: 8px; font-weight: 700; text-transform: uppercase; }
.scenes__reference > p { position: absolute; right: 6px; bottom: 5px; left: 6px; z-index: 3; margin: 0; padding: 4px 5px; background-color: rgba(255, 255, 255, .92); color: var(--muted); font-size: 8px; }
.scenes__pilotage { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.scenes__reglette { display: grid; flex: 1; gap: 5px; color: var(--muted); font-size: 10px; }
.scenes__reglette input { width: 100%; min-height: 24px; accent-color: var(--brand-primary); }
.scenes__reduit { min-height: 44px; display: inline-flex; align-items: center; gap: 7px; color: var(--muted); font-size: 10px; cursor: pointer; }
.scenes__reduit input { width: 18px; height: 18px; accent-color: var(--brand-primary); }
.scenes__raccourcis { color: var(--muted); font-size: 9px; white-space: nowrap; }
.scenes__lecture { display: grid; align-content: start; gap: 11px; margin: 0; padding-left: 12px; border-left: 1px solid var(--line); }
.scenes__actions { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.scenes__actions .atelier__bouton { min-height: 44px; padding: 7px 10px; font-size: 10px; }
.scenes__retour { min-width: 160px; flex: 1; color: var(--muted); font-size: 10px; line-height: 1.25; }
.scenes__detail.est-mouvement-reduit .scenes__cadre,
.scenes__detail.est-mouvement-reduit .scenes__cadre * { animation: none !important; transition: none !important; }

@media (max-width: 1100px) {
  .scenes__detail { grid-template-columns: minmax(0, 1fr); }
  .scenes__lecture { grid-column: 1; grid-row: auto; grid-template-columns: repeat(2, 1fr); padding: 8px 0 0; border-top: 1px solid var(--line); border-left: 0; }
}

@media print { #atelier-panneau-scenes { display: none !important; } }
/* --- Le noyau central -----------------------------------------------------
   L'argument arrive, il est arrêté À la périphérie, la périphérie encaisse —
   et le noyau ne bouge pas d'un pixel. C'est là tout le propos, donc
   l'animation le porte : elle passe le critère.

   Le noyau n'a délibérément AUCUNE règle animée. Son immobilité est
   l'information ; lui donner ne serait-ce qu'une pulsation la détruirait.
   -------------------------------------------------------------------------- */

.noyau__trait {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.noyau__pointe { opacity: 0; }
.noyau__tenir { opacity: 0; }

.noyau__peripherie {
  transition: stroke var(--dur-3) var(--ease-out),
              stroke-width var(--dur-3) var(--ease-out);
}

.slide[data-etat="impact"] .noyau__trait {
  animation: noyau-approche 760ms var(--ease-out) both;
}
@keyframes noyau-approche { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

.slide[data-etat="impact"] .noyau__pointe {
  animation: noyau-buter 260ms var(--ease-out) 700ms both;
}
/* Elle bute : elle arrive, dépasse à peine, et revient. Un arrêt, pas un
   atterrissage en douceur. */
@keyframes noyau-buter {
  0%   { opacity: 0; transform: translateX(-14px); }
  60%  { opacity: 1; transform: translateX(3px); }
  100% { opacity: 1; transform: translateX(0); }
}

.slide[data-etat="impact"] .noyau__peripherie {
  stroke: var(--chaud);
  stroke-width: 5;
}

.slide[data-etat="impact"] .noyau__tenir {
  animation: noyau-conclure var(--dur-3) var(--ease-out) 1080ms both;
}
@keyframes noyau-conclure { from { opacity: 0; } to { opacity: 1; } }

/* Le cœur : posé sur la périphérie, et SANS aucune règle animée. Son
   immobilité est l'information même — une pulsation, si discrète soit-elle,
   dirait le contraire de ce que la slide démontre. Ne pas en ajouter. */
.noyau__coeur { stroke: var(--paper-bright); stroke-width: 3; }

.noyau__mot { letter-spacing: .02em; }
.noyau__etiquette { dominant-baseline: auto; }

/* À l'impression, la slide est finie : l'argument a buté, la périphérie a cédé. */
@media print {
  .noyau__trait { stroke-dashoffset: 0 !important; }
  .noyau__pointe,
  .noyau__tenir { opacity: 1 !important; }
  .noyau__peripherie { stroke: var(--chaud) !important; stroke-width: 5 !important; }
}

/* La chute d'un flux : la conséquence, détachée des étapes qui y mènent. */
.flux__chute {
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--line);
  font-size: var(--fs-500);
}

/* --- La pièce — qui est là, et qui décide quand même --------------------
   Une scène plutôt qu'un paragraphe. Deux silhouettes dans le cadre de la
   consultation, une troisième dehors qui s'allume, puis les contraintes.

   Trois temps, et chacun ajoute un habitant à la pièce sans rien déplacer :
   c'est le propos — personne ne sort, tout s'ajoute.
   -------------------------------------------------------------------------- */

.piece__cadre {
  fill: var(--paper-bright);
  stroke: var(--line-strong);
  stroke-width: 2;
}
.piece__legende { fill: var(--muted); }

.piece__personne { fill: var(--brand-primary); }
.piece__nom { fill: var(--ink); }

/* Le tiers est là depuis le début, mais on ne le voit qu'au second temps. */
.piece__tiers { fill: var(--chaud); opacity: 0; }
.piece__nom--tiers { fill: var(--chaud); opacity: 0; }

/* OPACITÉ SEULE, jamais transform : un transform CSS REMPLACE l'attribut
   transform du SVG, il ne s'y ajoute pas. La silhouette du tiers, placée par
   translate(840 200), repartait à l'origine dès que l'animation touchait au
   transform — elle atterrissait en haut à gauche de la slide.
   Le piège est déjà consigné dans ANIMATIONS.md, et il a resservi. */
.slide[data-etat="tiers"] .piece__nom--tiers,
.slide[data-etat="contexte"] .piece__nom--tiers,
.slide[data-etat="contexte"] .piece__contrainte {
  animation: piece-paraitre var(--dur-3) var(--ease-out) both;
}
/* Le tiers paraît — mais ne devient JAMAIS opaque : absent, et pourtant là.
   Sa translucidité est l'information (mise en scène du 25 août). */
.slide[data-etat="tiers"] .piece__tiers,
.slide[data-etat="contexte"] .piece__tiers {
  animation: piece-paraitre-mi var(--dur-3) var(--ease-out) both;
}
@keyframes piece-paraitre { from { opacity: 0; } to { opacity: 1; } }
@keyframes piece-paraitre-mi { from { opacity: 0; } to { opacity: .55; } }

.piece__contrainte { opacity: 0; }
.piece__contrainte circle { fill: var(--brand-accent); }
/* Les contraintes PRESSENT les murs : un trait et une pointe vers le cadre. */
.piece__contrainte line { stroke: var(--brand-accent); stroke-width: 3; }
.piece__contrainte path { fill: var(--brand-accent); }
.piece__sol { stroke: var(--line); stroke-width: 2; }
.piece__contrainte text {
  fill: var(--muted);
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  font-weight: 600;
}
.slide[data-etat="contexte"] .piece__contrainte:nth-of-type(2) { animation-delay: 120ms; }
.slide[data-etat="contexte"] .piece__contrainte:nth-of-type(3) { animation-delay: 240ms; }

@media print {
  .piece__nom--tiers, .piece__contrainte {
    opacity: 1 !important; animation: none !important;
  }
  .piece__tiers { opacity: .55 !important; animation: none !important; }
}

/* --- L'élan — deux mouvements de sens contraires -------------------------
   Une variante d'`.opposition` pour les cas où les deux termes ne diffèrent
   pas par leur contenu mais par leur DIRECTION : s'éloigner de quelque chose,
   ou aller vers. Deux flèches qui se dessinent en sens inverse le disent en
   une seconde, là où le texte demandait trois lignes.
   -------------------------------------------------------------------------- */

.elan__trait { display: block; width: 100%; height: 40px; margin: var(--space-4) 0; }
.elan__tige { stroke-dasharray: 1; stroke-dashoffset: 1; }
.elan__pointe { opacity: 0; }

.slide[data-etat="elan"] .elan__tige {
  stroke-dashoffset: 0;
  animation: elan-tracer 620ms var(--ease-io) both;
}
@keyframes elan-tracer { from { stroke-dashoffset: 1; } }

.slide[data-etat="elan"] .elan__pointe {
  opacity: 1;
  animation: elan-poser 200ms var(--ease-out) 560ms both;
}
@keyframes elan-poser { from { opacity: 0; } to { opacity: 1; } }


.elan__chute {
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--line);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  text-align: center;
  color: var(--brand-primary);
  opacity: 0;
}
.slide[data-etat="elan"] .elan__chute {
  opacity: 1;
  animation: elan-poser var(--dur-3) var(--ease-out) 820ms both;
}

@media print {
  .elan__tige { stroke-dashoffset: 0 !important; }
  .elan__pointe, .elan__chute { opacity: 1 !important; animation: none !important; }
}

/* --- Écarter, puis poser ------------------------------------------------
   Pour les slides dont le titre dit « ni… ni… ». Les fausses pistes sont
   écrites PUIS barrées d'un trait qui se tire : tant qu'on ne les a pas
   écartées explicitement, la salle continue de les chercher.
   -------------------------------------------------------------------------- */

.ecartees { display: grid; gap: var(--space-3); margin-bottom: var(--space-8); }
.ecartee {
  margin: 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  color: var(--muted);
}
.ecartee > span { position: relative; display: inline-block; }
/* Le trait se TIRE d'un bord à l'autre : barrer est un geste, pas un état. */
.ecartee > span::after {
  content: "";
  position: absolute;
  left: 0;
  top: 52%;
  height: 2px;
  width: 0;
  background: var(--chaud);
}
.ecartee.est-revele > span::after {
  animation: ecarter 420ms var(--ease-io) 160ms both;
}
@keyframes ecarter { from { width: 0; } to { width: 100%; } }

.restent__amorce {
  margin: 0 0 var(--space-4);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  color: var(--ink);
}
.restent__liste {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-6);
  margin: 0;
  padding: 0;
  list-style: none;
}
.restent__liste > li { padding-top: var(--space-3); border-top: 3px solid var(--froid); }
.restent__liste > li > b {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  font-weight: 400;
  line-height: 1.15;
}
.restent__liste > li > span {
  display: block;
  font-size: var(--fs-200);
  line-height: 1.35;
  color: var(--muted);
}

/* --- La frontière — ce qu'on croit donné et qui est tracé ----------------
   Deux domaines, un trait entre eux. Premier temps : il est net, on le croit
   donné. Second temps : il devient pointillé et se déplace — il était tracé.

   C'est le geste qui porte l'idée de Canguilhem, et deux paragraphes de prose
   ne le faisaient pas voir.
   -------------------------------------------------------------------------- */

/* @famille frontiere · verbe CATÉGORISER · registre nommé */
.frontiere__cote { fill: var(--seq-1); }
.frontiere__cote[data-cote="mal"] { fill: rgba(235, 104, 52, .10); }
.frontiere__nom { fill: var(--muted); }

.frontiere__trait {
  stroke: var(--brand-primary);
  stroke-width: 4;
  stroke-linecap: round;
  transition: transform 720ms var(--ease-io),
              stroke-dasharray 320ms var(--ease-out),
              stroke 320ms var(--ease-out);
}
/* Le trait est posé par des attributs x1/x2 : on le déplace donc par
   translate, et JAMAIS en réécrivant sa géométrie — un transform CSS
   remplacerait un attribut transform s'il en avait un, celui-ci n'en a pas. */
/* LA FRONTIÈRE NE S'ARRÊTE PAS — demande de Nicolas, 26 août : « le curseur
   doit toujours bouger ». Elle se déplaçait UNE fois, de 158 px, et se
   figeait : la slide disait « elle est tracée, ET ELLE BOUGE » pendant qu'à
   l'écran elle ne bougeait plus. La forme contredisait sa propre chute.
   Ici elle dérive sans fin, lentement, entre 46 et 212 px. Ce n'est pas de
   l'ambiance : le mouvement EST le contenu — une frontière normative n'est
   jamais posée une bonne fois, elle est retracée à chaque époque, chaque
   milieu, chaque technique nouvelle.
   La valeur d'ARRIVÉE reste écrite dans la règle d'état : sous mouvement
   réduit et au tirage, la frontière est déplacée et immobile — ce qui est
   vrai, et lisible. */
.slide[data-etat="tracee"] .frontiere__trait {
  transform: translateX(-130px);
  stroke: var(--chaud);
  stroke-dasharray: 9 8;
}
@media (prefers-reduced-motion: no-preference) {
  .slide[data-etat="tracee"] .frontiere__trait {
    animation: frontiere-deriver 14s var(--ease-io) infinite;
  }
}
/* ELLE PASSE D'UN CÔTÉ À L'AUTRE — croquis de Nicolas, 26 août : un rond
   posé sur la JOINTURE des deux zones. Elle dérivait de 46 à 212 px, c'est-
   à-dire toujours DANS « la maladie » : elle bougeait, mais d'un seul côté.
   Une frontière normative ne se déplace pas dans une direction — elle est
   retracée, tantôt plus loin, tantôt plus près. De -130 à +130, elle visite
   les deux.
   L'arrivée, elle, est à GAUCHE : au tirage et sous mouvement réduit, la
   frontière a mordu sur le corps sain. C'est la médicalisation, et c'est ce
   que la chute commente — accepter un traitement pour un corps qui n'est pas
   malade. */
/* ELLE PART DU MILIEU — « l'animation du curseur commence au milieu, là elle
   saute » (26 août). Elle sautait, en effet : le trait est posé à 0, et
   l'animation démarrait sa première image à -130. Le saut faisait 130 px et
   se jouait avant que l'œil ait trouvé la figure.
   Quatre arrêts au lieu de deux, et plus d'`alternate` : le cycle part de 0,
   visite la gauche, la droite, et revient à 0. Aucune première image qui
   contredise la position posée. La période reste la même — 14 s pour un
   aller-retour complet, comme les 7 s alternées d'avant. */
@keyframes frontiere-deriver {
  0%   { transform: translateX(0); }
  25%  { transform: translateX(-130px); }
  75%  { transform: translateX(130px); }
  100% { transform: translateX(0); }
}

.frontiere__verdict { fill: var(--muted); }
.frontiere__verdict--vraie { fill: var(--chaud); font-weight: 700; opacity: 0; }
.slide[data-etat="tracee"] .frontiere__verdict { opacity: 0; }
.slide[data-etat="tracee"] .frontiere__verdict--vraie {
  animation: frontiere-dire var(--dur-3) var(--ease-out) 420ms both;
}
@keyframes frontiere-dire { from { opacity: 0; } to { opacity: 1; } }

.frontiere__question { margin-top: var(--space-6); font-size: var(--fs-400); }
/* L'AMBIVALENCE — l'idée que Nicolas a posée le 26 août, et qui vient de sa
   thèse : « une expérience partagée par les femmes, que les hommes ne peuvent
   pas vraiment comprendre mais qu'elles éprouvent ». C'est le versant que la
   frontière ne dit pas : la ligne se déplace, soit, mais ce qu'on éprouve de
   part et d'autre, c'est LES DEUX À LA FOIS.
   Elle vient APRÈS la question, et pas à sa place : la question ouvre la
   consultation, l'ambivalence dit pourquoi elle mérite d'être posée. */
.frontiere__ambivalence { margin-top: var(--space-4); }

@media print {
  /* LA MÊME VALEUR QU'À L'ÉTAT, sinon le tirage ment. Elle est restée à +158
     quand l'état est passé à -130, et le PDF montrait la frontière du mauvais
     côté — à droite, dans la maladie, alors qu'elle a mordu sur le corps sain.
     Une valeur d'arrivée s'écrit à DEUX endroits ; les deux se suivent. */
  .frontiere__trait { transform: translateX(-130px) !important;
                      stroke: var(--chaud) !important; stroke-dasharray: 9 8 !important; }
  .frontiere__verdict { opacity: 0 !important; }
  .frontiere__verdict--vraie { opacity: 1 !important; animation: none !important; }
}

/* --- Une chronologie dont le dernier temps DURE -------------------------
   Variante de `.chrono` pour les cas où le dernier jalon n'est pas un
   événement mais un effet. Une barre s'étend sous la portée : ce n'est pas un
   point de plus sur l'axe, c'est quelque chose qui reste.
   -------------------------------------------------------------------------- */

.chrono__portee {
  position: relative;
  margin: var(--space-8) 0 0;
  padding-top: var(--space-6);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.34;
  color: var(--brand-primary);
}
.chrono__barre {
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--chaud), var(--brand-primary));
}
/* Elle s'étend au lieu de paraître : un effet qui dure ne s'allume pas, il
   gagne du terrain. */
.fragment.est-revele .chrono__barre,
.chrono__portee.est-revele .chrono__barre {
  animation: chrono-durer 900ms var(--ease-io) both;
}
@keyframes chrono-durer { from { width: 0; } to { width: 100%; } }

/* --- 5b.9 La carrière — deux rails ---------------------------------------
   Une carrière n'est jamais seulement subjective : ce sont les institutions
   qui en découpent les étapes. La slide le montre en deux lignes parallèles
   qui partagent les mêmes bornes — au-dessus ce que la femme traverse, en
   dessous ce que l'extérieur autorise au même moment.

   Trois temps, et chacun a sa raison d'être animé :

     (entrée)              le rail du vécu se trace ; une carrière s'acquiert
                           par étapes, on la voit s'acquérir ;
     data-etat="cadre"     le second rail apparaît sous le premier ;
     data-etat="ecarts"    les endroits où les deux ne coïncident pas.

   Sept colonnes égales de 163 px, libellés de 152 px : les deux rails DOIVENT
   partager les mêmes bornes, donc l'alternance haut/bas de la première
   version — qui donnait plus de place — n'était plus tenable.
   -------------------------------------------------------------------------- */

/* @famille carriere · registre nommé */
.carriere {
  --carriere-vecu: 104px;     /* ordonnée du premier rail dans la piste */
  --carriere-cadre: 168px;    /* ordonnée du second */
  display: grid;
  grid-template-rows: auto auto auto;
  align-content: center;      /* et NON « 1fr » sur la piste : elle prendrait
                                 toute la hauteur, le titre du premier rail
                                 viendrait coller au chapô et le second au
                                 filet de source. Ici le bloc se centre et
                                 respire des deux côtés. */
  gap: var(--space-3);
  width: 100%;
  height: 100%;
}

.carriere__rail {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.carriere__rail[data-rail="cadre"] {
  color: var(--brand-accent);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="cadre"] .carriere__rail[data-rail="cadre"],
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__rail[data-rail="cadre"] { opacity: 1; }

.carriere__piste { position: relative; min-height: 272px; }

.carriere__axe {
  position: absolute;
  left: 0;
  right: 0;
  height: 4px;
}
.carriere__axe[data-rail="vecu"]  { top: var(--carriere-vecu); }
.carriere__axe[data-rail="cadre"] { top: var(--carriere-cadre); }

.carriere__trait { stroke-dasharray: 1; stroke-dashoffset: 1; }

.carriere__axe[data-rail="vecu"] .carriere__trait {
  /* 1100 ms : le plafond de la bande de démonstration. Était à 1300 —
     au-dessus du plafond que la charte pose, sans raison écrite. */
  animation: carriere-tracer 1100ms var(--ease-io) 120ms both;
}
.slide[data-etat="cadre"] .carriere__axe[data-rail="cadre"] .carriere__trait,
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__axe[data-rail="cadre"] .carriere__trait {
  animation: carriere-tracer 900ms var(--ease-io) both;
}
@keyframes carriere-tracer { to { stroke-dashoffset: 0; } }

.carriere__stations { margin: 0; padding: 0; list-style: none; }

.carriere__station {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 152px;
  margin-left: -76px;
  transition: opacity var(--dur-3) var(--ease-out), transform 420ms var(--ease-io);
  display: grid;
  grid-template-rows: var(--carriere-vecu)
                      calc(var(--carriere-cadre) - var(--carriere-vecu))
                      1fr;
  text-align: center;
}

/* --- Le rail du vécu ---------------------------------------------------- */

.carriere__vecu {
  position: relative;
  grid-row: 1;
  /* Le bloc OCCUPE toute la rangée au lieu d'être collé au rail : le libellé
     s'aligne alors par le haut, la description reste calée sur le rail. Sans
     ça, un libellé sur deux lignes (« Avant un premier enfant ») pousse sa
     colonne vers le haut et la ligne des titres monte en escalier — visible
     seulement au tirage. Aucune hauteur fixe : la structure suffit. */
  align-self: stretch;
  display: grid;
  grid-template-rows: auto 1fr;
  padding-bottom: 15px;
  animation: carriere-poser var(--dur-3) var(--ease-out) both;
  animation-delay: calc(var(--i) * 150ms + 240ms);
}
.carriere__vecu::before {          /* le point sur le rail */
  content: "";
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: 11px;
  height: 11px;
  margin-left: -5.5px;
  border-radius: var(--radius-pill);
  background: var(--froid);
}
.carriere__vecu > b {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.carriere__vecu > span { align-self: end; }

.carriere__vecu > span,
.carriere__cadre > span {
  display: block;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-200);
  line-height: 1.34;
  color: var(--muted);
}

/* LA FRISE DÉGRAISSÉE MONTE D'UNE MARCHE.
   Mesurée le 26 août : vingt blocs de texte à 12 et 14 px sur une seule page,
   682 mots. Le remède n'était pas de scinder — deux des trois écarts sont
   développés en pleine page juste après (le DIU « après un enfant », 26 ans),
   et une slide de détail aurait fait doublon. C'est la GLOSE qui partait :
   les notes d'orateur sont le cours, l'écran porte la structure. 142 mots
   restent, et le corps peut enfin monter d'une marche.
   Portée au registre, pour ne pas déplacer la frise des autres decks. */
.slide[data-registre="demonstration"] .carriere__vecu > b { font-size: var(--fs-200); }
.slide[data-registre="demonstration"] .carriere__vecu > span,
.slide[data-registre="demonstration"] .carriere__cadre > span { font-size: var(--fs-300); }
.slide[data-registre="demonstration"] .carriere__rail { font-size: var(--fs-200); }

/* --- Le rail institutionnel --------------------------------------------- */

.carriere__cadre {
  position: relative;
  grid-row: 3;
  padding-top: 15px;
  opacity: 0;
}
/* Point ÉVIDÉ, là où celui du vécu est plein. Les deux rails partagent les
   mêmes bornes : sans cette différence, le second se lit comme une répétition
   du premier au lieu d'un autre registre — ce qui vient du dehors. */
.carriere__cadre::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -5px;
  box-sizing: border-box;
  width: 11px;
  height: 11px;
  margin-left: -5.5px;
  border: 2.5px solid var(--brand-accent);
  border-radius: var(--radius-pill);
  background: var(--paper);
}
.slide[data-etat="cadre"] .carriere__cadre,
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__cadre {
  animation: carriere-poser var(--dur-3) var(--ease-out) both;
  animation-delay: calc(var(--i) * 105ms + 180ms);
}

/* --- Les décalages ------------------------------------------------------
   Ce n'est pas une décoration : c'est la thèse de la slide. Là où les deux
   rails ne coïncident pas naissent les réticences et les renoncements. */

/* Le point d'interrogation d'un passage qui n'en est peut-être pas un : il
   doit peser, sinon on lit la question comme un constat. */
.carriere__doute {
  font-style: normal;
  font-weight: 800;
  font-size: 1.5em;
  color: var(--chaud);
  vertical-align: -0.06em;
}

.carriere__ecart {
  grid-row: 2;
  justify-self: center;
  width: 2px;
  height: 100%;
  opacity: 0;
}
.carriere__station[data-ecart] .carriere__ecart {
  background: repeating-linear-gradient(
    to bottom, var(--chaud) 0 5px, transparent 5px 10px);
}
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__station[data-ecart] .carriere__ecart {
  animation: carriere-ecart 420ms var(--ease-out) both;
  animation-delay: calc(var(--i) * 90ms);
}
@keyframes carriere-ecart {
  from { opacity: 0; transform: scaleY(0); }
  to   { opacity: 1; transform: scaleY(1); }
}
.carriere__station[data-ecart] .carriere__ecart { transform-origin: top; }

/* --- La loupe — s'attarder sans quitter la frise ---------------------------
   Cinquième mécanique du moteur (demandée par Nicolas, 25 août) : l'ATTENTION.
   L'état vise UNE station : elle grossit, les autres reculent en opacité —
   l'orateur s'attarde, la salle sait où regarder, et la frise reste là.
   Les états loupe sont EXCLUSIFS (la loupe se déplace) ; l'état « ensemble »
   repose tout — c'est lui qui s'imprime. */
.slide[data-etat^="loupe-"] .carriere__station { opacity: .38; }
.slide[data-etat="loupe-enfant"]    .carriere__station[data-loupe="enfant"],
.slide[data-etat="loupe-26"]        .carriere__station[data-loupe="26"],
.slide[data-etat="loupe-menopause"] .carriere__station[data-loupe="menopause"] {
  opacity: 1;
  transform: scale(1.16);
  z-index: 2;
}
@media print {
  .carriere__station { opacity: 1 !important; transform: none !important; }
}

/* --- LA MÉTAMORPHOSE — trois lectures d'un même moment (REQUALIFIER) -------
   Tenue en réserve depuis le 25 août, récoltée le 26 : « le même objet,
   plusieurs sens superposés qui se succèdent au même endroit ».

   PREMIÈRE VERSION, ÉCARTÉE : une plaquette qu'on renommait « un traitement »
   puis « un signe ». Nicolas : « c'est vraiment une animation pauvre, et ton
   pictogramme n'est pas très clair ». Il avait raison deux fois — une étiquette
   qui change n'est pas une démonstration, et une plaquette à huit pastilles ne
   se lit pas à quinze mètres.

   CE QUI MANQUAIT SURTOUT : la troisième lecture. La prescription traite,
   l'entourage lit — et ELLE éprouve. Les premiers effets, des mois parfois
   difficiles, un rapport à la contraception qui se construit là pour
   longtemps. C'est celle-là que personne ne lit, et c'est la seule qui dure.

   D'OÙ LA FORME : une ligne de vie. Les deux lectures extérieures se posent
   sur l'instant et S'Y FERMENT — deux traits courts, bornés. La troisième part
   du même instant et NE S'ARRÊTE PAS : elle traverse toute la ligne. La
   persistance n'est pas dite, elle est dessinée.
   Le chaud est sur la sienne, et sur elle seule : c'est le jalon de la slide. */
/* @famille metamorphose · verbe REQUALIFIER · registre mixte */
.metamorphose__vie {
  stroke: var(--line-strong);
  stroke-width: 3;
}
.metamorphose__horizon, .metamorphose__quand { fill: var(--muted); letter-spacing: .08em; }
.metamorphose__instant {
  stroke: var(--line-strong);
  stroke-width: 3;
  stroke-dasharray: 5 5;
}

/* Les deux lectures du dehors : elles se ferment sur l'instant. */
.metamorphose__lecture path {
  fill: none;
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 520ms var(--ease-io);
}
/* Plus de texte dans la figure : les trois volets nomment les lectures juste
   dessous. Répéter les mots dans un ruban de 132 unités forçait un corps de
   9 px — sous le plancher de la charte, et invisible en salle. La figure porte
   la forme, les volets portent les mots. */
.metamorphose__lecture[data-lecture="traite"] path { stroke: var(--froid, var(--data-cool)); }
.metamorphose__lecture[data-lecture="lu"] path { stroke: var(--brand-accent); }

.slide:is([data-etat="traite"], [data-etat="lu"], [data-etat="eprouve"], [data-etat="echappe"])
  .metamorphose__lecture[data-lecture="traite"] path { stroke-dashoffset: 0; }
.slide:is([data-etat="traite"], [data-etat="lu"], [data-etat="eprouve"], [data-etat="echappe"])

.slide:is([data-etat="lu"], [data-etat="eprouve"], [data-etat="echappe"])
  .metamorphose__lecture[data-lecture="lu"] path { stroke-dashoffset: 0; }
.slide:is([data-etat="lu"], [data-etat="eprouve"], [data-etat="echappe"])


/* La sienne : elle part du même instant et traverse toute la ligne.
   1 100 ms — le plafond de la bande de démonstration, parce qu'ICI LA DURÉE
   EST LE PROPOS : un trait qui traverse vite dirait un événement. */
.metamorphose__trace {
  fill: none;
  stroke: var(--chaud, var(--data-warm));
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 1100ms var(--ease-io);
}
.metamorphose__dur {
  fill: var(--chaud, var(--data-warm));
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 320ms;
}
/* LES ÉTATS SONT CUMULATIFS, et celui-ci ne l'était pas.
   Mesuré le 26 août sur les quatre états, après « y'a un bug sur l'animation
   de la barre en orange » : à `echappe`, le trait orange DISPARAISSAIT. La
   règle ne visait que `eprouve` ; l'état suivant cessait de la faire
   correspondre, et la ligne se rétractait toute seule.
   C'est la règle d'or du dossier, écrite sur la levée : « passage REDIT tout
   ce que levee a posé ». Un état qui avance ne défait rien. */
.slide:is([data-etat="eprouve"], [data-etat="echappe"]) .metamorphose__trace { stroke-dashoffset: 0; }
.slide:is([data-etat="eprouve"], [data-etat="echappe"]) .metamorphose__dur { opacity: .22; }

@media print {
  .metamorphose__lecture path { stroke-dashoffset: 0 !important; }
  .metamorphose__trace { stroke-dashoffset: 0 !important; }
  .metamorphose__dur { opacity: .22 !important; }
}

/* --- LA BOUCLE MENUE QUI TOURNE VRAIMENT (26 août, demande de Nicolas) -----
   « Un effet d'animation continue entre les flèches, et quand ça atteint le
   bord des représentations et de la réalité, le contour s'allume lui aussi. »

   La menue s'était exclue des boucles infinies de la slide 11 — à juste titre :
   elle héritait d'un rythme réglé pour un point qui n'existe pas chez elle.
   Elle a maintenant le sien, calé sur SON circuit.

   Ce n'est pas une ambiance : le circuit qui s'auto-entretient EST ce que la
   figure démontre, et c'est la boucle légitime que le dossier reconnaît depuis
   le début. Les cadres s'allument au TURQUOISE, jamais au chaud : le chaud est
   le jalon rare, et deux pôles qui clignotent en chaud n'en seraient plus un.

   Les repères du tour, sur 5,5 s : l'aller occupe ~48 %, la descente 2 %, le
   retour ~48 %, la remontée 2 %. Chaque cadre s'allume donc à l'arrivée du
   grain — 48 % pour la droite, 98 % pour la gauche — et s'éteint aussitôt. */
/* --- LA BOULE QUI FAIT LE TOUR (26 août, troisième version) ---------------
   Nicolas, reformulé et validé : « une seule boule qui traverse la ligne. La
   ligne est de base grise. Quand la boule arrive dessus, elle prend la
   couleur, puis s'éteint progressivement quand elle s'éloigne. Quand la boule
   arrive sur réalité et représentation, les contours s'allument. »

   DEUX VERSIONS ÉCARTÉES AVANT CELLE-CI, et les deux pour de bonnes raisons :
   un circuit qui tournait sans fin dès l'ouverture — deux horloges dans la
   même image, avec une chronologie qui, elle, avance par à-coups ; puis deux
   grains, un par arc, déclenchés par leur fait — mais alors la boucle ne
   bouclait plus. Ici les deux exigences tiennent : LES FAITS AUTORISENT LE
   CIRCUIT, et le circuit tourne.

   LES ARCS RESTENT GRIS. C'est le renversement de cette version : la couleur
   n'est plus une propriété du trait, c'est un ÉVÉNEMENT qui le traverse. Le
   fait ne colore donc plus l'arc à demeure — il ouvre le tour, et c'est tout.

   LA COMÈTE se fait sans dégradé le long du trait, qui n'existe pas en SVG
   sans y mettre du script : une tête courte et pleine, une traîne plus longue
   et plus pâle décalée d'un cran, les deux filant ensemble. L'œil compose.

   Au TIRAGE, il n'y a pas de boule : les arcs reprennent alors leur couleur
   (bloc print inchangé). Le papier écrit ce que le mouvement aurait montré —
   c'est la logique du contrat d'arrivée. */
.boucle__boule--menue { opacity: 0; fill: var(--brand-primary); }
.boucle__comete {
  stroke-linecap: round;
  opacity: 0;
  stroke: var(--brand-primary);
}
/* PLUS FINE QU'AU PREMIER JET — 5 et 3,5 px faisaient un NŒUD au pôle.
   Mesuré : à t = 3000 ms la tête allait de (832, 294) à (851, 320), c'est-à-
   dire qu'elle se repliait sur elle-même. Les deux arcs ne sont distants que
   de 24 unités à leur extrémité ; un trait de 5 px qui fait demi-tour dans
   24 px ne dessine pas un virage, il dessine une tache. Le circuit a gagné un
   vrai virage (voir le balisage), et la comète a maigri pour y passer.
   La traîne raccourcit aussi : à 0,15 elle enveloppait le virage ET les deux
   arcs à la fois, et la comète paraissait des deux côtés en même temps.
   3 px et non 2,8 : le contrôle a refusé 2,8 comme passant sous le plancher
   de lisibilité, et il a eu raison de ne pas m'écouter — une traîne « qui
   n'est qu'une ambiance » reste un trait porteur tant qu'elle bouge. */
.boucle__comete[data-part="tete"]   { stroke-width: 4;   stroke-dasharray: 0.045 0.955; }
.boucle__comete[data-part="traine"] { stroke-width: 3;   stroke-dasharray: 0.11 0.89; }

/* Les arcs ne se colorent plus avec les faits : ils restent gris, et seule la
   boule les éclaire. Les têtes de flèche, elles, paraissent — elles disent le
   sens, pas l'intensité. */
.boucle--menue .boucle__trace { opacity: 0 !important; }

@media (prefers-reduced-motion: no-preference) {
  .slide:is([data-etat="aller"], [data-etat="retour"]) .boucle--menue .boucle__boule--menue {
    opacity: 1;
    animation: menue-tour 6s linear infinite, menue-couleur-fond 6s linear infinite;
  }
  .slide:is([data-etat="aller"], [data-etat="retour"]) .boucle--menue .boucle__comete {
    opacity: 1;
    animation: menue-filer-tete 6s linear infinite, menue-couleur-trait 6s linear infinite;
  }
  /* LA BOULE EST DEVANT, LA QUEUE DERRIÈRE.
     Le décalage se pose sur la COURSE, pas sur le temps. Un délai négatif
     AVANCE l'animation : la traîne partait donc en tête, et la comète
     précédait la boule au lieu de la suivre — visible d'un coup d'œil sur
     l'arc du retour, où la couleur s'étalait à gauche du point.
     Avec dasharray [d, 1-d] et pathLength 1, le trait est peint là où
     (s + décalage) mod 1 < d. Un décalage de -t place donc le tiret sur
     [t, t+d] — DEVANT. On veut [t-d, t] : le décalage part de +d. */
  .slide:is([data-etat="aller"], [data-etat="retour"])
    .boucle--menue .boucle__comete[data-part="tete"] { animation-name: menue-filer-tete, menue-couleur-trait; }
  .slide:is([data-etat="aller"], [data-etat="retour"])
    .boucle--menue .boucle__comete[data-part="traine"] { animation-name: menue-filer-traine, menue-couleur-trait; }

  /* Les cadres s'allument au passage de la boule, et s'éteignent derrière.
     Elle atteint les représentations à 48 % du tour, la réalité à 98 %. */
  .slide:is([data-etat="aller"], [data-etat="retour"])
    .boucle--menue .boucle__cadre[data-pole="droite"] {
    animation: menue-toucher-droite 6s linear infinite;
  }
  .slide:is([data-etat="aller"], [data-etat="retour"])
    .boucle--menue .boucle__cadre[data-pole="gauche"] {
    animation: menue-toucher-gauche 6s linear infinite;
  }
}
@keyframes menue-tour  { from { offset-distance: 0%; } to { offset-distance: 100%; } }
/* La tête FINIT sur la boule ; la traîne finit sur la tête. */
@keyframes menue-filer-tete   { from { stroke-dashoffset:  0.045; } to { stroke-dashoffset: -0.955; } }
@keyframes menue-filer-traine { from { stroke-dashoffset:  0.155; } to { stroke-dashoffset: -0.845; } }
/* La couleur de l'arc parcouru : navy en haut, chaud en bas. La boule et le
   trait la prennent ensemble — l'animation ne colore pas, elle révèle. */
/* LA COULEUR BASCULE AU PÔLE, ELLE N'Y FOND PAS.
   De 44 % à 52 %, le navy et le chaud s'interpolaient : au virage, la boule
   et la comète viraient au BRUN — une couleur qui n'est ni dans la marque ni
   dans la palette de données, et qui apparaissait pile là où l'œil regarde.
   Le changement de couleur dit un changement de SENS ; un sens ne se mélange
   pas. Un pour cent de transition, soit 60 ms : l'œil lit une bascule. */
@keyframes menue-couleur-fond {
  0%, 49%   { fill: var(--brand-primary); }
  50%, 99%  { fill: var(--chaud, var(--data-warm)); }
  100%      { fill: var(--brand-primary); }
}
@keyframes menue-couleur-trait {
  0%, 49%   { stroke: var(--brand-primary); }
  50%, 99%  { stroke: var(--chaud, var(--data-warm)); }
  100%      { stroke: var(--brand-primary); }
}
@keyframes menue-toucher-droite {
  0%, 40%   { stroke: var(--line-strong); stroke-width: 1; }
  48%, 54%  { stroke: var(--brand-primary); stroke-width: 3; }
  64%, 100% { stroke: var(--line-strong); stroke-width: 1; }
}
@keyframes menue-toucher-gauche {
  0%, 4%    { stroke: var(--chaud, var(--data-warm)); stroke-width: 3; }
  14%, 90%  { stroke: var(--line-strong); stroke-width: 1; }
  98%, 100% { stroke: var(--chaud, var(--data-warm)); stroke-width: 3; }
}
@media print {
  .boucle__boule--menue, .boucle__comete { opacity: 0 !important; animation: none !important; }
  .boucle--menue .boucle__trace { opacity: 1 !important; }
}

/* --- L'ÉCHO ENTRE DEUX SLIDES — les quatre signes qui reviennent ----------
   Nicolas : « une transition visuelle entre les deux slides qui se répondent ;
   ça servirait le propos. »

   « Et pourtant, aucune n'est un choix sans conditions » et « Quatre raisons »
   portent LES MÊMES QUATRE SIGNES, dans le même ordre, sur la même grille de
   quatre colonnes — paroles, horloge, cœur, personnes, le quatrième en chaud.
   L'écho était donc déjà écrit ; il ne se voyait simplement pas, parce que les
   icônes changent de taille d'une slide à l'autre.

   Le moteur ne fait aucun effet de PASSAGE entre slides, et cette règle-là
   n'est pas levée. Mais rien n'interdit à une slide de COMMENCER comme
   finissait la précédente : les quatre signes arrivent à leur taille d'avant,
   grands, et se rangent dans le plan. La continuité se joue entièrement à
   l'intérieur de la slide 7 — c'est elle qui cite la 6.

   Échelonné de 90 ms : les quatre se rangent dans l'ordre où on les a lus. */
/* L'icône est SŒUR du titre, pas dedans : l'éteindre pour la faire arriver
   éteindrait aussi le signe qu'on veut voir.

   ET ELLE NE BOUGE PRESQUE PAS. Premier essai : les signes arrivaient à 2,4×,
   censés « citer » la slide d'avant. Or en la REGARDANT, ses quatre signes
   sont petits, en tête de colonne — `ico--bloc` veut dire bloc, pas grand.
   L'écho inventait donc une taille qui n'a jamais existé.
   Le vrai écho est plus discret et plus fort : les quatre signes sont AU MÊME
   ENDROIT, À LA MÊME TAILLE que sur la slide d'avant, et ils restent là
   pendant que le plan s'écrit dessous. La continuité se joue en NE bougeant
   PAS. Le 1,2 → 1 ne sert qu'à faire sentir qu'ils arrivent. */
.flux--echo li > .ico {
  display: block;
  margin: 0 auto var(--space-3);
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(1.2);
  transition: transform 420ms var(--ease-out);
}
/* LE REMPLACEMENT — l'ancien texte cède la place au nouveau, au même endroit.
   Nicolas : « ça perd en fluidité graphique ; on pourrait presque imaginer un
   effet de remplacement où l'ancien texte est remplacé par le nouveau, un peu
   le style morphose. »

   Les deux slides portent les mêmes quatre signes, dans le même ordre, sur la
   même grille. La 07 commence donc en montrant LES TITRES DE LA 06 — « ce que
   j'ai entendu » — puis chacun est relayé par le sien : « je ne pense pas
   seule ». Même colonne, même ligne de base, aucun déplacement : c'est le
   principe de la métamorphose (REQUALIFIER), appliqué à un passage.

   Les deux titres occupent la MÊME cellule de grille : la hauteur est celle du
   plus grand, rien ne saute quand ils s'échangent. */
/* @famille morphe · verbe REQUALIFIER · registre nommé */
.flux--echo li b.morphe {
  display: grid;
  grid-template-areas: "titre";
}
.flux--echo li b.morphe > span {
  grid-area: titre;
  transition: opacity 420ms var(--ease-out);
}
.flux--echo li b.morphe > [data-avant] { opacity: 1; color: var(--muted); }
.flux--echo li b.morphe > [data-apres] { opacity: 0; }
.slide[data-etat="plan"] .flux--echo li b.morphe > [data-avant] { opacity: 0; }
.slide[data-etat="plan"] .flux--echo li b.morphe > [data-apres] { opacity: 1; }
/* Échelonné : les quatre se relaient dans l'ordre où on les a lus. */
.slide[data-etat="plan"] .flux--echo li:nth-child(2) b.morphe > span { transition-delay: 90ms; }
.slide[data-etat="plan"] .flux--echo li:nth-child(3) b.morphe > span { transition-delay: 180ms; }
.slide[data-etat="plan"] .flux--echo li:nth-child(4) b.morphe > span { transition-delay: 270ms; }

.flux--echo li span:not([data-avant]):not([data-apres]) {
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 480ms;
}
.slide[data-etat="plan"] .flux--echo li > .ico { transform: scale(1); }
.slide[data-etat="plan"] .flux--echo li span:not([data-avant]) { opacity: 1; }
.slide[data-etat="plan"] .flux--echo li:nth-child(2) > .ico { transition-delay: 90ms; }
.slide[data-etat="plan"] .flux--echo li:nth-child(3) > .ico { transition-delay: 180ms; }
.slide[data-etat="plan"] .flux--echo li:nth-child(4) > .ico { transition-delay: 270ms; }

@media print {
  .flux--echo li > .ico { transform: scale(1) !important; transition: none !important; }
  .flux--echo li b.morphe > [data-avant] { display: none !important; }
  .flux--echo li b.morphe > [data-apres],
  .flux--echo li span { opacity: 1 !important; transition: none !important; }
}
/* --- LES QUATRE FONCTIONS, ENSEMBLE (26 août) -----------------------------
   Le chapô dit « une même représentation remplit ces quatre fonctions EN MÊME
   TEMPS », et la slide montrait quatre cartes révélées l'une après l'autre —
   c'est-à-dire quatre choses séparées, le contraire de ce que la phrase promet.
   Elle est restée en dette pendant toute la journée sous ce motif exact
   (SCHEMAS.md §3 : la forme exécute l'opération que le texte annonce).

   On les lit donc une par une — il faut bien commencer quelque part — puis un
   dernier temps LES RALLUME TOUTES À LA FOIS. La simultanéité n'est plus dite,
   elle est jouée : quatre filets qui s'épaississent ensemble, quatre fonds qui
   se posent ensemble. C'est le seul moment de la slide où rien n'est
   échelonné, et c'est tout le propos. */
.matrice__case {
  transition: border-color var(--dur-3) var(--ease-out),
              background-color var(--dur-3) var(--ease-out);
}
.slide[data-etat="ensemble"] .matrice__case {
  border-color: var(--brand-primary);
  background-color: var(--surface-tint);
}
@media print {
  .matrice__case { transition: none !important; }
}

/* --- Le crible — ce qu'un rangement laisse dehors -------------------------
   CATÉGORISER, variante « exclure ». `.volets--reunion` montre une catégorie
   qui se FABRIQUE ; le crible montre un critère qui TRIE, et surtout ce qu'il
   met dehors.

   Née le 26 août d'un défaut : « le remboursement n'est pas un fait médical »
   posait « ce qu'il retient » et « ce qu'il laisse de côté » sur deux colonnes
   côte à côte. Deux colonnes disent « deux choses existent ». Elles ne disent
   jamais qu'UNE EST MISE DEHORS — qui est pourtant toute la phrase.
   (SCHEMAS.md §3 : la forme exécute l'opération que le texte annonce.)

   Les trois avancent ensemble vers le cadre. Deux y entrent et s'y rangent.
   La troisième avance aussi — elle ne renonce pas — et bute contre la paroi.
   C'est le fait d'AVANCER PUIS BUTER qui fait la démonstration : un jeton
   posé d'emblée à l'extérieur dirait qu'il n'a jamais été candidat.          */
/* @famille crible · verbe CATÉGORISER · registre nommé */
.crible__cadre {
  fill: none;
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3;
  stroke-dasharray: 10 7;
}
.crible__nom { fill: var(--brand-primary); letter-spacing: .1em; }

.crible__jeton rect {
  fill: var(--paper-bright);
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3;
}
.crible__jeton text { fill: var(--ink); font-size: 26px; }
.crible__jeton {
  transform: translateX(var(--dx, -360px));
  transition: transform 720ms var(--ease-io) var(--retard, 0ms);
}
.slide[data-etat="crible"] .crible__jeton,
.slide[data-etat="decide"] .crible__jeton { transform: translateX(0); }

/* Le refusé passe au chaud QUAND il bute, pas avant : le jalon ne paraît
   jamais avant le temps qu'il commente (SCHEMAS.md §5). */
.crible__jeton[data-sort="dehors"] rect,
.crible__jeton[data-sort="dehors"] text { transition: stroke 320ms var(--ease-out) 720ms,
                                                      fill 320ms var(--ease-out) 720ms; }
.slide[data-etat="crible"] .crible__jeton[data-sort="dehors"] rect,
.slide[data-etat="decide"] .crible__jeton[data-sort="dehors"] rect {
  stroke: var(--chaud, var(--data-warm));
}
.slide[data-etat="crible"] .crible__jeton[data-sort="dehors"] text,
.slide[data-etat="decide"] .crible__jeton[data-sort="dehors"] text {
  fill: var(--chaud, var(--data-warm));
}

.crible__verdict {
  fill: var(--chaud, var(--data-warm));
  letter-spacing: .1em;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 900ms;
}
.slide[data-etat="crible"] .crible__verdict,
.slide[data-etat="decide"] .crible__verdict { opacity: 1; }

/* La chute — le renversement : ce qui est dehors est ce qui décide. */
.crible__decide {
  fill: var(--ink);
  font-size: 27px;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="decide"] .crible__decide { opacity: 1; }

@media print {
  .crible__jeton { transform: translateX(0) !important; transition: none !important; }
  .crible__jeton[data-sort="dehors"] rect { stroke: var(--chaud, var(--data-warm)) !important; }
  .crible__jeton[data-sort="dehors"] text { fill: var(--chaud, var(--data-warm)) !important; }
  .crible__verdict, .crible__decide { opacity: 1 !important; transition: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .crible__jeton { transition: none; }
}

/* --- Le tri — trois registres, trois réponses -----------------------------
   Le garde-fou du fil conducteur : « une méfiance est une représentation ;
   un arrêt est une pratique ; un reste à charge n'est pas une croyance ».
   Trois colonnes qui se distinguent par leur FILET, pas par leur couleur de
   fond : ce sont des registres, pas des niveaux.                           */
/* @famille tri · registre nommé */
.tri { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }
.tri__registre {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding-top: var(--space-3);
  border-top: 3px solid var(--froid, var(--data-cool));
}
.tri__registre[data-registre="pratique"]  { border-top-color: var(--brand-primary); }
.tri__registre[data-registre="condition"] { border-top-color: var(--chaud, var(--data-warm)); }
.tri__registre > b {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500); font-weight: 400; letter-spacing: -.02em;
  color: var(--froid, var(--data-cool));
}
.tri__registre[data-registre="pratique"]  > b { color: var(--brand-primary); }
.tri__registre[data-registre="condition"] > b { color: var(--chaud, var(--data-warm)); }
.tri__exemple {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400); line-height: 1.35; color: var(--ink);
}
.tri__reponse {
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  font-size: var(--fs-200); color: var(--muted);
}
.tri__reponse strong { color: var(--ink); }

/* --- Ce que la méthode demande — un axe, et une bande qui s'épaissit -------
   Les contraintes du dehors — le coût, le délai, la distance — se voient et se
   nomment. Celle-ci ne se voit pas : plus une méthode affranchit du système,
   plus elle demande à la personne. Le dispositif de longue durée ne demande
   presque rien après la pose ; l'observation du cycle demande du temps, de la
   littératie et de la régularité, tous les jours.

   UNE SEULE VARIABLE, UN SEUL CANAL. La bande dit l'exigence par son ÉPAISSEUR
   et par rien d'autre — pas de dégradé, pas de changement de teinte. Encoder
   deux fois la même chose donnerait à croire qu'il y en a deux.

   LE SUJET DE LA PHRASE EST LA MÉTHODE, JAMAIS LA FEMME. C'est la garde de
   cette slide, et elle vaut jusque dans les libellés : « ce que la méthode
   demande », pas « ce dont elle est capable ». Formulée à l'envers, la
   démonstration s'entend comme un procès en incompétence — l'inverse exact de
   ce que la séance tient depuis la première minute.                         */
.exigence__axe { stroke: var(--line-strong); stroke-width: 3; stroke-linecap: round; }
.exigence__pointe { fill: var(--line-strong); }
/* TROIS STRATES QUI SE DÉPOSENT, une par ressource nommée. La bande disait
   l'exigence ; maintenant elle l'ACCUMULE. Chaque strate est plus épaisse à
   droite qu'à gauche — 4 px contre 24 —, et les trois ensemble refont le coin.

   Elles sont de la MÊME teinte, séparées par trois pixels de vide. Un dégradé
   de tons encoderait une seconde variable là où il n'y en a qu'une : c'est
   l'épaisseur qui parle, trois fois. Le vide ne dit rien non plus — il rend
   seulement les couches dénombrables.

   C'est le geste inverse de `.sol-choix`, où des barres RACCOURCISSENT pour
   resserrer. Ici on empile. Les deux appartiennent au même mouvement de
   pensée — ce que les conditions font à une marge — et se lisent l'un contre
   l'autre.

   CHAQUE STRATE ARRIVE AVEC SA RESSOURCE, par le même `data-etat` : la liste
   cesse d'être posée à côté de la figure, elle en devient la légende. */
/* LE SCHÉMA NE SE LAISSE PAS ÉCRASER. `.schema` porte `max-height: 100%`, et
   dans un `.corps` en colonne flexible il se fait comprimer par tout ce qu'on
   ajoute au-dessous : ajouter la ligne de légende lui a pris 80 px de hauteur,
   et comme un SVG en `meet` se met à l'échelle sur sa plus petite dimension,
   le dessin a rétréci EN LARGEUR — figure au tiers du cadre, libellés
   illisibles. Rien dans le code ne le disait ; le tirage, si. */
/* @famille exigence · verbe OPPOSER · registre nommé */
.exigence { flex: none; max-height: none; }

.exigence__strate {
  fill: var(--seq-2);
  transform: scaleY(0);
  transform-box: fill-box;
  transform-origin: center top;
  transition: transform 620ms var(--ease-out);
}
.slide[data-etat="temps"]      .exigence__strate[data-couche="1"],
.slide[data-etat="litteratie"] .exigence__strate[data-couche="1"],
.slide[data-etat="litteratie"] .exigence__strate[data-couche="2"],
.slide[data-etat="regularite"] .exigence__strate[data-couche="1"],
.slide[data-etat="regularite"] .exigence__strate[data-couche="2"],
.slide[data-etat="regularite"] .exigence__strate[data-couche="3"] { transform: scaleY(1); }

@media print {
  /* l'arrivée est le coin complet : les trois strates déposées */
  .exigence__strate { transform: none !important; transition: none !important; }
}
.exigence__pole {
  fill: var(--brand-primary); font-weight: 700;
  letter-spacing: .1em;
}
/* LA LÉGENDE A DISPARU, DEUX FOIS. Posée sous l'extrémité fine, elle tombait
   DANS la bande dès que celle-ci s'épaississait. Remontée en titre de liste,
   elle coûtait 45 px de hauteur — et la chute passait sous la source. Elle
   n'est plus là du tout : la liste des trois ressources EST la légende de la
   bande, une ligne par strate, et le titre de la slide dit déjà « ressources ».
   Une légende qui répète le titre n'était de toute façon qu'un doublon. */

/* ⌁ LA RECETTE SIGNALE UN RECOUVREMENT ENTRE STRATES. Elle mesure des boîtes
   englobantes, et trois coins obliques ont des boîtes qui se chevauchent
   largement alors que les formes, elles, gardent leurs 2 px de vide. Vérifié
   au tirage. Ne pas « réparer ». */

.exigence__ressources {
  width: 62%;
  margin: var(--space-2) 0 0 auto;   /* sous la partie épaisse, et pas ailleurs */
  padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: var(--space-2);
}
.exigence__ressources li {
  display: grid; grid-template-columns: auto 1fr;
  gap: var(--space-4); align-items: baseline;
  padding-left: var(--space-4);
  border-left: 2px solid var(--line);
}
.exigence__ressources b {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400); font-weight: 400;
  color: var(--brand-primary); white-space: nowrap;
}
.exigence__ressources span { font-size: var(--fs-200); line-height: 1.35; color: var(--muted); }

/* --- Deux regards, et un écart qu'on franchit -----------------------------
   « Se mettre à sa place » se dit comme une évidence. Pris au mot, c'est se
   SUPERPOSER — et qui se superpose ne voit plus ce que l'autre regarde : il
   ne voit plus que ce qu'elle ressent. La figure le fait plutôt que de le
   dire. Deux silhouettes, un objet entre elles, deux axes de regard qui
   arrivent au même endroit par deux angles nettement différents.

   TROIS CHOSES PORTENT LA DÉMONSTRATION, et aucune n'est décorative.

   1. L'objet s'efface EN MÊME TEMPS que la fusion, jamais après. S'il partait
      après, on lirait une conséquence ; simultané, on lit une identité — se
      confondre EST perdre l'objet. C'est tout l'argument.
   2. La séparation est plus lente que la fusion (900 contre 620 ms). Se
      confondre est instantané, comprendre prend du temps : la différence de
      durée dit la phrase sans un mot.
   ⌁ LA RECETTE SIGNALE UN RECOUVREMENT DE 100 % à l'état « compatir », et
     c'est exactement ce qu'on veut voir : les deux silhouettes se confondent.
     Ne pas « réparer ». Le canal ⌁ montre des choses à regarder, pas des
     fautes — celle-ci, regardée, est la démonstration.

   3. Le pointillé n'est PAS chaud. Dans ce deck le saumon signale ce qui fait
      problème (SCHEMAS §11) ; ici seule la superposition l'est. Le geste juste
      reste bleu.

   LE TRAJET DU TEMPS 4, et pourquoi il n'est pas ce qui était demandé. « La
   ligne de vous pivote pour venir dans l'axe de celle d'elle » ne se construit
   pas : vous est à DROITE de l'objet, une parallèle à l'axe d'elle part donc
   vers le hors-cadre, et une colinéaire exigerait de poser vous sous le sol.
   Ce que la figure trace à la place dit la même chose, en mieux : un chemin
   pointillé part de vous, rejoint l'endroit où ELLE se tient, puis redescend
   exactement sur son axe. Le corps ne bouge pas — seul le regard fait le
   détour. C'est la différence avec le temps 2, où c'est le corps qui se
   déplace. Et le détour se voit : comprendre est un travail.               */
/* @famille regards · verbe OPPOSER · registre nommé */
.regards__sol { stroke: var(--line-strong); stroke-width: 2; }
.regards__elle { fill: var(--brand-primary); }
.regards__vous {
  fill: var(--froid);
  transition: transform 900ms var(--ease-out), fill 900ms var(--ease-out);
}
.regards__decision {
  fill: var(--neutre); stroke: var(--line-strong); stroke-width: 3;
  transition: opacity 900ms var(--ease-out);
}
.regards__axe { stroke: var(--froid); stroke-width: 3; fill: none; stroke-linecap: round; }
.regards__axe--vous { transition: opacity 900ms var(--ease-out); }
.regards__nom { fill: var(--muted); }
.regards__objet { fill: var(--muted); }

/* le détour : deux segments, révélés l'un après l'autre — d'abord rejoindre
   son point de vue, ensuite seulement suivre son axe. L'ordre est le propos. */
.regards__detour {
  stroke: var(--froid); stroke-width: 3; fill: none;
  stroke-dasharray: 3 9; stroke-linecap: round;
  opacity: 0; transition: opacity 350ms var(--ease-out);
}

.regards__verdict {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 25px; fill: var(--brand-primary);
  opacity: 0; transition: opacity var(--dur-3) var(--ease-out);
}
.regards__verdict[data-temps="compatir"] { fill: var(--chaud); }
.regards__glose {
  font-size: 15px; fill: var(--muted);
  opacity: 0; transition: opacity var(--dur-3) var(--ease-out);
}

/* TEMPS 2 — la superposition. 620 ms : la borne basse du registre de
   démonstration (CHARTE), et la plus rapide qu'il autorise. */
.slide[data-etat="compatir"] .regards__vous {
  transform: translateX(-556px);
  fill: var(--chaud);
  transition-duration: 620ms;
  transition-timing-function: var(--ease-io);
}
.slide[data-etat="compatir"] .regards__decision {
  opacity: 0;
  transition-duration: 620ms;              /* SIMULTANÉ, sans délai : voir 1 */
  transition-timing-function: var(--ease-io);
}
.slide[data-etat="compatir"] .regards__axe--vous { opacity: 0; transition-duration: 620ms; }
.slide[data-etat="compatir"] .regards__verdict[data-temps="compatir"],
.slide[data-etat="compatir"] .regards__glose[data-temps="compatir"] { opacity: 1; }

/* TEMPS 3 — la séparation ne s'écrit pas : l'état « separation » cesse
   simplement d'appliquer les règles ci-dessus, et les transitions de base
   ramènent tout en 900 ms. C'est l'écart des deux durées qui démontre. */

/* TEMPS 4 — le détour */
.slide[data-etat="comprendre"] .regards__axe--vous { opacity: 0; }
.slide[data-etat="comprendre"] .regards__detour { opacity: 1; }
.slide[data-etat="comprendre"] .regards__detour[data-segment="axe"] { transition-delay: 350ms; }
.slide[data-etat="comprendre"] .regards__verdict[data-temps="comprendre"],
.slide[data-etat="comprendre"] .regards__glose[data-temps="comprendre"] { opacity: 1; }

@media print {
  /* L'ARRIVÉE EST LE TEMPS 4. Une animation « both » rendrait sa première
     image — ici l'état de départ, qui ne dit rien. */
  .regards__vous { transform: none !important; fill: var(--froid) !important; }
  .regards__decision { opacity: 1 !important; }
  .regards__axe--vous { opacity: 0 !important; }
  .regards__detour { opacity: 1 !important; transition: none !important; }
  .regards__verdict[data-temps="compatir"],
  .regards__glose[data-temps="compatir"] { opacity: 0 !important; }
  .regards__verdict[data-temps="comprendre"],
  .regards__glose[data-temps="comprendre"] { opacity: 1 !important; }
}

/* --- À emporter — le récapitulatif qu'on photographie ---------------------
   Une slide-document, et c'est un genre à part : elle n'est pas faite pour
   être dite, elle est faite pour être PRISE. D'où deux règles qui vont contre
   les habitudes du deck. Le corps descend à 14 px — sous le plancher qu'on
   s'impose en salle, mais lisible sur un écran de téléphone, qui est le vrai
   support de celle-ci. Et surtout : AUCUNE APPARITION PAR COLONNE. Une salle
   qui photographie ne photographie qu'une fois ; une colonne révélée après
   coup est une colonne absente du cliché. La slide arrive entière.

   Pas de pulsation sur les signes non plus : ce serait une boucle nouvelle,
   et le non-négociable 5 l'interdit. Le seul mouvement est la chute, qui
   monte quand tout le reste est déjà là.                                   */
/* @famille emporter · verbe RÉCAPITULER · registre nommé */
.emporter { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }
.emporter__colonne {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding-left: var(--space-6);
  border-left: 1px solid var(--line);
}
.emporter__colonne:first-child { padding-left: 0; border-left: 0; }
.emporter__titre {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-100); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--brand-primary);
}
.emporter__titre .ico { width: 17px; height: 17px; flex: none; }
.emporter__question {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic; font-size: var(--fs-400); line-height: 1.3;
  color: var(--ink); margin: 0;
}
.emporter__liste { list-style: none; margin: 0; padding: 0;
                   display: flex; flex-direction: column; gap: var(--space-2); }
.emporter__liste li {
  position: relative; padding-left: 13px;
  font-size: var(--fs-200); line-height: 1.38; color: var(--muted);
}
.emporter__liste li::before {
  content: "·"; position: absolute; left: 2px; top: -1px;
  color: var(--brand-accent); font-weight: 700;
}
.emporter__liste li strong { color: var(--ink); font-weight: 600; }

/* --- Le dézoom — jamais l'individu seul ------------------------------------
   La posture de la discipline, en une figure (demandée par Nicolas le 25
   août : « la vision de la sociologie c'est toujours celle du système,
   jamais d'un individu isolé »). Une personne au centre, seule. Puis le
   champ s'élargit : ce qui la tenait apparaît autour d'elle — et elle n'a
   pas bougé d'un pixel. C'est le point : on ne remplace pas la personne par
   le système, on cesse de la regarder découpée.                            */
.dezoom__personne { fill: var(--brand-primary); }
/* LE CHAMP S'ÉLARGIT — et c'est la figure qui doit le faire.

   Les deux halos PARAISSAIENT, à rayon fixe, et les liens aussi : la slide
   s'appelle « le champ s'élargit » et rien ne s'élargissait. Tout le mouvement
   était de l'opacité — ce qui explique aussi que le plancher de lisibilité ne
   les voyait pas au début, puisqu'il ne comptait que les animations.

   Ils croissent maintenant depuis la personne ; les liens se TRACENT vers ce
   qui la tient ; les acteurs se posent au bout du leur. Elle, elle n'a pas
   bougé d'un pixel — c'est tout le propos, et il est enfin joué.
   Traits portés de 1,5 à 3,5 px : c'est la figure qui pose l'angle de la
   séance, vue du fond de la salle (SCHEMAS.md §1 et §4). */
.dezoom__halo {
  fill: none;
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3.5;
  opacity: 0;
  transform-box: view-box;
  transform-origin: 572px 165px;
  transform: scale(.12);
  transition: opacity var(--dur-3) var(--ease-out),
              transform 760ms var(--ease-io);
}
.dezoom__lien {
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3.5;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 640ms var(--ease-io) 240ms;
}
.dezoom__acteur { opacity: 0; transition: opacity var(--dur-3) var(--ease-out); }
.dezoom__acteur circle { fill: var(--paper-bright); stroke: var(--froid, var(--data-cool)); stroke-width: 3.5; }
.dezoom__acteur text { fill: var(--ink); }
.dezoom__seule { opacity: 1; transition: opacity var(--dur-2) var(--ease-out); }
/* Le champ s'ouvre : les liens d'abord, puis ce qu'ils relient. */
.slide[data-etat="champ"] .dezoom__halo { opacity: .5; transform: scale(1); }

/* L'AMBIANCE — le champ, une fois élargi, respire. Troisième registre du
   moteur (ANIMATIONS.md) : ça ne démontre rien, et c'est sa définition. Le
   retirer ne change rien à l'argument.
   Amplitude 1 → 1,035 sur 5,2 s : sous le seuil du remarquable. On ne le voit
   pas battre, on sent que ce n'est pas figé — c'est toute la différence entre
   agréable et cheap. Et jamais PENDANT la croissance : l'ambiance occupe le
   temps mort, pas le temps du propos. */
@media (prefers-reduced-motion: no-preference) {
  .slide[data-etat="champ"] .dezoom__halo {
    animation: respirer 5.2s var(--ease-io) 900ms infinite;
  }
  .slide[data-etat="champ"] .dezoom__halo:nth-of-type(2) { animation-delay: 2.5s; }
}
@keyframes respirer {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.035); }
}
@media print { .dezoom__halo { animation: none !important; } }
.slide[data-etat="champ"] .dezoom__halo:nth-of-type(2) { transition-delay: 0ms, 110ms; }
.slide[data-etat="champ"] .dezoom__lien { stroke-dashoffset: 0; }
.slide[data-etat="champ"] .dezoom__acteur { opacity: 1; transition-delay: 700ms; }
.slide[data-etat="champ"] .dezoom__acteur:nth-of-type(2) { transition-delay: 90ms; }
.slide[data-etat="champ"] .dezoom__acteur:nth-of-type(3) { transition-delay: 180ms; }
.slide[data-etat="champ"] .dezoom__acteur:nth-of-type(4) { transition-delay: 270ms; }
.slide[data-etat="champ"] .dezoom__seule { opacity: 0; }
@media print {
  .dezoom__halo { opacity: .5 !important; transform: scale(1) !important; }
  .dezoom__lien { stroke-dashoffset: 0 !important; }
  .dezoom__acteur { opacity: 1 !important; }
  .dezoom__seule { opacity: 0 !important; }
}

/* --- La réglette — la lecture qui descend ---------------------------------
   Idée de l'audit du 25 août (angle « l'attention »), sœur de la loupe :
   là où la loupe grossit, la réglette ACCOMPAGNE. Une bande claire glisse
   d'une entrée à l'autre — le même geste d'examen, posé successivement.
   Elle passe SOUS le texte et n'emprunte jamais le chaud : ce n'est pas un
   jalon, c'est un regard. */
/* Ce flux-là est en COLONNES (le moteur range .flux en rangée) : la réglette
   se déplace donc EN LARGEUR, d'une colonne à l'autre. La première version
   descendait verticalement et sortait de la slide — le bug de la 28, vu par
   Nicolas. La leçon : une figure d'accompagnement doit suivre l'axe de ce
   qu'elle accompagne, pas l'axe qu'on lui a imaginé. */
.flux--reglette { position: relative; --pas: 0; --colonnes: 3; }
.flux--reglette > li { position: relative; z-index: 1; }
.flux--reglette::before {
  content: "";
  position: absolute;
  inset-block: calc(var(--space-4) * -1);
  left: 0;
  width: calc(100% / var(--colonnes));
  border-radius: var(--radius-md);
  background-color: var(--surface-tint);
  opacity: 0;
  transform: translateX(calc(var(--pas) * 100%));
  transition: transform 420ms var(--ease-io), opacity var(--dur-2) var(--ease-out);
}
.slide[data-etat="q1"] .flux--reglette { --pas: 0; }
.slide[data-etat="q2"] .flux--reglette { --pas: 1; }
.slide[data-etat="q3"] .flux--reglette { --pas: 2; }
.slide:is([data-etat="q1"], [data-etat="q2"], [data-etat="q3"]) .flux--reglette::before { opacity: 1; }
@media print {
  /* Au tirage, la lecture est finie : la réglette se retire, les trois
     entrées se valent à nouveau. */
  .flux--reglette::before { opacity: 0 !important; }
}

/* --- L'incise — la phrase fait place à sa nuance ---------------------------
   Idée de l'audit (angle « le texte ») : une affirmation s'ouvre en son
   milieu pour laisser entrer sa condition. La règle pose l'inline-size
   FINALE ; l'impression et reduced-motion montrent la phrase entière. */
/* @famille incise · registre nommé */
.incise {
  display: inline-block;
  inline-size: 0;
  overflow: clip;
  white-space: nowrap;
  vertical-align: baseline;
  color: var(--brand-primary);
  transition: inline-size 720ms var(--ease-io);
}
.slide[data-etat="nuance"] .incise { inline-size: var(--incise, 24ch); }
@media print { .incise { inline-size: var(--incise, 24ch) !important; } }

/* --- L'égrenage — la charge n'est pas un événement, c'est une répétition --
   Vingt-huit cases s'encrent une à une : le mois de la prise quotidienne.
   Le jour manqué reste en réserve, cerclé de chaud — statistiquement normal,
   et c'est le propos. Idée de l'audit du 25 août (angle « le temps »).
   La cadence EST le contenu : un battement par jour, 1100 ms pour le mois. */
/* @famille egrenage · registre nommé */
.egrenage { display: grid; justify-items: center; gap: var(--space-6); }
.egrenage__mois {
  display: grid;
  /* SEPT colonnes, parce que 28 = 4 × 7 et que le chapô dit « tous les jours ».
     En quatorze, la grille était un rectangle : elle comptait juste et rangeait
     faux. En sept, c'est un calendrier — ou une plaquette — et personne n'a
     besoin qu'on le lui explique. (SCHEMAS.md §6.) */
  grid-template-columns: repeat(7, 1fr);
  gap: 8px;
}
.egrenage__jour {
  /* Plus grandes qu'en quatorze colonnes : la grille a perdu la moitié de sa
     largeur, elle reprend en présence. */
  width: 34px;
  height: 34px;
  border-radius: 4px;
  border: 1.5px solid var(--froid, var(--data-cool));
  background-color: transparent;
}
/* L'état pose le remplissage FINAL ; l'animation ne fait que la vague. */
.slide[data-etat="mois"] .egrenage__jour {
  background-color: var(--froid, var(--data-cool));
  animation: egrenage-encrer 220ms var(--ease-out) both;
  animation-delay: calc(var(--j) * 39ms);
}
@keyframes egrenage-encrer { from { background-color: transparent; } }
/* Le jour manqué : jamais encré, cerclé de chaud. Le seul jalon.

   IL S'ALLUME À SON TEMPS. Il portait `animation: none` : le cerclage chaud
   était donc là dès la première image, une seconde avant que la vague
   l'atteigne — la salle voyait le trou avant que le compte y arrive, et le
   jalon vendait la mèche. Il partage maintenant le délai de ses voisines :
   jusqu'à son tour il leur ressemble, puis toutes s'encrent et lui reste vide.
   (SCHEMAS.md §5.) Le trait passe à 3 px : il est animé, donc porteur, donc
   soumis au plancher de lisibilité — §1 vaut pour lui aussi. */
.slide[data-etat="mois"] .egrenage__jour[data-manque] {
  background-color: transparent;
  border-color: var(--chaud, var(--data-warm));
  border-width: 3px;
  animation: egrenage-manquer 220ms var(--ease-out) both;
  animation-delay: calc(var(--j) * 39ms);
}
@keyframes egrenage-manquer {
  from { border-color: var(--froid, var(--data-cool)); border-width: 1.5px; }
}
.egrenage__compte {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  color: var(--brand-primary);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 900ms;
}
.slide[data-etat="mois"] .egrenage__compte { opacity: 1; }
@media print {
  .egrenage__jour { background-color: var(--froid, var(--data-cool)) !important; animation: none !important; }
  .egrenage__jour[data-manque] {
    background-color: transparent !important; animation: none !important;
    border-color: var(--chaud, var(--data-warm)) !important; border-width: 3px !important;
  }
  .egrenage__compte { opacity: 1 !important; }
}

.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__station[data-ecart] .carriere__vecu > b,
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__station[data-ecart] .carriere__cadre > span {
  color: var(--chaud);
}
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__station[data-ecart] .carriere__vecu > span {
  color: var(--ink);
}
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__station[data-ecart] .carriere__vecu::before,
.slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"]) .carriere__station[data-ecart] .carriere__cadre::before {
  background-color: var(--chaud);
}

@keyframes carriere-poser {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* À l'impression, la slide est finie : les deux rails et leurs décalages. */
@media print {
  .carriere__trait { stroke-dashoffset: 0 !important; }
  .carriere__rail[data-rail="cadre"],
  .carriere__vecu,
  .carriere__cadre { opacity: 1 !important; animation: none !important; transform: none !important; }
  .carriere__station[data-ecart] .carriere__ecart {
    opacity: 1 !important; animation: none !important; transform: none !important;
  }
}

/* --- 5b.10 bis. Les logiques croisées ------------------------------------
   Distinct du glissement, et il fallait le distinguer : le glissement dit
   « l'objet a glissé », donc une méprise. La croisée dit « quelque chose se
   perd ET quelque chose se gagne, dans deux registres différents » — donc un
   échange, pas une erreur.

   Correction de Nicolas, 21 août : sur la ménopause j'avais écrit une méprise
   là où il y a un échange. « Non, ce n'est pas ça » — ce qui se gagne, c'est
   la fin d'une contrainte ; ce qui se perd, c'est le suivi, et certaines le
   vivent comme un abandon.

   D'où la croix : les deux versants ne s'additionnent pas, ils se croisent.
   -------------------------------------------------------------------------- */

/* @famille croisees · registre nommé */
.croisees {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: stretch;          /* les deux cartes à la même hauteur : c'est
                                    la « taille » que Nicolas signalait */
}

/* @famille croisee · verbe ORDONNER · registre nommé */
.croisee {
  display: flex;
  flex-direction: column;
  padding: var(--space-6) var(--space-6) var(--space-8);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-bright);
  transition: border-color var(--dur-3) var(--ease-out);
}
.croisee__moment {
  margin: 0 0 var(--space-2);
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.croisee__nature {
  margin: 0 0 var(--space-6);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  font-style: italic;
  color: var(--muted);
}
/* Les deux versants CÔTE À CÔTE, séparés par l'échange : c'est la disposition
   qui dit qu'ils ne s'additionnent pas. Empilés, ils se lisaient comme une
   suite ; en vis-à-vis, comme un troc. */
.croisee__paire {
  display: grid;
  grid-template-columns: 1fr 132px 1fr;
  gap: var(--space-4);
  align-items: center;
}

.croisee__terme > b {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.croisee__terme > span {
  display: block;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  line-height: 1.28;
}
.croisee__terme > i {
  display: block;
  margin-top: var(--space-2);
  font-style: normal;
  font-size: var(--fs-200);
  color: var(--muted);
}
.croisee__terme[data-role="perte"] > b { color: var(--chaud); }
.croisee__terme:is([data-role="gain"], [data-role="arrivee"]) { opacity: 0; }
.croisee__terme[data-role="gain"] > b { color: var(--brand-primary); }
.croisee__terme[data-role="gain"] > span { font-weight: 600; }

/* --- L'échange ------------------------------------------------------------
   Deux voies qui se croisent, en sens contraires : ce qui part s'en va vers
   la gauche, ce qui vient arrive par la droite. Un grain parcourt chacune —
   c'est le mouvement qui fait l'échange, une croix immobile n'en montrait
   rien. */

.croisee__echange { width: 132px; height: 108px; }
.croisee__voie { stroke-dasharray: 1; stroke-dashoffset: 1; }
.croisee__pointe { opacity: 0; }
.croisee__grain { opacity: 0; }

/* Le grain suit exactement le tracé de sa voie. */
.croisee__grain[data-sens="part"]  { offset-path: path("M118 26 L 20 82"); }
.croisee__grain[data-sens="vient"] { offset-path: path("M14 26 L 112 82"); }

.slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__voie,
.slide[data-etat="menopause"] .croisee .croisee__voie,
.slide[data-etat="chute"] .croisee .croisee__voie {
  animation: croiser 520ms var(--ease-io) both;
}
@keyframes croiser { to { stroke-dashoffset: 0; } }

.slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__grain,
.slide[data-etat="menopause"] .croisee .croisee__grain,
.slide[data-etat="chute"] .croisee .croisee__grain {
  animation: croisee-parcourir 760ms var(--ease-io) 420ms both;
}
/* Les deux grains partent ensemble et se croisent au milieu : donner des
   durées différentes casserait la rencontre, qui est tout le propos. */
@keyframes croisee-parcourir {
  0%   { offset-distance: 0%;   opacity: 0; }
  12%  { opacity: 1; }
  86%  { offset-distance: 100%; opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}

.slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__pointe,
.slide[data-etat="menopause"] .croisee .croisee__pointe,
.slide[data-etat="chute"] .croisee .croisee__pointe {
  animation: croisee-poser 220ms var(--ease-out) 1080ms both;
}

.slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__terme[data-role="gain"],
.slide[data-etat="menopause"] .croisee .croisee__terme[data-role="gain"],
.slide[data-etat="chute"] .croisee .croisee__terme:is([data-role="gain"], [data-role="arrivee"]) {
  animation: croisee-poser 420ms var(--ease-out) 900ms both;
}
@keyframes croisee-poser {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* La colonne de gauche ne se rejoue pas quand la droite arrive. */
.slide[data-etat="menopause"] .croisee[data-cle="preservatif"] .croisee__voie,
.slide[data-etat="menopause"] .croisee[data-cle="preservatif"] .croisee__grain,
.slide[data-etat="chute"] .croisee[data-cle="preservatif"] .croisee__voie,
.slide[data-etat="chute"] .croisee[data-cle="preservatif"] .croisee__grain { animation-delay: 0ms; }

.croisees__chute {
  margin: var(--space-8) 0 0;
  padding-top: var(--space-6);
  border-top: 1px solid var(--line);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.3;
  text-align: center;
  color: var(--brand-primary);
  opacity: 0;
}
.slide[data-etat="chute"] .croisees__chute {
  animation: croisee-poser var(--dur-3) var(--ease-out) 120ms both;
}

@media print {
  .croisee__voie { stroke-dashoffset: 0 !important; }
  .croisee__grain { opacity: 0 !important; }      /* un grain figé n'a pas de sens */
  .croisee__pointe,
  .croisee__terme:is([data-role="gain"], [data-role="arrivee"]),
  .croisees__chute { opacity: 1 !important; animation: none !important; }
}

/* --- 5b.10 Le glissement — deux logiques qui divergent --------------------
   Ce que la patiente croit viser, puis ce qu'elle vise en fait. La flèche se
   dessine entre les deux : c'est le glissement lui-même, et c'est pour cela
   qu'elle est animée plutôt que posée d'emblée.
   -------------------------------------------------------------------------- */

/* @famille glissements · registre nommé */
.glissements {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: start;
}
/* Trois colonnes : le geste vaut aussi pour « ce qu'on dit → ce qu'on
   pourrait dire ». Réemployer la forme plutôt qu'en inventer une autre, c'est
   ce qui fait qu'à la troisième occurrence la salle la lit sans y penser. */
.glissements[data-colonnes="3"] { grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }

/* Le propos de la slide est TROIS GESTES PARALLÈLES : « ce qu'on a tendance à
   dire » → la flèche → « une question qui ouvre ». Si une citation court sur
   deux lignes et les autres sur une, sa flèche et son libellé descendent d'un
   cran et le parallèle se casse — visible au tirage, jamais dans le code.

   On réserve donc deux lignes à la citation dans les trois colonnes. Le
   subgrid a été essayé et écarté : il aligne bien, mais chaque rangée y prend
   le maximum des trois colonnes, la carte vaut la somme des pires cas et
   déborde des 720 px. Une citation de trois lignes ferait revenir le
   désalignement — c'est alors la citation qu'il faut raccourcir. */
.glissements[data-colonnes="3"] .glissement__terme[data-role="cru"] > span {
  display: block;
  min-height: 2.68em;      /* deux lignes à line-height 1.34 */
}
.glissements[data-colonnes="3"] .glissement { padding: var(--space-6) var(--space-4) var(--space-6); }
.glissements[data-colonnes="3"] .glissement__terme > span { font-size: var(--fs-300); }
.glissements[data-colonnes="3"] .glissement__nature { margin-bottom: var(--space-4); }

/* La suite d'une reformulation, en retrait : ce qu'on fait APRÈS avoir posé
   la question. Elle ne doit pas peser autant que la question elle-même. */
.glissement__terme > span > i {
  display: block;
  margin-top: var(--space-2);
  font-style: normal;
  font-weight: 400;
  font-size: var(--fs-200);
  color: var(--muted);
}

/* @famille glissement · verbe REQUALIFIER · registre nommé */
.glissement {
  padding: var(--space-6) var(--space-6) var(--space-8);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-bright);
  transition: border-color var(--dur-3) var(--ease-out);
}
.glissement__moment {
  margin: 0 0 var(--space-2);
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.glissement__nature {
  margin: 0 0 var(--space-6);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  font-style: italic;
  color: var(--muted);
}
.glissement__paire { display: grid; justify-items: stretch; }

.glissement__terme > b {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.glissement__terme > span {
  display: block;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  line-height: 1.34;
}
.glissement__terme[data-role="reel"] { opacity: 0; }
.glissement__terme[data-role="reel"] > b { color: var(--froid, var(--data-cool)); }
.glissement__terme[data-role="reel"] > span { color: var(--ink); font-weight: 600; }

.glissement__fleche { width: 24px; height: 60px; justify-self: center; }
.glissement__tige {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.glissement__pointe { opacity: 0; }

/* Chaque colonne s'anime à son propre fragment. */
.slide[data-etat="preservatif"] .glissement[data-cle="preservatif"],
.slide[data-etat="menopause"] .glissement,
.slide[data-etat="chute"] .glissement { border-color: var(--line-strong); }

.slide[data-etat="preservatif"] .glissement[data-cle="preservatif"] .glissement__tige,
.slide[data-etat="menopause"] .glissement .glissement__tige,
.slide[data-etat="chute"] .glissement .glissement__tige {
  animation: glisser 520ms var(--ease-io) both;
}
@keyframes glisser { to { stroke-dashoffset: 0; } }

.slide[data-etat="preservatif"] .glissement[data-cle="preservatif"] .glissement__pointe,
.slide[data-etat="menopause"] .glissement .glissement__pointe,
.slide[data-etat="chute"] .glissement .glissement__pointe {
  animation: glissement-paraitre 200ms var(--ease-out) 470ms both;
}

.slide[data-etat="preservatif"] .glissement[data-cle="preservatif"] .glissement__terme[data-role="reel"],
.slide[data-etat="menopause"] .glissement .glissement__terme[data-role="reel"],
.slide[data-etat="chute"] .glissement .glissement__terme[data-role="reel"] {
  animation: glissement-poser 420ms var(--ease-out) 560ms both;
}
@keyframes glissement-paraitre { from { opacity: 0; } to { opacity: 1; } }
@keyframes glissement-poser {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* La colonne de droite n'attend pas son tour si l'on est déjà passé outre. */
.slide[data-etat="menopause"] .glissement[data-cle="preservatif"] .glissement__tige,
.slide[data-etat="chute"] .glissement[data-cle="preservatif"] .glissement__tige { animation-delay: 0ms; }

.glissements__chute {
  margin: var(--space-8) 0 0;
  padding-top: var(--space-6);
  border-top: 1px solid var(--line);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.3;
  text-align: center;
  color: var(--ink);
  opacity: 0;
}
.slide[data-etat="chute"] .glissements__chute {
  animation: glissement-poser var(--dur-3) var(--ease-out) 120ms both;
}

/* À l'impression, les deux slides sont finies. */
@media print {
  .carriere__trait { stroke-dashoffset: 0 !important; }
  .carriere__station { opacity: 1 !important; transform: none !important; animation: none !important; }
  .glissement__tige { stroke-dashoffset: 0 !important; }
  .glissement__pointe,
  .glissement__terme[data-role="reel"],
  .glissements__chute { opacity: 1 !important; animation: none !important; }
}


/* =============================================================================
   5 quinquies. LES FIGURES DE RAISONNEMENT — deuxième fournée (25 août 2026)
   Sept formes nées des retours sur le deck JSF : chacune existe parce qu'une
   slide précise en avait besoin, et chacune passe le critère — le mouvement
   EST la démonstration, pas son habillage. Le banc d'essai vivant est dans
   presentations/_maquettes/figures/deck.html ; l'entrée de chacune est dans
   PRESETS.md, sous son verbe.
   ========================================================================== */

/* --- Volets réunis : la catégorie se fabrique sous les yeux de la salle ---
   Variante de .volets pour un regroupement qui est un FAIT et non une
   propriété des objets : trois objets sans rapport convergent, une accolade
   se dessine, un mot les nomme. Le mouvement EST la démonstration — sans
   lui, il ne reste qu'une liste, et une liste ne démontre rien.

   Deux temps, cumulatifs :
     data-etat="rang"  les trois objets se rangent ;
     data-etat="mot"   l'accolade se dessine, le mot paraît, les détails
                       passent en retrait.                                  */

.volets--reunion {
  /* Plus étroite que la grille pleine : les positions écartées débordent de
     ±96 px, et la slide rogne (overflow: clip) tout ce qui dépasse. */
  width: 908px;
  margin-inline: auto;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  row-gap: var(--space-3);
}

.volets--reunion > .volet {
  text-align: center;
  /* 640 ms, hors des trois durées de la charte, pour la même raison que la
     boucle : ce trajet n'est pas un habillage, il EST le contenu — la
     catégorie naît du rapprochement, à une vitesse qu'on peut suivre. */
  transition: transform 640ms var(--ease-io);
}

/* ÉTAT INITIAL — écartés ET désalignés. Le désalignement dit « sans
   rapport » mieux que l'écart seul : trois objets sans ligne commune n'ont
   pas encore de catégorie commune. Les positions de DÉPART vivent ici, dans
   la règle par défaut : la règle d'état n'a que l'arrivée à poser, et le
   tirage n'a que transform: none à forcer. */
.volets--reunion > .volet[data-place="gauche"] { transform: translate(-96px, -22px); }
.volets--reunion > .volet[data-place="milieu"] { transform: translate(0, 24px); }
.volets--reunion > .volet[data-place="droite"] { transform: translate(96px, -12px); }

/* PREMIER TEMPS — la réunion. Valeurs finales posées directement (règle
   d'or) ; la transition ne fait que le trajet. Pas d'échelonnement : les
   trois arrivent ENSEMBLE — c'est le rang qui fait la catégorie, pas un
   ordre. CUMULATIF : « mot » redéclare le rang, car data-etat ne garde que
   la dernière valeur posée. */
.slide[data-etat="rang"] .volets--reunion > .volet,
.slide[data-etat="mot"]  .volets--reunion > .volet { transform: translate(0, 0); }

/* --- L'accolade — le geste qui referme la catégorie ---------------------- */
.volets__accolade {
  grid-column: 1 / -1;
  display: block;
  width: 100%;
  height: auto;
}
.volets__trace {
  fill: none;
  /* Le regroupement est le fait SOCIAL : en chaud, comme le jalon de
     rupture de .flux. Les volets restent froids — c'est le régime
     pharmacologique. Une seule couleur pour un seul fait. */
  stroke: var(--chaud);
  stroke-width: 3;
  stroke-linecap: round;
  opacity: 0;
}

/* SECOND TEMPS — l'accolade se dessine, le mot paraît. La règle pose les
   valeurs FINALES (opacity, dashoffset) ; l'animation ne fait que le
   trajet, et repart de ses propres images-clés — une transition n'aurait
   pas de point de départ si l'on appuie dès l'arrivée sur la slide. */
.slide[data-etat="mot"] .volets__trace { opacity: 1; stroke-dashoffset: 0; }

@media (prefers-reduced-motion: no-preference) {
  .slide[data-etat="mot"] .volets__trace {
    /* 620 ms : le geste doit être vu comme un geste — c'est lui qui referme
       la catégorie. La petite attente laisse le rang finir de se poser.
       Était à 560 ms, dans la zone morte entre l'interface (420) et la
       démonstration (620) : le plancher de la bande sert précisément ce que
       le commentaire demandait déjà. */
    animation: volets-accoler 620ms var(--ease-io) 120ms both;
  }
}
@keyframes volets-accoler { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

/* --- Le mot — la catégorie, nommée --------------------------------------- */
.volets__mot {
  grid-column: 1 / -1;
  justify-self: center;
  margin: 0;
  font-family: Georgia, "Times New Roman", serif;
  /* Le mot est la chute de la figure : il pèse autant qu'un titre. */
  font-size: var(--fs-800);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.1;
  /* Même couleur que l'accolade : une seule couleur pour un seul fait. */
  color: var(--chaud);
  opacity: 0;
  transform: translateY(-6px);   /* il descend de l'accolade, déposé par elle */
  /* 560 ms : le mot se dépose pendant que l'accolade finit de se fermer —
     même avance relative qu'avant l'allongement du tracé. */
  transition: opacity var(--dur-3) var(--ease-out) 560ms,
              transform var(--dur-3) var(--ease-out) 560ms;
}
.slide[data-etat="mot"] .volets__mot { opacity: 1; transform: none; }

/* Une fois le mot posé, la catégorie recouvre les différences : les voies
   d'administration passent en retrait. Geste d'ORAL seulement — le bloc
   print les rétablit, un PDF n'a pas la voix qui dit pourquoi c'est gris. */
.volets--reunion > .volet > span { transition: opacity var(--dur-2) var(--ease-out); }
.slide[data-etat="mot"] .volets--reunion > .volet > span { opacity: .55; }

@media print {
  /* Chrome rend la première image : tout ce qui pousse depuis zéro écrit
     ici son état d'arrivée. Le rang serré EST l'état d'arrivée. */
  .volets--reunion > .volet { transform: none !important; transition: none !important; }
  .volets__trace { opacity: 1 !important; stroke-dashoffset: 0 !important;
                   animation: none !important; }
  .volets__mot   { opacity: 1 !important; transform: none !important;
                   transition: none !important; }
  /* Les voies d'administration restent pleines : l'atténuation est un geste
     d'oral, le PDF n'a pas la voix qui explique le gris. */
  .volets--reunion > .volet > span { opacity: 1 !important; }
}

/* --- LA SUBSTITUTION — barrer un mot, poser le terme juste ----------------
   Variante d'OPPOSER pour le cas « corriger » : les deux termes ne se posent
   pas côte à côte comme dans `.opposition`, ils se succèdent au même endroit.
   La typographie EST le schéma : aucune boîte, aucune flèche — un mot en
   grand serif, un trait qui le barre (chaud), le terme juste qui monte
   prendre sa place (navy). Deux temps : data-etat="barre", data-etat="juste".
   La figure doit vivre dans `.corps.graphe` : c'est `.graphe` qui expose
   `--chaud` — hors de lui, le trait perdrait sa couleur.                    */

/* @famille substitution · registre nommé */
.substitution {
  margin: 0;                       /* <figure> a une marge UA qu'on annule  */
  text-align: center;
  font-family: Georgia, "Times New Roman", serif;
}

/* Le mot fautif. <del> pour le sens — la sémantique dit ce que la figure
   montre — mais SANS le line-through du navigateur : le trait est le nôtre,
   il doit se TIRER, et un text-decoration ne s'anime pas.                   */
.substitution__faux {
  position: relative;
  display: inline-block;           /* porte le pseudo-trait à sa mesure     */
  font-size: var(--fs-900);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  transition: color var(--dur-3) var(--ease-out);
}

/* Le trait, en chaud : le premier temps de la figure, donc un vrai élément
   animable. Posé à 58 % de la hauteur, pas à 50 % : la masse optique des
   bas-de-casse est sous la médiane, un trait à mi-boîte flotterait au-dessus
   du mot. Il déborde d'un dixième de cadratin de chaque côté : un trait qui
   s'arrête au ras des lettres a l'air de manquer son geste.                 */
.substitution__faux::after {
  content: "";
  position: absolute;
  left: -0.1em;
  right: -0.1em;
  top: calc(58% - 3px);
  height: 6px;
  background-color: var(--chaud);
  transform: scaleX(0);            /* scaleX, jamais width : pas de reflow  */
  transform-origin: left center;   /* le trait se tire dans le sens du geste */
}

/* Temps 1 — barrer. La règle pose la valeur FINALE (scaleX(1)) ; l'animation
   ne fait que le trajet — reduced-motion coupe l'animation et montre l'état
   juste. 640 ms, hors des trois durées : autorisé parce que le trait EST le
   propos de la slide — ne pas recopier cette durée ailleurs. Les deux états
   sont groupés, avec les MÊMES valeurs : « juste » re-déclare ce que
   « barre » a allumé, sinon le trait se retirerait au second clic — et à
   déclaration identique, l'animation ne repart pas de zéro.                 */
.slide[data-etat="barre"] .substitution__faux::after,
.slide[data-etat="juste"] .substitution__faux::after {
  transform: scaleX(1);
  animation: substitution-barrer 640ms var(--ease-io) both;
}
@keyframes substitution-barrer { from { transform: scaleX(0); } }

/* Une fois barré, le mot recule en gris : encore lisible — c'est le point,
   on ne corrige pas en effaçant — mais il a cessé d'être le terme.          */
.slide[data-etat="barre"] .substitution__faux,
.slide[data-etat="juste"] .substitution__faux {
  color: var(--muted);
}

/* Temps 2 — le terme juste. <ins> pour le sens (souligné par défaut : on le
   neutralise, le navy suffit). Navy et non couleur de donnée : ce n'est pas
   un encodage, c'est la voix du titre — le mot exact a l'autorité du titre.
   Sa place est réservée dès le départ (opacité seule) : s'il paraissait en
   display, toute la figure sauterait au second clic.                        */
.substitution__juste {
  display: block;
  margin-top: var(--space-6);
  font-size: var(--fs-800);
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--brand-primary);
  text-decoration: none;
  opacity: 0;
}
.substitution__juste > b {
  font-size: var(--fs-900);        /* le sigle en tête, au format du mot barré :
                                      c'est LUI qui prend la place            */
  font-weight: 700;
}
.slide[data-etat="juste"] .substitution__juste {
  opacity: 1;
  transform: none;                 /* valeur finale posée, règle d'or        */
  animation: substitution-monter var(--dur-3) var(--ease-out) both;
}
/* Il MONTE : il vient occuper la place que le mot barré tient encore.       */
@keyframes substitution-monter {
  from { opacity: 0; transform: translateY(26px); }
}

/* La ligne qui justifie le trait : pourquoi ce mot est fautif. Elle paraît
   au premier temps, APRÈS que le trait a fini de se tirer (délai 560 ms >
   640 ms de trajet entamé) : on barre, puis on dit pourquoi. Italique serif,
   la voix de la nuance — même registre que le <span> de `.enonce`.          */
.substitution__pourquoi {
  margin: var(--space-6) auto 0;
  max-width: 58ch;
  font-size: var(--fs-400);
  font-style: italic;
  line-height: 1.45;
  color: var(--muted);
  opacity: 0;
}
.slide[data-etat="barre"] .substitution__pourquoi,
.slide[data-etat="juste"] .substitution__pourquoi {
  opacity: 1;
  animation: substitution-poser var(--dur-3) var(--ease-out) 560ms both;
}
@keyframes substitution-poser {
  from { opacity: 0; transform: translateY(5px); }
}

/* La chute, même gabarit que `.glissements__chute` : la phrase qu'on
   retient, détachée de la figure par un filet. C'est un fragment ORDINAIRE
   (troisième flèche) : le moteur gère son apparition et son impression —
   pas de data-etat, pas de règle print à écrire pour elle.                  */
.substitution__chute {
  margin: var(--space-8) 0 0;
  padding-top: var(--space-6);
  border-top: 1px solid var(--line);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.3;
  text-align: center;
  color: var(--brand-primary);
}

/* La variante menue : la même figure, aux tailles d'un contexte chargé —
   quand la substitution partage la slide avec ce qui la motive (une une de
   presse, une donnée). L'originale reste la forme pleine page. */
.substitution--menue .substitution__faux { font-size: var(--fs-700); }
.substitution--menue .substitution__faux::after { height: 4px; top: calc(58% - 2px); }
.substitution--menue .substitution__juste { margin-top: var(--space-4); font-size: var(--fs-600); }
.substitution--menue .substitution__juste > b { font-size: var(--fs-700); }
.substitution--menue .substitution__pourquoi { margin-top: var(--space-4); font-size: var(--fs-300); }

/* --- Impression -----------------------------------------------------------
   Chrome ne joue pas les animations au tirage : il rend la première image.
   Le moteur pose l'état final et .est-revele au beforeprint, mais tout ce
   qui pousse depuis zéro (scaleX, opacités animées) doit écrire son arrivée
   en !important — sinon le PDF montrerait le mot intact et la place vide.   */
@media print {
  .substitution__faux { color: var(--muted) !important; transition: none !important; }
  .substitution__faux::after { transform: scaleX(1) !important; animation: none !important; }
  .substitution__pourquoi,
  .substitution__juste { opacity: 1 !important; transform: none !important;
                         animation: none !important; }
}



/* --- La levée — une norme tombe, la pratique suit avec retard ------------
   Un portillon retient une file de pastilles. La lisse se lève LENTEMENT —
   900 ms, hors des trois durées, et c'est assumé : la lenteur EST le propos,
   le retard de la pratique sur la recommandation. Puis la file passe.
   Deux temps, cumulatifs : data-etat="levee", puis data-etat="passage".
   Les deux fragments muets font partie du préset — sans eux, la chute et
   les labels d'arrivée ne paraissent jamais.
   ------------------------------------------------------------------------- */

/* @famille levee · verbe CATÉGORISER · registre nommé */
.levee__sol { stroke: var(--line-strong); stroke-width: 2; }

/* Le portillon entier est chaud : c'est lui, le point de friction. */
.levee__montant { fill: var(--chaud); transition: fill var(--dur-3) var(--ease-io); }
.levee__pivot { fill: var(--chaud, var(--data-warm)); }  /* le jalon : là où ça se joue */
.levee__lisse   { stroke: var(--chaud); stroke-width: 8; stroke-linecap: round;
                  transition: stroke var(--dur-3) var(--ease-io); }

/* La norme parle en chaud tant qu'elle tient. */
.levee__norme { fill: var(--chaud); transition: fill var(--dur-3) var(--ease-io); }

/* Ce qui n'existe pas encore naît éteint. */
.levee__verdict { fill: var(--brand-primary); opacity: 0; }
/* Les pastilles sont des PERSONNES, donc une donnée : le froid de la palette
   de données — jamais le noir d'encre, qui n'est pas une couleur de marque
   pour un aplat, ni le navy, qui n'encode jamais une donnée. */
.levee__pastille { fill: var(--froid); }
.levee__toutes  { fill: var(--brand-primary); opacity: 0; }
.levee__trajet  { stroke: var(--brand-accent); stroke-width: 3; fill: none;
                  stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; }
.levee__pointe  { fill: var(--brand-accent); opacity: 0; }
.levee__chute   { opacity: 0; }

/* --- Premier temps : la levée --------------------------------------------
   RÈGLE D'OR : l'état pose les valeurs FINALES, l'animation ne fait que le
   trajet — prefers-reduced-motion montre donc l'état juste. Et "passage"
   REDIT tout ce que "levee" a posé : les états sont cumulatifs.

   La rotation CSS d'un élément SVG tourne autour de l'origine du viewBox,
   pas de l'élément : on encadre donc rotate() par la paire translate
   aller/retour, en coordonnées ABSOLUES du pivot (440,200) — dans la règle
   d'état, dans la keyframe, et dans le bloc print. Aucun transform-origin. */
.slide[data-etat="levee"]   .levee__lisse,
.slide[data-etat="passage"] .levee__lisse {
  /* 60°, PAS 105. À 105° la lisse finissait à 14° du montant : les deux
     devenaient un seul mât, avec le pivot flottant au milieu, et l'anatomie de
     la barrière disparaissait. À 60° elle reste franchement penchée — on voit
     un bras levé, pas un poteau. */
  /* Le montant traverse le pivot : il reste visible AU-DESSUS et EN DESSOUS.
     Il ne faisait que 50 px pour un bras de 173 — l'œil lisait le bras levé
     comme le poteau, et on ne voyait plus ce qui avait bougé. */
  transform: translate(434px, 244px) rotate(-60deg) translate(-434px, -244px);
  stroke: var(--hors);
  /* 900 ms : la seule animation du deck autorisée hors des trois durées,
     parce qu'elle est le propos — le retard, regardé en direct. */
  animation: levee-lever 900ms var(--ease-io) both;
}
@keyframes levee-lever {
  from { transform: translate(434px, 244px) rotate(0deg) translate(-434px, -244px); }
}
/* La keyframe générique tourne autour de l'ancien pivot (434,244). La barre
   pend du sien, plus haut : sans cette variante, elle partait de travers. */
.levee--barre .levee__lisse { animation-name: levee-lever-barre !important; }
@keyframes levee-lever-barre {
  from { transform: translate(434px, 152px) rotate(0deg) translate(-434px, -152px); }
}

/* Une fois levée, la barrière ne disparaît pas : elle devient caduque.
   Le portillon passe en --hors, la norme en --muted — c'est la variante
   « ce qui a changé » : le retrait reste visible dans la scène. */
.slide[data-etat="levee"]   .levee__montant,
.slide[data-etat="passage"] .levee__montant { fill: var(--hors); }
.slide[data-etat="levee"]   .levee__pivot,
.slide[data-etat="passage"] .levee__pivot   { stroke: var(--hors); }
.slide[data-etat="levee"]   .levee__norme,
.slide[data-etat="passage"] .levee__norme   { fill: var(--muted); }

/* Le verdict paraît quand la lisse est presque en haut — pas avant :
   c'est la levée qu'on regarde, le label ne fait que la signer. */
.slide[data-etat="levee"]   .levee__verdict,
.slide[data-etat="passage"] .levee__verdict {
  opacity: 1;
  animation: levee-paraitre var(--dur-3) var(--ease-out) 720ms both;
}
@keyframes levee-paraitre { from { opacity: 0; } }

/* --- Second temps : le passage -------------------------------------------
   Les pastilles sont dessinées à leur position d'attente par leurs attributs
   de géométrie (cx) — jamais par un attribut transform, qu'un transform CSS
   REMPLACERAIT. L'arrivée est un translateX pur, indépendant de l'origine. */
.slide[data-etat="passage"] .levee__pastille {
  transform: translateX(420px);
  animation: levee-passer var(--dur-3) var(--ease-io) both;
}
/* L'échelonnement : une file passe une par une, pas en bloc. */
.slide[data-etat="passage"] .levee__pastille:nth-of-type(2) { animation-delay: 160ms; }
.slide[data-etat="passage"] .levee__pastille:nth-of-type(3) { animation-delay: 320ms; }
@keyframes levee-passer { from { transform: translateX(0); } }

/* Le trajet se tire pendant la traversée : 640 ms — la mesure des tracés de
   la boucle — pour couvrir les trois passages échelonnés (740 ms en tout). */
.slide[data-etat="passage"] .levee__trajet {
  opacity: 1;
  stroke-dashoffset: 0;
  animation: levee-tracer 640ms var(--ease-io) both;
}
@keyframes levee-tracer { from { stroke-dashoffset: 1; } }

.slide[data-etat="passage"] .levee__pointe {
  opacity: 1;
  animation: levee-paraitre var(--dur-2) var(--ease-out) 560ms both;
}

/* L'arrivée se nomme quand la file est passée, la chute ferme la scène. */
.slide[data-etat="passage"] .levee__toutes {
  opacity: 1;
  animation: levee-paraitre var(--dur-3) var(--ease-out) 680ms both;
}
.slide[data-etat="passage"] .levee__chute {
  opacity: 1;
  animation: levee-paraitre var(--dur-3) var(--ease-out) 900ms both;
}

/* Au tirage, Chrome rend la PREMIÈRE image des animations `both` — donc la
   barrière fermée et la file en attente. On écrit l'arrivée, pas le trajet. */
@media print {
  .levee__lisse {
    transform: translate(434px, 244px) rotate(-60deg) translate(-434px, -244px) !important;
    stroke: var(--hors) !important;
    animation: none !important;
  }
  .levee__montant { fill: var(--hors) !important; }
  .levee__pivot   { stroke: var(--hors) !important; }
  .levee__norme   { fill: var(--muted) !important; }
  .levee__pastille { transform: translateX(420px) !important; animation: none !important; }
  .levee__trajet  { opacity: 1 !important; stroke-dashoffset: 0 !important;
                    animation: none !important; }
  .levee__pointe, .levee__verdict, .levee__toutes, .levee__chute {
    opacity: 1 !important; animation: none !important;
  }
}

@media print {
  /* Chrome rend la première image des animations `both` — donc la barrière
     fermée et la file en attente. On écrit l'arrivée, pas le trajet. */
  .levee__lisse {
    transform: translate(434px, 244px) rotate(-60deg) translate(-434px, -244px) !important;
    stroke: var(--hors) !important;
    animation: none !important;
  }
  .levee__montant { fill: var(--hors) !important; }
  .levee__pivot   { stroke: var(--hors) !important; }
  .levee__norme   { fill: var(--muted) !important; }
  .levee__pastille { transform: translateX(420px) !important; animation: none !important; }
  .levee__trajet  { opacity: 1 !important; stroke-dashoffset: 0 !important;
                    animation: none !important; }
  .levee__pointe, .levee__verdict, .levee__toutes, .levee__chute {
    opacity: 1 !important; animation: none !important;
  }
}

/* =============================================================================
   LA DIVISION — verbe RELIER, variante « se singulariser »
   Un groupe compact de pastilles froides se résout en trajectoires
   singulières : chaque pastille part de son côté et laisse derrière elle la
   trace de son parcours. La figure dit la charnière collectif/individuel :
   la représentation est partagée, personne n'en fera la même chose.
   Deux temps : data-etat="parcours" (la séparation), puis data-etat="suivie"
   (facultatif — LA pastille dont on suivra l'histoire passe en chaud).
   Le bloc @media print est livré à part, à coller à la suite.
   ========================================================================== */

/* --- La trace -------------------------------------------------------------
   Le trait n'est pas une flèche : c'est le chemin PARCOURU, laissé derrière
   la pastille — d'où le geste draw-line de la charte (pathLength="1",
   dasharray 1, dashoffset 1 → 0). Froid comme les pastilles : la couleur
   n'encode qu'un fait (le collectif), la singularité est dans les formes. */
.division__trajet {
  fill: none;
  stroke: var(--froid);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  opacity: 0;
  /* Le passage en chaud du second temps glisse au lieu de sauter. */
  transition: stroke var(--dur-2) var(--ease-io);
}

/* --- La pastille ----------------------------------------------------------
   PAS de cx/cy : l'offset-path la place, position de départ comprise
   (offset-distance: 0% = le début du tracé, dans le groupe serré). Un cx
   résiduel s'AJOUTERAIT au trajet et décalerait tout d'autant.
   Le liseré paper-bright sépare les pastilles là où le groupe est serré. */
.division__pastille {
  fill: var(--froid);
  stroke: var(--paper-bright);
  stroke-width: 3;
  offset-rotate: 0deg; /* une pastille n'a pas d'orientation — et la forme qui la remplacerait un jour ne doit pas tournoyer */
  offset-distance: 0%;
  transition: fill var(--dur-2) var(--ease-io);
}

/* Chaque pastille porte SON tracé : la même chaîne que le d de sa trace,
   au caractère près. Toute retouche de géométrie se fait aux deux endroits.
   Les coordonnées se lisent dans le repère du viewBox (920 × 430) : comme
   pour le radial, on garde le viewBox et on déplace des points — on ne
   recalcule pas une échelle. */
.division__pastille[data-p="1"] { offset-path: path("M150 183 C330 150 540 60 760 60"); }
.division__pastille[data-p="2"] { offset-path: path("M122 205 C340 228 560 150 790 150"); }
.division__pastille[data-p="3"] { offset-path: path("M178 205 C400 190 600 248 800 235"); }
.division__pastille[data-p="4"] { offset-path: path("M134 237 C340 262 560 330 785 320"); }
.division__pastille[data-p="5"] { offset-path: path("M166 237 C350 305 540 392 745 392"); }

/* L'échelonnement des départs — un pas de 120 ms, l'unité de la charte.
   Un départ en bloc dirait le CONTRAIRE du propos : elles resteraient un
   groupe jusque dans la séparation. Chacune part quand elle part. */
.division__pastille[data-p="2"], .division__trajet[data-p="2"] { --depart: 120ms; }
.division__pastille[data-p="3"], .division__trajet[data-p="3"] { --depart: 240ms; }
.division__pastille[data-p="4"], .division__trajet[data-p="4"] { --depart: 360ms; }
.division__pastille[data-p="5"], .division__trajet[data-p="5"] { --depart: 480ms; }

/* --- Premier temps : les parcours -----------------------------------------
   RÈGLE D'OR : l'état pose les valeurs FINALES (offset-distance 100 %,
   dashoffset 0, opacité 1) ; les keyframes ne portent que le point de
   DÉPART. prefers-reduced-motion, qui coupe les animations, montre donc
   directement la figure résolue.
   « suivie » vient APRÈS « parcours » et le REMPLACE sur la slide : chaque
   règle liste les deux états, sinon le second clic replierait le premier. */
.slide[data-etat="parcours"] .division__pastille,
.slide[data-etat="suivie"]   .division__pastille {
  offset-distance: 100%;
  /* 900 ms : hors des trois durées de la charte, et c'est voulu — le trajet
     EST le propos, comme le point de la boucle. backwards : pendant son
     retard, la pastille attend encore DANS le groupe, pas déjà en route. */
  animation: division-partir 900ms var(--ease-io) var(--depart, 0ms) backwards;
}
.slide[data-etat="parcours"] .division__trajet,
.slide[data-etat="suivie"]   .division__trajet {
  opacity: 1;
  stroke-dashoffset: 0;
  /* Mêmes durée, retard et courbe que la pastille : c'est cette égalité qui
     fait que le trait paraît laissé PAR elle. Une retouche va par deux. */
  animation: division-tracer 900ms var(--ease-io) var(--depart, 0ms) backwards;
}

/* Le libellé d'arrivée ne nomme les parcours que lorsqu'ils existent : il
   paraît quand la dernière pastille se pose (480 ms de retard + 900 ms de
   trajet ≈ 1 380 ms ; 980 + 420 = 1 400 — les deux fins coïncident). */
.division__arrivee {
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 980ms;
}
.slide[data-etat="parcours"] .division__arrivee,
.slide[data-etat="suivie"]   .division__arrivee { opacity: 1; }

/* --- Second temps : celle dont on va suivre l'histoire ---------------------
   UNE pastille et SA trace passent en chaud — une seule couleur pour un
   seul fait : « c'est elle qu'on suit ». Les autres ne s'éteignent pas :
   les parcours qu'on ne suivra pas n'en existent pas moins. */
.slide[data-etat="suivie"] .division__pastille[data-suivie] { fill: var(--chaud); }
.slide[data-etat="suivie"] .division__trajet[data-suivie]   { stroke: var(--chaud); }

/* Les keyframes ne disent que d'où l'on part — l'arrivée est dans l'état. */
@keyframes division-partir { from { offset-distance: 0%; } }
@keyframes division-tracer { from { stroke-dashoffset: 1; } }

@media print {
  /* Chrome fige la première image : on écrit l'ARRIVÉE, pas le trajet.
     Le chaud du second temps n'a pas besoin d'aide — le moteur pose le
     data-etat final au beforeprint, et fill/stroke sont des règles
     statiques ; seules les propriétés animées se forcent ici. */
  .division__pastille { offset-distance: 100% !important;
                        animation: none !important; transition: none !important; }
  .division__trajet   { opacity: 1 !important; stroke-dashoffset: 0 !important;
                        animation: none !important; transition: none !important; }
  .division__arrivee  { opacity: 1 !important; transition: none !important; }
}

/* --- La chrono qui se CHARGE ----------------------------------------------
   Variante de `.chrono` pour les cas où la succession ne suffit pas : ce qui
   compte est ce qui s'ACCUMULE d'un temps à l'autre. Sous chaque jalon, son
   segment d'axe se remplit à son tour et reste plein — l'axe finit chargé
   d'un bout à l'autre, et la couleur porte l'argument : froid, froid, CHAUD.
   La conquête et l'évidence déposent ; la charge est ce dépôt devenu poids.

   Trois temps, portés par les jalons eux-mêmes (data-etat sert deux fois :
   recopié sur la slide pour l'état, lu sur le li pour désigner SON segment) :

     data-etat="conquete"   le premier segment se remplit, en froid ;
     data-etat="evidence"   le deuxième, froid encore — l'acquis s'étend ;
     data-etat="charge"     le dernier, en CHAUD, jusqu'au bord droit :
                            aujourd'hui n'est pas clos.

   Le remplissage pousse la LARGEUR d'un fond UNI — licite parce qu'uni, et
   seulement pour ça : un fond non uni (dégradé, motif) glisserait avec sa
   boîte, il faudrait alors découper au clip-path (règle de dataviz).
   -------------------------------------------------------------------------- */

/* Le segment est le ::after du li : l'axe de la base (border-top) reste le
   support vide, le segment vient l'épaissir par-dessus. Chaque li occupe
   toute sa colonne et la grille n'a pas de gouttière : les segments se
   touchent, l'axe chargé se lit d'un seul tenant, de point en point. */
.chrono--chargee > li::after {
  content: "";
  position: absolute;
  z-index: 0;
  left: 0;
  top: -2px;            /* à cheval sur le filet : il l'épaissit, il ne le double pas */
  height: 5px;
  width: 0;             /* vide tant que son temps n'est pas venu */
  background-color: var(--froid);
}
/* data-jalon colore déjà le point en chaud dans la base : le segment suit.
   Un seul attribut décide du chaud — le point et sa période ne peuvent pas
   se contredire. */
.chrono--chargee > li[data-jalon]::after { background-color: var(--chaud); }
/* Le point reste AU-DESSUS de son segment : le jalon marque, le segment
   mesure — le second ne doit pas avaler le premier. */
.chrono--chargee > li::before { z-index: 1; }

/* LE CHARGEMENT EST CUMULATIF. data-etat est recopié — pas empilé — sur la
   slide : chaque état re-déclare donc les segments des états antérieurs.
   La déclaration d'animation est LA MÊME d'un état à l'autre : le navigateur
   ne la rejoue pas, un segment chargé tient sans clignoter. */
.slide[data-etat="conquete"] .chrono--chargee > li[data-etat="conquete"]::after,
.slide[data-etat="evidence"] .chrono--chargee > li[data-etat="conquete"]::after,
.slide[data-etat="charge"]   .chrono--chargee > li[data-etat="conquete"]::after,
.slide[data-etat="evidence"] .chrono--chargee > li[data-etat="evidence"]::after,
.slide[data-etat="charge"]   .chrono--chargee > li[data-etat="evidence"]::after {
  width: 100%;
  animation: chrono-charger 640ms var(--ease-io) both;
}
/* Le dernier segment prend son temps : 900 ms, hors gamme, parce que ce
   remplissage-là EST le propos — une charge ne surgit pas, elle s'installe. */
.slide[data-etat="charge"] .chrono--chargee > li[data-etat="charge"]::after {
  width: 100%;
  animation: chrono-charger 900ms var(--ease-io) both;
}
/* Seul le trajet vit dans l'animation ; l'arrivée (width : 100 %) est posée
   par les règles d'état. prefers-reduced-motion, qui coupe tout, montre donc
   l'axe déjà chargé — l'état juste, sans le trajet. */
@keyframes chrono-charger { from { width: 0; } }

@media print {
  /* Chrome fige la première image : un remplissage qui pousse depuis zéro
     s'imprimerait vide. On pose l'arrivée, on coupe le trajet. */
  .chrono--chargee > li::after { width: 100% !important; animation: none !important; }
}



/* --- L'élan à origine commune — un refus, deux directions -----------------
   Variante d'`.elan` pour les cas où les deux mouvements ne se font pas
   face : ils PARTENT DU MÊME POINT. L'`.elan` de base montrait les deux
   sens ; il ne montrait pas ce qui est partagé. Ici l'origine est nommée
   dans un cartouche central, et les deux flèches s'en écartent en se
   dessinant ENSEMBLE — la simultanéité dit : même départ, deux demandes.
   S'écrit `class="opposition elan elan--origine"` : la typographie vient
   d'`.opposition`, tiges et pointes d'`.elan` ; la variante ne redéfinit
   que la géométrie et les deux temps.
   -------------------------------------------------------------------------- */

.opposition.elan--origine {
  /* Trois colonnes : pôle | origine | pôle. La centrale est `auto` — c'est
     le cartouche qui décide de sa largeur, pas la grille. Deux rangs :
     l'axe des flèches en haut, la parole des pôles en dessous. */
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: auto auto;
  column-gap: var(--space-4);
  row-gap: var(--space-6);
  align-items: center;
}
/* Le filet vertical d'`.opposition` SÉPARAIT ; ici l'origine RELIE.
   Le garder contredirait la figure. */
.opposition.elan--origine::before { content: none; }

/* --- L'origine : un cartouche calme --------------------------------------
   Ni chaud ni navy : l'origine n'appartient à aucun des deux pôles, elle
   les précède. D'où le fond à peine détaché et l'encre neutre — un fait
   posé, pas un cri. */
.elan--origine .elan__origine {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  margin: 0;
  padding: var(--space-3) var(--space-6);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background-color: var(--paper-bright);
  text-align: center;
  color: var(--ink);
  opacity: 0;                       /* paraît au premier temps */
}
/* `.opposition b` fournit déjà les petites capitales du repère ; on ne
   reprend que la couleur (neutre, voir ci-dessus) et l'interligne serré
   du cartouche. */
.elan--origine .elan__origine b {
  margin-bottom: var(--space-1);
  color: var(--muted);
}

/* --- Les deux tiges, nées au bord du cartouche ---------------------------
   Le `preserveAspectRatio` du balisage (xMax à gauche, xMin à droite) colle
   chaque dessin à la paroi de l'origine : le trait NAÎT au bord du
   cartouche, il ne flotte pas au milieu de sa colonne. La marge verticale
   d'`.elan__trait` saute : ici c'est la grille qui espace. */
.elan--origine .elan__trait { margin: 0; grid-row: 1; }
.elan--origine .elan__trait--gauche { grid-column: 1; }
.elan--origine .elan__trait--droite { grid-column: 3; }

/* --- Les pôles -----------------------------------------------------------
   Centrés : la figure est symétrique autour de l'origine, l'alignement à
   gauche des oppositions ordinaires la déséquilibrerait. */
.elan--origine .elan__pole { opacity: 0; text-align: center; }
.elan--origine > .elan__pole[data-sens="retrait"] { grid-column: 1; grid-row: 2; }
.elan--origine > .elan__pole[data-sens="vers"]    { grid-column: 3; grid-row: 2; }

/* `.opposition` colore les `b` par le RANG (:first-child/:last-child,
   spécificité 0-2-2) — or ici le premier enfant est le cartouche, et le
   pôle « vers », dernier enfant, serait repeint en chaud. On re-décide par
   le SENS, avec trois classes pour l'emporter. Ne pas « simplifier ». */
.elan--origine > .elan__pole[data-sens="retrait"] b { color: var(--chaud); }
.elan--origine > .elan__pole[data-sens="vers"] b    { color: var(--brand-primary); }

/* La parole entendue : c'est elle qu'on lit, elle monte d'un cran. */
.elan--origine .elan__voix {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-600);
}
/* Ce qu'elle appelle : un repère, pas une phrase — petites capitales
   neutres, seul le mot-clé prend la couleur de son pôle. */
.elan--origine .elan__besoin {
  margin: 0;
  font-family: Inter, Avenir, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs-200);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.elan--origine > .elan__pole[data-sens="retrait"] .elan__besoin strong { color: var(--chaud); }
.elan--origine > .elan__pole[data-sens="vers"] .elan__besoin strong    { color: var(--brand-primary); }

/* --- Temps 1 : l'origine paraît ------------------------------------------
   Les états sont cumulatifs : « ecart » remplace « origine » sur la slide,
   donc la règle est groupée sur les deux — et la déclaration étant
   IDENTIQUE, l'animation ne rejoue pas au second clic. */
.slide[data-etat="origine"] .elan--origine .elan__origine,
.slide[data-etat="ecart"]   .elan--origine .elan__origine {
  opacity: 1;
  animation: elan-origine-poser var(--dur-3) var(--ease-out) both;
}
/* Le keyframe ne décrit que le DÉPART : l'arrivée est dans la règle d'état.
   Ainsi prefers-reduced-motion, qui coupe l'animation, montre l'état juste. */
@keyframes elan-origine-poser { from { opacity: 0; } }

/* --- Temps 2 : les deux flèches s'écartent ENSEMBLE ----------------------
   Aucun décalage entre gauche et droite : la simultanéité EST le propos —
   même origine, même instant, deux directions. 620 ms comme l'`.elan` de
   base : le tracé porte le propos, il a le droit de sortir des trois
   durées de la charte. */
.slide[data-etat="ecart"] .elan--origine .elan__tige {
  stroke-dashoffset: 0;
  animation: elan-origine-tracer 620ms var(--ease-io) both;
}
@keyframes elan-origine-tracer { from { stroke-dashoffset: 1; } }

/* La pointe se pose quand la tige arrive — même cadence que l'`.elan`. */
.slide[data-etat="ecart"] .elan--origine .elan__pointe {
  opacity: 1;
  animation: elan-origine-poser var(--dur-2) var(--ease-out) 560ms both;
}

/* Chaque pôle finit le geste de sa flèche : il glisse de quelques pixels
   dans le MÊME sens qu'elle, puis se pose. Div HTML, donc le transform
/* @famille piece · verbe ARTICULER · registre nommé */
   animé est sans danger — le piège du transform d'attribut SVG (`.piece`)
   ne s'applique pas ici. */
.slide[data-etat="ecart"] .elan--origine > .elan__pole[data-sens="retrait"] {
  opacity: 1;
  transform: none;
  animation: elan-origine-pole-gauche var(--dur-3) var(--ease-out) 620ms both;
}
.slide[data-etat="ecart"] .elan--origine > .elan__pole[data-sens="vers"] {
  opacity: 1;
  transform: none;
  animation: elan-origine-pole-droite var(--dur-3) var(--ease-out) 620ms both;
}
@keyframes elan-origine-pole-gauche { from { opacity: 0; transform: translateX(14px); } }
@keyframes elan-origine-pole-droite { from { opacity: 0; transform: translateX(-14px); } }

/* La chute attend que les pôles soient posés : elle tire la conclusion,
   elle ne peut pas la précéder. 1160 ms = tracé (620) + pôles (420) + un
   souffle. La typographie vient de l'`.elan__chute` de base. */
.slide[data-etat="ecart"] .elan__chute {
  opacity: 1;
  animation: elan-origine-poser var(--dur-3) var(--ease-out) 1160ms both;
}

/* --- Impression ----------------------------------------------------------
   Chrome fige la PREMIÈRE image de chaque animation ; avec `both`, c'est la
   frame `from` — flèches à zéro, pôles absents. Le moteur pose l'état final
   au beforeprint ; ici on écrit les arrivées en dur. `.elan__chute` double
   volontairement la règle print de l'`.elan` de base : le bloc reste
   complet même relu seul. */
@media print {
  .elan--origine .elan__tige {
    stroke-dashoffset: 0 !important;
    animation: none !important;
  }
  .elan--origine .elan__origine,
  .elan--origine .elan__pointe,
  .elan--origine .elan__pole,
  .elan__chute {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
}



/* --- 5b.1 bis. Le report — la grandeur qui s'installe ---------------------
   Variante de .barres pour le cas où LE DÉPLACEMENT est le propos : une
   grandeur quitte sa valeur d'avant et s'installe dans la nouvelle, en
   laissant l'ancienne sur place, en fantôme. Slide type : la crise de la
   pilule — le recours glisse de ≈ 1/2 à ≈ 4/10 pendant que la barre
   d'ensemble ne bouge pas. Le report se voit ; la disparition, non.

   GARDE-FOU. Un chiffre qui bouge sans raison est un chiffre qu'on croit
   moins. Cette variante ne sert QUE quand le déplacement lui-même est le
   propos — pour comparer des grandeurs immobiles, .barres suffit.
   -------------------------------------------------------------------------- */

/* La piste remplace le <i> nu de .barres : il faut deux couches — la
   grandeur d'avant (fantôme) et la mesure qui bouge — donc un ancrage
   commun. Même hauteur que .barre > i : la variante doit se lire comme
   une .barres ordinaire. */
.barres--report .barre__piste {
  position: relative;
  height: 22px;
}

/* Le fantôme est là DÈS LE DÉPART, sous la mesure : avant le report il est
   entièrement recouvert ; après, il affleure dans la zone libérée. Rien ne
   « paraît » au clic — la mesure se retire et le découvre. Aplat --neutre
   et contour pointillé : le costume du « ce n'est plus là, mais ça l'était ». */
.barres--report .barre__fantome {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--avant, 0%);
  background-color: var(--neutre);
  border: 1px dashed var(--line-strong);
}

/* La mesure démarre à la grandeur D'AVANT : au premier regard, la slide
   montre l'état ancien — pas une barre vide qui attendrait son chiffre. */
.barres--report .barre__mesure {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--avant, 0%);
  background-color: var(--froid);
  border: 1px solid var(--line-strong);
  /* Le trajet sort des trois durées parce qu'il EST le propos : c'est le
     glissement qu'on regarde, pas une entrée de contenu. --ease-io parce
     qu'il est réversible — la flèche arrière retire l'état et la barre
     revient. Le contrat reduced-motion de charte.css coupe la transition,
     et la règle [data-etat] a déjà posé la valeur d'arrivée. */
  transition: width 760ms var(--ease-io);
}
.barres--report .barre[data-ton="chaud"] .barre__mesure { background-color: var(--chaud); }

/* RÈGLE D'OR : l'état pose la valeur FINALE, la transition ne fait que le
   trajet. Une ligne sans --v retombe sur --avant : elle ne bouge pas — et
   c'est ainsi qu'on écrit « inchangé », pas en dupliquant la ligne. */
.slide[data-etat="report"] .barres--report .barre__mesure {
  width: var(--v, var(--avant));
}

/* Les deux valeurs occupent leur place dès le départ : quand l'après
   paraît, rien ne se déplace — seule l'encre change. L'après d'abord dans
   le flux, pour qu'une fois posé, le chiffre neuf se lise au-dessus du
   repère d'avant. */
.barres--report .barre > b {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
}
.barres--report .barre__apres {
  opacity: 0;
  /* Le chiffre confirme le geste, il ne le devance pas : il paraît quand
     la barre a fait le gros de son trajet. */
  transition: opacity var(--dur-3) var(--ease-out) 460ms;
}
.barres--report .barre__avant {
  transform-origin: right center;
  transition: color var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}

/* L'ancienne valeur ne disparaît pas : elle devient le repère d'avant.
   Réduite par transform — jamais par font-size, la place réservée ne doit
   pas bouger — et grisée, pas barrée : elle n'était pas fausse, elle date. */
.slide[data-etat="report"] .barres--report .barre__avant {
  color: var(--muted);
  transform: scale(.72);
}
.slide[data-etat="report"] .barres--report .barre__apres { opacity: 1; }

/* La pastille du fantôme porte son costume exact : la légende montre la
   convention, elle ne la paraphrase pas. */
.barres--report .legende__pastille[data-ton="fantome"] {
  background-color: var(--neutre);
  border-style: dashed;
}

/* L'entrée reprend le geste de .barres : les deux couches poussent depuis
   leur origine, comme n'importe quelle barre du deck. */
@media (prefers-reduced-motion: no-preference) {
  .barres--report .barre__mesure,
  .barres--report .barre__fantome {
    transform-origin: left center;
    animation: bar-grow var(--dur-3) var(--ease-out) both;
  }
  .barres--report .barre:nth-child(2) .barre__mesure,
  .barres--report .barre:nth-child(2) .barre__fantome { animation-delay: 60ms; }
  .barres--report .barre:nth-child(3) .barre__mesure,
  .barres--report .barre:nth-child(3) .barre__fantome { animation-delay: 120ms; }
}

@media print {
  /* Chrome fige la première image : tout ce qui pousse doit écrire son
     arrivée. Le moteur pose l'état final avant le tirage — le PDF montre
     donc le report ACCOMPLI : barre installée, fantôme découvert, les deux
     valeurs posées. */
  .barres--report .barre__mesure,
  .barres--report .barre__fantome {
    animation: none !important;
    transform: none !important;   /* bar-grow partait de scaleX(0) : sans
                                     ceci, des barres invisibles au tirage */
  }
  .barres--report .barre__mesure {
    width: var(--v, var(--avant)) !important;
    transition: none !important;
  }
  .barres--report .barre__avant {
    color: var(--muted) !important;
    transform: scale(.72) !important;
    transition: none !important;
  }
  .barres--report .barre__apres {
    opacity: 1 !important;
    transition: none !important;
  }
}


/* =============================================================================
   5 sexies. LE CONTRAT D'ARRIVÉE — l'impression déclarée, pas rattrapée
   (proposition de l'audit externe du 25 août, adoptée)
   -------------------------------------------------------------------------
   Jusqu'ici, chaque figure qui part d'un état incomplet écrivait SON bloc
   @media print — treize rattrapages dispersés, et l'oubli de l'un d'eux a
   déjà imprimé des fausses pistes non barrées. Le contrat inverse la
   charge : la figure déclare ses variables d'arrivée sur l'élément, et UNE
   règle d'impression les force toutes.

   OPT-IN : poser data-arrivee sur la figure, consommer les variables dans
   ses couches. Les états DISCRETS (une position parmi trois, un terme
   parmi deux) gardent leur règle print explicite — une variable commune ne
   sait pas les dire.

   Les nouvelles figures l'adoptent ; les existantes migrent à l'occasion,
   jamais en bloc. */

[data-arrivee] {
  --arrivee-opacite: 0;      /* ce qui paraît                 */
  --arrivee-decoupe: 100%;   /* ce qui se découvre (clip)     */
  --arrivee-trace: 1;        /* ce qui se dessine (dashoffset) */
}

@media print {
  [data-arrivee] {
    --arrivee-opacite: 1 !important;
    --arrivee-decoupe: 0% !important;
    --arrivee-trace: 0 !important;
  }
}

/* --- Le lexique des raisons — du marqueur au système de signes -----------
   (audit externe, adopté tel quel dans l'esprit)
   Quatre icônes déjà employées à la slide du retournement deviennent un
   lexique STABLE : représentations, trajectoire, corps, relations. Il se
   rappelle sur les chapitres et la chute — jamais sur toutes les slides,
   où il deviendrait du chrome. Le couple signe + libellé est obligatoire :
   une icône ne remplace jamais le mot. */

/* @famille lexique-raisons · verbe RÉCAPITULER · registre nommé */
.lexique-raisons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-6);
}
.lexique-raisons > span {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line-strong);
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.lexique-raisons .ico { width: 16px; height: 16px; flex: none; }

/* La raison ACTIVE se dit par la slide : data-raison sur la section. */
.slide[data-raison="representations"] .lexique-raisons > [data-raison="representations"],
.slide[data-raison="trajectoire"]     .lexique-raisons > [data-raison="trajectoire"],
.slide[data-raison="corps"]           .lexique-raisons > [data-raison="corps"],
.slide[data-raison="relations"]       .lexique-raisons > [data-raison="relations"] {
  color: var(--brand-primary);
  border-top: 3px solid var(--brand-primary);
  /* compenser l'épaisseur pour que la ligne de base ne saute pas */
  padding-top: calc(var(--space-3) - 2px);
}

/* Sur fond navy (les chapitres, la chute) : le lexique passe en clair. Le
   signe actif est BLANC, pas turquoise — à cette taille (fs-100), le
   turquoise sur navy n'a pas le contraste d'un petit texte. */
.slide[data-fond="navy"] .lexique-raisons > span {
  color: rgba(255, 255, 255, .55);
  border-top-color: rgba(255, 255, 255, .28);
}
.slide[data-fond="navy"][data-raison="representations"] .lexique-raisons > [data-raison="representations"],
.slide[data-fond="navy"][data-raison="trajectoire"]     .lexique-raisons > [data-raison="trajectoire"],
.slide[data-fond="navy"][data-raison="corps"]           .lexique-raisons > [data-raison="corps"],
.slide[data-fond="navy"][data-raison="relations"]       .lexique-raisons > [data-raison="relations"] {
  color: #ffffff;
  border-top-color: #ffffff;
}

/* --- La marche (ORDONNER) : le seuil administratif ------------------------
   Un sol qui descend d'une marche à une borne — « 26 ans » — et une
   voyageuse qui la franchit. Le corps est le même des deux côtés : c'est le
   sol qui change de niveau. Demandé par Nicolas le 25 août (« la slide 20,
   ça manque d'animation »). Le sol est STATIQUE : la règle administrative
   préexiste à celle qui la rencontre. */
/* 5 px, pas 3 : le sol EST la démonstration — c'est lui qui change de niveau,
   pas le corps. Un trait de 3 px sur une figure large de 1 144 unités disparaît
   au fond de la salle (SCHEMAS.md §1). */
.marche__sol { fill: none; stroke: var(--line-strong); stroke-width: 5; }
.marche__jalon { fill: var(--chaud, var(--data-warm)); opacity: 0; transition: opacity var(--dur-3) var(--ease-out); }
.marche__libelle { opacity: 0; transition: opacity var(--dur-3) var(--ease-out) 200ms; }
.marche__libelle[data-fixe] { opacity: 1; }
/* Cumulatif : « franchit » redéclare tout ce que « avance » a allumé. */
/* @famille marche · registre nommé */
.slide[data-etat="avance"] .marche .voyageuse,
.slide[data-etat="franchit"] .marche .voyageuse {
  offset-distance: 46%;                /* au bord de la marche, pile         */
  transition: offset-distance 900ms var(--ease-io);
}
.slide[data-etat="avance"] .marche__jalon,
.slide[data-etat="franchit"] .marche__jalon { opacity: 1; }
.slide[data-etat="avance"] .marche__libelle[data-temps="avance"],
.slide[data-etat="franchit"] .marche__libelle[data-temps="avance"] { opacity: 1; }
.slide[data-etat="franchit"] .marche .voyageuse {
  offset-distance: 100%;
  transition: offset-distance 1100ms var(--ease-io);
}
.slide[data-etat="franchit"] .marche__libelle[data-temps="franchit"] { opacity: 1; }
@media print {
  .marche__jalon, .marche__libelle { opacity: 1 !important; }
}

/* --- La marge (ORDONNER) : le couloir qui se resserre — sans se fermer ----
   Les contraintes tombent une à une et resserrent le passage ; il en reste
   un — la marge — et une voyageuse le traverse. « Ce qui contraint n'est
   pas ce qui décide » : la figure de la slide 35. */
.marge__mur {
  fill: none;
  stroke: var(--gris-bleu, #a9c5d6);
  stroke-width: 28;
  stroke-linecap: round;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.marge__etiquette {
  fill: var(--ink);
  font-weight: 650;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 160ms;
}
.marge__passage { fill: var(--froid, var(--data-cool)); opacity: 0; transition: opacity var(--dur-3) var(--ease-out); }
.marge__nom { fill: var(--chaud, var(--data-warm)); opacity: 0; transition: opacity var(--dur-3) var(--ease-out) 200ms; }
/* Quatre états cumulatifs : cout → delai → distance → marge. */
/* @famille marge · registre nommé */
.slide[data-etat="cout"]     .marge [data-temps="cout"],
.slide[data-etat="delai"]    .marge [data-temps="cout"],
.slide[data-etat="delai"]    .marge [data-temps="delai"],
.slide[data-etat="distance"] .marge [data-temps="cout"],
.slide[data-etat="distance"] .marge [data-temps="delai"],
.slide[data-etat="distance"] .marge [data-temps="distance"],
.slide[data-etat="marge"]    .marge [data-temps="cout"],
.slide[data-etat="marge"]    .marge [data-temps="delai"],
.slide[data-etat="marge"]    .marge [data-temps="distance"] { opacity: 1; }
.slide[data-etat="marge"] .marge__passage { opacity: .18; }
.slide[data-etat="marge"] .marge__nom { opacity: 1; }
.slide[data-etat="marge"] .marge .voyageuse {
  offset-distance: 100%;
  transition: offset-distance 1100ms var(--ease-io) 200ms;
}
@media print {
  .marge__mur, .marge__etiquette, .marge__nom { opacity: 1 !important; }
  .marge__passage { opacity: .18 !important; }
}


/* --- Le faisceau : des trajectoires qui se suivent, et qui peuvent bifurquer
   (verbe : ORDONNER · figure de Nicolas, 25 août — remplace la division pour
   la charnière collectif / individuel)
   -------------------------------------------------------------------------
   Le propos exact : dans un groupe d'appartenance, les trajectoires TENDENT À
   SE SUIVRE — mêmes représentations de la contraception, de la parentalité,
   de la légitimité du professionnel, même accès. Le collectif ne disparaît
   jamais : il est la pente commune. Mais on peut changer : une RENCONTRE —
   bonne ou mauvaise — fait bifurquer.

   Deux temps, cumulatifs :
     data-etat="suivre"     le faisceau se dessine, groupé — la pente commune ;
     data-etat="rencontre"  le point paraît (chaud : c'est le jalon), et UNE
                            trajectoire s'écarte. Les autres continuent — la
                            bifurcation n'annule pas le groupe.

   PREMIER ADOPTANT DU CONTRAT D'ARRIVÉE : les couches consomment les
   variables --arrivee-* ; l'impression est forcée par la règle générale de
   la section 5 sexies — aucun bloc @media print à écrire ici. */

/* @famille faisceau · registre mixte */
.faisceau { display: block; width: 100%; height: auto; }

/* Variante de la charnière JSF : le temps monte et la bifurcation se fait
   LATÉRALEMENT. Les arrivées restent sur un même horizon ; le dessin ne peut
   donc pas transformer « ailleurs » en « mieux » ou « pire ». Le champ clair
   matérialise ce que le collectif continue de tenir. */
.faisceau--vertical { max-height: 330px; }
/* LA VARIANTE AMPLE — et c'est ici que se voyait la cause du deck plat.
   `max-height: 330px` n'est pas une conséquence du dessin : c'est un PLAFOND,
   écrit en CSS, qui interdisait à la démonstration de dépasser la bande quoi
   qu'elle contienne. Le cadre haut le relève à ce que la slide mesure
   vraiment, et le temps du faisceau passe de 222 à 366 px : la bifurcation
   devient lisible au lieu d'être devinée. */
.faisceau--ample { max-height: 479px; }
.faisceau__champ {
  fill: var(--seq-1);
  opacity: .58;
}
.faisceau__horizon {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 2;
}
.faisceau__mot,
.faisceau__explication {
  fill: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
}
.faisceau__explication {
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out) 200ms;
}
.faisceau__appel {
  fill: none;
  stroke: var(--chaud, var(--data-warm));
  stroke-width: 3;
  stroke-linecap: round;
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-2) var(--ease-out);
}
.faisceau__conclusion {
  margin: 0 auto;
  max-width: 46ch;
}

.faisceau__trace {
  fill: none;
  stroke: var(--froid);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: var(--arrivee-trace);
  opacity: var(--arrivee-opacite);
  /* 900 ms : c'est la démonstration, pas l'interface — la pente commune doit
     se voir se poser. Dans la bande 620-1100 ms d'ANIMATIONS.md. */
  transition: stroke-dashoffset 900ms var(--ease-io),
              opacity var(--dur-3) var(--ease-out);
}
/* La trajectoire qu'on suivra garde aussi le MÊME POIDS avant la rencontre :
   rien ne la prédestine, ni la couleur ni l'épaisseur. */
.faisceau__trace[data-suivie] { stroke-width: 3.4; }

/* La rencontre : un jalon, donc le chaud — la convention de tout le moteur.
   La couleur marque l'ÉVÉNEMENT, pas la trajectoire : la branche qui s'écarte
   reste dans l'encre du faisceau. */
.faisceau__rencontre {
  fill: var(--chaud);
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-2) var(--ease-out);
}
.faisceau__bifurcation {
  fill: none;
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: var(--arrivee-trace);
  opacity: var(--arrivee-opacite);
  transition: stroke-dashoffset 720ms var(--ease-io) 120ms,
              opacity var(--dur-2) var(--ease-out);
}

/* Les deux temps posent les variables du contrat — valeurs FINALES directes,
   la transition ne fait que le trajet (règle d'or : reduced-motion saute au
   bon état). CUMULATIF : « rencontre » redéclare « suivre ». */
.slide[data-etat="suivre"]    .faisceau [data-temps="suivre"],
.slide[data-etat="rencontre"] .faisceau [data-temps="suivre"] {
  --arrivee-opacite: 1;
  --arrivee-trace: 0;
}
.slide[data-etat="rencontre"] .faisceau [data-temps="rencontre"] {
  --arrivee-opacite: 1;
  --arrivee-trace: 0;
}

/* LES LIENS DU GROUPE. Quatrième version (Nicolas : « on a du mal à voir
   qu'au début c'est un groupe, AVEC DES LIENS ») : un petit réseau relie les
   positions de départ — un groupe, ce sont des liens, pas une proximité.
   Quand les trajectoires partent, les liens s'estompent sans disparaître :
   la pente commune reste, le réseau d'origine aussi. */
.faisceau__liens path {
  fill: none;
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3.2;
  opacity: 1;
  transition: opacity 620ms var(--ease-out);
}
.slide[data-etat="suivre"] .faisceau__liens path,
.slide[data-etat="rencontre"] .faisceau__liens path { opacity: .14; }
/* Au tirage, les voyageuses sont PARTIES : à .35 le réseau restait lisible
   comme une forme, un échafaudage vide à ses nœuds. À .22 il redevient ce
   qu'il est — la trace de l'origine commune, pas un objet de la scène. */
@media print { .faisceau__liens path { opacity: .22 !important; } }

/* LA SECONDE DENT DE LA FOURCHE — celle qu'elle n'a pas prise.

   Le libellé dit « bonne ou mauvaise, elle fait bifurquer », et les notes
   disent « une consultation qui se passe bien ou mal ». Le texte refuse deux
   fois de juger. Or la seule branche dessinée PLONGEAIT — deux fois plus fort
   que la pente commune — et une courbe qui tombe ne se lit pas « ailleurs »,
   elle se lit « moins bien ». Le dessin tranchait ce que la phrase refusait
   de trancher, sur la slide que les notes appellent la charnière de la séance.

   Une bifurcation a deux dents. En dessiner une seule, c'est avoir déjà
   choisi. La seconde, en pointillé, s'ouvre dans la direction opposée sur
   l'axe qui ne porte aucune valeur : c'est le chemin qu'une autre rencontre
   aurait pu ouvrir. La voyageuse en prend une — ç'aurait pu être l'autre.   */
.faisceau__possible {
  fill: none;
  stroke: var(--froid, var(--data-cool));
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 9 8;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out) 620ms;
}
.slide[data-etat="rencontre"] .faisceau__possible { opacity: .5; }
@media print { .faisceau__possible { opacity: .5 !important; transition: none !important; } }

/* LES VOYAGEUSES — troisième version, sur la demande de Nicolas : « le but
   c'est que les petites figures se déplacent, pas juste les lignes ». La
   mécanique du grain (offset-path), appliquée aux personnes : chaque femme
   PARCOURT sa trajectoire. Le chemin est inline dans le HTML — la géométrie
   vit avec la géométrie — ici, seulement les états. Et le coup de théâtre
   est gratuit : à offset-distance 0, les cinq positions de départ se
   chevauchent — LE GROUPE EST l'état initial des personnes. Pas de grappe
   dessinée : ce sont les voyageuses avant le départ. */
.faisceau__voyageuse,
/* @famille voyageuse · verbe ORDONNER · registre nommé */
.voyageuse {
  offset-rotate: 0deg;                 /* elles restent debout dans la pente  */
  offset-distance: 0%;
  fill: var(--froid, var(--data-cool));
}
/* États cumulatifs : « rencontre » redéclare ce que « suivre » a posé. La
   règle pose la valeur FINALE ; la transition ne fait que le trajet —
   reduced-motion et l'impression sautent au bon état. */
.slide[data-etat="suivre"] .faisceau__voyageuse,
.slide[data-etat="rencontre"] .faisceau__voyageuse {
  offset-distance: 100%;
  transition: offset-distance 900ms var(--ease-io);
}
/* Celle qui sera bifurquée s'arrête à la rencontre au premier temps. La
   variante verticale a une branche plus longue : son arrêt arrive plus tôt
   dans la longueur totale du chemin. */
.slide[data-etat="suivre"] .faisceau__voyageuse[data-suivie] { offset-distance: 61%; }
.slide[data-etat="suivre"] .faisceau--vertical .faisceau__voyageuse[data-suivie] { offset-distance: 30%; }
.slide[data-etat="suivre"] .faisceau--ample .faisceau__voyageuse[data-suivie] { offset-distance: 30%; }
/* Elle repart au second temps sans changer d'encre : seul le jalon est chaud,
   conformément au préset. */
.slide[data-etat="rencontre"] .faisceau__voyageuse[data-suivie] {
  offset-distance: 100%;
  fill: var(--froid, var(--data-cool));
  transition: offset-distance 720ms var(--ease-io) 120ms;
}
@media print {
  /* Le moteur pose l'état final avant le tirage ; sans transition, la
     valeur posée s'applique telle quelle. */
  .faisceau__voyageuse, .voyageuse { transition: none !important; }
}

/* LES PERSONNES STATIQUES (variante de réserve) : quand une slide veut la
   grappe ET les arrivées sans mouvement — un état, pas un trajet. */
.faisceau__personne {
  fill: var(--froid);
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out) 420ms;
}
.faisceau__personne[data-fixe] { opacity: 1; }   /* la grappe : l'état de départ */
.faisceau__personne[data-bifurquee] { fill: var(--brand-accent); }

/* Les libellés : des repères, pas des données. Celui de la rencontre est
   chaud comme son jalon. */
.faisceau .t-repere { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out) 200ms; }
.faisceau .t-repere[data-fixe] { opacity: 1; }
.faisceau__legende { fill: var(--muted); }
.faisceau .t-repere[data-ton="chaud"] { fill: var(--chaud); }
/* Générique : tout repère chaud d'un schéma l'est vraiment (la marche
   l'attendait), et porte un halo couleur de fond — un libellé posé sur un
   tracé reste lisible sans déplacer ni l'un ni l'autre. */
/* Le HALO, pour tout repère d'un schéma : un libellé posé sur un tracé
   reste lisible sans déplacer ni l'un ni l'autre. Il ne décide PAS de la
   couleur — data-ton s'en charge. */
.schema .t-repere,
.faisceau .t-repere {
  paint-order: stroke;
  stroke: var(--paper, #f7f8fc);
  stroke-width: 5px;
  stroke-linejoin: round;
}
/* Le chaud, lui, ne se pose que sur le jalon. */
.schema .t-repere[data-ton="chaud"],
.faisceau .t-repere[data-ton="chaud"] { fill: var(--chaud, var(--data-warm)); }


/* ═══════════════════════════════════════════════════════════════════════
   LES FORMES QUI DISENT LA PHRASE
   Entrées au moteur le 26 août, après que Nicolas a comparé deux refontes
   de la même séance et tranché entre elles.

   LA LEÇON, ET ELLE EST DE MÉTHODE. L'une des deux refontes n'avait changé
   que l'ÉCHELLE — cadres mesurés, figures agrandies, registres de densité.
   C'est utile et ça ne suffit pas : un cadre plus grand rend une figure plus
   LISIBLE, il ne la rend pas plus JUSTE. L'autre avait changé la FORME, et
   c'est celle-là qui a été retenue, slide après slide.

   La question à poser devant une slide qu'on reprend n'est donc pas
   « comment lui donner plus de place ? » mais **QUELLE FORME DIRAIT CETTE
   PHRASE ?** Les six ci-dessous sont les réponses trouvées ce jour-là. Elles
   vivaient dans une feuille locale ; elles montent ici pour que le catalogue
   les voie et qu'un autre deck puisse les employer.
   ═══════════════════════════════════════════════════════════════════════ */

/* --- Slide 5 : le surligneur guide la lecture d'un seul segment.
   Le linear-gradient est ici un aplat de surlignage, pas un dégradé décor. */
/* @famille surligneur · registre nommé */
.surligneur {
  padding: 0;
  color: inherit;
  background-color: transparent;
  background-image: linear-gradient(rgba(235, 104, 52, .22), rgba(235, 104, 52, .22));
  background-repeat: no-repeat;
  background-position: 0 88%;
  background-size: 0 38%;
  transition: background-size 620ms var(--ease-out);
}

.slide:is([data-etat="phrase-1"], [data-etat="phrase-2"], [data-etat="phrase-3"], [data-etat="phrase-4"])
  .paroles--lecture .parole:nth-child(1) .surligneur,
.slide:is([data-etat="phrase-2"], [data-etat="phrase-3"], [data-etat="phrase-4"])
  .paroles--lecture .parole:nth-child(2) .surligneur,
.slide:is([data-etat="phrase-3"], [data-etat="phrase-4"])
  .paroles--lecture .parole:nth-child(3) .surligneur,
.slide[data-etat="phrase-4"] .paroles--lecture .parole:nth-child(4) .surligneur {
  background-size: 100% 38%;
}

/* --- Slide 6 : les conditions sont le sol du choix. */
/* @famille sol-choix · registre nommé */
.sol-choix {
  display: grid;
  justify-items: center;
  gap: var(--space-4);
  width: min(980px, 100%);
  margin: 0 auto;
}

.sol-choix__mot {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  color: var(--brand-primary);
}

.sol-choix__mot strong {
  font-size: var(--fs-200);
  font-weight: 800;
  letter-spacing: .12em;
}

.sol-choix__mot span {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-700);
  letter-spacing: -.04em;
}

.sol-choix__strates {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  width: 100%;
}

.sol-choix__strate {
  position: relative;
  display: grid;
  grid-template-columns: 38px minmax(270px, .82fr) 1fr;
  align-items: center;
  gap: var(--space-4);
  min-height: 48px;
  padding: var(--space-3) var(--space-6) var(--space-2);
}

.sol-choix__strate:nth-child(1) { width: 100%; }
.sol-choix__strate:nth-child(2) { width: 94%; }
.sol-choix__strate:nth-child(3) { width: 88%; }
.sol-choix__strate:nth-child(4) { width: 82%; }

.sol-choix__strate::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background-color: var(--brand-accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 620ms var(--ease-out);
}

.sol-choix__strate:is(.est-posee, .est-revele)::before { transform: scaleX(1); }
.sol-choix__strate .ico { width: 28px; height: 28px; color: var(--brand-primary); }
.sol-choix__strate b {
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  font-weight: 400;
}
.sol-choix__strate span { color: var(--muted); font-size: var(--fs-300); }

/* --- Slide 7 : un plan vertical, lisible comme une progression. */
.flux.flux--lignes {
  grid-auto-flow: row;
  grid-auto-columns: auto;
  grid-template-columns: 1fr;
  width: min(980px, 100%);
  margin: 0 auto;
}

.flux.flux--lignes > li {
  display: grid;
  grid-template-columns: 52px minmax(330px, .9fr) 1fr;
  align-items: center;
  gap: var(--space-4);
  min-height: 76px;
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line-strong);
}

.flux.flux--lignes > li + li { padding-left: 0; }
.flux.flux--lignes > li + li::before { display: none; }
.flux.flux--lignes li > .ico {
  width: 30px;
  height: 30px;
  margin: 0;
  color: var(--brand-primary);
}

.flux.flux--lignes li > b.morphe {
  display: grid;
  grid-template-areas: "numero titre";
  grid-template-columns: 44px 1fr;
  align-items: center;
  font-size: var(--fs-600);
}

.flux.flux--lignes li > b::before {
  grid-area: numero;
  margin: 0;
}
.flux.flux--lignes li > b.morphe > span { grid-area: titre; }
.flux.flux--lignes li > span { font-size: var(--fs-400); line-height: 1.3; }

/* --- Slide 22 : trois lectures franches, sans trois cartes explicatives. */
.metamorphose--ample { width: 100%; max-height: 170px; }
/* @famille lectures-rencontre · registre nommé */
.lectures-rencontre {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
}

/* @famille lecture-rencontre · registre nommé */
/* Les rangées ne s’étirent pas : une colonne sans glose gardait deux rangées
   au lieu de trois, le grid les répartissait sur toute la hauteur, et son
   groupe verbal descendait de seize pixels sous les deux autres. Or c’est
   l’alignement des trois qui fait lire le triptyque d’un coup. */
.lecture-rencontre {
  display: grid;
  align-content: start;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 3px solid var(--brand-accent);
}

.lecture-rencontre[data-ton="chaud"] { border-top-color: var(--chaud); }
.lecture-rencontre > span {
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .1em;
}
.lecture-rencontre > b {
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  font-weight: 400;
  letter-spacing: -.03em;
}
.lecture-rencontre[data-ton="chaud"] > b { color: var(--chaud); }
.lecture-rencontre > small { color: var(--muted); font-size: var(--fs-300); }

/* --- Slide 28 : le circuit se ferme une fois, puis reste stable. */
/* @famille travail-continu · registre nommé */
.travail-continu { max-height: 252px; overflow: visible; }
.travail-continu__fond,
.travail-continu__trace {
  fill: none;
  stroke-linecap: round;
}
.travail-continu__fond { stroke: var(--line-strong); stroke-width: 3; }
.travail-continu__trace {
  stroke: var(--froid);
  stroke-width: 5;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 620ms var(--ease-io);
}

.slide[data-etat="travail-1"] .travail-continu__trace { stroke-dashoffset: .75; }
.slide[data-etat="travail-2"] .travail-continu__trace { stroke-dashoffset: .5; }
.slide[data-etat="travail-3"] .travail-continu__trace { stroke-dashoffset: .25; }
.slide:is([data-etat="travail-4"], [data-etat="travail-question"], [data-etat="travail-lecture"])
  .travail-continu__trace { stroke-dashoffset: 0; }

.travail-continu__centre { fill: var(--brand-primary); font-size: var(--fs-700); }
.travail-continu__repere { fill: var(--muted); }
.travail-continu__etape.fragment:not(.est-revele) { transform: none; }
.travail-continu__etape circle {
  fill: var(--paper-bright);
  stroke: var(--froid);
  stroke-width: 3;
}
/* LES ICÔNES N'AVAIENT AUCUNE CLASSE et tombaient donc sur le noir plein par
   défaut : quatre taches, là où le vocabulaire du dossier veut des traits.
   `color` ne suffisait pas — les glyphes du sprite se peignent au `stroke`.
   Elles reçoivent `schema__ico`, et la couleur de l'étape par-dessus. */
.travail-continu__etape .schema__ico { stroke: var(--brand-primary); }
.travail-continu__etape text {
  fill: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
}
.travail-continu__etape[data-ton="chaud"] circle { stroke: var(--chaud); }
.travail-continu__etape[data-ton="chaud"] .schema__ico { stroke: var(--chaud); }
.travail-continu__etape[data-ton="chaud"] text { fill: var(--chaud); }

/* --- Slide 18 : la courbe chaude EST la trajectoire, pas un pointeur.
   L'arbitrage de Codex, et il est juste. Chez moi le chaud dessinait un
   « appel » — une courbe partant du jalon vers la légende « UNE RENCONTRE » —
   pendant que la trajectoire bifurquée restait bleue comme les quatre autres.
   La couleur désignait donc un MOT, quand elle doit désigner une CONSÉQUENCE :
   ce qu'une rencontre a fait au parcours. Le chaud passe sur la bifurcation,
   qui rejoint la femme à l'horizon ; l'appel disparaît, il n'a plus rien à
   montrer que la courbe ne montre déjà. Un seul geste chaud sur la slide. */
.faisceau--ample .faisceau__bifurcation { stroke: var(--chaud, var(--data-warm)); }
.faisceau--ample .faisceau__appel { display: none; }

/* --- LA MATRICE SOUDÉE — quatre lectures d'UN SEUL objet -----------------
   La chute de la slide dit : « elles ne se relaient pas : elles tiennent
   ENSEMBLE. C'est pour ça qu'on ne les déloge pas une par une. » La matrice
   ordinaire montrait quatre boîtes séparées par une gouttière, chacune close
   sur elle-même, avec son propre contour et son propre fond — soit exactement
   quatre choses qu'on peut retirer une à une. La forme disait le contraire du
   texte (SCHEMAS §3).

   Ici les quatre PARTAGENT LEURS BORDS. Elles arrivent l'une après l'autre,
   séparées par des cloisons fines ; à l'état « ensemble », les cloisons
   s'effacent et il ne reste qu'un bloc. La démonstration est dans la
   disparition des cloisons, pas dans un mot de plus.

   Le liseré de tête reste sur chacune : il nomme les quatre fonctions, et le
   propos n'est pas qu'elles se confondent — c'est qu'elles ne se séparent
   pas. */
.matrice--soudee {
  gap: 0;
  /* SA HAUTEUR NATURELLE, et rien d'autre. `.corps` est un flex en colonne
     avec `min-height: 0` : sans ce garde, il ÉCRASE la matrice pour la faire
     entrer, les cases se compriment et leur texte sort par le bas. Le défaut
     se déplaçait à chaque coupe — le bloc rétrécissait autant que le contenu,
     donc couper ne réparait jamais rien. Avec `flex: none`, un excès devient
     un vrai débordement de slide, que la console sait voir. */
  flex: none;
  /* PAS DE BORD HAUT : les liserés des deux premières cases le dessinent
     déjà, et le doubler faisait une double ligne au sommet du bloc. */
  border: 1px solid var(--line-strong);
  border-top: 0;
  border-radius: var(--radius-md);
  /* SURTOUT PAS `overflow: hidden` — mesuré le 26 août, il avalait 48 px de
     texte en silence, et aucun contrôle ne pouvait le voir : la slide, elle,
     ne débordait pas. Un bloc qui rogne son contenu sans rien dire est pire
     qu'un bloc qui déborde. Les fonds des cases sont transparents, les coins
     arrondis n'ont donc rien à découper. */
  background-color: var(--paper-bright);
  transition: background-color var(--dur-3) var(--ease-out),
              border-color var(--dur-3) var(--ease-out);
}
.matrice--soudee .matrice__case {
  border: 0;
  border-top: 3px solid var(--brand-accent);
  padding: var(--space-3) var(--space-6) var(--space-3);
  border-radius: 0;
  background-color: transparent;
  transition: border-color var(--dur-3) var(--ease-out);
}
.matrice--soudee .matrice__case[data-ton="chaud"] { border-top-color: var(--brand-caution); }
/* les cloisons INTÉRIEURES, et elles seules */
.matrice--soudee .matrice__case:nth-child(odd) { border-right: 1px solid var(--line); }
/* ENSEMBLE : les cloisons tombent, le bloc devient un */
.slide[data-etat="ensemble"] .matrice--soudee .matrice__case { border-right-color: transparent; }
.slide[data-etat="ensemble"] .matrice--soudee {
  background-color: var(--surface-tint);
  border-color: var(--brand-primary);
}
@media print {
  .matrice--soudee .matrice__case { border-right-color: transparent !important; }
  .matrice--soudee { background-color: var(--surface-tint) !important;
                     border-color: var(--brand-primary) !important; }
}

/* --- LE CROCHET DU TRI — rendre visible « deux fois sur trois » ----------
   La chute de la slide dit : « tout ramener aux croyances, c'est se tromper
   de réponse DEUX FOIS SUR TROIS ». Trois colonnes propres montraient le
   résultat d'un tri réussi ; le rapport, lui, n'existait nulle part dans la
   figure — il n'était que dans la phrase (SCHEMAS §6 : le nombre et
   l'arrangement disent quelque chose).

   Le crochet embrasse les deux colonnes de droite et les nomme. Deux sur
   trois se VOIENT, au lieu de se compter.

   IL EST NEUTRE, ET C'EST DÉLIBÉRÉ. Les trois registres portent déjà la
   rampe de données — froid, navy, chaud — comme une ÉCHELLE. Un crochet
   chaud par-dessus donnerait au chaud deux sens sur la même slide : une
   position dans l'échelle, et une erreur. Le gris ne dit qu'une chose :
   voilà ceux-là. */
.tri__rate {
  grid-column: 2 / 4;
  position: relative;
  margin: 0;
  padding-top: var(--space-4);
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-align: center;
}
.tri__rate::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 10px;
  border: 1px solid var(--line-strong);
  border-bottom: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .tri__rate.fragment { transition: opacity var(--dur-3) var(--ease-out); }
}

/* --- LA LISSE DU CÔTÉ DE LA FILE ----------------------------------------
   `levee--gauche`, variante déclarée le 26 août. La lisse partait du montant
   vers la DROITE alors que les trois femmes attendent à GAUCHE : elle ne
   barrait donc le passage de personne — elle fermait l'espace d'après. Une
   barrière posée hors du chemin ne barre rien, et son ouverture ne libère
   rien. C'est ce que Nicolas voyait sans que la mécanique soit en cause :
   « l'ouverture n'est pas du bon côté ».

   Pointant à gauche, elle se lève dans l'AUTRE SENS — rotation positive, donc
   le bras monte vers le haut-gauche, au-dessus de celles qu'il retenait. Le
   verdict a quitté ce coin, que le bras levé occupe désormais.

   La figure vit dans quatre decks ; la variante est déclarée sur le <svg>
   pour qu'ils gardent leur géométrie et que le contrôle n'y voie pas une
   divergence. */
/* LA BARRE SE RELÈVE VERS LA DROITE, autour de son attache haute.
   Elle pend de (434,152) à (434,298) : le bas est libre. Une rotation de
   -105° amène son extrémité à (575,114) — AU-DESSUS de l'attache, franchement
   relevée vers la droite. C'est le geste que Nicolas a dessiné.
   -72° avait été essayé et écarté : le bras finissait sous son attache, et
   une barrière qui pointe vers le bas ne se lit pas comme ouverte, même
   lorsque le passage est libre. Ce qui compte n'est pas que la voie soit
   dégagée — c'est que le dessin le dise.
   Le poteau a disparu : il n'y a plus qu'une pièce longue, donc plus rien à
   confondre. Trois corrections de mécanique avaient échoué parce que le
   défaut était dans l'anatomie. */
.slide[data-etat="levee"]   .levee--barre .levee__lisse,
.slide[data-etat="passage"] .levee--barre .levee__lisse {
  transform: translate(434px, 152px) rotate(-105deg) translate(-434px, -152px);
}
@media print {
  .levee--barre .levee__lisse {
    transform: translate(434px, 152px) rotate(-105deg) translate(-434px, -152px) !important;
  }
}

/* --- LE POINT QUI CIRCULE SUR LE TRAVAIL CONTINU -------------------------
   « Un point qui circule et qui éclaire les icônes quand il passe dessus »
   (croquis du 26 août). La boucle disait déjà « continu » par sa forme ; le
   point le fait VOIR — un travail qui tourne, et quatre charges qui se
   rallument à chaque tour.

   IL NE PART QU'UNE FOIS LE CIRCUIT FERMÉ. Tant que les quatre charges
   arrivent, le tracé se dessine ; ensuite seulement il tourne. Deux horloges
   dans la même image, c'est la faute qu'on a déjà payée sur la boucle menue.

   L'ÉCLAT SE PHASE PAR UN DÉLAI NÉGATIF, et non par quatre keyframes. Le
   point met 7 s au tour et croise les étapes à 0, 25, 50 et 75 % ; chaque
   étape joue la MÊME animation, avancée du quart qui lui revient. Une seule
   règle à relire, et le phasage ne peut pas se désaccorder.

   LE POINT EST FROID : « la charge mentale » porte déjà le seul chaud de la
   slide, et le chaud ne se donne pas deux fois. */
/* La variante est déclarée sur le <svg> — `travail-continu--point` : la figure
   existe aussi sans le point dans une autre copie, et une géométrie voulue
   n'est pas une divergence. C'est l'idiome du dépôt, celui de boucle--menue. */
.travail-continu__point { fill: var(--froid); opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .slide:is([data-etat="travail-4"], [data-etat="travail-question"], [data-etat="travail-lecture"])
    .travail-continu__point {
    opacity: 1;
    animation: travail-tourner 7s linear infinite;
  }
  .slide:is([data-etat="travail-4"], [data-etat="travail-question"], [data-etat="travail-lecture"])
    .travail-continu__etape circle {
    animation: travail-eclat 7s linear infinite;
  }
  .slide:is([data-etat="travail-4"], [data-etat="travail-question"], [data-etat="travail-lecture"])
    .travail-continu__etape[data-etat="travail-2"] circle { animation-delay: -5.25s; }
  .slide:is([data-etat="travail-4"], [data-etat="travail-question"], [data-etat="travail-lecture"])
    .travail-continu__etape[data-etat="travail-3"] circle { animation-delay: -3.5s; }
  .slide:is([data-etat="travail-4"], [data-etat="travail-question"], [data-etat="travail-lecture"])
    .travail-continu__etape[data-etat="travail-4"] circle { animation-delay: -1.75s; }
}
@keyframes travail-tourner { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@keyframes travail-eclat {
  0%, 3%    { stroke-width: 6; }
  12%, 100% { stroke-width: 3; }
}
@media print { .travail-continu__point { opacity: 0 !important; animation: none !important; } }

/* --- LE LOT DU RAIL — plusieurs slides prises ensemble --------------------
   ⌘-clic ajoute et retire, ⇧-clic prend l'intervalle, Échap vide. Tirer
   n'importe laquelle les emmène toutes.
   Le liseré est à GAUCHE et pas un fond : le rail montre déjà la slide
   courante par un fond, et deux fonds concurrents ne se distinguent plus.
   Un liseré et un fond disent deux choses différentes — « je suis ici » et
   « je fais partie du lot » — et peuvent donc coexister sur la même ligne. */
.rail__ligne.est-du-lot {
  position: relative;
  box-shadow: inset 3px 0 0 var(--chaud, var(--data-warm));
}
.rail__ligne.est-du-lot .rail__num { color: var(--chaud, var(--data-warm)); font-weight: 800; }
.rail__lot {
  position: sticky;
  bottom: 0;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background-color: var(--chaud, var(--data-warm));
  color: #fff;
  font-size: var(--fs-100);
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
}
.rail__lot[hidden] { display: none; }

/* --- LA SYNTHÈSE — ce qu'une partie a montré, en quatre traits ------------
   Demandée par Nicolas le 26 août : « le 15 doit faire une espèce de synthèse
   des représentations avec un message clair ». La slide portait un repère de
   consultation isolé — juste, mais qui ne récapitulait rien : la partie venait
   de démontrer quatre choses, et aucune n'était reprise.

   Le TITRE porte le message, et les quatre traits le tiennent. Chacun est un
   MOT, pas une phrase : c'est ce qui rend une synthèse relisable d'un coup
   d'œil, et c'est ce qui la distingue d'un résumé. Le dernier prend le chaud
   — le mouvement est ce que la partie a de moins évident et de plus utile.

   Sur navy, la règle de séparation ne peut pas être `--line` : elle y
   disparaît. C'est une transparence blanche, comme la source-note du même
   fond. */
/* @famille synthese · verbe RÉCAPITULER · registre nommé */
.synthese {
  display: grid;
  gap: 0;
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
}
.synthese > li {
  display: grid;
  grid-template-columns: minmax(190px, 0.34fr) 1fr;
  align-items: baseline;
  gap: var(--space-6);
  padding: var(--space-3) 0;
  border-top: 1px solid rgba(255, 255, 255, .22);
}
.synthese > li > b {
  color: var(--paper-bright);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  font-weight: 400;
  letter-spacing: -.02em;
}
.synthese > li > span { color: rgba(255, 255, 255, .82); font-size: var(--fs-300); line-height: 1.4; }
.synthese > li[data-ton="chaud"] > b { color: var(--brand-focus-light); }
.synthese__chute { margin-top: var(--space-6); color: rgba(255, 255, 255, .92); }

/* --- CE QUE LE MOT FAIT — la langue performative, montrée --------------
   Sur « le mot stérilet », la slide barrait le terme et posait le bon. Elle
   DISAIT que le nom fait exister une crainte ; elle ne le montrait pas.
   Ici la flèche part du mot et arrive sur la crainte, et elle ne va que dans
   ce sens : c'est toute la démonstration. Un objet ne produit pas de peur —
   un mot, si. La fertilité revient dès le retrait ; le mot, lui, reste.

   Elle paraît APRÈS le mot barré et AVANT le mot juste : on comprend d'abord
   pourquoi il fallait le changer, on voit ensuite par quoi. */
/* @famille performatif · verbe REQUALIFIER · registre nommé */
.performatif {
  display: grid;
  grid-template-columns: auto 96px auto;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide:is([data-etat="fait"], [data-etat="juste"]) .performatif { opacity: 1; }
.performatif__mot,
.performatif__crainte {
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-600);
  letter-spacing: -.02em;
}
.performatif__mot { color: var(--muted); }
.performatif__crainte { color: var(--chaud, var(--data-warm)); }
.performatif__fleche { width: 96px; height: 24px; }
.performatif__fleche path { fill: none; stroke: var(--chaud, var(--data-warm)); stroke-width: 3; stroke-linecap: round; }
/* Plus spécifique que `.performatif__fleche path`, sinon le `fill: none` de
   la règle générale gagne et la pointe sort CREUSE — vu sur le tirage. */
.performatif__fleche .performatif__pointe { fill: var(--chaud, var(--data-warm)); stroke: none; }
.performatif__verdict {
  grid-column: 1 / -1;
  margin: var(--space-2) 0 0;
  color: var(--muted);
  font-size: var(--fs-300);
  text-align: center;
}
@media print { .performatif { opacity: 1 !important; } }


/* =============================================================================
   5 septies. LES BARRES, ET CE QU'ELLES OCCUPENT ENSEMBLE
   -------------------------------------------------------------------------
   Ajouté le 27 août 2026 pour CONSULTE 2027. Six barres montrent six
   grandeurs ; elles ne montrent pas l'INTERVALLE que les six occupent, ni
   le NIVEAU auquel elles se comparent — et c'est là tout le propos de deux
   slides : « huit points d'amplitude », « le niveau moyen de citation ».

   Extension de `.barres` (DÉNOMBRER), pas un préset neuf : le verbe existe.

   LA GRILLE JUMELLE, et c'est la seule difficulté. Le calque de repères doit
   tomber exactement sur la piste des barres. `.barre` ouvre sur
   `minmax(120px, 240px)` — largeur résolue par le CONTENU, donc un calque
   vide s'y rangerait à 120 px et le repère serait décalé. Le cadre fixe donc
   la première colonne pour les deux grilles : aucun nombre magique, aucune
   mesure en JS, et les deux se superposent au pixel.
   ========================================================================== */

/* @famille barres-cadre · registre nommé */
.barres-cadre { position: relative; }

/* Les deux grilles, déclarées ensemble : elles ne peuvent plus diverger. */
.barres-cadre .barre,
.barres__calque {
  grid-template-columns: 240px 1fr 92px;
  gap: var(--space-4);
}
/* LA FIGURE REMPLIT SON CADRE. Six barres à l'espacement par défaut
   occupaient les deux tiers du corps sur une slide de démonstration : « un
   cadre plus grand ne suffit pas, le dessin doit le remplir » (PRESETS.md).
   Mesuré au tirage, pas au code. */
.barres-cadre .barres { gap: var(--space-6); }
.barres-cadre .barre > i { height: 30px; }
.barres__calque {
  position: absolute;
  inset: 0;
  display: grid;
  align-items: stretch;
  pointer-events: none;
}
.barres__calque > span {
  grid-column: 2;
  position: relative;
  display: block;
}

/* --- L'AMPLITUDE — l'intervalle que les barres occupent ensemble ---------
   Une bande claire, du minimum au maximum, posée SUR la piste. Elle ne
   colore rien : elle cerne. Le libellé direct est dedans, pas en légende —
   une légende obligerait l'œil à faire l'aller-retour que la figure existe
   précisément pour éviter.                                                  */
.barres__bande {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--de, 0%);
  width: calc(var(--a, 0%) - var(--de, 0%));
  border-left: 3px solid var(--chaud);
  border-right: 3px solid var(--chaud);
  background-color: color-mix(in srgb, var(--chaud) 9%, transparent);
  /* Le geste : la bande se pose de gauche à droite, en découvrant. Le clip
     plutôt qu'une largeur — la largeur déplacerait les bordures pendant tout
     le trajet, et les deux montants sont ce qui DIT l'intervalle. */
  clip-path: inset(0 var(--arrivee-decoupe) 0 0);
  transition: clip-path 700ms var(--ease-out);
}
.fragment.est-revele .barres__bande { --arrivee-decoupe: 0%; }

/* --- LE FILET — le niveau auquel les barres se comparent -----------------
   Vertical, sur toute la hauteur des barres, avec son libellé direct. Il se
   pose du haut vers le bas : un repère descend sur la série, il ne pousse
   pas depuis le sol.                                                        */
.barres__filet {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--x, 0%);
  width: 3px;
  background-color: var(--chaud);
  clip-path: inset(0 0 var(--arrivee-decoupe) 0);
  transition: clip-path 700ms var(--ease-out);
}
.fragment.est-revele .barres__filet { --arrivee-decoupe: 0%; }

/* Le libellé du repère, dans la figure et non en légende. Il se détache du
   fond comme les libellés posés sur un tracé (SCHEMAS.md §10) : halo de
   papier, sinon une barre passe derrière le mot. */
/* AU-DESSUS DES BARRES, PAS DESSUS. Posé à `top: 0`, le libellé tombait sur
   la première barre — vu au tirage, invisible au code. Le repère commente la
   série entière : sa place est au-dessus d'elle. */
.barres__libelle {
  position: absolute;
  top: -26px;
  left: calc(var(--x, 0%) + 10px);
  padding: 2px 6px;
  background-color: var(--paper);
  color: var(--chaud);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}
.barres__bande > .barres__libelle { left: 50%; transform: translateX(-50%); }

/* --- LES GROUPES — deux régimes dans la même série ------------------------
   Huit propositions, quatre en haut et quatre en bas, et quinze points entre
   les deux. Sur une échelle linéaire l'écart se voit ; ce qui ne se voit pas,
   c'est qu'il sépare DEUX RÉGIMES et non un dégradé. La rupture se dessine
   donc — un filet, pas un axe brisé : briser l'axe sur les valeurs
   fabriquerait une discontinuité que les données n'ont pas.               */
.barres__groupe { position: relative; }
.barres__groupe + .barres__groupe { margin-top: var(--space-4); }

/* La rupture. Un filet en travers, et le blanc de part et d'autre : c'est le
   blanc qui dit les deux régimes, le filet ne fait que le déclarer. */
.barres__rupture {
  height: 0;
  margin: var(--space-4) 0;
  border-top: 2px dashed var(--line-strong);
}

/* L'APLAT — il couvre le groupe, il ne colore pas les barres. Une barre
   recolorée dirait que la GRANDEUR change de nature ; l'aplat dit que le
   groupe en change. Il déborde légèrement pour ne pas raser les libellés. */
.barres__aplat {
  position: absolute;
  inset: calc(var(--space-2) * -1) calc(var(--space-3) * -1);
  background-color: color-mix(in srgb, var(--chaud) 8%, transparent);
  border-left: 3px solid var(--chaud);
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
  pointer-events: none;
}

/* Le nom du groupe, dans la figure et non en légende. Il se pose au-dessus
   de ses barres, du côté où elles commencent. */
.barres__nom {
  margin: 0 0 var(--space-2);
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
/* Un modificateur, pas `data-ton` : le contrôle du jalon unique compte les
   attributs, et le groupe chaud aurait fait deux marques là où il n'y a
   qu'une conséquence — l'aplat. */
.barres__groupe--chaud .barres__nom { color: var(--chaud); }

/* LES DEUX RÉGIMES SE NOMMENT ENSEMBLE, avec l'aplat. Séparer le nom du
   groupe chaud de son aplat ferait deux clics pour une seule opération. */
.slide[data-etat="regimes"] .barres__aplat,
.slide[data-etat="regimes"] .barres__nom { --arrivee-opacite: 1; }

/* HUIT BARRES NE TIENNENT PAS À L'ESPACEMENT DE SIX. Le registre aéré est
   calibré sur six lignes ; à huit, plus deux noms de groupe et une rupture,
   la figure sortait du cadre. Mesuré au tirage. */
.barres-cadre[data-dense] { margin-top: var(--space-2); }
.barres-cadre[data-dense] .barres { gap: var(--space-2); }
.barres-cadre[data-dense] .barre > i { height: 22px; }

@media print {
  /* Chrome rend la première image : sans ceci, la bande et le filet
     s'imprimeraient entièrement rognés — donc absents, et la slide
     démontrerait le contraire de ce qu'elle dit. */
  .barres__bande,
  .barres__filet { clip-path: none !important; transition: none !important; }
}

/* =============================================================================
   5 septies bis. L'OPPOSITION QUI COMPARE DEUX MESURES
   -------------------------------------------------------------------------
   Extension d'`.opposition` (OPPOSER), ajoutée le 27 août 2026.

   L'opposition du dossier tient deux colonnes de PROSE. Ici chaque pôle porte
   les mêmes deux mesures, et c'est leur inversion qui fait la démonstration :
   à gauche beaucoup de retrait et peu de refus, à droite l'inverse. Les deux
   mesures se nomment donc une fois, en tête de colonne, et les valeurs
   s'alignent dessous — sinon on relit six fois le même libellé.
   ========================================================================== */

/* ⚠ CHAQUE RÈGLE PORTE LE PRÉFIXE `.opposition`, et ce n'est pas du style.
   `.opposition p` pose déjà Georgia à --fs-500 en (0,1,1) : une classe
   seule perd, et les têtes de colonne sortaient en gros serif gras. Vu au
   tirage, invisible au code. */
.opposition .opposition__mesures {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  column-gap: var(--space-4);
  row-gap: var(--space-2);
}
.opposition .opposition__tete {
  margin: 0 0 var(--space-2);
  align-self: end;
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 700;
  line-height: 1.2;
  text-align: right;
}
.opposition .opposition__tete:first-child { text-align: left; }
.opposition .opposition__nom {
  margin: 0;
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  line-height: 1.25;
}
.opposition .opposition__valeur {
  margin: 0;
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  font-variant-numeric: tabular-nums;
  text-align: right;
  /* Sa place est réservée dès le départ : en display, les deux colonnes
     sauteraient au clic qui les remplit. */
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.opposition .opposition__valeur[data-jalon] { color: var(--chaud); }
.opposition .opposition__note {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-100);
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="remplies"] .opposition__valeur,
.slide[data-etat="remplies"] .opposition__note { --arrivee-opacite: 1; }

/* La ligne du champ libre est un `.echo` — voir « 4.5 bis ». Elle vivait ici
   en propre avant que la même forme apparaisse trois fois. */

/* =============================================================================
   5 octies. LA SUBSTITUTION QUI REMPLACE UN INDICATEUR, PAS UN MOT
   -------------------------------------------------------------------------
   Extension de `.substitution` (REQUALIFIER), ajoutée le 27 août 2026.

   Le préset remplace un MOT par un terme : le mot est le propos, il n'y a
   rien à mettre sous lui. Ici les deux termes sont des INDICATEURS, et
   chacun porte sa mesure — c'est la comparaison des deux mesures qui fait la
   démonstration, pas la substitution seule. La mesure du terme barré RESTE :
   on ne compare pas à ce qu'on ne voit plus.
   ========================================================================== */

.substitution__mesure {
  margin: var(--space-2) 0 0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.substitution__mesure b { font-weight: 700; color: var(--ink); }
.substitution__mesure[data-ton="chaud"] b { color: var(--chaud); }

/* Celle du terme juste arrive avec lui, et par le même geste. Sa place est
   réservée dès le départ : en display, la figure sauterait au dernier clic. */
.substitution__mesure[data-temps="juste"] { opacity: 0; }
.slide[data-etat="juste"] .substitution__mesure[data-temps="juste"] {
  opacity: 1;
  animation: substitution-monter var(--dur-3) var(--ease-out) 80ms both;
}

/* =============================================================================
   5 nonies. LE NUAGE — deux indicateurs croisés, les objets PLACÉS
   -------------------------------------------------------------------------
   Extension de `.matrice` (JUXTAPOSER), ajoutée le 27 août 2026.

   `.matrice` range quatre blocs de texte sur deux axes : elle dit qu'il y a
   quatre cas. Elle ne dit pas OÙ chaque objet tombe, ni de combien deux
   objets se séparent sur un axe quand ils sont à égalité sur l'autre — et
   c'est cette distance-là que la slide démontre.

   Les deux bornes sont NOMMÉES et écrites dans la figure, jamais posées à
   l'œil : la charte l'exige pour toute classe discrète, et un quart vide
   dont la frontière est arbitraire ne démontre rien.

   ⚠ AUCUNE RÈGLE NE PORTE LE PRÉFIXE `.nuage`, et ce n'est pas du style.
   Le contrôle du plancher de lisibilité attribue à une classe ANIMÉE tous
   les traits qu'elle porte ; un cadre à 1,5 px écrit `.nuage .nuage__cadre`
   ferait donc crier sur le nuage entier, alors que les axes et les filets
   gardent leur finesse par règle (SCHEMAS.md §1). Les noms sont uniques :
   le préfixe n'apportait rien et coûtait un faux signalement.
   ========================================================================== */

.nuage__cadre { fill: none; stroke: var(--line-strong); stroke-width: 1.5; }
.nuage__borne {
  stroke: var(--line-strong);
  stroke-width: 1.5;
  stroke-dasharray: 6 5;
}
.nuage__axe { fill: var(--muted); font-size: 15px; font-family: Inter, system-ui, sans-serif; }
.nuage__borne-nom {
  fill: var(--muted);
  font-size: 13px;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: .05em;
}
.nuage__point { fill: var(--froid); }
.nuage__nom {
  fill: var(--ink);
  font-size: 16px;
  font-family: Georgia, "Times New Roman", serif;
}
/* LE HALO — la borne horizontale traverse le libellé de la prévention, et
   elle le traversera toujours : cette priorité est à 44,8 % pour un niveau
   moyen de 45,5, elle est POSÉE sur la borne et c'est le propos. Rien ne
   voyage sur ce trait, donc c'est au mot de se détourer, pas au trait de
   s'écarter (SCHEMAS.md §10). Même règle que `.schema .t-repere`. */
.nuage__nom, .niveaux__nom, .niveaux__valeur {
  paint-order: stroke;
  stroke: var(--paper, #f7f8fc);
  stroke-width: 5px;
  stroke-linejoin: round;
}
.nuage__valeur {
  fill: var(--muted);
  font-size: 13px;
  font-family: Inter, system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
}

/* Les objets paraissent au premier temps. Ils ne se déplacent pas : une
   coordonnée qui bouge est une coordonnée qu'on croit moins. */
.nuage__objet { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out); }
.slide[data-etat="places"] .nuage__objet,
.slide[data-etat="zones"]  .nuage__objet,
.slide[data-etat="vide"]   .nuage__objet { --arrivee-opacite: 1; }

/* Les trois zones occupées se cernent ENSEMBLE — même durée, même départ.
   Les faire défiler ferait lire une hiérarchie entre elles ; elles sont de
   même rang, et c'est la quatrième, restée vide, qui est le résultat. */
.nuage__zone {
  fill: none;
  stroke: var(--brand-accent);
  stroke-width: 3;
  /* pathLength="1" se pose sur l'ÉLÉMENT, jamais ici : sans lui,
     stroke-dasharray:1 donne des pointillés d'un pixel, donc un trait
     invisible. Les deux vont toujours ensemble. */
  stroke-dasharray: 1;
  stroke-dashoffset: var(--arrivee-trace);
}
.slide[data-etat="zones"] .nuage__zone,
.slide[data-etat="vide"]  .nuage__zone {
  --arrivee-trace: 0;
  animation: nuage-cerner 800ms var(--ease-out) both;
}
@keyframes nuage-cerner { from { stroke-dashoffset: 1; } }

.nuage__etiquette {
  fill: var(--brand-accent);
  font-size: 13px;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: .07em;
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="zones"] .nuage__etiquette,
.slide[data-etat="vide"]  .nuage__etiquette { --arrivee-opacite: 1; }

/* LE QUART VIDE — le seul élément chaud de la figure, et il ne désigne pas
   un objet : il désigne une ABSENCE, qui est le résultat (SCHEMAS.md §11). */
.nuage__vide { fill: var(--chaud); }
.nuage__vide-cadre { fill: none; stroke: var(--chaud); stroke-width: 3; stroke-dasharray: 8 6; }
.nuage__vide, .nuage__vide-cadre {
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="vide"] .nuage__vide,
.slide[data-etat="vide"] .nuage__vide-cadre { --arrivee-opacite: 1; }

/* =============================================================================
   5 decies. LES NIVEAUX — deux directions opposées, mesurées
   -------------------------------------------------------------------------
   Extension d'`.elan` (ORDONNER), ajoutée le 27 août 2026.

   L'élan du dossier oppose deux directions dans le PLAN : deux flèches
   horizontales, deux pôles qualitatifs. Ici les deux mouvements sont
   MESURÉS, et leur direction est verticale — l'un monte, l'autre descend,
   et c'est le sens du dessin qui porte le sens du propos (SCHEMAS.md §4).

   Famille propre, et non `.elan__*` : `elan__tige` et `elan__pointe` sont
   déjà pris par l'élan horizontal. Réemployer les noms aurait restylé une
   figure qui sert ailleurs, en silence.

   Les deux tiges ont la MÊME durée : elles partent ensemble et arrivent
   ensemble alors qu'elles ne parcourent pas la même distance. C'est la
   divergence qui est le propos, pas la vitesse.
   ========================================================================== */

.niveaux__base { stroke: var(--line-strong); stroke-width: 1.5; }
.niveaux__grille { stroke: var(--line); stroke-width: 1.5; stroke-dasharray: 5 5; }
.niveaux__amorce { stroke: var(--line-strong); stroke-width: 1.5; fill: none; }
.niveaux__groupe {
  fill: var(--muted);
  font-size: 14px;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: .07em;
}
.niveaux__nom { fill: var(--ink); font-size: 17px; font-family: Georgia, "Times New Roman", serif; }
.niveaux__valeur {
  fill: var(--ink);
  font-size: 17px;
  font-family: Georgia, "Times New Roman", serif;
  font-variant-numeric: tabular-nums;
}
.niveaux__marque { fill: var(--froid); }

.niveaux__depart { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out); }
.slide[data-etat="niveaux"] .niveaux__depart,
.slide[data-etat="ecart"]   .niveaux__depart,
.slide[data-etat="mesures"] .niveaux__depart { --arrivee-opacite: 1; }

.niveaux__tige {
  fill: none;
  stroke: var(--froid);
  stroke-width: 4;
  stroke-linecap: round;
  /* pathLength="1" sur l'élément — voir la note du nuage. */
  stroke-dasharray: 1;
  stroke-dashoffset: var(--arrivee-trace);
}
.niveaux__pointe { fill: var(--froid); opacity: var(--arrivee-opacite); }
/* Le chaud va sur la flèche qui RECULE — c'est le sens qui décide, pas le
   rang (PRESETS.md, `.elan`). */
[data-sens="retrait"] .niveaux__tige { stroke: var(--chaud); }
[data-sens="retrait"] .niveaux__pointe { fill: var(--chaud); }

.slide[data-etat="ecart"] .niveaux__tige,
.slide[data-etat="mesures"] .niveaux__tige {
  --arrivee-trace: 0;
  animation: niveaux-tirer 700ms var(--ease-io) both;
}
@keyframes niveaux-tirer { from { stroke-dashoffset: 1; } }
/* Le geste composé se mesure ENTIER : 700 + 200 à 660 ms de délai = 860 ms,
   dans la bande de démonstration. Ne pas mesurer ses moitiés. */
.slide[data-etat="ecart"] .niveaux__pointe,
.slide[data-etat="mesures"] .niveaux__pointe {
  --arrivee-opacite: 1;
  animation: niveaux-poser var(--dur-2) var(--ease-out) 660ms both;
}
@keyframes niveaux-poser { from { opacity: 0; } }

.niveaux__ecart {
  fill: var(--froid);
  font-size: 17px;
  font-family: Georgia, "Times New Roman", serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
[data-sens="retrait"] .niveaux__ecart { fill: var(--chaud); }
.slide[data-etat="mesures"] .niveaux__ecart { --arrivee-opacite: 1; }

/* =============================================================================
   7 bis (suite). LE PLAN SUR UNE SLIDE D'ANNONCE
   -------------------------------------------------------------------------
   `data-plan="seul"` — ajouté le 27 août 2026. Une ouverture qui annonce les
   parties avant d'y entrer reçoit le plan GÉNÉRÉ, comme les portes : sans ce
   point d'entrée, elle recopiait les titres de partie à la main, et une
   liste recopiée diverge du jour où une partie bouge.

   Aucune entrée n'est « en cours » : rien n'a commencé, et c'est ce que la
   slide dit. Même encre que la porte, sur le même papier.
   ========================================================================== */

/* LE CHAUD N'EXISTE PAS SUR NAVY : il s'y noierait. Le jalon d'une
   conclusion de partie passe au turquoise clair — un seul par slide, comme
   partout ailleurs. Sans cette règle, `data-jalon` était compté par le
   contrôle et invisible sur le tirage : le pire des deux mondes. */
/* La spécificité compte ici : `.repere .prose strong` pose déjà du blanc
   (0,2,1). Une règle en 0,2,0 posée plus bas dans le fichier ne gagne pas —
   le jalon restait blanc, compté par le contrôle et invisible au tirage. */
.repere .prose strong[data-jalon] { color: var(--brand-focus-light); }

.slide[data-plan="seul"] .plan {
  margin-top: var(--space-8);
  gap: var(--space-2);
}
/* La slide d'annonce donne au plan la place d'une figure : c'est SA figure.
   Au corps de la porte, il se lisait comme une note de bas de titre. */
.slide[data-plan="seul"] .plan li {
  padding: var(--space-3) 0 var(--space-3) var(--space-4);
  border-left: 2px solid var(--line);
  color: var(--muted);
  font-size: var(--fs-400);
}
.slide[data-plan="seul"] .plan li b { color: var(--brand-accent); font-size: var(--fs-300); }


/* =============================================================================
   5 undecies. LE SURLIGNEUR QUAND LA MULTIPLICITÉ EST LE PROPOS
   -------------------------------------------------------------------------
   Extension de `.surligneur` (ÉNONCER), ajoutée le 27 août 2026.

   Le surligneur pose UN aplat chaud sur le segment qui fait basculer la
   phrase : il désigne une saillance. Ici la démonstration est l'inverse —
   une seule contribution porte QUATRE demandes de même rang, et en faire
   ressortir une détruirait le propos. Les quatre aplats passent donc au bleu
   de marque, et aucun chaud ne paraît sur la slide.

   C'est une extension, pas un second préset : même geste, même mécanique de
   `background-size`, une couleur qui dit « de même rang » au lieu de « celui-là ».
   ========================================================================== */

/* .34 était calé sur le chaud du préset (.22) — à tort : l'orange se
   détache du papier par la TEINTE, le bleu seulement par la clarté. Sur
   le tirage, quatre aplats à .34 se lisaient comme un gris. À .46 ils
   redeviennent quatre aplats, et le texte qui les porte reste à l'encre. */
.surligneur[data-ton="froid"] {
  background-image: linear-gradient(rgba(67, 127, 179, .46), rgba(67, 127, 179, .46));
}
/* Les quatre se posent ENSEMBLE. Les poser l'un après l'autre ferait lire une
   lecture guidée ; on veut le choc de la simultanéité. */
.slide[data-etat="registres"] .surligneur { background-size: 100% 38%; }

/* @famille registres · registre nommé */
.registres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  margin: var(--space-6) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="registres"] .registres { --arrivee-opacite: 1; }
.registres > span {
  color: var(--froid);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
}
/* Les libellés se lisent dans l'ORDRE DES APLATS, pas par une couleur : les
   quatre aplats sont de la même teinte, une légende par couleur mentirait.
   C'est pourquoi ils sont en ligne et numérotés par leur rang de lecture. */
.registres > span::before {
  content: counter(registre) " · ";
  counter-increment: registre;
  color: var(--muted);
}
.registres { counter-reset: registre; }

/* =============================================================================
   9 bis. LE RECLASSEMENT — où vont les réponses quand on les range
   -------------------------------------------------------------------------
   CATÉGORISER, et le dossier l'avait réservé : « à venir quand une slide le
   demandera : la frontière de classement, le cas-limite, LE RECLASSEMENT ».
   La slide 17 de CONSULTE 2027 l'a demandé le 27 août 2026.

   Six portes d'entrée à gauche, les destinations à droite, une bande par
   entrée vers sa destination majoritaire. **Les bandes se croisent, et c'est
   le croisement qui démontre** : une entrée ne se reconstitue jamais à
   l'arrivée, deux entrées différentes peuvent atterrir au même endroit, et
   une entrée peut ne pas arriver dans la case qui porte son propre nom.

   Ce n'est pas ORDONNER : il n'y a ni avant ni après, rien ne progresse. Le
   rangement AGIT — c'est la définition du verbe 9.

   Les bandes ont toutes la MÊME épaisseur. L'épaisseur proportionnelle a été
   écartée : elle encoderait un volume qu'il faudrait recalculer (part ×
   effectif d'origine), donc un chiffre sans périmètre affiché. La part captée
   est écrite sur la bande, en toutes lettres.
   ========================================================================== */

.reclassement__axe { fill: none; stroke: var(--line-strong); stroke-width: 1.5; }
.reclassement__noeud { fill: var(--froid); }
.reclassement__nom { fill: var(--ink); font-size: 16px; font-family: Georgia, "Times New Roman", serif; }
.reclassement__effectif {
  fill: var(--muted);
  font-size: 13px;
  font-family: Inter, system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
}
.reclassement__cible {
  fill: var(--muted);
  font-size: 14px;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 700;
}
.reclassement__part {
  fill: var(--froid);
  font-size: 15px;
  font-family: Georgia, "Times New Roman", serif;
  font-variant-numeric: tabular-nums;
  /* Halo : la part est POSÉE sur sa bande, et c'est sa place — elle dit ce
     que cette bande transporte (SCHEMAS.md §10). */
  paint-order: stroke;
  stroke: var(--paper, #f7f8fc);
  stroke-width: 5px;
  stroke-linejoin: round;
}

.reclassement__depart { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out); }
.slide[data-etat="entrees"] .reclassement__depart,
.slide[data-etat="bandes"] .reclassement__depart,
.slide[data-etat="convergence"] .reclassement__depart { --arrivee-opacite: 1; }

/* LES SIX BANDES SE TRACENT ENSEMBLE, même durée. Les tracer une par une
   ferait lire six faits ; c'en est un seul — la dispersion. */
.reclassement__bande {
  fill: none;
  stroke: var(--froid);
  stroke-width: 9;
  stroke-linecap: round;
  stroke-opacity: .5;
  /* pathLength="1" se pose sur l'élément — sans lui, un trait invisible. */
  stroke-dasharray: 1;
  stroke-dashoffset: var(--arrivee-trace);
}
.reclassement__part,
.reclassement__arrivee { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out); }
.slide[data-etat="bandes"] .reclassement__bande,
.slide[data-etat="convergence"] .reclassement__bande {
  --arrivee-trace: 0;
  animation: reclasser 900ms var(--ease-io) both;
}
@keyframes reclasser { from { stroke-dashoffset: 1; } }
.slide[data-etat="bandes"] .reclassement__part,
.slide[data-etat="convergence"] .reclassement__part,
.slide[data-etat="bandes"] .reclassement__arrivee,
.slide[data-etat="convergence"] .reclassement__arrivee { --arrivee-opacite: 1; }

/* LA CONVERGENCE — le seul chaud de la figure, et il désigne un POINT
   d'arrivée partagé, pas une bande : ce qui a changé, c'est que deux
   questions différentes finissent au même endroit (SCHEMAS.md §11). */
.reclassement__convergence {
  fill: none;
  stroke: var(--chaud);
  stroke-width: 3;
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.reclassement__verdict {
  fill: var(--chaud);
  font-size: 13px;
  font-family: Inter, system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: .07em;
  opacity: var(--arrivee-opacite);
  transition: opacity var(--dur-3) var(--ease-out);
}
.slide[data-etat="convergence"] .reclassement__convergence,
.slide[data-etat="convergence"] .reclassement__verdict { --arrivee-opacite: 1; }

/* =============================================================================
   3 bis. LE GLISSEMENT EN RANGS — trois lectures qui basculent ensemble
   -------------------------------------------------------------------------
   Extension de `.glissements` (ORDONNER / REQUALIFIER), ajoutée le 27 août.

   Le préset range ses cartes en colonnes, chacune avec sa paire verticale :
   il compare des SITUATIONS. Ici les trois lignes disent la même bascule sur
   trois objets, et c'est leur simultanéité qui démontre — trois rangs, une
   paire horizontale, un seul temps qui les retourne toutes.

   Le libellé de la colonne de droite se réécrit sur place (`b.morphe`) : la
   slide cite sa propre première lecture au lieu de la commenter. Ce n'est PAS
   une `.substitution` — la seule du deck est en slide 6.
   ========================================================================== */

.glissements--rangs { grid-template-columns: 1fr; gap: var(--space-2); }
.glissements--rangs .glissement { padding: var(--space-2) var(--space-6); }
.glissements--rangs .glissement__paire,
.glissements__tete {
  display: grid;
  grid-template-columns: 1fr 64px 1fr;
  align-items: center;
  column-gap: var(--space-6);
}
.glissements__tete { margin: 0; padding: 0 var(--space-6) var(--space-1); }
.glissements__tete > * {
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.glissements__tete > b { grid-column: 3; }
.glissements--rangs .glissement__fleche { width: 64px; height: 24px; }
.glissements--rangs .glissement__terme > span { font-size: var(--fs-300); }

/* Le morphe, hors du `.flux--echo` où il était né : le geste vaut pour tout
   libellé qui se réécrit sur place. Les règles de `.flux--echo` restent plus
   spécifiques, donc rien ne bouge là-bas. */
b.morphe { display: grid; grid-template-areas: "titre"; }
b.morphe > span { grid-area: titre; transition: opacity 420ms var(--ease-out); }
b.morphe > [data-avant] { opacity: 1; }
b.morphe > [data-apres] { opacity: 0; }
.slide[data-etat="requalifie"] b.morphe > [data-avant] { opacity: 0; }
.slide[data-etat="requalifie"] b.morphe > [data-apres] { opacity: 1; color: var(--brand-primary); }

/* Les trois rangs basculent ENSEMBLE, et l'état suivant redéclare le
   précédent : sans quoi la colonne de droite se retirerait au clic du
   morphe. Même construction que `.substitution`, pour la même raison. */
.slide:is([data-etat="vote"], [data-etat="requalifie"]) .glissements--rangs .glissement__tige {
  animation: glisser 520ms var(--ease-io) both;
}
.slide:is([data-etat="vote"], [data-etat="requalifie"]) .glissements--rangs .glissement__pointe {
  animation: glissement-paraitre 200ms var(--ease-out) 470ms both;
}
.slide:is([data-etat="vote"], [data-etat="requalifie"]) .glissements--rangs .glissement__terme[data-role="reel"] {
  animation: glissement-poser 420ms var(--ease-out) 560ms both;
}
.glissements--rangs .glissement__terme[data-role="reel"] > b { color: var(--froid); }
.glissements--rangs .glissement__terme[data-jalon] > span { color: var(--chaud); }




/* =============================================================================
   5 duodecies. LE SEUIL D'UN COMPTAGE — ce qu'un inventaire laisse dehors
   -------------------------------------------------------------------------
   Extension de `.chiffres` (DÉNOMBRER), ajoutée le 27 août 2026.

   Deux nombres et un seuil entre eux. Le seuil n'est pas une décoration : il
   est le propos — ce qui se répète est une couche mince, et tout ce qui passe
   dessous ne remonte dans aucun tableau. Même geste que `.barres__rupture` :
   un filet en travers, du blanc de part et d'autre, et le libellé du seuil
   écrit en toutes lettres.
   ========================================================================== */

/* @famille seuil · registre nommé */
.seuil {
  margin: 0;
  padding: var(--space-3) 0 0;
  border-top: 2px dashed var(--line-strong);
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.seuil::before { content: "\2193\00a0\00a0"; }

/* =============================================================================
   11 bis. LA FOCALE — le même matériau, à deux résolutions
   -------------------------------------------------------------------------
   ARTICULER, récolté le 27 août 2026. C'est le DÉZOOM À L'ENVERS : là où le
   dézoom élargit le champ pour montrer ce qui tient une personne, la focale
   le resserre pour montrer ce qu'un niveau de lecture écrase.

/* @famille dezoom · verbe ARTICULER · registre nommé */
   ⚠ POURQUOI UN NOM PROPRE ET PAS `.dezoom__*`. Les noms du dézoom décrivent
   une personne et ses satellites — halo, acteur, lien, seule. Les réemployer
   pour deux blocs de texte imbriqués aurait rendu le CSS menteur. Le verbe est
   le même, la géométrie n'a rien de commun.

   LE SECOND EST DANS LE PREMIER, littéralement : le bloc fin est un enfant du
   bloc large. C'est la seule façon de dire « ce n'est pas un sous-ensemble
   qu'on extrait, c'est le même corpus qu'on regarde de plus près ».

   Le resserrement est LENT (900 ms) : c'est la lenteur qui fait lire un
   changement de focale plutôt qu'un remplacement.
   ========================================================================== */

.focale__cadre {
  padding: var(--space-3) var(--space-6) var(--space-4);
  border: 2px solid var(--line-strong);
}
.focale__niveau {
  margin: 0 0 var(--space-2);
  color: var(--muted);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.focale__theme {
  margin: 0;
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-500);
  line-height: 1.25;
}
.focale__mesure {
  margin: var(--space-1) 0 0;
  color: var(--muted);
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
}
.focale__verdict {
  margin: var(--space-2) 0 0;
  color: var(--muted);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  font-style: italic;
}
.focale__fin {
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border: 3px solid var(--froid);
  background-color: var(--paper-bright);
  opacity: var(--arrivee-opacite);
  transform: scale(.94);
  transform-origin: 50% 0;
  transition: opacity 900ms var(--ease-out), transform 900ms var(--ease-io);
}
.slide[data-etat="fin"] .focale__fin { --arrivee-opacite: 1; transform: scale(1); }
.focale__liste {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 2;
  column-gap: var(--space-8);
}
.focale__liste li {
  margin: 0 0 2px;
  break-inside: avoid;
  color: var(--ink);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-300);
  line-height: 1.35;
}
@media print {
  /* Chrome rend la première image : sans ceci, le bloc fin s'imprimerait
     réduit et translucide — la démonstration à moitié faite. */
  .focale__fin { transform: none !important; transition: none !important; }
}

/* --- LE SEUIL DU NUAGE — le filet migré de la page retirée ----------------
   Le niveau moyen de citation vivait sur sa propre page ; elle a disparu, il
   descend sur l'axe vertical de la matrice. Il prend son temps à lui : sans
   lui, les deux zones du bas se lisent comme un classement au lieu de se lire
   comme deux groupes. */
.nuage__seuil { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out); }
.slide[data-etat="seuil"] .nuage__seuil,
.slide[data-etat="zones"] .nuage__seuil,
.slide[data-etat="vide"]  .nuage__seuil { --arrivee-opacite: 1; }


/* =============================================================================
   LE JALON SE VOIT — partout, et par défaut
   -------------------------------------------------------------------------
   Trois fois en deux jours, un `data-jalon` posé sur un mot a été COMPTÉ par
   le contrôle du jalon unique et rendu en encre ordinaire : la marque existait
   au code et pas à l'écran. Le pire des deux mondes — un contrôle satisfait,
   une slide sans son point saillant, et rien pour le dire.

   Le chaud devient donc le défaut pour tout élément de TEXTE qui porte
   l'attribut. Les fonds navy (`.repere .prose strong[data-jalon]`), les
   légendes de bulle et les figures gardent leurs règles propres : elles sont
   plus spécifiques et gagnent.
   ========================================================================== */

b[data-jalon],
strong[data-jalon],
em[data-jalon],
mark[data-jalon],
.echo[data-jalon] { color: var(--chaud); }


/* =============================================================================
   4 bis. LA BALANCE QUI NE PENCHE PAS
   -------------------------------------------------------------------------
   Variante de `.balance` (OPPOSER), ajoutée le 27 août 2026 pour la page où
   le bureau doit trancher.

   La balance du concept penche d'un côté, puis de l'autre, puis revient : elle
   démontre que le même poids ne pèse pas pareil selon la main. Ici les deux
   plateaux ne sont pas deux points de vue sur un objet, ce sont **deux options
   offertes à la salle** — et l'orateur restitue l'arbitrage, il ne le rend pas.
   Un fléau incliné répondrait à la place du bureau.

   LES TROIS TEMPS RESTENT, et c'est ce qui compte pour le contrôle des modèles
   appauvris : un plateau, l'autre, puis le fléau. Ce qui change, c'est que les
   deux premiers ne l'inclinent plus, et que le troisième le fait CHERCHER son
   équilibre pour le retrouver droit. C'est la seule figure du deck qui s'anime
   sans rien déplacer : le mouvement dit qu'un équilibre a été cherché, pas
   trouvé.

   ⚠ AUCUN `data-ton="chaud"` SUR LES VOLETS, contrairement au concept. Le
   concept réserve le chaud au volet qu'on entend le moins ; ici les deux
   options sont de même rang, et colorer l'une pencherait par la couleur ce que
   la figure refuse de pencher par le fléau. Le seul jalon est sur le pivot.
   ========================================================================== */

.balance--droite .balance__fleau,
.slide[data-etat="vecu"] .balance--droite .balance__fleau,
.slide[data-etat="evalue"] .balance--droite .balance__fleau { transform: rotate(0deg); }

@media (prefers-reduced-motion: no-preference) {
  /* 800 ms, bande de démonstration : le geste EST le contenu. L'oscillation
     d'ambiance reprend ensuite, avec le même retard que partout. */
  .slide.est-active[data-etat="rendue"] .balance--droite .balance__fleau {
    animation: balance-chercher 800ms var(--ease-io) both,
               balance-osciller 6s var(--ease-io) 900ms infinite;
  }
}
@keyframes balance-chercher {
  0%   { transform: rotate(0deg); }
  30%  { transform: rotate(-2.2deg); }
  62%  { transform: rotate(1.6deg); }
  84%  { transform: rotate(-0.7deg); }
  100% { transform: rotate(0deg); }
}
@media print {
  /* Chrome rend la première image, qui est ici la bonne : le fléau droit. */
  .balance--droite .balance__fleau { animation: none !important; transform: rotate(0deg) !important; }
}


/* --- LA RÉUNION QUAND ELLE NE CONCLUT PAS PAR LA COULEUR ------------------
   `.volets--reunion[data-ton="froid"]`, ajouté le 27 août 2026.

   Le préset met l'accolade et le mot en chaud : le regroupement y est un
   FAIT SOCIAL, et le chaud le désigne. Ici les trois volets sont trois
   chemins de méthode qui convergent, et chauffer l'accolade ferait conclure
   la figure par la couleur ce qu'elle doit conclure par le mouvement.
   Le geste reste le même, il cesse de désigner un coupable.                 */
.volets--reunion[data-ton="froid"] .volets__trace { stroke: var(--brand-primary); }
.volets--reunion[data-ton="froid"] .volets__mot { color: var(--brand-primary); }

/* --- LE TRI DES USAGES — trois natures, et le chaud reste libre -----------
   Le préset colore son troisième registre en chaud. Sur la page « nouveau ET
   prioritaire », le chaud est déjà pris par le jalon (« l'angle mort le plus
   net »), et un seul jalon par slide ne se négocie pas. Les trois filets se
   distinguent donc en froid, navy et neutre — trois natures, aucune hiérarchie
   de couleur. Ajouté le 27 août 2026.                                        */
.tri--usages .tri__registre[data-registre="mobiliser"] { border-top-color: var(--brand-primary); }
.tri--usages .tri__registre[data-registre="mobiliser"] > b { color: var(--brand-primary); }
.tri--usages .tri__registre[data-registre="suivre"] { border-top-color: var(--muted); }
.tri--usages .tri__registre[data-registre="suivre"] > b { color: var(--muted); }

/* Le croisement — « à la fois nouveau ET prioritaire ». Les trois lignes de
   convergence s'allument ENSEMBLE, à leur propre temps : un titre qui promet
   un croisement doit le faire jouer, sinon trois registres en cascade disent
   trois choses séparées (SCHEMAS.md §3). */
.tri--usages .tri__reponse { opacity: var(--arrivee-opacite); transition: opacity var(--dur-3) var(--ease-out); }
.slide[data-etat="croise"] .tri--usages .tri__reponse { --arrivee-opacite: 1; }

/* --- LE RELEVÉ DE CITATIONS — la proposition à gauche, ce qu'on y reconnaît
   à droite. Ajouté le 27 août 2026.

   Le préset met une CLÉ courte à gauche et son développement à droite. Ici
   c'est l'inverse : à gauche une proposition dans ses mots, qui prend la
   place, et à droite le champ qu'on y reconnaît, qui tient en une ligne.
   Colonnes inversées, la citation en serif italique — c'est une parole, pas
   un intitulé. Sans ça, quatre verbatims dans une colonne de 260 px
   débordaient le corps de 161 px, mesurés dans la page.                     */
.releve--citations .releve__item { grid-template-columns: 1fr 320px; align-items: start; padding: var(--space-2) 0; }
.releve--citations .releve__cle {
  color: var(--ink);
  font-size: var(--fs-400);
  font-style: italic;
  letter-spacing: 0;
  line-height: 1.35;
}
.releve--citations .releve__texte {
  color: var(--froid);
  font-family: Inter, system-ui, sans-serif;
  font-size: var(--fs-200);
  line-height: 1.4;
}


/* =============================================================================
   LA MÉTHODE DESCEND AU PIED — le périmètre reste au chiffre
   -------------------------------------------------------------------------
   Retour de Nicolas, 27 août 2026 : « les précisions méthodologiques
   pourraient être mises là en plus petit, parce que ça écrase un peu la
   lecture ». Mesuré : 18 pages portaient un `.scope-note` de plus de
   90 signes, 4 302 signes en tout, et 19 pages portaient les CINQ blocs —
   titre, chapô, figure, périmètre, source. Même silhouette partout.

   La cause est dans la forme du `.scope-note` : même corps que la source
   (--fs-100), mais CAPITALES, graisse 800, interlettrage 0,06 em et filet
   navy. À 240 signes, ça ne colle plus à un chiffre, ça crie un paragraphe.

   LE PARTAGE. Le `.scope-note` garde ce que la charte lui confie et rien de
   plus : le PÉRIMÈTRE, ce que le chiffre compte et sur quelle base, collé au
   chiffre. Ce qui relève de la MÉTHODE — un recalcul, une convention, une
   unité qu'on ne peut pas soustraire, un ordre de composition — descend au
   pied avec la source, en bas de casse.

   Ce n'est pas un renvoi : la précision reste intégralement lisible sur la
   slide qu'elle concerne. C'est la charte, nuance de lecture comprise.
   ========================================================================== */

/* @famille source-note · hors-vocabulaire */
.source-note > p { margin: 0; }
/* @famille methode · hors-vocabulaire */
.source-note .methode { font-style: italic; opacity: .82; line-height: 1.35; }
/* Sur navy la source est déjà à 86 % de blanc : un second voile la ferait
   passer sous le seuil de contraste d'un petit texte. */
.slide[data-fond="navy"] .source-note .methode { opacity: .95; }


/* --- LES QUATRE FIGURES DU DECK PORTENT LEUR NOM, ET IL SERT ---------------
   Trouvé à la passe du vocabulaire, le 27 août : `nuage`, `niveaux`,
   `reclassement` et `focale` étaient écrits dans le HTML et employés par
   aucune règle. Une étiquette inerte n'est pas du vocabulaire — et le
   catalogue les rangeait « employée, non documentée » sans qu'on sache si
   c'était la doc ou la classe qui manquait. C'était la classe.

   Le réglage est celui qu'un <svg> de figure demande de toute façon : occuper
   la largeur de son corps et se mettre à l'échelle de son viewBox. */
/* @famille nuage · registre mixte */
/* @famille niveaux · registre nommé */
/* @famille reclassement · registre nommé */
.nuage, .niveaux, .reclassement { display: block; width: 100%; height: auto; }
/* @famille focale · registre nommé */
.focale { display: block; }

/* --- LE MOUVEMENT RÉDUIT DOIT VOIR LE TRACÉ, PAS LE VIDE -------------------
   Non-négociable 6 : « prefers-reduced-motion coupe le mouvement, pas le
   raisonnement. La règle [data-etat] pose les valeurs FINALES ; l'animation ne
   fait que le trajet. » Six tracés y échappaient, tous de la même façon : leur
   règle d'écran pose le DÉPART (stroke-dashoffset: 1, trait invisible) et leur
   arrivée n'existe que dans les @keyframes et sous @media print.

   `charte/charte.css` coupe toute animation en mouvement réduit. Ces six
   figures y restaient donc à leur premier état : un anneau vide, deux rails
   blancs, une boucle non tracée. La démonstration disparaissait entièrement
   pour qui a réglé son système ainsi — le contraire exact de ce que le
   non-négociable promet.

   Ce bloc RECOPIE l'arrivée que @media print déclare déjà, mais seulement
   DANS L'ÉTAT qui l'a atteinte. Le papier montre la slide finie ; l'écran sans
   mouvement doit encore respecter son raisonnement par fragments. Une règle
   globale révélerait la conclusion avant le clic. Trouvé le 31 août par le
   CSSOM du navigateur ; `verifier.py` le mesure désormais, pour qu'un septième
   ne s'ajoute pas. */
@media (prefers-reduced-motion: reduce) {
  .slide:is([data-etat="alimentation"], [data-etat="influence"]) .radial__anneau {
    stroke-dashoffset: 0;
  }
  .slide[data-etat="impact"] .noyau__trait { stroke-dashoffset: 0; }
  .slide[data-etat="impact"] .noyau__pointe,
  .slide[data-etat="impact"] .noyau__tenir { opacity: 1; }

  .slide:is([data-etat="tiers"], [data-etat="contexte"]) .piece__nom--tiers {
    opacity: 1;
  }
  .slide:is([data-etat="tiers"], [data-etat="contexte"]) .piece__tiers {
    opacity: .55;
  }
  .slide[data-etat="contexte"] .piece__contrainte { opacity: 1; }

  .slide[data-etat="tracee"] .frontiere__verdict--vraie { opacity: 1; }

  /* Le rail du vécu appartient à l'état initial ; le second attend `cadre`. */
  .carriere__axe[data-rail="vecu"] .carriere__trait { stroke-dashoffset: 0; }
  .slide:is([data-etat="cadre"], [data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"])
    .carriere__axe[data-rail="cadre"] .carriere__trait { stroke-dashoffset: 0; }
  .slide:is([data-etat="cadre"], [data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"])
    .carriere__cadre { opacity: 1; transform: none; }
  .slide:is([data-etat="ecarts"], [data-etat="loupe-enfant"], [data-etat="loupe-26"], [data-etat="loupe-menopause"], [data-etat="ensemble"])
    .carriere__station[data-ecart] .carriere__ecart {
    opacity: 1;
    transform: scaleY(1);
  }

  .slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__voie,
  .slide:is([data-etat="menopause"], [data-etat="chute"]) .croisee .croisee__voie {
    stroke-dashoffset: 0;
  }
  .slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__pointe,
  .slide[data-etat="preservatif"] .croisee[data-cle="preservatif"] .croisee__terme:is([data-role="gain"], [data-role="arrivee"]),
  .slide:is([data-etat="menopause"], [data-etat="chute"]) .croisee .croisee__pointe,
  .slide:is([data-etat="menopause"], [data-etat="chute"]) .croisee .croisee__terme:is([data-role="gain"], [data-role="arrivee"]),
  .slide[data-etat="chute"] .croisees__chute {
    opacity: 1;
  }
  .slide[data-etat="preservatif"] .glissement[data-cle="preservatif"] .glissement__tige,
  .slide:is([data-etat="menopause"], [data-etat="chute"]) .glissement .glissement__tige,
  .slide:is([data-etat="vote"], [data-etat="requalifie"]) .glissements--rangs .glissement__tige {
    stroke-dashoffset: 0;
  }
  .slide[data-etat="preservatif"] .glissement[data-cle="preservatif"] .glissement__pointe,
  .slide[data-etat="preservatif"] .glissement[data-cle="preservatif"] .glissement__terme[data-role="reel"],
  .slide:is([data-etat="menopause"], [data-etat="chute"]) .glissement .glissement__pointe,
  .slide:is([data-etat="menopause"], [data-etat="chute"]) .glissement .glissement__terme[data-role="reel"],
  .slide:is([data-etat="vote"], [data-etat="requalifie"]) .glissements--rangs .glissement__pointe,
  .slide:is([data-etat="vote"], [data-etat="requalifie"]) .glissements--rangs .glissement__terme[data-role="reel"] {
    opacity: 1;
    transform: none;
  }
  .slide[data-etat="chute"] .glissements__chute {
    opacity: 1;
    transform: none;
  }

  .slide:is([data-etat="aller"], [data-etat="retour"])
    .boucle--menue .boucle__trace[data-arc="aller"],
  .slide[data-etat="retour"] .boucle--menue .boucle__trace[data-arc="retour"] {
    /* Le mode courant cache ces arcs avec !important pour laisser la comète
       les dessiner ; sans la même force, le mode réduit resterait invisible. */
    opacity: 1 !important;
    stroke-dashoffset: 0;
  }
}


/* --- LE SOCLE — ce qui est établi, avant ce qui se discute ----------------
   Une slide qui pose une question doit dire, AVANT, sur quoi elle s'appuie.
   Sans ce partage, l'auditoire lit la question comme une thèse déguisée, et
   c'est exactement ce que le ton analytique refuse.

   Le socle est de l'APPAREIL, pas une démonstration : des jalons, rien qu'eux,
   sans axe ni échelle de temps. Il ne prétend pas à la frise — `.chrono` est
   là pour ça, et elle demande un axe qu'une énumération de repères n'a pas.
   Ce qui distingue les deux registres à l'œil : le socle est en Inter, corps
   réduit, sur un filet ; ce qui se discute reste en Georgia, au corps de la
   chute. Une différence de FAMILLE, pas seulement de taille — une taille seule
   se lit comme une hiérarchie, une famille se lit comme un statut.

   Les jalons non datés ne sont pas des oublis : une institution qui dure n'a
   pas la date d'un événement. Leur `<b>` est simplement absent.             */
/* @famille socle · verbe RÉCAPITULER · registre champ */
.socle {
  flex: none;                        /* de l’appareil : il ne se laisse pas comprimer */
  margin: 0 0 var(--space-2);
  padding-top: var(--space-2);
  border-top: 2px solid var(--brand-accent);
}
.socle__quoi {
  margin: 0 0 var(--space-2);
  color: var(--muted);
  font-size: var(--fs-100);
  font-weight: 800;
  letter-spacing: 0.08em;
  line-height: 1.35;
  text-transform: uppercase;
}
.socle__jalons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.socle__jalons > li {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  padding-left: var(--space-2);
  border-left: 1px solid var(--line-strong);
}
.socle__jalons > li > b {
  color: var(--brand-primary);
  font-size: var(--fs-100);
  font-weight: 800;
}
.socle__jalons > li > span {
  color: var(--muted);
  font-size: var(--fs-100);
  line-height: 1.3;
}
/* Un jalon pris hors du corpus national se marque, sinon il se lit comme une
   confusion dans une chronologie française. L’italique le dit sans glose. */
.socle__jalons > li[data-ailleurs] > span { font-style: italic; }

/* Ce qui se discute porte l'interrogation dans sa forme : italique, et la
   couleur retenue du commentaire — la chute affirmative, elle, garde le navy
   droit du titre. Deux registres, deux voix.                                */
.substitution__chute[data-ton="question"] {
  color: var(--muted);
  font-style: italic;
}


/* --- La mention réglementaire d'un financeur ------------------------------
   Elle n'est ni une source ni un périmètre : c'est un texte imposé, qu'on ne
   réécrit pas et qu'on ne résume pas. Elle se cale au pied comme la référence,
   avec le même filet et le même dégagement pour le numéro de slide, et au plus
   petit corps de l'échelle — la lire n'est pas l'objet de la slide, la voir
   l'est.                                                                    */
.slide > .interets__mention {
  margin: 0;
  padding-top: var(--space-3);
  padding-right: 116px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--fs-100);
  line-height: 1.4;
  text-align: justify;
}


/* --- LA CONDUITE — la ligne qui dit ce qu'on en fait ----------------------
   Une slide de constat laisse la salle avec un fait et rien à en faire. Le
   `.repere` navy existe pour ça, mais il occupe une slide entière : il est
   hors de portée d'un constat qui n'en mérite pas une.

   La conduite est la version en une ligne, au pied, en Inter et au corps de
   l'appareil. Elle ne concurrence pas la chute : la chute est ce qu'on retient
   (Georgia, grand, centré), la conduite est ce qu'on fait (Inter, petit,
   aligné à gauche). Deux registres, deux familles de caractères — la
   différence se lit avant d'être lue.

   Elle se cale au pied comme la référence, et dégage le numéro de slide.   */
/* @famille conduite · verbe ÉNONCER · registre champ */
.slide > .conduite {
  margin: 0 0 var(--space-3);
  padding-left: var(--space-3);
  border-left: 3px solid var(--brand-accent);
  color: var(--brand-primary);
  font-size: var(--fs-200);
  font-weight: 600;
  line-height: 1.4;
}
.slide[data-fond="navy"] > .conduite { color: rgba(255, 255, 255, .9); }


/* LE CHAPEAU DE LA COLONNE — le concept qui coiffe les ressources.
   Retour de Nicolas, 10 septembre : posé en quatrième item, le capital
   culturel redisait mot pour mot ce que les trois autres lignes disaient
   déjà — « lire un résultat » y figurait deux fois. Ce n'est pas une
   ressource de plus, c'est le nom de ce qui les distribue. Il coiffe donc la
   colonne au lieu d'y prendre rang, et s'illustre par ce qui ne se mesure
   pas : l'aisance avec l'écrit, la familiarité avec le raisonnement médical,
   le fait de se sentir autorisée à contredire.
   Il n'est PAS un fragment : un chapeau pose la grille, il ne s'y ajoute pas
   — et les trois clics restent trois, comme la note du deck le dit. */
.exigence__chapeau {
  width: 62%;
  margin: var(--space-4) 0 0 auto;   /* même colonne que les ressources */
  font-size: var(--fs-300);
  line-height: 1.45;
  color: var(--muted);
}
.exigence__chapeau b {
  color: var(--brand-primary);
  font-family: Georgia, "Times New Roman", serif;
  font-size: var(--fs-400);
  font-weight: 400;
}
/* =============================================================================
   RÉSERVE SCÉNIQUE · prototypes à valider humainement
   Ces deux familles reprennent des principes de profondeur et de propagation
   observés dans ThreeUI, sans copie de code, d'asset ni de géométrie.
   ========================================================================== */

/* @famille arc-predictif · verbe ANTICIPER · registre nommé */
.arc-predictif {
  position: relative;
  min-height: 340px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background-color: var(--surface-tint);
}
.arc-predictif svg { width: 100%; height: 340px; }
.arc-predictif__axe,
.arc-predictif__futur {
  fill: none;
  stroke: var(--data-cool);
  stroke-linecap: round;
}
.arc-predictif__axe { stroke-width: 5; }
.arc-predictif__futur {
  stroke-width: 4;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  opacity: .18;
  transition: stroke-dashoffset 1100ms var(--ease-io), opacity 620ms var(--ease-io);
}
.arc-predictif__borne { fill: var(--data-neutral); stroke: var(--muted); stroke-width: 2; }
.arc-predictif__signal { fill: var(--data-warm); opacity: 0; transition: opacity 420ms var(--ease-out) 620ms; }
.arc-predictif__texte { fill: var(--muted); font: 800 14px Inter, Arial, sans-serif; letter-spacing: .08em; }
.slide[data-etat="anticipe"] .arc-predictif__futur { stroke-dashoffset: 0; opacity: 1; }
.slide[data-etat="anticipe"] .arc-predictif__signal { opacity: 1; }

/* @famille resonance · verbe PROPAGER · registre nommé */
.resonance {
  position: relative;
  min-height: 340px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background-color: var(--surface-tint);
}
.resonance svg { width: 100%; height: 340px; }
.resonance__liaison { fill: none; stroke: rgba(88,101,122,.34); stroke-width: 3; }
.resonance__position { fill: var(--paper-bright); stroke: var(--data-cool); stroke-width: 3; }
.resonance__onde {
  fill: none;
  stroke: var(--data-warm);
  stroke-width: 5;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(.16);
  transition: transform 1100ms var(--ease-io), opacity 420ms var(--ease-out);
}
.resonance__texte { fill: var(--muted); font: 800 14px Inter, Arial, sans-serif; letter-spacing: .06em; }
.slide[data-etat="propage"] .resonance__onde { opacity: .84; transform: scale(1); }

@media print, (prefers-reduced-motion: reduce) {
  .arc-predictif__futur { stroke-dashoffset: 0 !important; opacity: 1 !important; }
  .arc-predictif__signal { opacity: 1 !important; }
  .resonance__onde { opacity: .84 !important; transform: scale(1) !important; }
}
