/* ============================================================================
   <NOM DU PROJET> — main.css

   ┌──────────────────────────────────────────────────────────────────────────┐
   │ CECI N'EST PAS UNE DIRECTION ARTISTIQUE. C'EST LE SQUELETTE ACCESSIBLE.  │
   │                                                                          │
   │ Ce fichier porte ce qui est VRAI POUR TOUT PROJET et qui a été payé au    │
   │ moins une fois dans la flotte : les trois états de thème et leur          │
   │ `color-scheme`, les règles `.no-js`, la sortie d'un panneau de            │
   │ l'arborescence d'accessibilité, l'anneau de focus, le lien d'évitement,   │
   │ le triple garde-fou du mouvement, l'ossature de mise en page.             │
   │                                                                          │
   │ IL NE PORTE AUCUNE COULEUR DE MARQUE, AUCUNE POLICE, AUCUN GESTE DE       │
   │ STYLE. Les jetons de `:root` sont des NEUTRES DÉLIBÉRÉS : ils rendent le  │
   │ gabarit lisible et conforme dès le premier build, et ils sont là pour     │
   │ être REMPLACÉS. Un projet neuf reçoit sa DA d'un Fable, qui réécrit la    │
   │ section « LA DA DU PROJET » en bas de ce fichier et les valeurs de        │
   │ `:root` — et rien d'autre.                                               │
   │                                                                          │
   │ ⚠ SIX FEUILLES DE LA FLOTTE NE PARTAGENT PAS UN OCTET (443 Kio, 15        │
   │ règles de recoupement maximal entre deux projets) : chaque correction     │
   │ d'accessibilité se fait donc six fois à la main. C'est ce que ce fichier  │
   │ existe pour arrêter. Ce qui est ici se corrige UNE fois et se recopie.    │
   └──────────────────────────────────────────────────────────────────────────┘

   PLAN
     1. LES JETONS ET LES TROIS ÉTATS DE THÈME   (dont `color-scheme`)
     2. LES PRÉFÉRENCES DU PANNEAU               (taille, aération, contraste…)
     3. LE MOUVEMENT, TRIPLE-GARDÉ
     4. LE SOCLE                                 (reset, [hidden], focus, .vh)
     5. SANS JAVASCRIPT                          (`.sans-js`)
     6. L'OSSATURE DE PAGE                       (bandes, .wrap, tête, pied)
     7. LE PANNEAU                               (et sa sortie de l'a11y)
     8. LES COMPOSANTS DU MOTEUR DE BLOCS
     9. L'IMPRESSION
    10. LA DA DU PROJET — LA SEULE SECTION À ÉCRIRE
   ========================================================================== */

/* ==========================================================================
   1. LES JETONS ET LES TROIS ÉTATS DE THÈME

   ⚠ UN SEUL BLOC `:root` DE PREMIER NIVEAU, ET C'EST VOULU. Quatre feuilles de
   la flotte en déclarent deux à quatre, dont certaines à l'intérieur d'un
   `@media` : c'est légal en CSS, mais tout outil qui lit la feuille par
   `split(":root {")` prend alors le PREMIER morceau et fait retomber un état de
   thème en silence sur le thème clair. `tools/lib-contraste-*.js` lit la feuille
   avec un parseur à pile et n'est pas piégé ; un seul bloc reste néanmoins la
   forme la plus lisible pour un humain, et elle se tient sans effort.
   ========================================================================== */
:root {
  /* --- LES SURFACES — L'ARCHÉTYPE A CHANGÉ LE 06/08/2026 AU SOIR ----------
     🔴 VERDICT DE MAEL SUR LA MAQUETTE « ÉDITION SAVANTE » CORRIGÉE : « pas fan
     du tout, changement de design complet, plus clair, plus attirant, des vrais
     livres, vrai système quasi Amazon ». L'ARCHÉTYPE N'EST PLUS UNE BIBLIOTHÈQUE
     SAVANTE, C'EST UNE LIBRAIRIE EN LIGNE, et ce n'est pas un habillage : c'est
     un renversement de ce qui porte la page. Avant, la page portait de la PROSE
     sur une matière de papier. Maintenant elle porte des OBJETS — des
     couvertures — et la matière a disparu pour leur laisser la lumière.

     TROIS SURFACES AU LIEU D'UNE, ET C'EST TOUTE LA MÉCANIQUE :
       · `--fond`    la page : un blanc chaud et NET. Le grain est mort.
       · `--fond-2`  LE PLANCHER : la surface, un cran plus sourde, sur laquelle
                     les grilles reposent. C'est elle qui fait que les cartes se
                     LÈVENT au lieu de flotter dans du même.
       · `--carte`   le blanc FRANC d'une carte, d'une fiche, d'un feuillet.
     Une couverture colorée posée sur du blanc franc, lui-même posé sur un
     plancher plus sourd : voilà les trois valeurs qui font lire « un livre sur
     une table ». C'est le code de toute vitrine, et il ne coûte qu'un jeton.

     ⚠ LE GRAIN DE PAPIER VERGÉ EST RETIRÉ, ET C'EST LA DÉCISION LA PLUS
     STRUCTURELLE DE CETTE PASSE. Il a été payé deux fois (un dessin SVG, un
     jeton, neuf couples de contraste, une leçon sur `mask-image`), et il servait
     l'archétype exact que Mael vient de refuser : une matière de fond qui parle
     d'elle-même vole l'attention aux couvertures, qui sont maintenant le sujet.
     Une matière au service d'une prose austère devient un bruit devant une
     grille de couvertures. On ne l'atténue pas, on l'enlève : la retenir « au
     cas où » serait garder le coût sans le bénéfice. Le dessin reste dans
     l'historique du dépôt, il ne reste pas dans la feuille.
     ⚠ CONSÉQUENCE MESURÉE, ET ELLE VA DANS LE BON SENS : les neuf couples « sur
     la page grainée » disparaissent de `check-contraste.js`, et `--filet` peut
     REMONTER en clarté (il avait été assombri de `#D8D8D2` à `#C9C1AE` pour
     tenir son plancher sous un grain à 11 %). Un filet plus clair sur une page
     plus claire : c'est exactement ce que « plus clair » voulait dire. */
  /* --- 🔴 PASSE V2, 07/08/2026 : « PALETTE VERT CLAIR ET BLANC » (verbatim Mael)
     Verdict sur la maquette LIBRAIRIE : « direction bonne, mais y'a encore un
     sacré boulot », et l'ordre chromatique est explicite — ÉCLAIRCIR TOUTE
     L'AMBIANCE, blanc dominant, un VERT FRAIS en accent à la place du teal
     sombre. Les trois surfaces restent (c'est elles qui font l'objet posé), mais
     elles quittent la famille CHAUDE (blanc ivoire, plancher beige, garde
     ficelle) pour la famille VERTE PÂLE : le blanc devient franc, et le plancher
     devient le vert clair lui-même. C'est là que « vert clair et blanc » se joue
     — sur la SURFACE et non sur l'accent, parce qu'une page est faite de
     surfaces à 95 % et d'accent à 5 %. Repeindre le seul accent en vert clair
     aurait donné un site beige avec un bouton vert.
     ⚠ ET LE BLANC A GAGNÉ DU TERRAIN, MESURÉ : la page passe de 0,946 de
     luminance à 0,964, la carte reste le blanc pur, et le plancher monte de
     0,824 à 0,851. Il n'y a plus une seule surface chaude dans la feuille. */
  --fond: #FBFDF9;           /* la page : blanc franc, un souffle de vert      */
  --fond-2: #EDF3E7;         /* LE PLANCHER : LE VERT CLAIR, la surface-mère   */
  --fond-3: #E2E7DB;         /* le papier de garde : barres d'outils, colophon */
  /* 🔴 LE JETON NEUF DE CETTE DA, ET IL PORTE TOUT LE RESTE. Un catalogue sans
     surface de carte n'a que des filets pour dire où un objet commence : c'est
     ce que faisait l'ancienne maquette, et c'est pourquoi elle se lisait comme
     une suite de paragraphes plutôt que comme un rayon. */
  --carte: #FFFFFF;
  /* L'ENCRE SUIT LA FAMILLE, D'UN CHEVEU, ET C'EST TOUT CE QU'ELLE FAIT. Un
     noir bleuté sur une page verte se voit : les deux températures se battent
     dans les grands aplats de texte. Ces trois valeurs ne sont pas « du vert » :
     ce sont des noirs et des gris dont le point neutre a tourné vers la famille,
     invisibles seuls, justes ensemble.
     🔴 ET ELLES ONT ÉTÉ NEUTRALISÉES APRÈS UNE CAPTURE, PAS APRÈS UN CALCUL.
     Premier jet : `#2F3529` et `#5A6153`, douze et quatorze crans d'écart entre
     le canal vert et le canal bleu. Aucun verrou n'a rien dit (ils tenaient
     12,35:1 et 6,27:1), et la capture du catalogue a montré une prose
     franchement OLIVE : à mi-clarté, une chroma qui ne se voit pas sur un noir se
     voit sur un gris. Le remède est mesuré à l'œil et vérifié à la sonde
     (`getComputedStyle`, pas la feuille) : cinq à sept crans au lieu de douze à
     quatorze. Un texte courant est NEUTRE ; c'est la surface qui porte la teinte,
     jamais l'encre qui la lit. */
  --encre: #171A15;          /* 17,03:1 sur --fond · 17,42:1 sur --carte       */
  --encre-2: #32362F;        /* 12,04:1 · 12,32:1                              */
  --encre-3: #5D6159;        /* 6,13:1 · 6,27:1 — filets, pictos, GRAND texte  */
  /* ⚠ LES DEUX FILETS ONT DES RÔLES DISTINCTS, ET C'EST NEUF. Le premier est un
     trait de COMPOSITION (il sépare, il ne borde rien de cliquable) : son seuil
     est un plancher de distinction. Le second borde un COMPOSANT (WCAG 1.4.11)
     et il lui faut 3:1 — sur les QUATRE surfaces, page, plancher, carte et
     papier de garde, ce qui fait quatre couples pour un seul jeton. Séparer les
     deux rôles est ce qui permet à une carte de porter un filet DISCRET sans
     qu'un bouton perde sa limite lisible. */
  /* ⚠ ASSOMBRI PAR LA MESURE, PAS PAR LE GOÛT, ET C'EST LA DEUXIÈME FOIS QUE CE
     JETON SE FAIT REFUSER. Posé à `#D8D4C9`, il tenait 1,43:1 sur la page et
     1,48:1 sur la carte mais tombait à 1,28:1 sur LE PLANCHER, sous son plancher
     de 1,30. C'est le même mécanisme qu'en phase B : un jeton mesuré sur sa seule
     surface la plus favorable est un filet qu'on publie invisible ailleurs. Avec
     quatre surfaces au lieu de trois, l'erreur est quatre fois plus facile. */
  /* ⚠ TROISIÈME ASSOMBRISSEMENT DE CE JETON, ET LA MESURE L'A ENCORE IMPOSÉ.
     Posé à `#D3DBC9`, il tenait 1,44:1 sur la page et 1,48:1 sur la carte mais
     tombait à 1,26:1 sur LE PLANCHER, sous son plancher de 1,30 — parce que le
     plancher a MONTÉ en clarté avec le passage au vert clair. Le mécanisme est
     le même à chaque fois : éclaircir une surface resserre le filet qui la
     borde. Un jeton mesuré sur sa seule surface la plus favorable est un filet
     qu'on publie invisible ailleurs. */
  --filet: #CED7C3;          /* 1,45 page · 1,49 carte · 1,31 plancher · 1,20 garde */
  --filet-2: #7C8471;        /* COMPOSANT : 4,32 page · 4,42 carte · 3,92 plancher · 3,52 garde */

  /* --- 🔴 L'ACCENT UNIQUE : LE DUO DE VERTS -------------------------------
     ORDRE DE MAEL, 07/08/2026 : « palette vert clair et blanc », un VERT FRAIS
     à la place du teal sombre. Et l'ordre porte lui-même la difficulté : UN VERT
     CLAIR NE TIENT PAS EN TEXTE. Un vert assez clair pour être « frais » vaut
     autour de 0,60 de luminance, donc 1,6:1 sur du blanc : il ne peut porter ni
     un lien, ni un libellé, ni un filet de composant. Un vert assez sombre pour
     porter du texte n'est pas « frais », c'est un vert de forêt.

     LA RÉPONSE EST UN DUO, ET C'EST UN SEUL ACCENT — pas deux.
     La famille compte cinq membres d'une MÊME teinte (104-106°), du plus pâle au
     plus profond, et chacun a UN emploi nommé :
       · `--accent-fond`   #DFF0D9  la nappe : fond de note, de cartouche, de bande
       · `--accent-clair`  #B5DCA8  🆕 LE VERT FRAIS : aplats, étiquettes, pastilles,
                                    puces de filtre, remplissages de badge. Il ne
                                    porte JAMAIS de texte, il en REÇOIT.
       · `--accent`        #285A18  l'encre d'accent : liens, filets courts,
                                    aplat d'acte primaire, anneau de focus
       · `--accent-survol` #1B3D0F  le survol, l'appui
       · `--reliure`       #13260C  le plat, la fascia (voir plus bas)
     ⚠ CE N'EST PAS UN SECOND ACCENT, et le précédent est écrit dans cette
     feuille depuis le 06/08 : « une SURFACE n'est pas un ACCENT », posé pour la
     reliure, qui est le membre le plus PROFOND de la famille. `--accent-clair`
     est le membre le plus SUPERFICIEL de la même famille, et l'argument est
     exactement le même, dans l'autre sens. La preuve tient en un chiffre : les
     cinq membres sont à MOINS DE 2° de teinte les uns des autres. Deux accents
     seraient deux familles ; ici il y a une famille et cinq valeurs.
     ⚠ ET LA FRONTIÈRE D'EMPLOI EST CE QUI L'EMPÊCHE DE DEVENIR UN SECOND ACCENT :
     `--accent-clair` est une SURFACE. Il ne peint aucun texte, aucun filet de
     composant, aucun état. Ce qu'il porte se lit en `--encre` (11,59:1).

     LES MESURES, ET ELLES SONT MEILLEURES QUE CELLES DU TEAL :
     `--accent` #285A18, teinte 105,5°, saturation 73 %, luminance 0,0783.
     8,00:1 sur la page · 8,18:1 sur la carte · 7,24:1 sur le plancher ·
     6,50:1 sur le papier de garde. Il tient donc en TEXTE COURANT sur les quatre
     surfaces, ce qui est la seule façon d'avoir un accent unique sans en
     inventer un second pour les liens.

     🔴 ET IL SORT DE TOUTES LES FENÊTRES DU REGISTRE DE FLOTTE PAR LA TEINTE
     SEULE — LES NEUF, ET LES CINQ MEMBRES. C'est la première fois dans ce projet.
     Rappel de l'histoire : le vert de reliure `#1D4A33` de la phase A vivait à
     10° de l'émeraude de *Clic par clic* et ne passait QUE par sa valeur (72 %
     d'écart de clarté), c'est-à-dire par la seconde jambe de la règle ; le teal
     `#0A5A63` sortait par la teinte de l'émeraude (26,9°) et du bleu Europe
     (31,8°). Ce vert-ci, mesuré membre par membre :
       émeraude #0E8A5F (Clic par clic)     53,7° de teinte  (fenêtre : 20°)
       vert de contrôle #2F9E44 (L'établi)  25,9°
       ocre doré #7E4E10 (prog. Savoirs)    71,6°
       jaune surligneur #FFD84D             58,6°
       jaune de signal #FFD500              55,3°
       orange outil #D9480F                 88,5°
       rouge pompier #E2001A               112,4°
       rouge institutionnel #8C1515        105,5°
       bleu Europe #1B4FA8                 112,4°
     Le plus serré est le vert de contrôle de *L'établi* à 25,9°, et il reste à
     6° au-delà de la fenêtre AVEC 69 % d'écart de clarté par-dessus. La sûreté
     ne dépend donc d'aucun écart de valeur qu'un futur ajustement pourrait
     grignoter : c'est la propriété que le teal avait apportée, et elle est
     conservée en passant au vert. Le détail des mesures est au `CLAUDE.md`.
     ⚠ L'ocre doré du programme Savoirs reste honoré par le NOM au bandeau et
     jamais par la couleur. Le vert ne le prétend pas plus que le teal : ce qui
     rattache cette maison au programme est son NOM, écrit sur chaque page. */
  --accent: #285A18;         /* 8,00:1 sur --fond · 8,18:1 sur --carte         */
  --accent-survol: #1B3D0F;  /* 12,54:1                                        */
  /* 🆕 LE VERT FRAIS. Une SURFACE, jamais une encre. Voir la frontière ci-dessus. */
  --accent-clair: #B5DCA8;   /* --encre dessus : 11,59:1 · 1,53:1 sur la carte  */
  --accent-fond: #DFF0D9;    /* la nappe d'appui d'une note, d'un cartouche     */
  /* ⚠ LE LIBELLÉ D'ACTE EST UN JETON, JAMAIS UN BLANC EN DUR : l'accent se
     rééclaircit au thème sombre, où du blanc tomberait sous son seuil. */
  --acte-txt: #FFFFFF;       /* 8,18:1 sur --accent                            */

  /* --- LES PLATS — LA SIGNATURE QUI RESTE, DÉPLACÉE DANS SON RÔLE.
     Le site reste RELIÉ : il finit sur la toile sombre du plat inférieur, qui
     porte le colophon. C'est la fascia de la boutique, et une boutique claire a
     le droit d'avoir une enseigne sombre.
     ⚠ CE N'EST PAS UN SECOND ACCENT : une SURFACE n'est pas un ACCENT. C'est le
     membre le plus PROFOND de la même famille verte (teinte 103,8°, à 1,7° de
     l'accent), et `audit-contenu.js` §9.1 refuse tout emploi de `--reliure*` hors
     des plats et de ce qui vit dessus.
     ⚠ ET LE PLAT SUIT L'ACCENT, PARCE QU'IL DOIT LE SUIVRE. Le 06/08 au soir, la
     rampe duotone des photographies avait dû être refaite parce qu'elle partait
     d'une toile VERTE quand l'accent était devenu TEAL : deux familles
     chromatiques sur la même page, c'est-à-dire le second accent que la charte
     interdit, arrivé par les IMAGES là où aucun verrou ne le cherche. Le même
     piège existe à l'envers : garder un plat teal sous un accent vert
     reproduirait la faute exactement. Le plat passe donc au vert le plus profond,
     ET la rampe duotone repart de lui (voir `get-images.js`).
     ⚠ EN REVANCHE LE DOS A CHANGÉ DE DOMICILE, et c'est le geste dont je suis le
     plus sûr : le filet vertical de dos ne pend plus des têtes de page ni des
     blocs, il vit sur LA COUVERTURE, où un dos existe pour de vrai. La
     signature ne s'est pas perdue, elle a rejoint l'objet qu'elle imitait. */
  --reliure: #13260C;
  --reliure-encre: #F1F3EF;      /* 15,86:1 sur --reliure                      */
  --reliure-encre-2: #C0C8BA;    /*  9,45:1                                    */
  --reliure-accent: #A8D48F;     /*  9,60:1                                    */
  --reliure-filet: #2E4426;      /*  1,50:1 : une distinction, pas une limite   */

  /* --- L'ÉTAT D'ALERTE — L'ENCRE DE CORRECTION, et elle est seule.
     Toujours aucun jeton « ok » : l'accent n'est plus vert, mais un vert de
     réussite serait cette fois un second accent SANS MÊME la parenté de teinte.
     L'alerte, elle, est employée (`.panneau__danger`, `.note--alerte`) et ne
     porte JAMAIS l'information seule : le texte le dit toujours. */
  --alerte: #8A2A18;         /* 8,37:1 sur --fond                              */

  /* --- 🔴 LES SIX TEINTES DE COUVERTURE — L'OBJET, ET JAMAIS L'INTERFACE ----
     C'EST LA DÉCISION CHROMATIQUE CENTRALE DU REDESIGN, ET ELLE TIENT PAR UNE
     FRONTIÈRE STRICTE. Mael demande « des vrais livres » et « des couvertures
     colorées qui portent la couleur ». Une couverture par rayon, chacune avec sa
     teinte : c'est ce qui fait qu'une grille de vingt volumes se lit comme un
     rayon plein et non comme un tableau.
     LA FRONTIÈRE : une teinte de couverture ne SORT JAMAIS de sa couverture.
     Ni sur un filet de carte, ni sur une étiquette, ni sur une puce de filtre,
     ni sur un titre de rayon. Une couverture est un OBJET IMPRIMÉ que la page
     montre ; l'interface, elle, n'a qu'un accent, et c'est le teal. Sans cette
     frontière, six teintes deviendraient six accents, c'est-à-dire l'interdit de
     flotte le plus grave de tous. Avec elle, elles ne sont que ce qu'elles sont :
     l'encre des imprimeurs de ces six collections.
     ⚠ ELLES VIVENT DANS LA FEUILLE ET NON DANS LE CONTENU, ET C'EST UNE
     DÉCISION. Les mettre dans les sept `categories.json` aurait dupliqué
     dix-huit hex dans sept fichiers, et surtout les aurait mis HORS DE PORTÉE de
     `check-contraste.js`, qui ne lit que cette feuille : le registre des
     interdits de flotte ne les aurait jamais mesurées. Une teinte est une
     décision de DA ; sa place est ici, où une machine la surveille.
     ⚠ AJOUTER UN RAYON demande donc DEUX gestes, et le README le dit : une
     entrée dans les sept `categories.json`, et son trio de teintes ici.
     `build.js` REFUSE une catégorie dont le trio manque à cette feuille : une
     couverture sans teinte sortirait grise et personne ne l'aurait décidé.
     ⚠ LES SIX TEINTES NE CHANGENT PAS AU THÈME SOMBRE, et c'est voulu : un
     livre imprimé ne se repeint pas quand on baisse la lampe. Le contraste
     INTÉRIEUR de la couverture (son titre sur son fond) est donc mesuré une
     seule fois et vaut dans les cinq états.
     LES DISTANCES AU REGISTRE, MESURÉES : mathematiques 248° (30° du bleu
     Europe) · langues 324° (29° du rouge pompier) · sciences 74° (24° du jaune
     ALS) · histoire 267° (50° du bleu Europe) · lire-ecrire et methode sont des
     NEUTRES (saturation 7 % et 1 %), donc ils n'identifient personne. Aucune
     n'est à moins de 62° de notre propre accent : une couverture ne peut pas se
     confondre avec un bouton. */
  --couv-mathematiques: #3A2E8A;         /* indigo · titre 9,86:1              */
  --couv-mathematiques-encre: #F5F3FC;
  --couv-mathematiques-dos: #281E63;
  --couv-lire-ecrire: #EFEADE;           /* os · titre 7,35:1                  */
  --couv-lire-ecrire-encre: #41500F;
  --couv-lire-ecrire-dos: #41500F;
  --couv-langues: #7A1F55;               /* prune · titre 8,82:1               */
  --couv-langues-encre: #FBF1F6;
  --couv-langues-dos: #59133C;
  --couv-sciences: #414F12;              /* olive · titre 8,08:1               */
  --couv-sciences-encre: #F3F5E6;
  --couv-sciences-dos: #2C360A;
  --couv-histoire: #552A88;              /* violet · titre 9,10:1              */
  --couv-histoire-encre: #F5F1FB;
  --couv-histoire-dos: #3C1C63;
  --couv-methode-de-travail: #EDEDEA;    /* la couverture nue · titre 14,39:1  */
  --couv-methode-de-travail-encre: #1B1D21;
  --couv-methode-de-travail-dos: #1B1D21;

  /* --- LA TYPOGRAPHIE — LES DEUX FAMILLES RESTENT, LEURS RÔLES S'ÉCHANGENT.
     🔴 C'EST LE SECOND RENVERSEMENT DE CETTE PASSE, ET IL NE COÛTE PAS UN OCTET.
     Avant : Literata portait TOUT ce qui était titre, folio, étiquette, chiffre
     et chapeau. Un serif d'édition sur chaque libellé d'interface, c'est
     exactement la voix « savante » que le verdict refuse.
     Maintenant, la règle est une frontière d'OBJET, comme pour les teintes :
       · LITERATA est la voix DU LIVRE. Elle écrit les titres sur les
         couvertures, les titres de volume sur les cartes, le titre d'une fiche,
         et le corps d'un chapitre. Rien d'autre.
       · NOTO SANS est la voix DE LA LIBRAIRIE. Elle écrit toute l'interface :
         navigation, rayons, filtres, étiquettes, actes, compteurs, fil de page,
         titres des pages qui ne sont pas des livres.
     ⚠ LITERATA RESTE LA DISPLAY DE LA MAISON, et la règle de flotte qui l'exige
     UNIQUE est donc intacte : aucune vérification à refaire chez les six
     voisins, aucune licence neuve, aucun fichier de plus. Elle est même PLUS
     visible qu'avant : vingt couvertures sur une page de catalogue en font une
     présence typographique que quatre titres de section ne donnaient pas.
     C'est le geste le moins cher et le plus lisible de tout ce redesign. */
  --titre: "Literata", Georgia, "Times New Roman", serif;
  --corps: "Noto Sans", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* --- L'ÉCHELLE DE TEXTE. Le corps ne descend jamais sous 16 px.
     ⚠ LES DEUX CRANS DU HAUT ONT RÉTRÉCI, ET C'EST UN CHOIX DE DENSITÉ. Un
     titre à 41,6 px au-dessus d'une grille de couvertures écrase la grille : ce
     que le visiteur doit voir en premier, ce sont les livres. La référence
     étudiée écrit ses titres de fiche à 32 px et ses titres de carte à 14 px, et
     elle a raison — dans une librairie, l'objet est plus gros que son étiquette.
     Le bas de l'échelle ne bouge pas : c'est lui qui porte la lisibilité. */
  --t-base: 1.0625rem;
  --t-xs: 0.8125rem;
  --t-s: 0.9375rem;
  --t-m: 1.125rem;
  --t-l: 1.1875rem;          /* le SEUIL du grand texte WCAG à 700 : 19 px     */
  --t-xl: clamp(1.25rem, 1.02rem + 0.9vw, 1.55rem);
  --t-2xl: clamp(1.55rem, 1.15rem + 1.9vw, 2.15rem);

  /* --- L'ESPACEMENT, multiples de 4. `--u` est l'unité : l'aération du panneau
     la multiplie, et tout suit. */
  --u: 0.25rem;
  --s1: calc(var(--u) * 1);
  --s2: calc(var(--u) * 2);
  --s3: calc(var(--u) * 3);
  --s4: calc(var(--u) * 4);
  --s6: calc(var(--u) * 6);
  --s8: calc(var(--u) * 8);
  --s12: calc(var(--u) * 12);
  --s16: calc(var(--u) * 16);

  /* --- LA MISE EN PAGE. Le conteneur S'ÉLARGIT : une librairie a besoin de
     largeur pour tenir son rail de rayons ET quatre couvertures de front.
     Mesuré : 78rem donne 1170 px de contenu, soit un rail de 15rem et quatre
     colonnes de couverture d'environ 212 px. La colonne de LECTURE, elle, ne
     bouge pas d'un signe : un chapitre se lit à 66 caractères, et c'est la seule
     chose que cette DA n'a pas le droit de rendre plus large. */
  --large: 78rem;
  --etroit: 44rem;
  --mesure: 66ch;
  --gouttiere: clamp(1rem, 3vw, 2.5rem);

  /* --- 🔴 LES JETONS DE GRILLE — LES DEGRÉS DE LIBERTÉ, DÉCLARÉS.
     Un opus « design par page » passera après celui-ci pour composer chaque page
     différemment. Ces quatre jetons sont ce qu'une page a le DROIT de redéfinir
     sur elle-même, sans toucher un composant ni un couple de contraste :
       · `--grille-min`     la largeur minimale d'une cellule (l'auto-fill suit)
       · `--grille-gap`     l'air entre les cellules
       · `--couv-rapport`   le format d'une couverture
       · `--rail`           la largeur du rail de rayons
     Tout le reste (les teintes, les seuils, la mesure de lecture, les états de
     thème) est un INVARIANT et ne se redéfinit pas par page. La liste des deux
     côtés est écrite au `CLAUDE.md`, pour que la liberté du suivant soit une
     liste et non une interprétation. */
  --grille-min: 12.5rem;
  --grille-gap: var(--s6);
  --couv-rapport: 2 / 3;
  --rail: 15rem;

  /* ⚠ DEUX RAYONS, ET LA FRONTIÈRE EST LA MÊME QUE PARTOUT AILLEURS ICI :
     L'OBJET EST FRANC, LE CONTRÔLE EST ADOUCI. Une couverture, une carte, un
     feuillet gardent l'angle vif à zéro : un livre a des coins carrés, et la
     fiche technique demande la borne franche. Un bouton, une puce de filtre, une
     étiquette prennent 2 px : sur une page qui en aligne quarante, l'angle vif
     donne un air de formulaire administratif, et 2 px reste très en dessous de
     la borne de flotte (3 px) et très loin d'un « arrondi mou ».
     Seule autre exception, au socle : l'anneau de focus, à 1 px. */
  --rayon: 0;
  --rayon-acte: 2px;

  /* --- LES OMBRES — DURES, FLOU NUL, ET C'EST CE QUI FAIT L'OBJET.
     La règle de flotte interdit l'ombre molle et exige le flou nul ; une
     librairie a justement besoin que ses cartes soient POSÉES. Une ombre portée
     sans flou se lit comme une épaisseur de papier, pas comme un halo. */
  --ombre-1: 2px 2px 0 var(--filet);
  --ombre-2: 4px 4px 0 var(--filet-2);

  --duree: 200ms;
  --sortie: cubic-bezier(0.2, 0.8, 0.3, 1);

  /* ⚠ `color-scheme` ANNONCE LE SCHÉMA AU NAVIGATEUR, et c'est lui qui peint ce
     que nous ne peignons pas : la liste déroulante d'un `<select>` natif, les
     ascenseurs, les champs système. LES TROIS ÉTATS LA DÉCLARENT, et elle ne se
     retire jamais. Cette DA en a plus besoin que l'ancienne : le catalogue sert
     de VRAIS `<select>` de filtre, donc de vraies listes déroulantes système. */
  color-scheme: light;
}

/* --- ÉTAT 2 : LE THÈME SOMBRE CHOISI.
   ⚠ TOUTE RÈGLE DE THÈME SOMBRE S'ÉCRIT DEUX FOIS — l'attribut ET la media
   query — OU PAS DU TOUT. Les deux blocs ci-dessous sont jumeaux, et ils se
   corrigent ENSEMBLE.
   ⚠ ET LE TIRAGE DE NUIT A GAGNÉ UNE SURFACE, comme le jour : `--carte` existe
   dans les deux, sinon les cartes disparaîtraient dans la page la nuit et toute
   la mécanique de l'objet posé tomberait au premier visiteur en thème sombre. */
:root[data-theme="sombre"] {
  /* ⚠ LE TIRAGE DE NUIT SUIT LA FAMILLE, ET IL N'A PAS LE DROIT DE RESTER TEAL.
     Un thème sombre qui garde l'ancienne température fabrique deux sites : la
     personne qui bascule le thème changerait de maison. Les quatre surfaces
     tournent donc ensemble vers le vert profond, comme le jour tourne vers le
     vert clair. */
  --fond: #0F1310;
  --fond-2: #161B17;
  --fond-3: #232A22;
  /* ⚠ LA CARTE DE NUIT A DÛ S'ÉCLAIRCIR DEUX FOIS, ET LA MESURE L'A IMPOSÉ LES
     DEUX FOIS : à `#1A2023` (06/08) elle ne valait que 1,04:1 contre le plancher,
     et à `#1D241C` (07/08) elle retombait à 1,10:1 tout juste sous son seuil.
     Un objet posé qui ne se distingue pas de la table n'est pas posé. 1,15:1. */
  --carte: #202820;
  --encre: #E9EBE6;
  --encre-2: #B7BCB2;
  --encre-3: #8F9689;
  /* ⚠ UN FILET SE CALCULE CONTRE CE QU'IL Y A DERRIÈRE, ET IL Y A QUATRE
     « DERRIÈRE ». Mesuré contre les quatre, la carte comprise, qui est la plus
     serrée : 1,29:1 pour un plancher de 1,25. */
  --filet: #333A31;          /* 1,60 page · 1,49 plancher · 1,29 carte           */
  --filet-2: #6E7669;
  /* ⚠ LE DUO S'INVERSE AU THÈME SOMBRE, ET C'EST LA MÊME LOGIQUE À L'ENVERS : de
     nuit, c'est le vert CLAIR qui porte le texte (rapport 10,29:1 sur la page) et
     le vert SOMBRE qui fait la surface. Un accent n'est pas une couleur, c'est un
     RAPPORT — et le duo, c'est un rapport qui a deux sens de lecture. */
  --accent: #8FD173;
  --accent-survol: #A9DE90;
  --accent-clair: #2C4A23;   /* la SURFACE de nuit · --encre dessus : 8,29:1     */
  --accent-fond: #152113;
  --acte-txt: #0B140A;       /* 10,61:1 sur l'accent éclairci                   */
  --alerte: #E8A38F;
  --reliure: #080D06;
  --reliure-encre: #E9EBE6;
  --reliure-encre-2: #B7BCB2;
  --reliure-accent: #8FD173;
  --reliure-filet: #243020;
  color-scheme: dark;
}

/* --- ÉTAT 3 : LE THÈME AUTO, qui suit le système — ET IL FAUT L'AVOIR CHOISI.
   🔴 AMENDÉ LE 07/08/2026, ORDRE DE MAEL : « LE MODE CLAIR EST LE DÉFAUT ». Un
   visiteur qui n'a rien exprimé voit le site CLAIR, même si son système est en
   sombre. Ce bloc ne s'applique donc plus tout seul : il exige l'ATTRIBUT
   `data-theme="auto"`, que le panneau pose quand quelqu'un choisit « auto ».
   ⚠ AVANT, LE SÉLECTEUR ÉTAIT UN DOUBLE `:not()`, et c'était le contraire exact :
   l'état auto y était l'ABSENCE d'attribut, donc le sombre système s'appliquait
   à tout visiteur qui n'avait rien réglé. La règle d'or de la flotte dit que
   l'état initial est ce que la feuille sert SANS lecture de stockage ; le double
   `:not()` faisait de la préférence du SYSTÈME un défaut de site, ce qui est la
   même faute d'un cran plus loin.
   ⚠ `theme: "clair"` EST LE DÉFAUT DE `lib-html.js → PREFS_DEFAUT` ET DE
   `panneau.js → DEFAUT`, et les trois gestes sont inséparables : ce sélecteur ne
   reçoit son attribut que parce que « auto » diffère désormais du défaut. Rendre
   l'un sans les deux autres donne un site qui suit le système pour qui n'a rien
   choisi et l'ignore pour qui a choisi « auto ».
   ⚠ ET LES JETONS DES CINQ ÉTATS NE BOUGENT PAS D'UN OCTET : c'est le même
   tirage de nuit, il change seulement de CONDITION D'ENTRÉE. */
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --fond: #0F1310;
    --fond-2: #161B17;
    --fond-3: #232A22;
    --carte: #202820;
    --encre: #E9EBE6;
    --encre-2: #B7BCB2;
    --encre-3: #8F9689;
    --filet: #333A31;
    --filet-2: #6E7669;
    --accent: #8FD173;
    --accent-survol: #A9DE90;
    --accent-clair: #2C4A23;
    --accent-fond: #152113;
    --acte-txt: #0B140A;
    --alerte: #E8A38F;
    --reliure: #080D06;
    --reliure-encre: #E9EBE6;
    --reliure-encre-2: #B7BCB2;
    --reliure-accent: #8FD173;
    --reliure-filet: #243020;
    color-scheme: dark;
  }
}

/* ==========================================================================
   2. LES PRÉFÉRENCES DU PANNEAU
   Chacune est un attribut sur <html>, posé par l'inline du <head> AVANT la
   première peinture (voir lib-html.js → BASCULE_INLINE / PREFS_INLINE).
   Elles agissent sur les JETONS, jamais sur des règles une par une : c'est ce
   qui les rend gratuites à maintenir.
   ========================================================================== */
:root[data-taille="l"] { --t-base: 1.1875rem; }
:root[data-taille="xl"] { --t-base: 1.3125rem; }

:root[data-aeration="large"] { --u: 0.3125rem; line-height: 1.75; }

/* CONTRASTE RENFORCÉ. Il ne « fonce » pas les couleurs au hasard : il pousse
   l'encre et les filets, et il ÉTEINT tout ce qui est décoratif. C'est un état
   de thème à part entière, donc `lib-contraste` doit pouvoir le mesurer : il
   déclare `--encre` lui-même, ce qui lui sert de TÉMOIN.
   ⚠ IL N'A PLUS DE GRAIN À ÉTEINDRE, et c'est une simplification vraie : la
   matière de papier a disparu de la feuille, donc les deux lignes `--grain` à
   alpha nul (une par état sombre, écrites en double comme toute règle sombre)
   n'ont plus d'objet. Un mode qui éteint moins de choses est un mode dont on
   peut prouver plus facilement qu'il n'oublie rien.
   ⚠ EN REVANCHE IL GAGNE `--carte`, ET C'EST OBLIGATOIRE : la carte est
   maintenant ce qui porte les objets. Sans elle ici, une personne en contraste
   renforcé verrait la carte tomber sur la valeur du thème de base et perdrait la
   seule limite qui dit où un volume commence. En renforcé, la carte devient le
   BLANC PUR au clair et le NOIR PUR au sombre : l'écart maximal avec le
   plancher, ce qui est exactement ce que ce mode demande.
   ⚠ LES SIX TEINTES DE COUVERTURE NE SONT PAS TOUCHÉES ICI, et c'est une
   décision instruite. Le contraste renforcé pousse l'INTERFACE ; une couverture
   est un objet imprimé dont le titre tient déjà de 7,35:1 à 14,39:1 sur son
   propre fond, c'est-à-dire au-dessus du seuil renforcé de 7,0 pour six sur six.
   Les repeindre en renforcé reviendrait à réimprimer les livres du visiteur. */
:root[data-contraste="haut"] {
  --encre: #000000;
  --encre-2: #141414;
  --encre-3: #2E2E2E;
  --carte: #FFFFFF;
  --filet: #8A8F80;
  --filet-2: #42492F;   /* 3:1 largement dépassé : en renforcé, un filet de composant s'affirme */
  /* ⚠ LE VERT CLAIR NE SE RENFORCE PAS, ET C'EST LA MÊME DÉCISION QUE POUR LES
     COUVERTURES : ce mode pousse l'ENCRE et les FILETS, c'est-à-dire ce qui doit
     se LIRE. `--accent-clair` est une surface qui ne porte aucun texte, et ce
     qu'elle reçoit se lit en `--encre`, qui vient de passer au noir pur : le
     couple monte donc tout seul de 11,59:1 à 13,71:1. Repeindre la surface en
     plus aurait poussé un contraste déjà maximal. */
  --accent: #17380A;
  --accent-survol: #0B2004;
  --alerte: #5E1E13;
  /* Le plat s'assombrit encore, et son filet devient une VRAIE limite lisible :
     en contraste renforcé, ce qui était une nuance de composition doit devenir
     une frontière. */
  --reliure: #0A1704;
  --reliure-encre: #FFFFFF;
  --reliure-encre-2: #E4E9E1;
  --reliure-accent: #A9DE90;
  --reliure-filet: #8A9C84;
}
/* ⚠ LE CONTRASTE RENFORCÉ SOMBRE S'ÉCRIT DEUX FOIS, COMME TOUT THÈME SOMBRE :
   le thème CHOISI, et le thème SYSTÈME SUR CHOIX « auto ». Et le second DOIT
   vivre dans la media query : hors d'elle, il donnerait les jetons SOMBRES à un
   visiteur en contraste renforcé sur un système CLAIR.
   🔴 AMENDÉ LE 07/08/2026 AVEC L'ÉTAT 3 : le sélecteur exige l'ATTRIBUT
   `data-theme="auto"` au lieu d'un `:not([data-theme="clair"])`. L'ancienne forme
   était vraie dès qu'aucun attribut de thème n'était posé, c'est-à-dire pour tout
   visiteur qui n'avait rien réglé — ce que la commande « le mode clair est le
   défaut » interdit désormais.
   ⚠ IL FAUT LES DEUX ATTRIBUTS, ET LA SPÉCIFICITÉ EST LA RAISON : le bloc de
   contraste renforcé CLAIR est écrit plus haut avec la même spécificité que
   l'état 3 (deux classes chacun), donc il gagne par l'ORDRE sur les jetons que
   les deux déclarent. Ce bloc-ci en porte trois, il passe donc devant les deux.
   ⚠ ET C'EST CE QUI RÉCONCILIE LES DEUX CHEMINS VERS LE RENFORCÉ SOMBRE : le
   sombre CHOISI et le sombre SYSTÈME résolvent maintenant les mêmes valeurs, ce
   qui n'était pas vrai de l'ancienne forme (elle sortait le plat à `#080D06`
   d'un côté et `#0A1704` de l'autre, le CONTRAT ne nommant que le second). */
:root[data-contraste="haut"][data-theme="sombre"] {
  --encre: #FFFFFF;
  --encre-2: #E9EBE6;
  --encre-3: #C9CCC6;
  --carte: #000000;
  --filet: #878C82;
  /* le filet appuyé borde des COMPOSANTS : il lui faut 3:1 sur le fond, et en
     contraste renforcé plus encore qu'ailleurs. */
  --filet-2: #AEB3A9;
  --accent: #B7E8A0;
  --accent-survol: #D8F5CB;
  --alerte: #F2C7B6;
  --reliure-encre: #FFFFFF;
  --reliure-encre-2: #E4E9E1;
  --reliure-accent: #B7E8A0;
  --reliure-filet: #7C887A;
}
@media (prefers-color-scheme: dark) {
  :root[data-contraste="haut"][data-theme="auto"] {
    --encre: #FFFFFF;
    --encre-2: #E9EBE6;
    --encre-3: #C9CCC6;
    --carte: #000000;
    --filet: #878C82;
    --filet-2: #AEB3A9;
    --accent: #B7E8A0;
    --accent-survol: #D8F5CB;
    --alerte: #F2C7B6;
    --reliure-encre: #FFFFFF;
    --reliure-encre-2: #E4E9E1;
    --reliure-accent: #B7E8A0;
    --reliure-filet: #7C887A;
  }
}

/* MODE LECTURE : une colonne, rien autour. Il ne cache pas la navigation
   (on doit pouvoir partir), il retire le décor. */
:root[data-lecture="oui"] main { --large: var(--etroit); }
:root[data-lecture="oui"] .bloc + .bloc { margin-block-start: var(--s12); }

/* LIENS SOULIGNÉS : pour qui ne distingue pas la couleur, un lien doit se
   reconnaître à sa FORME. C'est une préférence, pas un correctif : les liens de
   corps de texte sont déjà soulignés par défaut en section 4. */
:root[data-liens="marques"] a { text-decoration: underline; text-underline-offset: 2px; }

/* ==========================================================================
   3. LE MOUVEMENT, TRIPLE-GARDÉ
   Trois gardes, et il en faut trois : la préférence SYSTÈME, le réglage du
   PANNEAU (pour qui ne l'a pas réglé dans son système), et le fait de n'animer
   que `transform`, `opacity` et `box-shadow` (le reste fait recalculer la mise
   en page à chaque image).
   ⚠ JAMAIS `duration: 0` : on met `0.01ms`, pour que `transitionend` et
   `animationend` continuent de se déclencher — un script qui attend la fin
   d'une transition qui n'a jamais commencé reste bloqué pour toujours.
   ========================================================================== */
:root[data-animations="off"] *,
:root[data-animations="off"] *::before,
:root[data-animations="off"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   4. LE SOCLE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* de quoi respirer sous un en-tête collant quand on saute à une ancre */
  scroll-padding-block-start: var(--s16);
}

body {
  margin: 0;
  background: var(--fond);
  color: var(--encre);
  font: var(--t-base) / 1.6 var(--corps);
  font-synthesis: none;   /* pas de faux gras : il casse la lisibilité */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ⚠ `[hidden]` NE GAGNE PAS contre un `display` déclaré : la règle du
   navigateur est `[hidden] { display: none }`, à spécificité 0-1-0, et
   n'importe quelle classe qui pose `display: flex` la bat. Un élément que du
   JavaScript vient de cacher restait donc À L'ÉCRAN dans la flotte — vu à
   l'œil sur une capture, pas deviné. Cette ligne vaut pour tout le site, et
   seulement DEUX projets sur six l'ont. */
[hidden] { display: none !important; }

h1, h2, h3 {
  font-family: var(--titre);
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 var(--s4);
  text-wrap: balance;   /* pas de dernier mot orphelin sur un titre */
}
h1 { font-size: var(--t-2xl); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-l); }
/* ⚠ `h4` N'EST PAS STYLÉ ICI, ET C'EST UNE DÉCISION.
   Les six feuilles de la flotte stylisent un `h4`, et ZÉRO page sur 1 366 en
   emploie un. C'est la trace la plus nette du copier-coller entre projets :
   une règle recopiée six fois pour un sélecteur que personne n'a jamais écrit.
   Le gabarit ne la reconduit pas. Un projet dont la hiérarchie descend
   vraiment à quatre niveaux ajoute son `h4` ICI, le jour où il en écrit un.
   (Et si la hiérarchie descend à quatre niveaux, la question à se poser
   d'abord est si la page ne devrait pas être deux pages.) */

p, li { max-width: var(--mesure); }
p { margin: 0 0 var(--s4); }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
a:hover { color: var(--accent-survol); }
/* Un lien DANS DU TEXTE est souligné par défaut : la couleur seule ne suffit
   pas à distinguer un lien pour qui ne la perçoit pas. Les liens de
   navigation, eux, sont identifiés par leur place et leur composant. */
.lecture a, p a, li a { text-decoration: underline; }

/* --- L'ANNEAU DE FOCUS. `:focus-visible` et pas `:focus` : un clic de souris
   ne doit pas laisser un anneau, mais une tabulation doit TOUJOURS en laisser
   un. `outline-offset` positif décolle l'anneau du bord et le rend visible
   même sur un fond de même teinte que l'élément. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 1px;
}
/* ⚠ Une cible de focus PROGRAMMATIQUE (un titre qu'on donne au lecteur d'écran
   après une bascule d'écran) reçoit le focus, PAS l'anneau : sur un
   `tabindex="-1"`, un anneau se lit comme un cadre décoratif et personne ne
   comprend ce qu'il désigne. */
[tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; }

/* --- LE LIEN D'ÉVITEMENT. Il existe, il est le PREMIER focusable du document,
   et il apparaît au focus. `inset-inline-start` et non `left` : voir la note
   sur les propriétés logiques en section 6. */
.saut {
  position: absolute;
  inset-inline-start: -200vw;
  inset-block-start: 0;
  z-index: 99;   /* au-dessus du panneau (48) : on doit pouvoir l'atteindre */
  background: var(--encre);
  color: var(--fond);
  padding: var(--s3) var(--s6);
  font-weight: 700;
  text-decoration: none;
}
.saut:focus { inset-inline-start: var(--s4); inset-block-start: var(--s4); }

/* --- POUR LES LECTEURS D'ÉCRAN SEULEMENT. `clip-path` ET `clip` : le second
   pour les moteurs anciens, le premier pour les modernes. `white-space: nowrap`
   empêche un texte long de se replier sur mille lignes de 1 px et de créer un
   défilement fantôme. */
.vh {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

img, svg, video { max-width: 100%; height: auto; }
.ic { width: 1.25em; height: 1.25em; flex: none; vertical-align: -0.2em; }
.ic--s { width: 1em; height: 1em; }
/* ⚠ LES PICTOS QUI ONT UN SENS SE RETOURNENT EN ÉCRITURE RTL : une flèche
   « suivant » pointe vers la gauche en arabe. `lib-html.js → ICONS_MIROIR` dit
   lesquels, et cette règle les retourne. */
[dir="rtl"] .ic--miroir { transform: scaleX(-1); }

/* ==========================================================================
   5. SANS JAVASCRIPT

   ⚠ CES RÈGLES NE SE SUPPRIMENT JAMAIS. Elles sont la garantie « lisible sans
   JavaScript », et leur consommateur n'est pas visible dans un navigateur de
   développement : c'est le visiteur dont le script n'a pas chargé. On ne
   « nettoie » pas une règle parce qu'on ne la voit jamais s'appliquer.

   ⚠ ET LA BASCULE `.sans-js` → `.avec-js` VIT DANS LE `<head>`, SYNCHRONE.
   Un projet de la flotte a mesuré un CLS de 0,73 en mobile parce qu'elle vivait
   dans un script DIFFÉRÉ : la première peinture montrait la navigation dépliée
   dans le flux (ces règles-ci), puis tout remontait. Deux autres projets ont
   encore la bascule dans leur `app.js` en `defer`, et l'un des deux déplie
   toute sa navigation avec `!important` : le bug y est reproductible.
   ========================================================================== */
.sans-js .nav { display: block; position: static; max-height: none; }
.sans-js .tete__bascule { display: none; }
/* Le panneau de réglages a BESOIN de JavaScript pour exister : sans lui, son
   bouton ne doit pas s'afficher. Une commande qui ne répond pas est pire
   qu'une commande absente. */
.sans-js .panneau-b,
.sans-js .panneau,
.sans-js .panneau-voile { display: none; }

/* ==========================================================================
   6. L'OSSATURE DE PAGE

   ⚠ TOUT EST EN PROPRIÉTÉS LOGIQUES (`inset-inline-start`, `margin-block`,
   `padding-inline`, `border-inline-start`), jamais `left`/`right`/`top`/
   `bottom`. Ce n'est pas du purisme : la flotte servira une langue RTL, et une
   feuille écrite en propriétés physiques demande alors une seconde feuille.
   `tools/audit-contenu.js` AVERTIT sur toute propriété physique introduite ici.
   La seule exception connue est `writing-mode: vertical-rl`, qui fait tourner
   les axes logiques : elle se commente sur place, le jour où elle arrive.
   ========================================================================== */

/* ⚠ `body > *` MET TOUTES LES BANDES À LA MÊME ALTITUDE, et à égalité c'est
   l'ORDRE DU DOCUMENT qui gagne. Un `z-index` posé DANS un contexte
   d'empilement ne classe son élément que parmi les descendants de ce contexte :
   un composant flottant qui doit SORTIR DE SA BANDE VERS LE BAS (un menu, une
   infobulle, la liste du sélecteur de langue) ne peut pas y arriver par son
   propre `z-index`. IL FAUT LEVER SA BANDE. D'où l'altitude de `.utilite`
   ci-dessous. Les débordements vers le HAUT, eux, sont sûrs. */
body > * { position: relative; z-index: 1; }
.utilite { z-index: 45; }   /* le panneau reste à 46-48, le lien d'évitement à 99 */

.wrap {
  width: 100%;
  max-width: var(--large);
  margin-inline: auto;
  padding-inline: var(--gouttiere);
}
.wrap--etroit { max-width: var(--etroit); }

/* --- la barre d'utilité : les liens d'organisme et la langue, au-dessus de tout */
.utilite {
  background: var(--fond-3);
  border-block-end: 1px solid var(--filet);
  font-size: var(--t-xs);
}
.utilite__in {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--s2) var(--s6);
  padding-block: var(--s2);
}
.utilite__liens { display: flex; flex-wrap: wrap; gap: var(--s4); }
.utilite a { display: inline-flex; align-items: center; gap: var(--s1); }

/* --- le sélecteur de langue : un <details> NATIF, donc il s'ouvre au clavier
   et il fonctionne SANS JavaScript. */
.langues { position: relative; }
.langues__b {
  display: inline-flex; align-items: center; gap: var(--s1);
  cursor: pointer; list-style: none;
  padding: var(--s1) var(--s2);
  min-height: 32px;
}
.langues__b::-webkit-details-marker { display: none; }
.langues[open] .langues__chev { transform: rotate(180deg); }
.langues__liste {
  position: absolute; inset-inline-end: 0; inset-block-start: 100%;
  z-index: 46;
  margin: 0; padding: var(--s2) 0;
  list-style: none; min-width: 11rem;
  background: var(--fond);
  border: 1px solid var(--filet-2);
}
.langues__liste a { display: block; padding: var(--s2) var(--s4); text-decoration: none; }
.langues__liste a[aria-current] { font-weight: 700; background: var(--accent-fond); }

/* --- l'en-tête */
.tete { background: var(--fond); border-block-end: 1px solid var(--filet); }
.tete__in {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--s4);
  padding-block: var(--s4);
}
.marque { display: inline-flex; align-items: center; gap: var(--s3); text-decoration: none; color: inherit; }
.marque__txt { display: flex; flex-direction: column; }
.marque__nom { font-family: var(--titre); font-weight: 700; font-size: var(--t-m); line-height: 1.1; }
.marque__sous { font-size: var(--t-xs); color: var(--encre-2); }

.tete__bascule {
  display: none;
  align-items: center; gap: var(--s2);
  min-height: 44px; padding: var(--s2) var(--s4);   /* cible ≥ 44 px, socle */
  background: none; border: 1px solid var(--filet-2); border-radius: var(--rayon);
  color: inherit; font: inherit; cursor: pointer;
}
.nav ul { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s6); margin: 0; padding: 0; list-style: none; }
.nav li { max-width: none; }
.nav a { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; }
.nav a[aria-current="page"] { font-weight: 700; box-shadow: inset 0 -2px 0 var(--accent); }

/* 🔴 LE CHROME MOBILE TENAIT 393 px AVANT LA PREMIÈRE LIGNE DE CONTENU, et c'est
   un défaut de mise en page, pas une fatalité de téléphone : la barre d'utilité,
   l'en-tête, le bouton « Menu » SUR SA PROPRE LIGNE et un bandeau d'identité de
   quatre lignes s'empilaient avant le titre. Sur un écran de 640 px de haut, la
   maison passait la moitié de son premier écran à se présenter.
   DEUX CORRECTIONS, ET AUCUNE NE CACHE QUOI QUE CE SOIT :
     1. l'en-tête devient une GRILLE `1fr auto` : la marque prend ce qui reste,
        le bouton « Menu » revient SUR LA LIGNE DU LOGO, et la navigation dépliée
        occupe la ligne suivante en entier. `minmax(0, 1fr)` et `min-width: 0`
        sont indispensables : sans eux, la marque refuse de se replier et pousse
        le bouton à la ligne, ce qui est exactement le défaut d'origine ;
     2. le bandeau d'identité se compose à `--t-xs` avec une gouttière serrée :
        ses deux chaînes restent SERVIES ET LUES (le « qui parle » est une
        obligation de socle, il ne se cache pas), elles tiennent simplement en
        deux lignes au lieu de quatre. */
@media (max-width: 52rem) {
  .tete__in {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--s3);
    padding-block: var(--s3);
  }
  .marque, .marque__txt { min-width: 0; }
  .tete__bascule { display: inline-flex; }
  .nav { display: none; grid-column: 1 / -1; width: 100%; }
  .nav[data-ouvert="oui"] { display: block; }
  .nav ul { flex-direction: column; gap: 0; }
  .nav a { padding-block: var(--s2); }
}

/* --- le bandeau d'identité : qui parle, sur toutes les pages */
.bandeau { background: var(--fond-2); border-block-end: 1px solid var(--filet); font-size: var(--t-s); }
.bandeau__in {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2) var(--s4);
  padding-block: var(--s3);
}
.bandeau__nom { font-weight: 700; }
.bandeau__fait { color: var(--encre-2); }
@media (max-width: 52rem) {
  .bandeau { font-size: var(--t-xs); }
  .bandeau__in { gap: 0 var(--s2); padding-block: var(--s2); }
}

/* --- les sections */
.section { padding-block: var(--s12); }
.section + .section { padding-block-start: 0; }
.tete-page { padding-block-end: var(--s8); }
.chapeau { font-size: var(--t-m); color: var(--encre-2); max-width: var(--mesure); }
/* 🔴 LE CHAPEAU DU HÉROS EST PASSÉ AU CORPS, ET C'EST UNE MESURE QUI L'A DIT.
   Il était en Literata à `--t-xl`, c'est-à-dire 29,6 px sur un écran de bureau :
   sur un téléphone de 360 px, la phrase de positionnement occupait SIX LIGNES et
   repoussait le premier acte hors de l'écran. Un chapeau n'est pas un titre : il
   se lit, donc il prend la police de LECTURE et le corps de la lecture appuyée
   (19 px). Le titre garde tout le poids de la page ; le chapeau reprend son rôle
   de sous-titre au lieu de faire un second titre. */
.hero__punch { font-size: var(--t-l); font-family: var(--corps); color: var(--encre-2); max-width: var(--mesure); }

/* --- le pied dense : navigation + coordonnées + légal (obligation de socle) */
.pied {
  margin-block-start: var(--s16);
  padding-block: var(--s12) var(--s8);
  background: var(--fond-3);
  border-block-start: 1px solid var(--filet-2);
  font-size: var(--t-s);
}
.pied__grille {
  display: grid;
  /* `auto-fit` + `minmax` : la grille se réarrange seule, sans media query et
     sans nombre de colonnes écrit en dur. Ajouter une colonne au pied ne
     demande alors aucune retouche de feuille. */
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--s8);
}
.pied h2 { font-size: var(--t-s); text-transform: uppercase; letter-spacing: 0.06em; }
.pied ul { margin: 0; padding: 0; list-style: none; }
.pied li { margin-block-end: var(--s2); max-width: none; }
.pied__contact { display: flex; flex-direction: column; gap: var(--s1); }
.pied__contact span { color: var(--encre-2); font-size: var(--t-xs); }
.pied__nom { font-family: var(--titre); font-weight: 700; margin-block: var(--s2) var(--s1); }
.pied__bas {
  margin-block-start: var(--s8); padding-block-start: var(--s4);
  border-block-start: 1px solid var(--filet);
  color: var(--encre-2); font-size: var(--t-xs);
}
.pied__bas p { margin-block-end: var(--s1); max-width: none; }

/* ==========================================================================
   7. LE PANNEAU

   ┌──────────────────────────────────────────────────────────────────────────┐
   │ UN PANNEAU FERMÉ SORT DE L'ARBORESCENCE D'ACCESSIBILITÉ.                 │
   │                                                                          │
   │ Un panneau seulement TRANSLATÉ hors écran reste dans l'arborescence avec  │
   │ son `role="dialog" aria-modal="true"` — ce qui déclare TOUT LE RESTE DU   │
   │ DOCUMENT INERTE pour un lecteur d'écran comme pour un agent — et laisse   │
   │ ses boutons dans l'ordre de tabulation, hors écran. Mesuré dans la flotte │
   │ sur chaque page d'un site entier. Il faut `visibility: hidden`.           │
   │                                                                          │
   │ ⚠ ET LA TRANSITION S'ÉCRIT `visibility 0s linear <durée>` FERMÉ /         │
   │ `visibility 0s linear 0s` OUVERT — JAMAIS `visibility <durée>`.           │
   │ Sous cette seconde forme, la valeur calculée est encore `hidden` à        │
   │ l'instant du clic, et le `focus()` synchrone de panneau.js sur « fermer » │
   │ ÉCHOUE EN SILENCE : le piège à focus ne s'amorce pas, la tabulation       │
   │ repart dans la page derrière le panneau. Deux projets ont payé cette      │
   │ ligne, un troisième garde encore un `visibility` non transitionné et perd │
   │ donc l'animation de fermeture.                                            │
   │ La bascule est INSTANTANÉE (0s) et RETARDÉE de la durée du glissement à   │
   │ la seule fermeture : le rendu est identique au pixel.                      │
   └──────────────────────────────────────────────────────────────────────────┘
   ========================================================================== */
.panneau-b {
  position: fixed;
  inset-inline-end: var(--s4); inset-block-end: var(--s4);
  z-index: 46;
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 44px; padding: var(--s3) var(--s4);
  background: var(--encre); color: var(--fond);
  border: 0; border-radius: var(--rayon);
  font: inherit; font-weight: 700; cursor: pointer;
}
.panneau-b__txt { font-size: var(--t-s); }

.panneau-voile {
  position: fixed; inset: 0; z-index: 47;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  /* ⚠ `pointer-events: none` à l'état FERMÉ : sans lui, un voile en cours de
     fermeture reste cliquable ~180 ms au-dessus du bouton du panneau et MANGE
     le clic de réouverture. Mesuré à la sonde dans la flotte. */
  pointer-events: none;
  transition: opacity var(--duree) linear, visibility 0s linear var(--duree);
}
.panneau-voile[data-ouvert="oui"] {
  opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity var(--duree) linear, visibility 0s linear 0s;
}

.panneau {
  position: fixed;
  inset-block: 0; inset-inline-end: 0;
  z-index: 48;
  width: min(26rem, 100%);
  display: flex; flex-direction: column;
  overflow-y: auto;
  overscroll-behavior: contain;   /* le défilement ne fuit pas sur la page */
  background: var(--fond);
  color: var(--encre);
  border-inline-start: 3px solid var(--accent);
  transform: translateX(101%);
  visibility: hidden;
  transition: transform var(--duree) var(--sortie), visibility 0s linear var(--duree);
}
/* En écriture RTL le panneau sort par l'autre bord : `translateX` est un axe
   PHYSIQUE, il ne suit pas `dir`. C'est la seule inversion à écrire à la main. */
[dir="rtl"] .panneau { transform: translateX(-101%); }
.panneau[data-ouvert="oui"] {
  transform: none; visibility: visible;
  transition: transform var(--duree) var(--sortie), visibility 0s linear 0s;
}

.panneau__tete {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s4) var(--s6);
  border-block-end: 1px solid var(--filet);
}
.panneau__tete h2 { margin: 0; font-size: var(--t-m); }
.panneau__x {
  min-width: 44px; min-height: 44px;
  background: none; border: 0; color: inherit; cursor: pointer;
}
.panneau__corps { flex: 1; padding: var(--s4) var(--s6); }
.panneau__bloc { margin-block-end: var(--s8); }
.panneau__bloc h3 {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--encre-2); margin-block-end: var(--s3);
}
.panneau__ligne {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--s2);
  margin-block-end: var(--s3);
}
.panneau__label { font-size: var(--t-s); }
.panneau__liste, .panneau__langues { margin: 0; padding: 0; list-style: none; }
.panneau__liste li, .panneau__langues li { max-width: none; }
.panneau__liste a, .panneau__liste button {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; min-height: 44px; padding: var(--s2) 0;
  background: none; border: 0; color: inherit;
  font: inherit; text-align: start; text-decoration: none; cursor: pointer;
}
.panneau__danger { color: var(--alerte); }
.panneau__pied {
  padding: var(--s4) var(--s6);
  border-block-start: 1px solid var(--filet);
  color: var(--encre-2); font-size: var(--t-xs); max-width: none;
}

/* le segment de choix : des boutons, pas un <select>. Un <select> prend la
   largeur de sa plus longue option et déborde sous 360 px. */
.seg { display: inline-flex; border: 1px solid var(--filet-2); border-radius: var(--rayon); overflow: hidden; }
.seg__b {
  min-height: 36px; padding: var(--s1) var(--s3);
  background: var(--fond); border: 0; color: var(--encre-2);
  font: inherit; font-size: var(--t-xs); cursor: pointer;
}
.seg__b + .seg__b { border-inline-start: 1px solid var(--filet); }
.seg__b[aria-pressed="true"] { background: var(--encre); color: var(--fond); font-weight: 700; }

/* ==========================================================================
   8. LES COMPOSANTS DU MOTEUR DE BLOCS
   Un par type de bloc de `pages.json`, et rien de plus : le squelette de ce
   que le générateur sait produire. La DA les REPEINT, elle n'en invente pas
   d'autres sans les ajouter au moteur.
   ========================================================================== */
.bloc + .bloc { margin-block-start: var(--s12); }
.lecture > * { max-width: var(--mesure); }

.liste-marquee { margin: 0; padding: 0; list-style: none; }
.liste-marquee li { display: flex; gap: var(--s3); margin-block-end: var(--s3); }
.liste-marquee .ic { color: var(--accent); margin-block-start: 0.3em; }

.cartes { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--s6); }
.carte { padding: var(--s6); background: var(--fond-2); border: 1px solid var(--filet); border-radius: var(--rayon); }
.carte h3 { margin-block-end: var(--s2); }
.carte p { margin: 0; max-width: none; }

.pas { counter-reset: pas; margin: 0; padding: 0; list-style: none; }
.pas li { counter-increment: pas; display: flex; gap: var(--s4); margin-block-end: var(--s6); }
.pas li::before {
  content: counter(pas);
  flex: none;
  display: grid; place-items: center;
  width: 2rem; height: 2rem;
  background: var(--encre); color: var(--fond);
  font-family: var(--titre); font-weight: 700; font-size: var(--t-s);
}

.qr { border-block-end: 1px solid var(--filet); }
.qr summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  min-height: 44px; padding-block: var(--s3);
  cursor: pointer; list-style: none;
  font-family: var(--titre); font-weight: 700;
}
.qr summary::-webkit-details-marker { display: none; }
.qr[open] summary .ic { transform: rotate(180deg); }
.qr > div { padding-block-end: var(--s4); }
/* ⚠ Chrome ≥ 131 masque le contenu d'un `<details>` fermé par
   `content-visibility` sur `::details-content`. La règle moderne s'écrit
   SÉPARÉMENT : UN SÉLECTEUR INCONNU JETTE TOUTE SA RÈGLE, et il ne doit donc
   emporter que lui. Groupée avec la précédente, elle les tuerait toutes les
   deux sur les moteurs qui ne connaissent pas `::details-content`.
   Corollaire pour les outils : ce contenu garde une géométrie MESURABLE, donc
   une sonde de débordement mobile doit exclure les descendants d'un `details`
   fermé. */
.qr:not([open])::details-content { content-visibility: hidden; }

.note {
  padding: var(--s4) var(--s6);
  background: var(--accent-fond);
  border-inline-start: 3px solid var(--accent);
}
.note p { margin: 0; }
.note--alerte { background: var(--fond-2); border-inline-start-color: var(--alerte); }

.liens-blocs { margin: 0; padding: 0; list-style: none; }
.liens-blocs li { max-width: none; margin-block-end: var(--s2); }
.liens-blocs a {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 44px; font-weight: 700; text-decoration: none;
}

.chiffres { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--s6); }
.chiffre { display: flex; flex-direction: column; gap: var(--s1); }
.chiffre__n { font-family: var(--titre); font-weight: 700; font-size: var(--t-2xl); line-height: 1; }
.chiffre__q { font-size: var(--t-s); color: var(--encre-2); }

.plan-liste { margin: 0 0 var(--s8); padding: 0; list-style: none; }
.plan-liste li { margin-block-end: var(--s1); max-width: none; }

/* --- LE BOUTON D'ACTE.
   ⚠ SON LIBELLÉ EST À `var(--t-l)`, C'EST-À-DIRE 19 px, ET EN 700.
   Ce n'est pas un choix de goût : du blanc sur un accent plein ne tient
   souvent qu'au seuil de 3:1 du GRAND TEXTE (≥ 18,66 px en gras au sens WCAG).
   Réduire ce corps fait basculer l'exigence à 4,5:1 et casse l'accessibilité du
   bouton SANS RIEN CHANGER À LA COULEUR. `tools/check-contraste.js` vérifie les
   DEUX : le ratio ET la taille déclarée ici. */
/* 🔴 CORRIGÉ LE 07/08/2026 : LES ACTES ÉTAIENT EN LITERATA, ET LA DA DISAIT LE
   CONTRAIRE DEPUIS LA VEILLE. Le renversement typographique du 06/08 au soir a
   écrit noir sur blanc que « NOTO SANS est la voix DE LA LIBRAIRIE : navigation,
   rayons, filtres, étiquettes, ACTES, compteurs, fil de page » — et cette règle
   est restée `font-family: var(--titre)`. La feuille contredisait donc son propre
   commentaire, sur le composant le plus vu du site.
   ⚠ ET AUCUN DES SEPT VERROUS NE POUVAIT LE DIRE : le contraste mesure des
   couples, l'audit mesure des jetons et des interdits, la recette mesure des
   débordements. Une FAMILLE mal attribuée n'est ni un contraste, ni un jeton, ni
   un débordement. C'est la sonde `getComputedStyle` sur la page servie qui l'a
   rendu (`.cat-filtres__reset` : Literata 700). Troisième fois dans ce projet
   qu'un défaut passe sept verrous verts et tombe au REGARD. */
.b {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 44px; padding: var(--s3) var(--s6);
  border-radius: var(--rayon);
  font-family: var(--corps); font-weight: 700;
  text-decoration: none; cursor: pointer;
}
.b--acte {
  background: var(--accent); color: var(--acte-txt); border: 0;
  font-size: var(--t-l);
}
.b--acte:hover { background: var(--accent-survol); color: var(--acte-txt); }

/* 🔴 L'ACTE SECOND — UN SEUL RÉGIME D'ACTE, ET IL EST DÉSORMAIS INFRANCHISSABLE.
   La maquette n'avait qu'UNE forme de bouton, donc les deux actes du héros
   sortaient tous les deux en APLAT D'ACCENT : deux boutons primaires côte à côte,
   c'est-à-dire aucun. La faute est sortie trois fois, et la troisième était sur
   la page arabe — signe qu'une règle qui n'existe qu'au premier acte fuit par les
   autres.
   LA RÈGLE, ÉCRITE UNE FOIS, VALABLE PARTOUT :
     · PRIMAIRE (`.b--acte`)   — aplat `--accent`, libellé `--acte-txt`, UN filet
       à 1 px décalé de 3 px. Un seul par groupe d'actes.
     · SECONDAIRE (`.b--second`) — AUCUN aplat, un filet de 1 px `--filet-2`,
       libellé `--encre`. Pas de second cadre, pas de demi-aplat, pas d'accent en
       fond : un acte secondaire se lit comme un lien encadré, et c'est tout ce
       qu'il doit être.
   ⚠ SON CORPS N'EST PAS `--t-l`. Le libellé secondaire est de l'encre sur du
   papier (19,4:1) : il n'a pas besoin du seuil du GRAND TEXTE, contrairement au
   blanc sur l'accent plein. Lui donner 19 px l'aurait fait concourir avec le
   primaire, ce qui est précisément ce qu'on corrige. */
.b--second {
  background: none;
  color: var(--encre);
  border: 1px solid var(--filet-2);
  font-size: var(--t-m);
}
.b--second:hover { color: var(--accent-survol); border-color: var(--accent); }

/* ==========================================================================
   9. L'IMPRESSION
   Une page imprimée n'a ni navigation, ni panneau, ni mouvement. Et une
   ANIMATION DONT L'ÉTAT INITIAL EST INVISIBLE se pense TOUJOURS avec ses
   médias morts : le papier ne défile pas, donc une révélation au défilement
   imprimerait une page BLANCHE. On neutralise ici toutes les animations.
   ========================================================================== */
@media print {
  .utilite, .nav, .tete__bascule, .panneau, .panneau-b, .panneau-voile, .saut { display: none !important; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  body { background: #FFFFFF; color: #000000; font-size: 11pt; }
  a { color: #000000; text-decoration: underline; }
  /* l'adresse d'un lien s'imprime : sur papier, un lien sans son adresse est
     une impasse. Les liens internes en sont exemptés (l'adresse relative
     n'aiderait personne). */
  .lecture a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; word-break: break-all; }
  .section { padding-block: var(--s6); }
  .qr[open] > div, .qr > div { display: block !important; }
  h1, h2, h3 { break-after: avoid; }
  .carte, .pas li { break-inside: avoid; }
}

/* ==========================================================================
   10. LA DA DU PROJET — LA SEULE SECTION À ÉCRIRE

   Tout ce qui précède est du socle de flotte : il se rétro-porte tel quel d'un
   projet à l'autre, et une correction d'accessibilité s'y fait UNE fois.
   Ici commence ce qui n'appartient qu'à ce projet.

   CE QUI SE FAIT ICI :
     · les `@font-face`, en woff2 LOCAUX depuis `../fonts/`, jamais un CDN.
       Nommage de flotte : `<famille>-<subset>-<poids>-<style>.woff2`.
       `font-display: swap` sur chaque face. `unicode-range` si latin-ext.
       Puis brancher `--titre` et `--corps` sur elles, en section 1.
     · les VALEURS de `:root` et des trois états de thème, aux hex du CONTRAT
       VISUEL de la fiche technique (§5). On remplace les valeurs, on ne
       renomme pas les jetons : les composants du socle lisent ces noms.
     · la matière et le geste propres au projet : filets, tampons, ombres,
       textures, la signature de forme.
     · les composants que le moteur de blocs du projet ajoute.

   CE QUI NE SE FAIT PAS ICI :
     · toucher aux sections 1 à 9 autrement qu'en changeant des VALEURS de
       jetons. Une règle de socle qui gêne la DA est une conversation à avoir,
       pas une ligne à supprimer.
     · ajouter un second accent (la charte l'interdit).
     · un dégradé, un glassmorphism, une ombre molle, un emoji en guise
       d'icône, un arrondi au-delà de 3 px.
     · ⚠ AJOUTER UN `h4` PARCE QUE LES AUTRES PROJETS EN ONT UN. Voir la note
       de la section 4 : les six en stylisent un, aucune page n'en emploie.

   APRÈS AVOIR ÉCRIT CETTE SECTION : `node tools/check-contraste.js`. Il lit
   LES JETONS DE CETTE FEUILLE (il ne contient aucune couleur), résout les
   quatre états de thème avec leur chaîne de repli, composite les alphas, et
   REFUSE un couple sous son seuil. On mesure, on ne repeint pas : un couple
   sous son seuil est une décision de DA, et elle appartient au président.
   ========================================================================== */

/* ==========================================================================
   10.1  LES QUATRE FACES

   Deux familles, deux graisses chacune, woff2 LOCAUX, sous-ensemble latin.
   Licences, auteurs et sources : `build/content/polices.json`, domicile unique
   du fait — et la page de crédits se CONSTRUIT depuis là, jamais à la main.
   `build.js` refuse tout écart entre ce catalogue, cette feuille et le disque.

   ⚠ AUCUNE FACE ITALIQUE N'EST EMBARQUÉE, ET C'EST UNE CONTRAINTE DE DESSIN,
   PAS UN OUBLI : `font-synthesis: none` est posé au socle (un faux gras et un
   faux italique cassent la lisibilité), donc un `font-style: italic` écrit
   quelque part rendrait du DROIT en silence. La DA n'emploie donc l'italique
   NULLE PART — les incises se marquent par la casse, le filet ou la mesure.

   ⚠ `font-display: swap` sur chaque face, et les DEUX faces du premier écran
   sont préchargées (`lib-html.js → POLICES_PRECHARGEES`).
   ⚠ DETTE NOMMÉE, à rembourser à la recette au navigateur : les métriques de
   repli (`size-adjust`, `ascent-override`, `descent-override`) ne sont PAS
   déclarées. Elles se MESURENT dans un moteur, elles ne se devinent pas, et
   écrire des valeurs inventées serait pire que ne rien écrire. Le risque réel
   est borné : 67,4 Ko au total, même origine, préchargées et précachées.
   ========================================================================== */
@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/literata-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/literata-latin-700-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Noto Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/noto-sans-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Noto Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/noto-sans-latin-700-normal.woff2") format("woff2");
}

/* ==========================================================================
   10.2  LA SIGNATURE DE FORME — LE LIVRE

   La fiche §5 la nomme : filets verticaux de dos et de tranches, folios,
   colophon, signet, lettrine sobre, angles droits, ombres dures ou nulles.
   Elle se tient sur TOUTE la surface, et c'est ce qui sépare un parti pris
   d'un effet : une DA qui coupe ses angles les coupe partout.
   ========================================================================== */

/* --- LES DEUX PLATS. Le site est RELIÉ : il se termine sur la toile sombre du
   plat inférieur, et les pages vivent au-dessus. Le pied N'EST PAS une bande de
   plus — c'est un changement de MATIÈRE, donc il n'a pas besoin d'un filet pour
   se séparer de la page. */
.pied {
  background: var(--reliure);
  color: var(--reliure-encre);
  border-block-start: 0;
}
.pied h2 { color: var(--reliure-encre); }
.pied a { color: var(--reliure-accent); }
.pied a:hover { color: var(--reliure-encre); }
.pied__contact span { color: var(--reliure-encre-2); }
.pied__nom { color: var(--reliure-encre); }
.pied__bas {
  color: var(--reliure-encre-2);
  border-block-start: 1px solid var(--reliure-filet);
}
/* La marque, sur le plat, est titrée à l'encre de la toile. */
.pied .marque__signe { color: var(--reliure-accent); }

/* --- LE TITRE COURANT. La barre d'utilité est la ligne de tête d'un livre :
   au-dessus du texte, en petit, séparée par un filet FRANC. */
.utilite {
  border-block-end: 1px solid var(--filet-2);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.utilite a { text-decoration: none; }
.utilite a:hover { text-decoration: underline; }
/* le sélecteur de langue garde sa casse : un autonyme ne se met pas en
   capitales (« हिन्दी » et « 简体中文 » n'en ont pas, et « Français » y perdrait
   sa lisibilité). */
.langues, .langues__liste { text-transform: none; letter-spacing: 0; }

/* --- LE FAUX-TITRE. Le bandeau d'identité porte le nom de la maison en petit,
   au-dessus de tout : qui édite, et ce qu'il promet. */
.bandeau { border-block-end: 1px solid var(--filet); }
.bandeau__nom { font-family: var(--titre); }

/* --- LE DOS. Chaque tête de page pend d'un filet vertical d'accent, comme une
   page pend de sa couture ; chaque bloc de contenu pend d'un filet de
   composition. C'est le geste le plus répété du site, donc celui qui le rend
   reconnaissable au premier écran, sans son logo. */
.hero > .wrap,
.tete-page > .wrap {
  border-inline-start: 3px solid var(--accent);
}
.bloc {
  border-inline-start: 1px solid var(--filet);
  padding-inline-start: var(--s6);
}

/* --- LE TITRE DE SECTION, avec son filet qui court jusqu'à la marge : la
   forme d'un intertitre d'édition savante. */
/* ⚠ DEUX SÉLECTEURS, ET C'EST LA GRILLE DE MARGE QUI L'EXIGE. Un bloc de prose
   met désormais son contenu dans `.bloc__corps` (la colonne de texte), donc son
   `h2` n'est plus un enfant DIRECT de `.bloc`. Le premier jet de la grille a
   emporté le titre courant de toutes les sections de prose sans qu'aucun verrou
   le dise — un `>` qui ne matche plus ne lève rien, il rend juste un titre nu.
   Vu sur une capture, pas déduit. Les deux formes sont écrites ici, ensemble,
   pour qu'on ne puisse plus en corriger une seule. */
main .bloc > h2,
main .bloc > .bloc__corps > h2 {
  display: flex;
  align-items: center;
  gap: var(--s4);
  font-size: var(--t-l);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-wrap: initial;   /* `balance` n'a pas de sens sur une ligne en flex */
}
main .bloc > h2::after,
main .bloc > .bloc__corps > h2::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--filet);
}

/* --- LES ACTES DU HÉROS : leur double filet mesure cinq pixels au-delà du
   bouton, donc deux boutons voisins verraient leurs encadrements se toucher.
   L'espace se pose ici et non dans le gabarit : c'est un fait de DA. */
.hero .b--acte { margin-block-start: var(--s6); }
.hero .b--acte + .b--acte { margin-inline-start: var(--s8); }

/* 🔴 SUR UN TRÈS PETIT ÉCRAN, DEUX ACTES CÔTE À CÔTE NE TIENNENT PAS, ET LE
   CONSTAT EST VENU D'UNE CAPTURE, PAS D'UNE MESURE. Le verrou de largeur était
   VERT : rien ne débordait, `scrollWidth` valait exactement le viewport. Mais à
   360 px le second libellé se repliait sur deux lignes et le signet de réglages,
   qui est FIXE en bas à droite, recouvrait son coin. Un débordement se mesure ;
   un recouvrement se REGARDE. Les deux contrôles sont nécessaires.
   Les actes s'empilent donc, chacun sur sa ligne pleine largeur, et le second
   garde sa marge SOUS le premier au lieu de la garder à côté. */
@media (max-width: 30rem) {
  .hero .b--acte { display: flex; width: 100%; justify-content: center; }
  .hero .b--acte + .b--acte { margin-inline-start: 0; margin-block-start: var(--s8); }
}

/* LE SIGNET SE RÉDUIT À SON SIGNE SUR UN PETIT ÉCRAN. Son libellé fait de lui un
   bloc de cent quarante pixels de large, posé sur le contenu : sur un téléphone,
   c'est le tiers d'une ligne. L'icône seule suffit, et rien n'est perdu pour
   l'accessibilité — le bouton garde son texte pour lecteur d'écran (`.vh`), son
   `aria-expanded` et son `aria-controls`, et sa cible reste au-delà de 44 px. */
@media (max-width: 26rem) {
  .panneau-b__txt { display: none; }
}

/* ET LA DERNIÈRE LIGNE DU DOCUMENT NE SE CACHE JAMAIS SOUS LE SIGNET. Un bouton
   fixe survole ce qui défile, et c'est son principe ; mais ce qui est au bout du
   document ne peut plus défiler, donc on réserve sa hauteur au pied. Sans cette
   ligne, la dernière ligne du colophon est inatteignable sur un téléphone. */
@media (max-width: 52rem) {
  .pied { padding-block-end: calc(var(--s8) + 3.5rem); }
}

/* --- LE FILET DE SÉCURITÉ D'UN BOUTON TROP LONG. PRÉVENTIF, PAS CORRECTIF.
   `.b` est un `inline-flex` sans largeur maximale : rien n'empêcherait un
   libellé long de pousser la page au-delà du viewport. `max-width: 100%`
   plafonne le bouton à son conteneur ; le libellé, qui est un élément flex
   anonyme dont la largeur minimale est celle de son plus long MOT, se replie
   alors sur deux lignes au lieu de pousser quoi que ce soit.

   ⚠ ET IL FAUT DIRE LA VÉRITÉ SUR CE QUI S'EST PASSÉ ICI, parce qu'un faux
   « payé » écrit dans une feuille égare le prochain lecteur pour toujours.
   Une capture d'écran à 390 px semblait montrer un débordement franc, libellé
   d'acte compris. LA MESURE L'A DÉMENTI : au protocole DevTools, avec
   `Emulation.setDeviceMetricsOverride`, `scrollWidth` vaut EXACTEMENT la largeur
   du viewport à 360 px comme à 500 px, et aucun élément ne dépasse. Le
   débordement apparent venait de l'OUTIL : `--window-size` de Chrome sans
   interface n'était pas appliqué avant la première mise en page, la page se
   composait à la largeur par défaut, et la capture la recadrait.
   La règle de la maison est exactement là-dessus : on ne conclut jamais d'une
   mesure d'outillage sans avoir vérifié l'outil, et un faux rouge non instruit
   coûte plus cher qu'un rouge. Cette ligne reste parce qu'elle est un bon filet,
   pas parce qu'elle a réparé quelque chose.

   ⚠ ET DANS TOUS LES CAS : ON NE RÉDUIT PAS `font-size` SOUS UNE MEDIA QUERY
   POUR RÉGLER UN LIBELLÉ TROP LONG. Le libellé blanc sur l'accent plein est jugé
   au seuil du GRAND TEXTE, et `check-contraste.js` relit
   `.b--acte { font-size: var(--t-l) }` dans cette feuille, toutes at-rules
   FUSIONNÉES : une surcharge mobile ferait échouer le verrou, et c'est
   exactement ce qu'il est là pour faire. Un libellé trop long se raccourcit
   dans le CONTENU. */
.b { max-width: 100%; }

/* --- LA LETTRINE. Sobre : un seul corps plus gros, à l'accent, dans la police
   de titre. Aucun cadre, aucun ornement. Elle ne se pose que sur le premier
   paragraphe d'une page majeure (option `lettrine` du bloc « texte »).
   `float: inline-start` et non `left` : la maison servira l'arabe, et une
   lettrine flottant à gauche d'un texte de droite à gauche serait un bug.
   ⚠ ET LE TEXTE QUI LA REÇOIT NE COMMENCE JAMAIS PAR UN ARTICLE ÉLIDÉ.
   `::first-letter` ne prend qu'UNE lettre : sur « L'ODERSA », la lettrine sort
   « L » et laisse « 'ODERSA » au corps, ce qui se lit mal. Vu au navigateur, pas
   déduit. La contrainte appartient donc au CONTENU, et elle est écrite ici parce
   que c'est ici qu'on la découvrira. */
.lettrine::first-letter {
  float: inline-start;
  font-family: var(--titre);
  font-weight: 700;
  font-size: 3.1em;
  line-height: 0.78;
  padding-inline-end: var(--s3);
  padding-block-start: 0.05em;
  color: var(--accent);
}

/* 🔴 LA LETTRINE EST UN GESTE D'ÉCRITURE LATINE, ET ELLE NE SE FORCE PAS AILLEURS.
   Signalé par les rédacteurs hindi et arabe en contrôle croisé, et c'est un vrai
   problème de typographie et non un détail de rendu :
     · en devanagari et en bengali, `::first-letter` prend un GROUPE DE GRAPHÈMES
       et les moteurs ne s'accordent pas sur lequel : sur « किताबों » on obtient
       « कि » chez l'un et « क » chez l'autre, c'est-à-dire une syllabe coupée en
       deux, ce qu'aucun lecteur ne lit ;
     · en arabe, les lettres sont LIÉES : détacher la première la met en forme
       isolée et casse la ligature du mot. Ce n'est pas laid, c'est faux ;
     · en han, une lettrine n'existe pas comme convention, et un caractère à
       trois corps au début d'un paragraphe se lit comme une erreur de balisage.
   ⚠ ON NE RETIRE PAS `lettrine` DU CONTENU POUR AUTANT : c'est une valeur
   MACHINE, identique dans les sept langues, et elle dit « ce paragraphe OUVRE un
   chapitre ». C'est à la direction artistique de décider comment honorer cette
   ouverture dans chaque écriture. Ici : un filet d'accent épais au bord de
   départ, qui est le vocabulaire de la maison et qui marche dans toutes les
   écritures, dans les deux sens de lecture.
   ⚠ Le sélecteur cible l'ÉTIQUETTE BCP 47 posée sur `<html>` (`zh-Hans`, pas
   `zh`), parce que c'est ce que le gabarit écrit. */
:root[lang="ar"] .lettrine::first-letter,
:root[lang="hi"] .lettrine::first-letter,
:root[lang="bn"] .lettrine::first-letter,
:root[lang="zh-Hans"] .lettrine::first-letter {
  float: none;
  font-family: inherit;
  font-weight: inherit;
  font-size: inherit;
  line-height: inherit;
  padding: 0;
  color: inherit;
}
:root[lang="ar"] .lettrine,
:root[lang="hi"] .lettrine,
:root[lang="bn"] .lettrine,
:root[lang="zh-Hans"] .lettrine {
  padding-inline-start: var(--s6);
  border-inline-start: 6px solid var(--accent);
}

/* --- LES TRANCHES. Une carte est un feuillet posé sur un autre : deux ombres
   DURES (flou zéro, la charte l'exige) dessinent le feuillet du dessous et son
   bord. Aucun flou, aucun dégradé, aucun arrondi.
   ⚠ `box-shadow` est un axe PHYSIQUE : il ne suit pas `dir`. La pile s'inverse
   donc à la main en écriture RTL, exactement comme le `translateX` du panneau
   au socle. C'est la seule inversion manuelle de cette section. */
.carte {
  background: var(--fond);
  border: 1px solid var(--filet-2);
  box-shadow: 4px 4px 0 var(--fond), 5px 5px 0 var(--filet);
}
[dir="rtl"] .carte {
  box-shadow: -4px 4px 0 var(--fond), -5px 5px 0 var(--filet);
}
.carte h3 { font-size: var(--t-m); }

/* --- LES FOLIOS. Le rang de la page dans le plan de l'ouvrage, et le nombre
   de feuillets. Les DEUX nombres sont COMPTÉS au build : le rang se déduit de
   l'ordre du plan, le total du sitemap. Aucun n'est écrit à la main.
   Il vit sur le plat inférieur, là où un folio vit dans un livre. */
.folio {
  display: flex;
  align-items: center;
  gap: var(--s4);
  margin: 0 0 var(--s8);
  padding-block-end: var(--s4);
  border-block-end: 1px solid var(--reliure-filet);
  color: var(--reliure-encre-2);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  max-width: none;
}
.folio b {
  color: var(--reliure-encre);
  font-family: var(--titre);
  font-weight: 700;
  letter-spacing: 0;
}
.folio::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--reliure-filet);
}

/* --- LE COLOPHON. « Achevé de composer » : ce qui a servi à fabriquer la page
   qu'on vient de lire. Entièrement DÉRIVÉ (polices lues au catalogue, date de
   composition lue à la config, compteurs comptés) : c'est un colophon
   d'imprimeur, pas une signature décorative. */
.colophon {
  margin: 0 0 var(--s4);
  padding-block: var(--s3);
  border-block: 1px solid var(--reliure-filet);
  color: var(--reliure-encre-2);
  font-size: var(--t-xs);
  max-width: none;
}
.colophon b { color: var(--reliure-encre); font-weight: 700; }

/* --- LE SIGNET. Le motif d'accent de la maison, et il est FONCTIONNEL : le
   bouton des réglages EST un signet, ruban et encoche comprises. `clip-path`
   et non un arrondi — la DA coupe ses angles. */
.panneau-b {
  background: var(--accent);
  color: var(--acte-txt);
  padding-block-end: calc(var(--s3) + 9px);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% calc(100% - 9px), 0 100%);
  /* 🔴 LE SIGNET EST AU RAS DU BORD, ET C'EST À LA FOIS UNE CORRECTION ET LA
     VÉRITÉ DE LA FORME. Le socle le décollait de 16 px : un signet qui flotte au
     milieu de la marge n'est pas un signet, c'est un bouton rond en carré, et sur
     un téléphone ces 16 px suffisaient à le faire CHEVAUCHER le texte de la
     colonne au lieu de mordre sur la marge. Un signet sort de la tranche du livre
     et il y est COLLÉ : `inset-inline-end: 0`. Il continue de flotter au-dessus du
     défilement (c'est son principe), mais il occupe désormais le bord et non la
     page, et la hauteur qu'il masque au bas du document est réservée au pied. */
  inset-inline-end: 0;
}
.panneau-b__txt {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
}
/* Le panneau lui-même s'ouvre sur son signet : le filet d'accent du socle. */
.panneau__bloc h3 { font-family: var(--titre); }

/* --- LES ACTES. UN SEUL FILET, DÉCALÉ DE TROIS PIXELS.
   🔴 LE DOUBLE CADRE EST TOMBÉ LE 06/08/2026, ET LE MOTIF EST DE COMPOSITION.
   Le premier jet posait un DOUBLE anneau (1 px d'accent, 3 px de page, 1 px
   d'accent), c'est-à-dire cinq pixels de bord autour d'un bouton déjà en aplat
   plein. Regardé sur une page : le bouton ne se lisait plus comme un acte mais
   comme une VIGNETTE ENCADRÉE, et son voisin secondaire, qui portait le même
   double cadre, devenait indistinguable de lui. Deux boutons ceints du même
   ornement ne hiérarchisent rien.
   IL RESTE UN SEUL FILET, ET C'EST LUI QUI FAIT LA SIGNATURE : un trait de 1 px
   décalé de 3 px, comme le filet d'un cartouche de page de titre. Le décalage est
   ce qui le distingue d'une bordure ; l'unicité est ce qui rend le régime
   secondaire lisible à côté.
   ⚠ FLOU NUL, donc l'anneau ne participe pas à la mise en page et ne peut pas
   créer de débordement — c'est pourquoi c'est une `box-shadow` et non une bordure.
   ⚠ Le corps du libellé reste `var(--t-l)` : `check-contraste.js` le RELIT dans
   cette feuille, parce que réduire ce corps changerait le seuil exigé sans
   qu'aucune couleur bouge. */
.b--acte {
  box-shadow: 0 0 0 3px var(--fond), 0 0 0 4px var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.b--acte:hover {
  box-shadow: 0 0 0 3px var(--fond), 0 0 0 4px var(--accent-survol);
}
/* L'ACTE SECOND N'A NI ANNEAU NI APLAT : sa bordure de 1 px EST sa forme. Le
   `box-shadow` est explicitement remis à zéro, parce qu'un jour quelqu'un
   écrira `.b--second.b--acte` par distraction et récupérerait les deux. */
.b--second { box-shadow: none; text-transform: uppercase; letter-spacing: 0.06em; }
.b--second:hover { box-shadow: none; }

/* --- LES PAS. Des folios de chapitre : un chiffre dans un carré au filet, pas
   une pastille pleine. La police de titre, comme un numéro de page. */
.pas li::before {
  background: none;
  border: 1px solid var(--filet-2);
  color: var(--encre);
}

/* --- LE TIRAGE. Les compteurs se lisent comme un relevé d'imprimeur : un
   filet au-dessus de chaque nombre, et la police de titre. */
.chiffre {
  border-block-start: 1px solid var(--filet-2);
  padding-block-start: var(--s3);
}
.chiffre__q { text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--t-xs); }

/* --- LES QUESTIONS. Un filet en tête de la première, pour fermer la série. */
.bloc > .qr:first-of-type,
.bloc > .bloc__corps > .qr:first-of-type { border-block-start: 1px solid var(--filet); }

/* --- LE PLAN. Une table des matières : le titre, puis le filet de conduite. */
.plan-liste a { text-decoration: none; }
.plan-liste a:hover { text-decoration: underline; }

/* ==========================================================================
   10.3  L'IMPRESSION DE LA RELIURE

   ⚠ UNE PAGE IMPRIMÉE N'A PAS DE PLATS. Le socle passe déjà le corps en
   noir sur blanc ; sans les quatre règles ci-dessous, le pied de page sortirait
   en aplat sombre pleine largeur — un demi-cartouche d'encre par page, et un
   colophon illisible. Les couleurs sont écrites en dur ICI et seulement ici,
   pour la même raison que le socle le fait : à l'impression il n'y a plus de
   thème, donc plus de jeton à lire.
   ========================================================================== */
@media print {
  .pied {
    background: #FFFFFF;
    color: #000000;
    border-block-start: 1px solid #000000;
  }
  .pied a, .pied h2, .pied__nom, .pied__contact span, .pied__bas { color: #000000; }
  .folio, .colophon { color: #000000; border-color: #000000; }
  .folio::after { background: #000000; }
  .carte { box-shadow: none; border-color: #000000; }
  .b--acte { box-shadow: none; }
}

/* ==========================================================================
   10.4  LA MATIÈRE DE LA PAGE : IL N'Y EN A PLUS, ET C'EST LE GESTE.

   🔴 ICI VIVAIT LE PAPIER VERGÉ, et il est retiré par le redesign du 06/08/2026
   au soir. Une trame SVG en masque, seize vergeures, trois pontuseaux, vingt
   fibres, un jeton mesuré et neuf couples de contraste : tout cela servait
   l'archétype « édition savante » que le verdict de Mael refuse.

   POURQUOI ON L'ENLÈVE AU LIEU DE L'ATTÉNUER. Une matière de fond est une
   figure : elle occupe l'œil. C'était juste quand la page portait de la prose
   et qu'il fallait lui donner un support. Ça devient un bruit quand la page
   porte vingt couvertures colorées, parce qu'alors deux figures se disputent le
   même plan et la plus fine perd. Baisser son alpha aurait reproduit exactement
   la faute de la phase B, où la trame était payée et invisible : on aurait gardé
   le coût et perdu le bénéfice.

   CE QUI FAIT LA MATIÈRE MAINTENANT, ET C'EST PLUS SOLIDE : trois valeurs de
   surface (`--fond`, `--fond-2`, `--carte`), un filet, et une ombre à flou nul.
   La page n'imite plus du papier : elle POSE des objets qui, eux, en sont.

   ⚠ LA LEÇON `mask-image` NE SE PERD PAS POUR AUTANT, parce qu'elle vaut pour
   toute la flotte et qu'elle a coûté une page entière : `mask-image` masque
   l'élément ET TOUS SES DESCENDANTS. Posée sur `html`, la trame a sorti le site
   entier en filigrane, texte compris, avec le build, la parité, le maillage et
   le contraste au VERT. Une seule capture l'a montré. Elle est écrite au
   `CLAUDE.md` et au `JOURNAL.md`, où elle survit au dessin qui l'a payée.
   ⚠ `body` REDEVIENT DONC OPAQUE. Il avait été rendu transparent pour qu'un
   pseudo-élément à z-index négatif passe derrière lui ; sans pseudo-élément, un
   `body` transparent est un piège qui attend le prochain fond qu'on y posera.
   ========================================================================== */
body { position: relative; background: var(--fond); }

/* ==========================================================================
   10.5  LES COMPOSANTS DE LA MAISON

   Un par type de bloc que le moteur de ce projet ajoute au socle. Chacun a une
   FORME propre, parce que la règle de la maison est que deux pages du même site
   ne se ressemblent pas : ce sont les composants qui portent cette différence,
   pas des variantes de couleur du même gabarit.
   ========================================================================== */

/* --- UN BLOC PLEINE MESURE. Le `.bloc` du socle pend d'un filet de composition
   et rentre de six unités ; une composition qui doit occuper toute la largeur de
   la colonne reprend cette gouttière au lieu de déborder. */
.bloc--pleine { border-inline-start: 0; padding-inline-start: 0; }

/* ==========================================================================
   LA GRILLE DE MARGE — LA CORRECTION DE GÉOMÉTRIE DU 06/08/2026

   🔴 C'ÉTAIT LA CAUSE N°1 DU VERDICT « PAS CONVAINCANT », ET C'EST MESURABLE.
   Le conteneur fait 74rem, soit 1107 px de contenu à 1280 px de large. La prose,
   elle, est plafonnée à la MESURE (66ch, 642 px) par la règle de socle
   `p, li { max-width: var(--mesure) }`. Les deux vivaient DANS LE MÊME BLOC :
   chaque section de texte laissait donc 465 px de blanc à sa droite, et le filet
   du titre de section, qui court « jusqu'à la marge », courait jusqu'à 1082 px,
   c'est-à-dire 440 px AU-DELÀ de son propre texte. Un filet plus large que son
   texte ne compose rien : il DÉSIGNE UN VIDE, et l'œil le lit comme une page
   inachevée. Les folios des feuillets, calés au bord droit de cartes pleine
   largeur, flottaient au milieu de nulle part pour la même raison.

   LA RÉGLE, DÉSORMAIS : PLUS JAMAIS UN FILET PLUS LARGE QUE SON TEXTE.
   Deux façons de la tenir, et le générateur choisit selon le type de bloc :
     · LES BLOCS DE PROSE prennent cette grille. La colonne de texte vaut
       exactement la mesure ; la colonne de droite prend TOUT le reste et porte la
       MARGINALIA (folio de section, date de vérification, planche, renvoi). Le
       vide devient une marge, c'est-à-dire une partie du livre.
     · LES BLOCS PLEINE LARGEUR (tableau, duo, cartes, galerie, rayon, voies,
       plan, chiffres) ne la prennent pas : leur contenu occupe vraiment le
       conteneur, donc leur filet est déjà à la mesure de ce qu'il souligne.

   ⚠ LE SEUIL EST À 64rem, ET IL SE CALCULE. La grille demande 642 px de texte,
   48 px de gouttière et au moins 200 px de marge, soit 890 px de contenu ; avec
   les gouttières du conteneur et le retrait du bloc, cela tombe à 994 px, donc
   62rem. On prend 64rem pour garder de l'air : entre les deux, la marge passerait
   sous le texte pour quelques pixels, ce qui est le pire des deux mondes.
   ⚠ ET `minmax(0, …)` SUR LA COLONNE DE TEXTE N'EST PAS DÉCORATIF : sans lui, un
   contenu à largeur minimale intrinsèque (une adresse sans espace, un tableau)
   refuserait de se replier et POUSSERAIT la marge hors de l'écran. C'est la même
   famille de faute que le `min-width: 0` de l'en-tête mobile.
   ========================================================================== */
@media (min-width: 64rem) {
  .bloc--marge {
    display: grid;
    grid-template-columns: minmax(0, var(--mesure)) minmax(200px, 1fr);
    column-gap: var(--s12);
    align-items: start;
  }
  .bloc--marge > .bloc__corps { grid-column: 1; min-width: 0; }
  .bloc--marge > .marge { grid-column: 2; }
}

/* LA MARGINALIA. Elle n'invente RIEN : tout ce qu'elle porte est dérivé (le rang
   de la section est compté, la date est lue à la config, le renvoi est déduit du
   plan, la planche est l'image que la page sert déjà). Sa forme est celle d'une
   marge de livre savant : petit corps, capitales espacées, et une COUTURE qui la
   sépare du texte — le même filet vertical que celui dont pend chaque bloc. */
.marge {
  display: flex;
  flex-direction: column;
  gap: var(--s6);
  font-size: var(--t-xs);
  color: var(--encre-2);
  border-block-start: 1px solid var(--filet);
  padding-block-start: var(--s4);
  margin-block-start: var(--s6);
}
@media (min-width: 64rem) {
  /* au-delà du seuil, la couture passe à la VERTICALE : c'est une marge, plus un
     appendice sous le texte. */
  .marge {
    border-block-start: 0;
    border-inline-start: 1px solid var(--filet);
    padding-block-start: 0;
    padding-inline-start: var(--s6);
    margin-block-start: 0;
  }
}
.marge p { margin: 0; max-width: none; }
.marge b {
  display: block;
  font-weight: 700;
  color: var(--encre-3);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
/* LE FOLIO DE SECTION : le rang, en gros, dans la police de titre — un numéro de
   section de traité, pas une pastille. */
.marge__section { display: flex; align-items: baseline; gap: var(--s2); }
.marge__section b {
  display: inline;
  font-family: var(--titre);
  font-size: var(--t-xl);
  line-height: 1;
  color: var(--accent);
  letter-spacing: 0;
}
.marge__section span {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--encre-3);
}
.marge__fait time { font-family: var(--titre); font-weight: 700; color: var(--encre); }
.marge__renvoi a { display: inline-block; margin-block: var(--s1); font-weight: 700; }
.marge__folio-cible {
  display: block;
  color: var(--encre-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
/* LA PLANCHE EN MARGE. Le même fichier que la page sert déjà : le navigateur l'a
   dans son cache, donc elle ne coûte pas un octet de réseau. Elle porte un filet
   comme toute planche de ce site, mais pas les deux ombres dures : à cette
   taille, une pile de feuillets deviendrait du bruit. */
.marge__planche img {
  display: block;
  width: 100%;
  max-width: 22rem;
  height: auto;
  margin-block-start: var(--s2);
  border: 1px solid var(--filet-2);
}

/* ==========================================================================
   LES BANDES DE VALEUR — CE QUE L'ACCUEIL N'AVAIT PAS

   🔴 L'ACCUEIL ÉTAIT UNE SEULE SURFACE DU HAUT EN BAS, ET C'EST LA DEUXIÈME
   CAUSE DU VERDICT. Dix blocs de contenu, tous sur le même papier, séparés par
   des titres de section : rien ne disait où la page changeait de registre, donc
   rien ne retenait l'œil. Le modèle est celui de la vitrine `odersa.org`, qui
   alterne une bande claire et une bande noire : LA VALEUR fait la structure, pas
   la taille des titres.
   TROIS BANDES, ET CHACUNE A UN RÔLE :
     · le HÉROS reste sur le papier — c'est la page qui parle ;
     · « CE QUI SE COMPTE » passe sur la TOILE DE RELIURE, texte clair, chiffres
       en Literata à 64 px : le relevé d'imprimeur, tiré sur le plat ;
     · « PAR OÙ CONTINUER » passe sur le FOND D'ACCENT PÂLE : la sortie de la
       page, plus douce que la toile, plus marquée que le papier.
   ⚠ UNE BANDE SORT DU CONTENEUR ET GARDE SES GOUTTIÈRES : la couleur va d'un
   bord à l'autre de l'écran, le texte reste dans la mesure du livre. C'est le
   `.wrap` intérieur qui s'en charge, jamais un `margin` négatif.
   ⚠ ET LA BANDE DE RELIURE EST UN TROISIÈME PLAT, PAS UN SECOND ACCENT. La règle
   « `--reliure*` ne peint que les plats » tient toujours : la liste des familles
   admises de `audit-contenu.js §9.1` gagne `.bande--reliure`, et c'est une
   DÉCISION de direction artistique, écrite ici et là-bas. Un plat de reliure au
   milieu du livre est un CARTON, une forme d'édition qui existe.
   ========================================================================== */
/* ⚠ `.section.bande` ET NON `.bande` SEUL : le socle pose
   `.section + .section { padding-block-start: 0 }`, à spécificité (0,2,0). Une
   règle `.bande` (0,1,0) perdrait, et la bande serait collée à la section
   précédente sans son air en haut. Deux classes contre deux classes, l'ordre du
   fichier tranche, et la DA passe après le socle. */
.section.bande { padding-block: var(--s16); }
.bande--reliure {
  background: var(--reliure);
  color: var(--reliure-encre);
}
/* SUR LA TOILE, LES FILETS SUIVENT LA MATIÈRE. Un filet de papier (`--filet`)
   posé sur la reliure disparaît : c'est la même raison qui fait qu'un plat n'a
   pas besoin de bordure pour se séparer de la page. */
.bande--reliure .bloc { border-inline-start-color: var(--reliure-filet); }
.bande--reliure .bloc > h2::after,
.bande--reliure .bloc > .bloc__corps > h2::after { background: var(--reliure-filet); }
.bande--reliure .chiffre { border-block-start-color: var(--reliure-filet); }
.bande--reliure .chiffre__n { color: var(--reliure-encre); }
.bande--reliure .chiffre__q { color: var(--reliure-encre-2); }
.bande--reliure a { color: var(--reliure-accent); }
.bande--reliure p { color: var(--reliure-encre-2); }

/* LES CHIFFRES DE LA BANDE. 64 px en Literata : un relevé de tirage se lit de
   loin, sinon ce n'est pas un relevé, c'est une note de bas de page. La taille
   est en `rem` et non en `px` pour suivre le réglage de corps du panneau. */
.bande .chiffres { gap: var(--s12) var(--s8); }
.bande .chiffre__n { font-size: 4rem; }
@media (max-width: 52rem) {
  .section.bande { padding-block: var(--s12); }
  .bande .chiffre__n { font-size: 3rem; }
}

.bande--accent {
  background: var(--accent-fond);
  color: var(--encre);
}

/* QUAND LA DERNIÈRE SECTION EST UNE BANDE, LE PLAT LA SUIT SANS BLANC.
   Le pied porte `margin-block-start: var(--s16)` (socle) : après une bande de
   valeur, ce blanc laisse une lisière de papier de 64 px entre la couleur et la
   toile, et cela se lit comme une bande inachevée. `:has()` est le SEUL moyen de
   l'atteindre en CSS — le pied n'est pas un frère de la section, il est le frère
   de `main`.
   ⚠ LA RÈGLE EST SEULE DANS SON BLOC, ET C'EST OBLIGATOIRE : un sélecteur inconnu
   JETTE TOUTE SA RÈGLE. Groupée avec autre chose, elle emporterait ses voisines
   sur un moteur sans `:has()`. Seule, elle ne fait rien du tout, et le blanc de
   64 px revient : c'est une amélioration, pas une dépendance. */
main:has(> .section.bande:last-child) + .pied { margin-block-start: 0; }

/* ==========================================================================
   LA LISTE DES CRÉDITS DE POLICES — LE GABARIT QUI COLLAIT SES CHAMPS

   🔴 BUG DE GABARIT, VU SUR LA PAGE SERVIE : « Literatatitres et
   foliosgraisses 400, 700 ». Le générateur rend un `<b>` puis deux
   `<span class="etiq">`, et AUCUNE règle ne les habillait : trois champs de
   données se lisaient donc comme un seul mot. C'est la faute la plus embarrassante
   de la maquette, parce qu'elle est sur la page qui prouve que la maison tient ses
   licences. Une étiquette est un CARTOUCHE : elle a son cadre, son air et sa casse.
   ========================================================================== */
.credits-liste { margin: 0; padding: 0; list-style: none; }
.credits-liste > li {
  max-width: none;
  padding-block: var(--s6);
  border-block-start: 1px solid var(--filet);
}
.credits-liste > li:first-child { border-block-start: 2px solid var(--filet-2); }
.credits-liste p { margin-block-end: var(--s2); }
.credits-liste__tete {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2) var(--s3);
  margin-block-end: var(--s3);
}
.credits-liste__tete b {
  font-family: var(--titre);
  font-size: var(--t-l);
  font-weight: 700;
}
.etiq {
  padding: 1px var(--s2);
  border: 1px solid var(--filet-2);
  color: var(--encre-2);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

/* ==========================================================================
   LA FIGURE — la photographie, et sa chaîne de crédit VÉRIFIÉE PAR UNE MACHINE

   🔴 LE CRÉDIT N'EST PAS DANS LA LÉGENDE, ET C'EST UN ARBITRAGE ÉCRIT.
   La règle de la maison dit « jamais deux crédits, jamais zéro ». Une légende
   qui porterait l'auteur et la licence, plus une page de crédits construite
   depuis le catalogue, en ferait DEUX — donc deux endroits à corriger, donc un
   qui vieillit. La légende porte donc le propos éditorial, et un lien nommé
   « Crédit » vers l'ENTRÉE de cette image à la page des crédits, par une ancre.
   Conséquence machine, et c'est ce qui rend l'arbitrage tenable :
   `check-links.js` vérifie que toute ancre existe dans la page visée. Une image
   servie dont l'entrée de crédit disparaîtrait de la galerie casserait donc le
   maillage, et la publication s'arrêterait. Le crédit n'est plus une promesse
   éditoriale, c'est un lien vérifié.
   ========================================================================== */
.fig { margin: 0; }
.fig picture { display: block; }
.fig img {
  display: block;
  width: 100%;
  height: auto;
  /* LE FEUILLET SOUS LE FEUILLET, comme les cartes : deux ombres DURES.
     ⚠ `box-shadow` est un axe PHYSIQUE, il ne suit pas `dir` : la pile
     s'inverse à la main en écriture RTL, plus bas. */
  border: 1px solid var(--filet-2);
  box-shadow: 5px 5px 0 var(--fond), 6px 6px 0 var(--filet);
}
[dir="rtl"] .fig img { box-shadow: -5px 5px 0 var(--fond), -6px 6px 0 var(--filet); }
.fig figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2) var(--s4);
  margin-block-start: var(--s6);
  padding-block-start: var(--s2);
  border-block-start: 1px solid var(--filet);
  font-size: var(--t-s);
  color: var(--encre-2);
}
.fig__leg { flex: 1 1 18rem; max-width: var(--mesure); }
.fig__cr {
  flex: none;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.fig__cr a { text-decoration: none; }
.fig__cr a:hover { text-decoration: underline; }

/* ⚠ AUCUN `aspect-ratio` ET AUCUN `object-fit` SUR CES IMAGES, ET C'EST UNE
   DÉCISION. Le recadrage est fait UNE FOIS, hors ligne, par `tools/get-images.js`
   au point de focus de la photographie ; les fichiers servis portent déjà la
   bonne forme, et le gabarit écrit leurs `width`/`height` RÉELS. Un
   `object-fit: cover` par-dessus recadrerait une seconde fois, à l'aveugle, en
   coupant justement ce que le point de focus avait servi à garder — et il
   ferait payer au visiteur des pixels qu'il ne voit pas. La place est donc
   réservée par les dimensions déclarées (règle de socle : tout média porte ses
   dimensions ; un décalage de mise en page ne se rattrape pas). */

/* L'ENCART : une planche insérée dans le cours du texte, aux DEUX TIERS de la
   chasse (740 px), l'une des trois largeurs normées de la maison. `max-width` en
   `rem` et non en pixels : à texte agrandi, la planche grandit avec lui, ce qui
   est le comportement qu'on attend d'une illustration de livre. */
.fig--encart { max-width: 46.25rem; }

/* ==========================================================================
   LE BANDEAU MOBILE — PLEIN BLEED, ET UN AUTRE CADRE

   🔴 UN 16:7 SUR UN TÉLÉPHONE DE 360 px RENDAIT UNE BANDE DE 157 px DE HAUT,
   bordée de deux gouttières et d'un filet : un timbre posé au milieu de la page,
   qui ne montrait plus rien de la photographie. Ce n'était pas un problème de
   poids de fichier : aucune largeur ne répare un RAPPORT.
   DEUX CORRECTIONS QUI VONT ENSEMBLE :
     1. le fichier change — `tools/get-images.js` prépare un SECOND CADRE,
        portrait 4:5, recadré au même point de focus, et `<picture>` le choisit
        par `media` (art direction, pas responsivité) ;
     2. la place change — la planche sort des gouttières et touche les deux bords
        de l'écran. Elle perd son filet et ses ombres dures : une image à fond
        perdu n'a pas de tranche, elle EST la page.
   ⚠ ET LE RAPPORT SE DÉCLARE ICI. Les `width`/`height` de la balise décrivent le
   cadre de BUREAU : sans `aspect-ratio` sous la même media query que la
   `<source>`, le navigateur réserverait la place d'un 16:7 puis y mettrait un 4:5,
   c'est-à-dire exactement le décalage de mise en page que la règle des dimensions
   sert à empêcher. C'est la SEULE exception à « aucun aspect-ratio sur ces
   images », et elle est écrite ici parce que c'est ici qu'on la comprend. */
@media (max-width: 40rem) {
  .fig--bandeau { margin-inline: calc(-1 * var(--gouttiere)); }
  .fig--bandeau img {
    aspect-ratio: 4 / 5;
    border: 0;
    box-shadow: none;
  }
  [dir="rtl"] .fig--bandeau img { box-shadow: none; }
  .fig--bandeau figcaption { padding-inline: var(--gouttiere); }
}

/* --- LA GALERIE DES CRÉDITS. Construite depuis le catalogue d'images : elle ne
   peut donc rien oublier ni rien inventer. Chaque entrée porte son `id`, qui est
   la cible du lien « Crédit » des figures. */
/* 🔴 LA GALERIE SERVAIT UNE QUATRIÈME LARGEUR, ET C'EST CE QUI LA FAISAIT LIRE
   COMME UN ACCIDENT. Ses entrées valaient `minmax(20rem, 1fr)`, soit environ
   340 px sur un écran de bureau : des vignettes sur une page composée à 1107,
   alors que le reste du site sert 1107, 740 et 642. Une maquette qui emploie
   quatre largeurs n'en emploie aucune.
   ELLE PASSE DONC À LA LARGEUR « COLONNE » (642 px, la mesure de lecture) ET À
   UNE SEULE ENTRÉE PAR LIGNE, la planche à gauche et ses faits de licence dans la
   colonne de droite : c'est la même grille que la marginalia, et c'est la forme
   d'une TABLE DES ILLUSTRATIONS. La page est plus longue, et c'est juste : une
   page de crédits se consulte, elle ne se survole pas.
   ⚠ `aspect-ratio` ET `object-fit` SONT ASSUMÉS ICI, ET SEULEMENT ICI. La règle
   de la maison est qu'on ne recadre pas deux fois (le point de focus a déjà
   tranché) ; mais un INDEX doit aligner ses planches, sinon onze rapports
   différents produisent un escalier. C'est un index, pas une planche éditoriale. */
.credits-img { display: grid; gap: var(--s12); margin: 0; padding: 0; list-style: none; }
.credits-img > li {
  max-width: none;
  padding-block-start: var(--s3);
  border-block-start: 2px solid var(--filet-2);
}
@media (min-width: 64rem) {
  .credits-img > li {
    display: grid;
    grid-template-columns: minmax(0, var(--mesure)) minmax(200px, 1fr);
    column-gap: var(--s12);
    align-items: start;
  }
  .credits-img > li > picture { grid-row: 1 / span 20; }
}
.credits-img img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 3 / 2; object-fit: cover;
  border: 1px solid var(--filet-2);
  margin-block-end: var(--s3);
}
.credits-img p { margin-block-end: var(--s1); max-width: none; font-size: var(--t-s); }
.credits-img__tete { font-family: var(--titre); font-weight: 700; font-size: var(--t-m); }

/* ==========================================================================
   LE RAYON — L'ÉTAGÈRE, ET L'ÉTAT VIDE HONNÊTE

   La composition du catalogue est DÉRIVÉE : un dos par livre réellement sur le
   disque, et rien quand il n'y en a aucun. Dessiner des dos vides pour meubler
   annoncerait des livres qui n'existent pas ; laisser un texte seul serait
   austère. La planche, elle, est toujours là : c'est le rayon qui attend, et
   c'est exactement ce que la maison a à montrer aujourd'hui.
   ========================================================================== */
.rayon { margin-block: var(--s8) 0; }
.rayon__dos {
  display: flex;
  align-items: flex-end;
  gap: var(--s2);
  min-height: 8rem;
  padding-inline: var(--s4);
}
.rayon__un {
  flex: 0 0 auto;
  width: 2.25rem;
  background: var(--accent-fond);
  border: 1px solid var(--accent);
  border-block-end: 0;
  writing-mode: vertical-rl;
  /* ⚠ `writing-mode: vertical-rl` FAIT TOURNER LES AXES LOGIQUES : dans ce bloc,
     `block` devient l'horizontale. C'est la seule exception connue à la règle
     des propriétés logiques du socle, et elle se commente sur place, comme
     annoncé §6. Les marges de ce composant sont donc posées par son parent. */
  padding: var(--s4) var(--s2);
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-xs);
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: 12rem;
}
/* LA PLANCHE. Un filet épais d'accent qui dépasse des deux côtés : c'est le
   même geste que le RAYON de la marque, et c'est lui qui fait lire trois
   rectangles comme des livres plutôt que comme n'importe quoi. */
.rayon__planche {
  height: 4px;
  background: var(--accent);
  margin-inline: 0;
}
/* 🔴 L'ÉTAGÈRE EST DESSINÉE, ET C'EST LE CHANGEMENT LE PLUS VISIBLE DE LA PAGE
   DU CATALOGUE. Avant : un cartouche de texte posé sur un filet, et la promesse
   « un rayon vide se regarde en face » n'était tenue par AUCUNE forme — on lisait
   une note d'erreur, pas une étagère. Maintenant : HUIT DOS DE LIVRES VIDES, en
   filets, sans un titre, dont UN SEUL en pointillé : la place du premier volume.
   ⚠ POURQUOI DES DOS VIDES NE SONT PAS UNE PROMESSE. La doctrine refuse
   « dessiner des dos pour meubler », et elle a raison : un dos TITRÉ annoncerait
   un livre. Un dos VIDE ne dit rien d'autre que ce qui est vrai — l'étagère
   existe, elle est construite, et elle attend. C'est la différence entre montrer
   une PLACE et annoncer un CONTENU, et c'est exactement ce que la page dit en mots
   depuis le premier jour.
   ⚠ ET LES HUIT DOS SONT DESSINÉS PAR LA FEUILLE, PAS PAR LE CONTENU : ils ne
   comptent rien, ils ne se traduisent pas, ils n'entrent dans aucun compteur. Le
   jour où un livre existe, `build.js` rend `.rayon__dos` (les vrais dos, titrés,
   comptés) et cette planche vide disparaît d'elle-même. */
.rayon__attente {
  display: flex;
  align-items: flex-end;
  /* ⚠ LES VOLUMES SE RANGENT AU BORD DE DÉPART, PAS AU CENTRE, ET LA PLANCHE EST
     PLUS LARGE QU'EUX. Le premier jet les centrait : huit dos au milieu d'une
     planche de 1082 px refaisaient exactement le défaut qu'on venait de corriger
     ailleurs (un filet plus large que son contenu, donc un vide désigné). Rangés
     au bord, la planche vide à leur droite ne DÉSIGNE plus un manque : elle EST
     le manque, et c'est ce que la page dit en mots. Une étagère à moitié garnie
     se remplit par un bout. */
  justify-content: flex-start;
  gap: var(--s3);
  min-height: 11rem;
  padding-inline: var(--s4);
}
.rayon__place {
  flex: 0 0 auto;
  width: 2.25rem;
  border: 1px solid var(--filet-2);
  border-block-end: 0;
  background: var(--fond-2);
  /* LES NERFS. Deux filets en travers du dos, comme les nervures d'une reliure à
     nerfs : c'est ce qui fait lire un rectangle comme un DOS et non comme une
     colonne. Ombres INTÉRIEURES à flou nul : elles ne participent pas à la mise
     en page et ne peuvent pas déborder. */
  box-shadow: inset 0 1.7rem 0 -1.5rem var(--filet-2), inset 0 -2.4rem 0 -2.2rem var(--filet-2);
}
/* Les hauteurs sont IRRÉGULIÈRES, et c'est ce qui distingue une étagère d'un
   graphique : huit volumes de la même hauteur exacte n'existent dans aucune
   bibliothèque. */
.rayon__place:nth-child(1) { height: 8.5rem; }
.rayon__place:nth-child(2) { height: 9.5rem; }
.rayon__place:nth-child(3) { height: 8rem; }
.rayon__place:nth-child(4) { height: 10rem; }
.rayon__place:nth-child(5) { height: 9rem; }
.rayon__place:nth-child(6) { height: 8.5rem; }
.rayon__place:nth-child(7) { height: 9.5rem; }
.rayon__place:nth-child(8) { height: 8rem; }
/* 🔴 CINQ PLACES AU LIEU DE HUIT SUR UN TÉLÉPHONE, ET C'EST LA RECETTE QUI L'A
   DIT. Mesuré à 360 px : `scrollWidth` valait 437 px pour un viewport de 360, et
   deux dos dépassaient de 29 et 77 px. Huit dos de 36 px plus leurs gouttières ne
   tiennent pas dans les 288 px utiles d'un téléphone, et une étagère qui fait
   défiler la page horizontalement est un défaut, pas une étagère.
   ⚠ ON EN MONTRE MOINS, ON N'EN RÉTRÉCIT PAS HUIT. Réduire les huit dos à 20 px
   les aurait transformés en peigne : ce ne sont plus des livres. Ces places sont
   DESSINÉES PAR LA FEUILLE et ne portent aucun contenu (aucun compteur, aucune
   traduction, aucun texte pour lecteur d'écran) : en montrer cinq n'ôte rien à
   personne, et une étagère plus courte sur un écran plus étroit est exactement ce
   qu'on attend d'une étagère. */
@media (max-width: 52rem) {
  .rayon__attente { gap: var(--s2); padding-inline: var(--s2); }
  .rayon__place { width: 1.75rem; }
  .rayon__place:nth-child(n+6) { display: none; }
}

/* LE PREMIER VOLUME : la seule place nommée, en pointillé, à l'accent. Le
   pointillé dit « pas encore » sans écrire un mot, et il le dit dans les sept
   écritures. */
.rayon__place--premier {
  width: 2.75rem;
  border: 1px dashed var(--accent);
  border-block-end: 0;
  background: var(--accent-fond);
  box-shadow: none;
  writing-mode: vertical-rl;
  padding: var(--s4) var(--s2);
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-xs);
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  white-space: nowrap;
  overflow: hidden;
}
@media (max-width: 52rem) {
  /* ⚠ LE LIBELLÉ DU PREMIER VOLUME SE LIT EN ENTIER, OU IL NE SE MET PAS.
     Vu sur une capture à 360 px : « PREMIER VOLU » — le dos faisait 136 px de haut
     pour un texte vertical de 120 px plus ses 32 px de rentrée, et `overflow:
     hidden` avait coupé le mot. Un libellé tronqué est pire qu'un libellé absent :
     il donne à lire une erreur. On rend donc CE dos plus haut que les autres, ce
     qui est d'ailleurs juste — c'est le seul qui porte un titre.
     ⚠ Deux classes, pour battre `:nth-child()` qui fixe les hauteurs. */
  .rayon__place.rayon__place--premier {
    width: 2.5rem;
    height: 10.5rem;
    padding-block: var(--s2);
  }
}
.rayon__vide {
  padding: var(--s8) var(--s6) var(--s6);
  border-block-start: 1px solid var(--filet);
}
.rayon__vide p { margin: 0; }
.rayon__vide p + p { margin-block-start: var(--s3); }
/* 🔴 LE BADGE NOIR EST DEVENU UN CARTOUCHE. Un aplat d'encre portant « RAYON
   VIDE » se lit comme un bandeau d'avertissement : la maison s'accusait
   elle-même en capitales noires sur sa propre page de catalogue. Le FAIT ne
   change pas (aucun livre n'est publié) mais il se dit comme un ÉTAT DE
   FABRICATION et non comme une panne : cartouche à l'accent pâle, filet d'accent,
   libellé « en préparation ». C'est le vocabulaire d'un éditeur, et il est vrai. */
.rayon__etat {
  display: inline-block;
  margin-block-end: var(--s4);
  padding: var(--s2) var(--s4);
  background: var(--accent-fond);
  border-inline-start: 3px solid var(--accent);
  color: var(--accent);
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* ==========================================================================
   LE DUO — DEUX COLONNES EN REGARD

   La forme d'un tableau de concordance : ce qui entre à gauche, ce qui n'entre
   pas à droite, séparés par la COUTURE (un filet vertical d'accent). Sous
   46rem les deux colonnes s'empilent et la couture passe à l'horizontale : un
   regard n'a pas de sens sur une seule colonne.
   ========================================================================== */
.duo { display: grid; gap: var(--s8); }
@media (min-width: 46rem) {
  .duo { grid-template-columns: 1fr 1fr; gap: var(--s12); }
  .duo__col + .duo__col {
    padding-inline-start: var(--s12);
    border-inline-start: 3px solid var(--accent);
    margin-inline-start: calc(-1 * var(--s6));
  }
}
.duo__col + .duo__col { padding-block-start: var(--s6); border-block-start: 3px solid var(--accent); }
@media (min-width: 46rem) {
  .duo__col + .duo__col { padding-block-start: 0; border-block-start: 0; }
}
.duo__col h3 {
  font-size: var(--t-s);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding-block-end: var(--s2);
  border-block-end: 1px solid var(--filet);
}
.duo__col ul { margin: 0; padding: 0; list-style: none; }
.duo__col li {
  margin-block-end: var(--s4);
  padding-inline-start: var(--s4);
  border-inline-start: 1px solid var(--filet);
}
.duo__col--refus li { border-inline-start-color: var(--alerte); }

/* ==========================================================================
   LE TABLEAU — un vrai tableau, et il DÉFILE SEUL

   ⚠ Un tableau à quatre colonnes ne rentre pas dans 360 px, et ce n'est pas un
   bug à corriger en réduisant le corps : c'est un conteneur à faire défiler.
   L'enveloppe porte `overflow-x: auto`, donc c'est ELLE qui défile et jamais la
   page. `tabindex="0"` sur l'enveloppe : une zone défilable doit être atteignable
   au clavier, sinon son contenu est hors d'atteinte sans souris.
   ========================================================================== */
.tableau-env { overflow-x: auto; margin-block: var(--s6) 0; border-block: 1px solid var(--filet-2); }
.tableau-env:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.tabl { width: 100%; border-collapse: collapse; font-size: var(--t-s); }
.tabl caption {
  padding-block-end: var(--s3);
  text-align: start;
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-s);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.tabl th, .tabl td {
  padding: var(--s3) var(--s4);
  text-align: start;
  vertical-align: top;
  border-block-end: 1px solid var(--filet);
  max-width: 24rem;
}
.tabl thead th {
  border-block-end: 2px solid var(--filet-2);
  font-family: var(--titre);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.tabl tbody th { font-family: var(--titre); }
.tabl tbody tr:nth-child(even) { background: var(--fond-2); }

/* ==========================================================================
   LES DÉFINITIONS — le vocabulaire de la maison

   La forme d'un glossaire d'ouvrage : le terme dans la police de titre, sa
   définition en retrait, un filet par entrée. Ce n'est ni une liste, ni une
   carte, ni une question : un mot qui se définit a sa propre forme.
   ========================================================================== */
.defs { margin: 0; }
.defs > div {
  padding-block: var(--s4);
  border-block-end: 1px solid var(--filet);
}
.defs > div:first-child { border-block-start: 1px solid var(--filet-2); }
.defs dt {
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-m);
  margin-block-end: var(--s1);
}
.defs dd {
  margin: 0;
  padding-inline-start: var(--s6);
  border-inline-start: 3px solid var(--accent);
  max-width: var(--mesure);
}

/* ==========================================================================
   LE JOURNAL — les entrées datées

   La forme d'un registre : la date en marge, dans la police de titre, et le
   texte en regard. Au-dessus de 46rem la date sort en colonne de marge, ce qui
   est exactement la place d'une date dans un registre d'atelier.
   ========================================================================== */
.journal { margin: 0; padding: 0; list-style: none; }
.journal > li {
  max-width: none;
  display: grid;
  gap: var(--s2) var(--s8);
  padding-block: var(--s6);
  border-block-start: 1px solid var(--filet);
}
.journal > li:first-child { border-block-start: 2px solid var(--filet-2); }
@media (min-width: 46rem) {
  .journal > li { grid-template-columns: 8rem minmax(0, 1fr); }
}
.journal__date {
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-xs);
  color: var(--encre-2);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}
.journal__corps h3 { font-size: var(--t-m); margin-block-end: var(--s2); }
.journal__corps p { margin: 0; }
.journal__corps p + p { margin-block-start: var(--s3); }

/* ==========================================================================
   LES RÉFÉRENCES — les sources, sous la forme d'une bibliographie

   Une bibliographie d'ouvrage : un filet fin, un rang, la source en évidence et
   son adresse en dessous. Rien à voir avec une liste de liens de navigation, qui
   est un ACTE ; une référence est une PREUVE, elle se lit et se recopie.
   ========================================================================== */
.refs { counter-reset: ref; margin: 0; padding: 0; list-style: none; }
.refs > li {
  counter-increment: ref;
  max-width: none;
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr);
  gap: var(--s4);
  padding-block: var(--s4);
  border-block-end: 1px solid var(--filet);
}
.refs > li::before {
  content: "[" counter(ref) "]";
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-s);
  color: var(--encre-3);
}
.refs__quoi { display: block; font-family: var(--titre); font-weight: 700; }
.refs__ou { display: block; font-size: var(--t-s); color: var(--encre-2); }
.refs__lien { display: inline-flex; align-items: center; gap: var(--s1); font-size: var(--t-s); }

/* 🔴 UNE ADRESSE AFFICHÉE EST UN MOT SANS ESPACE, ET ELLE POUSSE LA PAGE.
   Mesuré à la recette, à 360 px : sur la page des crédits, deux liens qui
   montrent une adresse débordaient de 72 et 142 px, et le corps du document
   défilait horizontalement à 502 px pour un viewport de 360. C'est le seul
   débordement réel que ce site ait produit, et il ne venait pas de la mise en
   page : il venait du CONTENU, une chaîne que rien ne peut couper.
   `overflow-wrap: anywhere` et non `word-break: break-all` : le second coupe les
   mots même quand il y a de la place, y compris dans du texte courant. Ici on ne
   coupe qu'en cas de nécessité, et seulement sur les liens qui montrent une
   adresse. */
.refs__lien, .credits-liste a, .credits-img a, .fig__cr a { overflow-wrap: anywhere; }

/* ==========================================================================
   LES VOIES — les chemins d'action de la page de contact

   Une page de contact n'est pas une liste de coordonnées : c'est un chemin. Une
   voie dit à QUI elle mène, POUR QUOI, et par OÙ passer. La forme est celle
   d'un formulaire de dépôt d'atelier : un cartouche par voie, l'adresse en
   dernier, jamais en premier.
   ========================================================================== */
.voies { display: grid; gap: var(--s8); margin: 0; padding: 0; list-style: none; }
@media (min-width: 52rem) {
  .voies { grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr)); }
}
.voies > li {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s6);
  background: var(--fond);
  border: 1px solid var(--filet-2);
  border-block-start: 4px solid var(--accent);
}
.voies h3 { margin: 0; font-size: var(--t-m); }
.voies dl { display: grid; gap: var(--s1) var(--s3); margin: 0; font-size: var(--t-s); }
@media (min-width: 30rem) {
  .voies dl { grid-template-columns: auto minmax(0, 1fr); }
}
.voies dt {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--encre-2);
  white-space: nowrap;
}
.voies dd { margin: 0; }
.voies__adresse {
  margin-block-start: auto;
  padding-block-start: var(--s3);
  border-block-start: 1px solid var(--filet);
  font-family: var(--titre);
  font-weight: 700;
  word-break: break-word;
}

/* ==========================================================================
   LES FEUILLETS — LA NARRATION DE L'ACCUEIL

   « Comment un livre naît » : des feuillets posés en escalier, chacun avec son
   titre courant et son rang. Ce n'est pas la liste numérotée `.pas` (une
   procédure sèche) : c'est une pile de pages qu'on tourne, et le décalage
   horizontal du bord haut la fait lire comme telle. Le décalage est un
   `padding`, jamais un `transform` : rien ne se recale à la première peinture,
   et rien ne peut déborder.
   ========================================================================== */
.feuillets { margin: 0; padding: 0; list-style: none; counter-reset: feuillet; }
.feuillets > li {
  counter-increment: feuillet;
  max-width: none;
  position: relative;
  padding: var(--s6);
  background: var(--fond);
  border: 1px solid var(--filet-2);
  box-shadow: 6px 6px 0 var(--fond), 7px 7px 0 var(--filet);
}
[dir="rtl"] .feuillets > li { box-shadow: -6px 6px 0 var(--fond), -7px 7px 0 var(--filet); }
.feuillets > li + li { margin-block-start: var(--s8); }
/* L'ESCALIER. Chaque feuillet rentre un peu plus que le précédent, jusqu'au
   quatrième : au-delà, la mesure de lecture deviendrait trop courte. */
@media (min-width: 52rem) {
  .feuillets > li:nth-child(2) { margin-inline-start: var(--s6); }
  .feuillets > li:nth-child(3) { margin-inline-start: var(--s12); }
  .feuillets > li:nth-child(n+4) { margin-inline-start: var(--s16); }
}
.feuillets__tete {
  display: flex;
  align-items: baseline;
  gap: var(--s4);
  margin-block-end: var(--s3);
  padding-block-end: var(--s2);
  border-block-end: 1px solid var(--filet);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--encre-2);
}
.feuillets__tete::after {
  content: counter(feuillet);
  margin-inline-start: auto;
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-m);
  color: var(--accent);
  letter-spacing: 0;
}
.feuillets h3 { font-size: var(--t-l); margin-block-end: var(--s2); }
.feuillets p { margin: 0; }
.feuillets p + p { margin-block-start: var(--s3); }

/* ==========================================================================
   LA CITATION — l'exergue

   Un seul geste, et il est rare sur ce site : une phrase mise en évidence par sa
   MESURE et par un filet d'accent épais, dans la police de titre. Pas de
   guillemets dessinés, pas d'italique (aucune face italique n'est embarquée) :
   la forme suffit à dire que c'est une citation.
   ========================================================================== */
.exergue {
  margin: 0;
  padding-inline-start: var(--s8);
  border-inline-start: 6px solid var(--accent);
}
.exergue p {
  font-family: var(--titre);
  font-size: var(--t-xl);
  line-height: 1.3;
  margin-block-end: var(--s3);
}
.exergue footer { font-size: var(--t-s); color: var(--encre-2); text-transform: uppercase; letter-spacing: 0.06em; }

/* ==========================================================================
   LE MEGA MENU — il n'apparaît QU'AU COMPTE RÉEL

   Règle de la fiche §5.2 : mega menu si le site atteint douze pages, navigation
   simple en dessous. Le nombre n'est pas écrit ici : `build.js` compte les
   entrées du plan pour robots et pose l'attribut `data-mega` sur la navigation.
   La feuille ne fait que peindre ce que le compte a décidé.
   ⚠ SANS JAVASCRIPT, LE MEGA MENU EST DÉPLIÉ (règles `.sans-js` du socle §5) :
   c'est la garantie « lisible sans JavaScript », et elle ne se retire jamais.
   ========================================================================== */
.nav--mega > ul { gap: var(--s2) var(--s6); }
.mega__b {
  display: inline-flex; align-items: center; gap: var(--s1);
  min-height: 44px; padding: 0;
  background: none; border: 0; color: inherit;
  font: inherit; cursor: pointer;
}
.mega__b[aria-expanded="true"] .ic { transform: rotate(180deg); }
.mega__panneau {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 100%;
  z-index: 44;
  padding-block: var(--s8);
  background: var(--fond);
  border-block: 1px solid var(--filet-2);
}
.mega__grille {
  display: grid;
  gap: var(--s8);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.mega__grille h3 {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--encre-2);
  padding-block-end: var(--s2);
  border-block-end: 1px solid var(--filet);
}
.mega__grille ul { margin: 0; padding: 0; list-style: none; }
.mega__grille li { max-width: none; }
.mega__grille a { display: flex; align-items: center; min-height: 40px; text-decoration: none; }
.mega__grille a:hover { text-decoration: underline; }
.mega__grille a[aria-current="page"] { font-weight: 700; }

/* ⚠ SANS JAVASCRIPT, LE MEGA MENU EST DÉPLIÉ, ET CES DEUX RÈGLES NE SE
   SUPPRIMENT JAMAIS. Le panneau est servi `hidden` (c'est son état fermé, et
   c'est aussi ce qui le sort de l'arborescence d'accessibilité) ; sans script,
   son bouton n'existe pas, donc rien ne pourrait l'ouvrir et la moitié de la
   navigation serait inatteignable. `!important` est obligatoire ici : la règle
   du socle est `[hidden] { display: none !important }`, et seule une
   spécificité supérieure ET un `!important` la battent. Même famille que les
   règles `.sans-js` du socle §5 : leur consommateur n'est pas visible dans un
   navigateur de développement, c'est le visiteur dont le script n'a pas chargé. */
.sans-js .mega__b { display: none; }
.sans-js .mega__panneau[hidden] {
  display: block !important;
  position: static;
  padding-block: var(--s4) 0;
  border: 0;
}
/* La tête doit pouvoir porter un panneau qui SORT vers le bas : elle est donc
   le contexte de positionnement, et sa bande est levée au-dessus du contenu.
   Les débordements vers le HAUT n'ont pas ce problème (voir socle §6). */
.tete { position: relative; z-index: 44; }
@media (max-width: 52rem) {
  /* Sur mobile, pas de survol et pas de place : le mega menu redevient une
     liste dépliée dans le tiroir de navigation. Aucun panneau flottant, aucune
     couche de plus. */
  .mega__b { display: none; }
  .mega__panneau { position: static; padding-block: var(--s4) 0; border: 0; }
  .mega__panneau[hidden] { display: block !important; }
  .mega__grille { gap: var(--s6); }
}

/* ==========================================================================
   LA NOTE DE STOCKAGE

   🔴 IL N'Y A PLUS DE BANDEAU DE MISE À JOUR ICI, ET C'EST UN ORDRE DE MAEL
   (07/08/2026). Ce bloc portait `.maj` : une barre fixe en bas de page, sur la
   toile du plat, avec « Une version plus récente est prête · Recharger
   maintenant · Plus tard ». Elle est RETIRÉE, composant compris : quinze règles
   de feuille, un `<div>` de gabarit, trois chaînes dans les sept `ui.json`, un
   message de service worker et un rechargement piloté.
   LA MISE À JOUR EST SILENCIEUSE : le worker n'appelle toujours pas
   `skipWaiting()`, donc la version neuve attend et prend la main au prochain
   chargement. Le seul danger que le bandeau évitait — deux versions mélangées
   dans un même onglet — est évité par L'ATTENTE, jamais par le prompt.
   ⚠ ET C'EST POURQUOI RIEN NE DOIT REVENIR ICI SANS QUE `skipWaiting()` RESTE
   ABSENT : le silence n'est sûr que parce que l'attente est tenue.
   Le raisonnement complet est dans `site/assets/js/app.js` §6.
   ========================================================================== */

/* LA NOTE DE STOCKAGE. Elle DIT que les préférences ne seront pas conservées,
   sans nommer la cause : « nous avons détecté que vous naviguez en privé » est
   à la fois faux (la cause peut être un réglage d'entreprise, un quota plein,
   une extension) et intrusif. C'est une information, pas un reproche. */
.panneau__stockage {
  margin: 0 0 var(--s4);
  padding: var(--s3) var(--s4);
  background: var(--fond-2);
  border-inline-start: 3px solid var(--alerte);
  font-size: var(--t-xs);
  max-width: none;
}

/* ==========================================================================
   10.7  LA LIBRAIRIE — LES COMPOSANTS DE L'ARCHÉTYPE NEUF (06/08/2026, soir)

   🔴 CETTE SECTION PORTE LE REDESIGN, ET ELLE COMMENCE PAR DÉFAIRE TROIS GESTES
   DE L'ANCIENNE DA. Verdict de Mael : « pas fan du tout, changement de design
   complet, plus clair, plus attirant, des vrais livres, vrai système quasi
   Amazon ». Ce qui suit n'ajoute pas un habillage : il retourne la hiérarchie de
   la page. Avant, la page était un TEXTE avec des ornements d'édition savante ;
   maintenant elle est une COLLECTION D'OBJETS avec du texte autour.

   ⚠ ELLE EST ÉCRITE COMME UN SYSTÈME OUVERT, PAS COMME UN GABARIT. Un opus
   « design par page » passera après celui-ci pour donner à chaque page une
   composition distincte. Les degrés de liberté sont donc DÉCLARÉS (les quatre
   jetons de grille en §1, les modificateurs de composition ici) et les invariants
   aussi. La liste des deux côtés vit au `CLAUDE.md` : la liberté du suivant doit
   être une LISTE, jamais une interprétation.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   10.7.0  🔴 LA SECTION DE PROSE RESSERRE SON CONTENEUR — LE DÉFAUT N°1 DE LA V1

   C'EST LE MÊME DÉFAUT QUE LE 06/08 AU MATIN, ET IL EST REVENU PAR LA PORTE DE
   DERRIÈRE. Ce jour-là, la cause n°1 du verdict « pas convaincant » avait été
   mesurée au pixel : un conteneur de 1 107 px portant une prose de 642 px, donc
   440 px de blanc à droite de chaque section de texte. Le remède avait été la
   grille de MARGINALIA, qui HABITAIT la colonne au lieu de la laisser vide.
   Puis, le 06/08 au SOIR, deux décisions justes l'ont défait sans le voir :
     · la marginalia est devenue FACULTATIVE (`marge: true`), et zéro bloc la
       demande, donc la colonne s'est vidée ;
     · `--large` est passé à 78rem parce que le CATALOGUE en a besoin (un rail de
       rayons plus quatre couvertures de front), donc la colonne s'est ÉLARGIE.
   Résultat mesuré sur la capture du 07/08 : 500 px de blanc à droite de chaque
   bloc de prose, sur les treize pages de devanture. Aucun des sept verrous ne
   pouvait le dire : ce n'est ni un contraste, ni un jeton, ni un débordement.

   LE REMÈDE EST UNE RÈGLE ET UN ATTRIBUT, ET IL NE TOUCHE QUE CE QU'IL DOIT.
   `build.js` pose `data-type` sur chaque bloc. Une section qui ne porte QUE des
   types de PROSE resserre son conteneur ; dès qu'elle porte un objet large (une
   grille de couvertures, un relevé de chiffres, un tableau, une galerie, un
   diptyque, un plan), elle garde toute la largeur, parce que cet objet l'occupe
   VRAIMENT — et c'est justement la distinction que la version précédente ne
   pouvait pas faire, faute de savoir ce qu'un bloc contenait.
   ⚠ LES CINQ TYPES DE LA LISTE SONT NOMMÉS UN PAR UN, ET NON DÉDUITS DE
   `.bloc--marge` : cette classe couvre dix-sept types, dont `faq`, `journal`,
   `references`, `feuillets`, `plan` et `polices`, qui sont de la prose au sens de
   la MESURE mais des objets au sens de la LARGEUR (un plan se met en colonnes,
   un registre porte un rail de dates, un appareil de notes porte trois champs).
   Les resserrer aurait cassé les compositions de page qui vivent dessus.
   ⚠ LA FIGURE EN « ENCART » EST DANS LA LISTE, LA FIGURE EN « BANDEAU » NON, et
   c'est pour cela que `build.js` sort aussi la VARIANTE : un encart fait 740 px et
   tient dans une colonne resserrée ; un bandeau fait 1 107 px et EST la largeur de
   la page. Sans cette distinction, la règle aurait écrasé les deux photographies
   pleine chasse de l'accueil, c'est-à-dire précisément ce qui donne son rythme à
   la page la plus vue du site.
   ⚠ ET LA RÈGLE NE S'APPLIQUE QU'AU-DELÀ DU SEUIL À DEUX COLONNES : sous 64rem
   il n'y a pas de blanc à récupérer, et un conteneur resserré au mobile ne serait
   qu'une gouttière de plus.
   --------------------------------------------------------------------------- */
/* 🔴 PREMIER ÉTAGE DU REMÈDE : LE CONTENEUR DE CONTENU N'EST PAS CELUI DE LA
   LIBRAIRIE. `--large: 78rem` existe pour UNE page, et elle en a vraiment besoin :
   le catalogue tient un rail de rayons plus quatre couvertures de front. Toutes
   les autres n'ont rien à mettre dans ces 1 168 px, et c'est ce qui fabriquait le
   vide. Le jeton reste donc à 78rem — c'est sa valeur de contrat — et il est
   RESSERRÉ sur `<main>` partout sauf au catalogue.
   ⚠ IL EST RESSERRÉ SUR `<main>` ET NON SUR `:root`, ET C'EST UN GESTE DE
   COMPOSITION : l'en-tête, le bandeau d'identité et le plat inférieur gardent la
   largeur de la maison, la colonne de contenu est plus étroite qu'eux. Un socle
   plus large que son contenu est une composition d'éditeur ; un socle à la même
   largeur que son contenu est une page de gabarit.
   ⚠ ET C'EST LA CLASSE DE PAGE QUI PORTE L'EXCEPTION, donc elle se lit : il n'y a
   pas de liste de pages à tenir à jour, seulement celle qui déroge.
   🔴 ⚠ LA SPÉCIFICITÉ EST DÉLIBÉRÉMENT LA PLUS BASSE POSSIBLE, ET C'EST UNE
   CORRECTION. Premier jet : `main:not(.p-catalogue)`, soit (0,1,1) — cela BATTAIT
   les fichiers de page, qui sont tous en `.p-<page>` à (0,1,0). Un agent de
   composition avait demandé 64rem sur sa page et recevait 62rem sans que rien le
   dise : un socle qui écrase silencieusement une décision de page n'est pas un
   défaut par sa valeur, il est un défaut par son SILENCE. La règle vaut donc pour
   `main` seul (0,0,1) et l'exception du catalogue vit sur sa classe (0,1,0), au
   même niveau que les fichiers de page — et la §11 étant écrite APRÈS, une page
   qui veut sa largeur l'obtient. `--large` est un degré de liberté DÉCLARÉ : il ne
   peut pas être déclaré libre et rendu inaccessible dans la même feuille. */
main { --large: 62rem; }
main.p-catalogue { --large: 78rem; }

@media (min-width: 64rem) {
  /* SECOND ÉTAGE : une section qui ne porte QUE de la prose resserre encore. */
  .section:not(.tete-page):not(.hero):not(:has(.bloc:not(:is(
      [data-type="texte"], [data-type="liste"], [data-type="note"],
      [data-type="citation"], [data-type="liens"],
      [data-type="figure"][data-variante="encart"])))) > .wrap {
    /* 52rem = 832 px, moins les deux gouttières (38 px chacune à 1 280 px) : environ
       755 px de contenu, pour une colonne de lecture de 642 px et un encart de
       740 px qui tient sans se réduire. Le blanc résiduel tombe donc à une
       centaine de pixels au lieu de cinq cents, et il devient la gouttière au lieu
       d'un vide — le même ordre de grandeur que les 38 px mesurés le 06/08. */
    max-width: 52rem;
  }
}

/* ---------------------------------------------------------------------------
   10.7.1  LES TROIS GESTES DÉFAITS

   Chacun était juste pour une bibliothèque savante et faux pour une librairie.
   Ils sont défaits ICI, en surcharge, plutôt que supprimés dans les sections du
   dessus : les règles de l'ancienne DA restent lisibles, ce qui les remplace est
   au même endroit, et un futur opus voit les deux états côte à côte.
   --------------------------------------------------------------------------- */

/* 🔴 ⓿ LE TITRE DE PAGE ÉTAIT RESTÉ EN LITERATA, ET LA DA DISAIT LE CONTRAIRE.
   CORRIGÉ LE 07/08/2026, ET C'EST LE MÊME DÉFAUT QUE LES ACTES (voir §10.1) :
   le renversement typographique du 06/08 au soir a écrit que « Noto Sans est la
   voix DE LA LIBRAIRIE : navigation, rayons, filtres, étiquettes, actes,
   compteurs, fil de page, ET LES TITRES DES PAGES QUI NE SONT PAS DES LIVRES ».
   Le `h2` a bien été retourné ci-dessous ; le `h1`, non. La feuille contredisait
   donc son commentaire sur le premier mot de chaque page.
   ⚠ ET LA PREUVE QUE C'EST UN OUBLI ET NON UN CHOIX EST DANS LA FEUILLE
   ELLE-MÊME : `.fiche__dit h1` et `.chap-tete h1` RE-DÉCLARENT `var(--titre)`
   explicitement (§10.7.7 et §10.7.8). Re-déclarer une valeur qu'on hérite déjà
   n'a de sens que si l'auteur croyait que le défaut avait changé. Les deux
   re-déclarations restent, et elles sont maintenant NÉCESSAIRES : une fiche de
   volume et une page de chapitre sont des LIVRES, donc elles gardent Literata.
   ⚠ RELEVÉ PAR UN AGENT DE COMPOSITION, qui avait commencé à le rustiner sur SA
   page et l'a signalé au rapport au lieu de le garder pour lui : quatorze
   rustines locales du même geste, c'est la divergence que la loi de flotte nomme.
   Corrigé UNE fois, ici. */
main .tete-page h1,
main .hero h1 { font-family: var(--corps); letter-spacing: -0.015em; }

/* ① LE TITRE COURANT EN CAPITALES ESPACÉES, SUIVI D'UN FILET PLEINE LARGEUR.
   C'était LA signature typographique de l'archétype savant, et c'est aussi ce
   qui rendait chaque section identique à la précédente : vingt titres en
   capitales suivis de vingt filets de la même longueur. Un titre de section doit
   dire « voici un nouveau sujet », pas « voici un nouveau paragraphe ».
   Ce qui le remplace : la casse normale, la voix de l'INTERFACE (Noto Sans), et
   un filet COURT d'accent posé AU-DESSUS. Le filet ne mesure plus la largeur du
   bloc, donc il ne peut plus désigner un vide : c'est la correction du 06/08 au
   matin, tenue par la forme au lieu d'être tenue par une grille. */
main .bloc > h2,
main .bloc > .bloc__corps > h2 {
  font-family: var(--corps);
  font-size: var(--t-xl);
  font-weight: 700;
  text-transform: none;
  letter-spacing: -0.005em;
  color: var(--encre);
  border-block-end: 0;
  padding-block-end: 0;
  padding-block-start: var(--s3);
  border-block-start: 2px solid var(--accent);
  width: fit-content;
  max-width: 100%;
}
main .bloc > h2::after,
main .bloc > .bloc__corps > h2::after { content: none; }

/* ② LA COLONNE DE MARGE DEVIENT UNE OPTION, ET ELLE EST VIDE PAR DÉFAUT.
   La marginalia (folio de section, date, renvoi, planche) répondait à un vrai
   défaut : une prose de 66ch dans un conteneur de 74rem laissait 440 px de blanc.
   Mais elle est un apparat d'édition savante, et « plus clair, plus direct » ne
   se compose pas avec une colonne d'apparat.
   LA RÈGLE DEVIENT : la prose habite une colonne de lecture CENTRÉE, et la marge
   ne se remplit que si un bloc la demande (`marge: true`). Le défaut est donc une
   colonne unique, et le vide résiduel n'existe plus puisqu'il n'y a plus de
   conteneur à remplir : c'est la mesure qui commande, pas la boîte.
   ⚠ `:has()` ET NON UNE CLASSE DE PLUS : la grille à deux colonnes ne se pose que
   si une marge existe VRAIMENT dans le DOM. Une classe posée par le générateur
   aurait pu mentir (une enveloppe à deux colonnes avec une marge vide donne
   exactement le vide de 440 px qu'on vient de payer deux fois). */
.bloc--marge { display: block; }
.bloc--marge > .bloc__corps { max-width: var(--mesure); }
.bloc--marge:has(> .marge) {
  display: grid;
  grid-template-columns: minmax(0, var(--mesure)) minmax(11rem, 1fr);
  gap: var(--s12);
  align-items: start;
}
@media (max-width: 64rem) {
  .bloc--marge:has(> .marge) { display: block; }
}

/* ③ LE DOS QUI PEND DE CHAQUE TÊTE DE PAGE ET DE CHAQUE BLOC.
   Le filet vertical d'accent était « le geste le plus répété du site », et c'est
   exactement le reproche : répété partout, il ne signifiait plus rien.
   IL N'EST PAS PERDU, IL A DÉMÉNAGÉ SUR L'OBJET. Un dos, sur ce site, est
   maintenant le dos d'un LIVRE : il vit sur la couverture (`.couv__dos`), où il
   désigne une vraie reliure. La signature de forme gagne en sens ce qu'elle perd
   en fréquence, et c'est le seul endroit du redesign où un geste de l'ancienne DA
   survit intact — au bon endroit. */
/* LE FILET DE 1 PX ET SON RETRAIT PARTENT DE CHAQUE BLOC, ET LA TETE DE PAGE
   GARDE LE SIEN. Deux mesures differentes pour deux roles differents : un signe
   d'identite en tete de page se voit une fois et porte la maison ; le meme signe
   repete sur vingt blocs ne dit plus rien et desaligne toute la prose du titre.
   ATTENTION : le premier jet de cette surcharge visait `::before`, qui n'existe
   pas. Le dos est un `border-inline-start`, donc la regle ne defaisait RIEN, et
   les sept verrous sont restes verts. C'est la CAPTURE qui l'a montre, et c'est
   la lecon de la passe precedente repayee : la mesure ne remplace pas le regard. */
.bloc { border-inline-start: 0; padding-inline-start: 0; }

/* ---------------------------------------------------------------------------
   10.7.2  🔴 LA COUVERTURE — LE COMPOSANT CENTRAL DU REDESIGN

   Une couverture n'est PAS une image : c'est une boîte, un aplat, un dos, un
   filet et deux textes. Elle est donc composée en HTML, dans la vraie Literata
   de la maison, et elle coûte zéro octet d'image sur une page qui en montre
   quinze. Le raisonnement complet est dans `build.js` §3quater.

   ⚠ TOUT L'INTÉRIEUR SE DIMENSIONNE EN `cqw`, ET C'EST CE QUI FAIT QU'IL N'Y A
   QU'UN SEUL GABARIT. La même couverture sert à 190 px dans la grille et à 300 px
   sur la fiche : elle n'a pas deux jeux de règles, elle a un conteneur. Sans les
   requêtes de conteneur il aurait fallu écrire deux composants jumeaux, et le
   second aurait divergé à la première retouche.
   --------------------------------------------------------------------------- */
.couv {
  container-type: inline-size;
  position: relative;
  display: block;
  aspect-ratio: var(--couv-rapport);
  overflow: hidden;
  /* le fond et l'encre viennent du RAYON, par le modificateur de catégorie */
  background: var(--couv-fond, var(--fond-3));
  color: var(--couv-encre, var(--encre));
  border: 1px solid var(--filet-2);
  border-radius: var(--rayon);
  /* une couverture est un objet POSÉ : ombre dure, flou nul (interdit de flotte
     sur l'ombre molle, et de toute façon une ombre floue ne ressemble pas à du
     papier). */
  box-shadow: var(--ombre-1);
  transition: box-shadow var(--duree) var(--sortie), transform var(--duree) var(--sortie);
}

/* LE DOS. Une bande verticale au bord de DÉPART : c'est la tranche reliée du
   volume, vue de face. ⚠ `inset-inline-start` et non `left` : en arabe, le dos
   passe à droite tout seul, parce que c'est de ce côté que s'ouvre le livre. */
.couv__dos {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 7cqw;
  background: var(--couv-dos, var(--filet-2));
}

/* LA CATÉGORIE, en petites capitales espacées : c'est la mention de collection
   d'un vrai livre, et elle est le seul texte d'interface admis sur l'objet. */
.couv__cat {
  position: absolute;
  inset-inline: 14cqw 8cqw;
  inset-block-start: 8cqw;
  font-family: var(--corps);
  font-size: 5.2cqw;
  font-weight: 700;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  opacity: 0.82;
}

/* LE TITRE, EN LITERATA : la pièce typographique du site.
   ⚠ `text-wrap: balance` fait un vrai travail ici : un titre de couverture sur
   trois lignes dont la dernière porte un mot seul se lit comme une erreur
   d'impression. Aucun repli à prévoir, un navigateur qui l'ignore compose
   simplement au fil. */
.couv__titre {
  position: absolute;
  inset-inline: 14cqw 8cqw;
  inset-block-end: 16cqw;
  font-family: var(--titre);
  font-size: 10cqw;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.015em;
  text-wrap: balance;
  /* ⚠ UN TITRE TRÈS LONG NE DOIT PAS SORTIR DE SA COUVERTURE. Cinq lignes est la
     limite de ce que le gabarit compose proprement ; au-delà on coupe, parce
     qu'un débordement de couverture est un pixel de trop que la recette refuse,
     et parce qu'un titre qui déborde n'est plus un titre. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  overflow: hidden;
}

/* LE FILET DE COUVERTURE : le trait d'imprimeur entre le titre et la marque. */
.couv__filet {
  position: absolute;
  inset-inline: 14cqw 8cqw;
  inset-block-end: 11cqw;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
}

/* LA MARQUE DE LA MAISON, en pied de couverture : l'ours de l'éditeur. */
.couv__marque {
  position: absolute;
  inset-inline: 14cqw 8cqw;
  inset-block-end: 5.5cqw;
  font-family: var(--corps);
  font-size: 4.4cqw;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  opacity: 0.7;
}

/* LES SIX RAYONS. Trois jetons chacun, et ils ne SORTENT JAMAIS de `.couv` :
   c'est la frontière qui empêche six teintes de devenir six accents. */
.couv--mathematiques { --couv-fond: var(--couv-mathematiques); --couv-encre: var(--couv-mathematiques-encre); --couv-dos: var(--couv-mathematiques-dos); }
.couv--lire-ecrire { --couv-fond: var(--couv-lire-ecrire); --couv-encre: var(--couv-lire-ecrire-encre); --couv-dos: var(--couv-lire-ecrire-dos); }
.couv--langues { --couv-fond: var(--couv-langues); --couv-encre: var(--couv-langues-encre); --couv-dos: var(--couv-langues-dos); }
.couv--sciences { --couv-fond: var(--couv-sciences); --couv-encre: var(--couv-sciences-encre); --couv-dos: var(--couv-sciences-dos); }
.couv--histoire { --couv-fond: var(--couv-histoire); --couv-encre: var(--couv-histoire-encre); --couv-dos: var(--couv-histoire-dos); }
.couv--methode-de-travail { --couv-fond: var(--couv-methode-de-travail); --couv-encre: var(--couv-methode-de-travail-encre); --couv-dos: var(--couv-methode-de-travail-dos); }

/* LES TROIS COMPOSITIONS. La variante est DÉRIVÉE de l'id du volume (somme des
   codes de caractères, modulo trois), donc stable pour un volume donné et
   idempotente au build. Une seule composition répétée quinze fois se lit comme un
   tableau ; trois se lisent comme une collection, ce qui est exactement ce que
   fait une maison d'édition avec une série. */

/* v1 : la composition de base, titre en bas. Rien à redéclarer. */

/* v2 : LE TITRE EN HAUT, la collection en pied. Le renversement classique. */
.couv--v2 .couv__cat { inset-block-start: auto; inset-block-end: 5.5cqw; opacity: 0.7; }
.couv--v2 .couv__titre { inset-block-end: auto; inset-block-start: 9cqw; }
.couv--v2 .couv__filet { inset-block-end: auto; inset-block-start: 7cqw; }
.couv--v2 .couv__marque { inset-block-end: auto; inset-block-start: 2.5cqw; font-size: 4cqw; opacity: 0.55; }

/* v3 : LE CADRE D'IMPRIMEUR. Un filet en encadrement, titre centré dans le
   cadre : la page de titre d'un manuel ancien, et la seule des trois qui emploie
   le filet comme une BOÎTE plutôt que comme un trait. */
.couv--v3 { --couv-cadre: 6cqw; }
.couv--v3 .couv__filet {
  inset: var(--couv-cadre) var(--couv-cadre) var(--couv-cadre) calc(var(--couv-cadre) + 7cqw);
  height: auto;
  background: none;
  border: 1px solid currentColor;
  opacity: 0.5;
}
.couv--v3 .couv__cat { inset-block-start: calc(var(--couv-cadre) + 5cqw); inset-inline: 18cqw 11cqw; }
.couv--v3 .couv__titre {
  inset-block-end: auto;
  inset-block-start: 32cqw;
  inset-inline: 18cqw 11cqw;
  font-size: 9cqw;
}
.couv--v3 .couv__marque { inset-block-end: calc(var(--couv-cadre) + 4cqw); inset-inline: 18cqw 11cqw; }

/* ---------------------------------------------------------------------------
   10.7.3  LA GRILLE ET LA CARTE PRODUIT
   --------------------------------------------------------------------------- */

/* LA GRILLE. `auto-fill` sur `--grille-min` : le nombre de colonnes est une
   CONSÉQUENCE de la place disponible, jamais un nombre écrit. C'est ce qui la
   rend juste de 360 px à un très grand écran sans une seule media query, et c'est
   aussi ce qui la rend redéfinissable par page (un opus suivant change
   `--grille-min` sur sa section et la grille suit).
   ⚠ MESURÉ SUR LA RÉFÉRENCE : deux colonnes à 360 px, et c'est le bon choix. Une
   grille de couvertures qui retombe à UNE colonne au mobile cesse d'être un
   rayon : on ne voit plus qu'un livre, donc on ne voit plus une collection.
   `minmax(0, 1fr)` en dessous de la largeur minimale garantit les deux colonnes
   sans jamais déborder. */
.grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--grille-min), 100%), 1fr));
  gap: var(--grille-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (max-width: 30rem) {
  .grille { grid-template-columns: repeat(2, minmax(0, 1fr)); --grille-gap: var(--s4); }
}

.carte-livre {
  display: flex;
  flex-direction: column;
  max-width: none;
  background: var(--carte);
  /* ⚠ LE FILET EST OBLIGATOIRE, ET LA MESURE L'A DÉCIDÉ : une carte blanche sur
     la page ne vaut que 1,03:1, c'est-à-dire rien. Sa limite ne dépend donc
     JAMAIS de sa surface, elle est dessinée. `check-contraste.js` mesure le
     filet, pas l'écart de fond, et le commentaire du couple le dit. */
  border: 1px solid var(--filet);
  border-radius: var(--rayon);
  padding: var(--s3);
}
.carte-livre__lien {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  text-decoration: none;
  color: inherit;
  flex: 1;
}
.carte-livre__lien:hover .couv,
.carte-livre__lien:focus-visible .couv {
  box-shadow: var(--ombre-2);
  transform: translate(-1px, -1px);
}
.carte-livre__corps { display: flex; flex-direction: column; gap: var(--s1); flex: 1; }
.carte-livre__cat {
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--encre-3);
}
/* LE TITRE DU VOLUME EST EN LITERATA, ET C'EST LA VOIX DU LIVRE.
   ⚠ IL EST PETIT EXPRÈS. La référence étudiée compose ses titres de carte à
   14 px pour une image de 248 px : dans une librairie, l'OBJET est plus gros que
   son étiquette. Un titre de carte à la taille d'un titre de section écraserait
   la couverture, c'est-à-dire la seule chose qu'on est venu voir. */
.carte-livre__titre {
  font-family: var(--titre);
  font-size: var(--t-s);
  font-weight: 700;
  line-height: 1.25;
  color: var(--encre);
}
.carte-livre__lien:hover .carte-livre__titre { color: var(--accent); text-decoration: underline; }
.carte-livre__sous {
  font-size: var(--t-xs);
  line-height: 1.45;
  color: var(--encre-2);
  /* deux lignes, et la coupe est assumée : la description entière vit sur la
     fiche. Une carte qui porte tout le texte n'est plus une carte. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.carte-livre__meta {
  font-size: var(--t-xs);
  color: var(--encre-3);
  margin-block-start: auto;
  padding-block-start: var(--s2);
}
.carte-livre__prix { margin: 0; padding-block-start: var(--s3); }
/* L'ÉTIQUETTE DE VOLUME DE RECETTE. Elle est en encre de CORRECTION, elle porte
   un mot, et elle est sur la carte AUTANT que sur la fiche : un volume d'essai
   qui se présenterait comme un volume de la maison serait précisément le
   mensonge que tout ce projet refuse. */
.carte-livre__test {
  margin: var(--s2) 0 0;
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--alerte);
  border-block-start: 1px solid var(--alerte);
  padding-block-start: var(--s2);
}

/* 🔴 L'ÉTIQUETTE « GRATUIT » — LA PLACE DU PRIX.
   Elle emprunte la GRAMMAIRE d'une boutique (l'endroit, le poids visuel, l'aplat
   de couleur) et jamais son COMMERCE. Ce qui n'existe nulle part dans cette
   feuille, et ne doit jamais y entrer : un prix, un prix barré, une remise, un
   panier, un compte, un délai, une rareté, une étoile d'avis. */
.gratuit { display: flex; flex-direction: column; gap: var(--s1); }
.gratuit__mot {
  align-self: start;
  background: var(--accent);
  color: var(--acte-txt);
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  padding: var(--s1) var(--s2);
  border-radius: var(--rayon-acte);
}
.gratuit__detail { font-size: var(--t-xs); color: var(--encre-3); }

/* ---------------------------------------------------------------------------
   10.7.4  LE CATALOGUE — LE PANNEAU DE FACETTES, LA BARRE, LES RAYONS
   🔴 REFAIT LE 07/08/2026, ORDRE DE MAEL : « /catalogue = un vrai catalogue style
   Amazon, avec filtres et tout ». C'est LA page qui doit impressionner.
   L'anatomie est celle d'une navigation à facettes réelle, et chaque pièce a été
   choisie pour une raison écrite à côté d'elle : colonne de facettes au bord de
   départ, barre d'outils au-dessus des résultats, grille sectionnée par rayon au
   tri par défaut, grille plate dès qu'un tri traverse les rayons.
   --------------------------------------------------------------------------- */
.catalogue { display: grid; gap: var(--s8); }
@media (min-width: 60rem) {
  .catalogue { grid-template-columns: var(--rail) minmax(0, 1fr); gap: var(--s16); align-items: start; }
}
.catalogue__corps { min-width: 0; }

/* LA COLONNE DE CÔTÉ porte le rail (sans script) PUIS le panneau de facettes
   (avec). Les deux ne coexistent jamais à l'écran : le script retire le rail
   quand il révèle le panneau, parce que l'axe « rayon » fait le même travail en
   mieux et que deux contrôles pour un geste est la redondance que cette DA
   refuse ailleurs.
   ⚠ `position: sticky` seulement au-dessus du seuil à deux colonnes : collé au
   mobile, un panneau deviendrait un bandeau qui suit le doigt. */
.catalogue__cote { min-width: 0; }
@media (min-width: 60rem) {
  .catalogue__cote {
    position: sticky;
    inset-block-start: var(--s4);
    /* il doit pouvoir DÉFILER seul : trois axes dépliés peuvent être plus hauts
       qu'un écran, et un panneau collé plus haut que la fenêtre coupe ses
       dernières cases sans qu'on puisse les atteindre. */
    max-block-size: calc(100vh - var(--s8));
    overflow-y: auto;
  }
}

/* LE RAIL DE RAYONS. De VRAIS liens d'ancre : il navigue sans une ligne de
   JavaScript, et c'est lui le chemin de secours quand les filtres n'existent pas. */
.rail { background: var(--carte); border: 1px solid var(--filet); padding: var(--s4); }
.rail__titre {
  font-family: var(--corps);
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--encre-2);
  margin: 0 0 var(--s3);
  padding-block-end: var(--s2);
  border-block-end: 1px solid var(--filet);
}
.rail__liste { margin: 0; padding: 0; list-style: none; }
.rail__liste li { max-width: none; }
.rail__liste a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  min-height: 44px;
  padding-inline: var(--s1);
  font-size: var(--t-s);
  text-decoration: none;
  border-block-end: 1px solid var(--filet);
}
.rail__liste li:last-child a { border-block-end: 0; }
.rail__liste a:hover { text-decoration: underline; }
.rail__n {
  flex: none;
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--encre-3);
  background: var(--fond-2);
  border-radius: var(--rayon-acte);
  padding: 0 var(--s2);
}

/* ---------------------------------------------------------------------------
   🆕 LE PANNEAU DE FACETTES
   ⚠ SERVI `hidden`, RÉVÉLÉ PAR `app.js` : une case à cocher sans script est un
   contrôle MORT, et la doctrine l'interdit explicitement. Sans JavaScript la
   grille entière est servie, sectionnée par rayon, et le rail suffit. Les règles
   ci-dessous ne peuvent donc pas se supprimer sans casser le sans-script.
   --------------------------------------------------------------------------- */
.cat-filtres {
  background: var(--carte);
  border: 1px solid var(--filet);
  /* LE FILET D'ACCENT EN TÊTE, pas un fond coloré : le panneau doit se lire comme
     un instrument POSÉ sur la table, et c'est la même grammaire que la carte d'un
     volume. Trois pixels vus une fois portent la maison. */
  border-block-start: 3px solid var(--accent);
  padding: var(--s4);
}
.cat-filtres__tete {
  margin: 0 0 var(--s3);
  padding-block-end: var(--s2);
  border-block-end: 1px solid var(--filet);
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--encre-2);
}

/* UN AXE = UN `<details>`. Repliable au clavier et annoncé par un lecteur
   d'écran SANS UNE LIGNE D'ARIA : un `aria-expanded` posé à la main sur un bouton
   est la façon la plus courante de mentir sur un état. */
.facette { border-block-end: 1px solid var(--filet); }
.facette:last-of-type { border-block-end: 0; }
.facette__tete {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-inline-end: var(--s8);
  font-size: var(--t-s);
  font-weight: 700;
  color: var(--encre);
  cursor: pointer;
  /* on retire le marqueur natif : sa forme et sa position changent d'un moteur à
     l'autre, et il n'y a pas de propriété pour l'aligner. */
  list-style: none;
}
.facette__tete::-webkit-details-marker { display: none; }
/* LE SIGNE D'OUVERTURE EST UN PLUS QUI DEVIENT UN MOINS, dessiné en deux barres.
   ⚠ PAS UN CHEVRON PIVOTÉ, et pas un caractère : un chevron demande une rotation
   (donc un `transform` sur un pseudo-élément que l'impression rend mal), et un
   caractère « + » dans un `content:` serait un signe posé par la feuille, ce que
   la règle « aucun mot dans un content » tolère mais que deux barres font mieux,
   à l'épaisseur exacte des filets de la maison. */
.facette__tete::before,
.facette__tete::after {
  content: "";
  position: absolute;
  background: var(--encre-2);
}
.facette__tete::after {
  inline-size: 11px;
  block-size: 2px;
  inset-inline-end: 0;
  inset-block-start: calc(50% - 1px);
}
.facette__tete::before {
  inline-size: 2px;
  block-size: 11px;
  inset-inline-end: 4px;
  inset-block-start: calc(50% - 5px);
}
.facette[open] > .facette__tete::before { display: none; }
.facette__liste { margin: 0 0 var(--s3); padding: 0; list-style: none; }
.facette__liste li { max-width: none; }

/* L'OPTION. ⚠ TOUTE LA SURFACE EST CLIQUABLE, et c'est la règle d'ergonomie la
   plus rentable d'un panneau de facettes : le `<label>` enveloppe la case ET son
   nom ET son compte, donc on ne vise jamais un carré de 13 px. */
.facette__opt {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
  min-height: 44px;
  padding-inline: var(--s1);
  font-size: var(--t-s);
  color: var(--encre);
  cursor: pointer;
}
/* ⚠ LA CASE ET LE COMPTE S'ALIGNENT SUR LA PREMIÈRE LIGNE DU NOM, PAS SUR SON
   MILIEU. « Apprendre une langue » se replie sur deux lignes dans une colonne de
   240 px : centrée, la case descendait entre les deux lignes et la colonne de
   cases perdait son alignement, c'est-à-dire la seule chose qui fait lire une
   liste de cases comme une liste. Mesuré à la capture. */
.facette__opt:has(.facette__nom) { align-items: start; }
.facette__opt input,
.facette__opt .facette__n { margin-block-start: 11px; }
.facette__opt .facette__nom { padding-block: var(--s2); }
.facette__opt input {
  /* `accent-color` teinte la case NATIVE : zéro dessin, zéro pseudo-élément, et
     elle garde son état de focus, son cochage clavier et son rendu de plateforme.
     Repeindre une case à la main est le premier pas vers un contrôle qui ne
     s'annonce plus. */
  accent-color: var(--accent);
  inline-size: 18px;
  block-size: 18px;
  margin: 0;
}
.facette__nom { min-width: 0; }
/* LE COMPTE DE L'OPTION, recalculé par le script à chaque geste. */
.facette__n {
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--encre-3);
  background: var(--fond-2);
  border-radius: var(--rayon-acte);
  padding: 0 var(--s2);
  font-variant-numeric: tabular-nums;
}
/* 🔴 L'ÉTAT COCHÉ EST LE SEUL ENDROIT DU SITE OÙ LE VERT CLAIR FAIT UN APLAT DE
   CONTRÔLE, ET C'EST EXACTEMENT SON EMPLOI : une SURFACE qui dit « actif ».
   ⚠ ET LA COULEUR N'EST JAMAIS SEULE : la case native est cochée (état annoncé),
   le nom passe en gras, et le compte reste écrit. Trois signaux, dont deux
   non chromatiques. */
.facette__opt:has(input:checked) { font-weight: 700; }
.facette__opt:has(input:checked) .facette__n {
  background: var(--accent-clair);
  color: var(--encre);
}
/* UNE OPTION QUI MÈNERAIT À ZÉRO RÉSULTAT SE DÉSACTIVE, ELLE NE DISPARAÎT PAS :
   une liste qui saute sous le doigt perd la place qu'on visait, et le focus avec.
   Son compte à zéro dit pourquoi elle est éteinte. */
.facette__opt:has(input:disabled) { color: var(--encre-3); cursor: default; }
/* ⚠ LE BOUTON NE PREND PAS TOUTE LA LARGEUR, ET C'EST UNE CORRECTION DE CAPTURE.
   À `inline-size: 100%` dans une colonne de 240 px, « Tout afficher » se coupait
   en deux lignes centrées et le bouton faisait 80 px de haut : un acte SECOND qui
   pèse plus lourd qu'un acte primaire. Il se dimensionne donc sur son texte,
   rangé au bord de départ comme tout le panneau, et son corps descend d'un cran :
   c'est le geste le moins important de la colonne, il ne doit pas être le plus
   gros. */
.cat-filtres__reset {
  margin-block-start: var(--s4);
  padding: var(--s2) var(--s4);
  font-size: var(--t-s);
}

/* ---------------------------------------------------------------------------
   🆕 LA BARRE D'OUTILS : la recherche, le tri, le compte.
   Elle vit AU-DESSUS des résultats et non dans la colonne de côté : c'est ce qui
   la rattache à ce qu'elle mesure. Un compte de résultats posé à côté du panneau
   de filtres se lit comme un compte de filtres.
   --------------------------------------------------------------------------- */
.cat-barre {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--s4);
  background: var(--fond-3);
  border: 1px solid var(--filet-2);
  border-radius: var(--rayon-acte);
  padding: var(--s3) var(--s4);
  margin-block-end: var(--s8);
}
.cat-barre__champ { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
/* le champ de recherche PREND LA PLACE QUI RESTE : c'est le geste le plus fréquent
   d'un catalogue, et un champ de recherche étroit se lit comme un champ secondaire. */
.cat-barre__champ--q { flex: 1 1 14rem; }
.cat-barre__champ label {
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--encre-2);
}
.cat-barre select, .cat-barre input[type="search"] {
  font: inherit;
  font-size: var(--t-s);
  color: var(--encre);
  background: var(--carte);
  border: 1px solid var(--filet-2);
  border-radius: var(--rayon-acte);
  /* 44 px de haut : le socle de la maison sur une cible, et on n'y descend pas. */
  min-height: 44px;
  padding: 0 var(--s3);
  max-width: 100%;
}
.cat-barre__compte {
  margin: 0;
  margin-inline-start: auto;
  align-self: center;
  font-size: var(--t-s);
  color: var(--encre-2);
}
/* ⚠ LE NOMBRE EST EN CHIFFRES TABULAIRES ET IL RÉSERVE SA PLACE : sans cela, la
   barre entière se décale quand le compte passe de 9 à 10, et un décalage de mise
   en page à chaque frappe est exactement ce que la règle du CLS interdit. */
.cat-barre__compte b {
  font-size: var(--t-m);
  color: var(--encre);
  font-variant-numeric: tabular-nums;
  display: inline-block;
  min-inline-size: 2.5ch;
  text-align: end;
}
.cat-barre__compte-lab { text-transform: uppercase; letter-spacing: 0.07em; font-size: var(--t-xs); }

/* LES SECTIONS DE RAYON. Un rayon est un LIEU : il porte son nom, son compte et
   sa présentation. C'est ce qui distingue une librairie rangée d'un tas de
   couvertures.
   ⚠ `scroll-margin-block-start` : le rail mène ici par une ancre, et sans cette
   marge le titre du rayon se colle sous l'en-tête. */
.rayon-sec { scroll-margin-block-start: var(--s16); }
.rayon-sec + .rayon-sec { margin-block-start: var(--s16); }
.rayon-sec__titre {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  font-family: var(--corps);
  font-size: var(--t-l);
  font-weight: 700;
  margin: 0 0 var(--s2);
  padding-block-start: var(--s2);
  border-block-start: 2px solid var(--accent);
  width: fit-content;
}
.rayon-sec__n { font-size: var(--t-xs); color: var(--encre-3); }
.rayon-sec__desc {
  max-width: var(--mesure);
  font-size: var(--t-s);
  color: var(--encre-2);
  margin: 0 0 var(--s6);
}
/* 🆕 LA GRILLE PLATE : la forme des résultats dès qu'un tri traverse les rayons.
   Elle n'a ni titre ni description de rayon, parce qu'un tri par titre range des
   volumes et non des lieux. */
.cat-plat { margin: 0; }

/* 🆕 L'ÉTAT « AUCUN RÉSULTAT », ET IL EST TRAITÉ COMME UNE PAGE ET NON COMME UNE
   ERREUR. Un catalogue qui ne rend rien n'a rien fait de mal : c'est la recherche
   qui était étroite. Il prend donc la forme d'un cartouche de la maison (filet
   d'accent COURT au-dessus, nappe d'accent, mesure de lecture) et son texte dit
   quoi faire, jamais ce qui a échoué. */
.catalogue__aucun {
  margin: 0;
  background: var(--accent-fond);
  border: 1px solid var(--filet-2);
  padding: var(--s8) var(--s6);
  font-size: var(--t-m);
  max-width: var(--mesure);
  position: relative;
}
.catalogue__aucun::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline-start: -1px;
  inline-size: 4rem;
  block-size: 3px;
  background: var(--accent);
}

/* ---------------------------------------------------------------------------
   10.7.5  LA VITRINE DE L'ACCUEIL
   Sa forme n'est PAS celle du catalogue, et c'est une exigence de flotte : deux
   pages du même site ne se ressemblent pas. Un volume en regard, puis une rangée.
   --------------------------------------------------------------------------- */
.vitrine { display: grid; gap: var(--s12); }
.vitrine__avant { display: grid; gap: var(--s8); align-items: center; }
@media (min-width: 46rem) {
  .vitrine__avant { grid-template-columns: 15rem minmax(0, 1fr); gap: var(--s12); }
}
.vitrine__couv { display: block; text-decoration: none; }
.vitrine__dit { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.vitrine__etiq {
  margin: 0;
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--accent);
}
.vitrine__cat { margin: 0; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.07em; color: var(--encre-3); }
.vitrine__titre { margin: 0; font-family: var(--titre); font-size: var(--t-2xl); font-weight: 700; line-height: 1.15; letter-spacing: -0.015em; }
.vitrine__titre a { text-decoration: none; color: var(--encre); }
.vitrine__titre a:hover { color: var(--accent); text-decoration: underline; }
.vitrine__sous { margin: 0; max-width: var(--mesure); color: var(--encre-2); }
.vitrine__prix { margin: var(--s2) 0 0; }
.vitrine__suite {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(9rem, 100%), 1fr));
  gap: var(--s6);
  margin: 0;
  padding-block-start: var(--s8);
  border-block-start: 1px solid var(--filet);
  list-style: none;
}
.vitrine__suite li { max-width: none; }
.vitrine__suite a { display: flex; flex-direction: column; gap: var(--s2); text-decoration: none; color: inherit; }
.vitrine__suite a:hover .couv { box-shadow: var(--ombre-2); transform: translate(-1px, -1px); }
.vitrine__suite-titre { font-family: var(--titre); font-size: var(--t-xs); font-weight: 700; line-height: 1.25; }
.vitrine__suite a:hover .vitrine__suite-titre { color: var(--accent); text-decoration: underline; }
.vitrine__tout { margin: 0; }

/* ---------------------------------------------------------------------------
   10.7.6  LE FIL D'ARIANE
   La fiche §5.2 disait « breadcrumb : NON », et elle avait raison sur une
   arborescence PLATE. Le corpus des livres crée quatre crans réels (catalogue,
   rayon, volume, chapitre) : le fil devient le seul composant qui dit à un
   visiteur arrivé par un moteur sur un chapitre où il se trouve. Il ne sort
   jamais sur les pages de la devanture.
   --------------------------------------------------------------------------- */
.ariane { margin-block-end: var(--s6); font-size: var(--t-xs); }
.ariane ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); margin: 0; padding: 0; list-style: none; }
.ariane li { max-width: none; }
.ariane__cran { display: flex; align-items: center; gap: var(--s2); color: var(--encre-3); }
/* ⚠ LE SÉPARATEUR EST UN CARACTÈRE DÉCORATIF, DONC IL EST MASQUÉ AUX LECTEURS
   D'ÉCRAN : sans ce drapeau, un fil de cinq crans annonce cinq fois « barre
   oblique ». Et il est en `content` plutôt que dans le HTML parce qu'un
   séparateur n'est pas du contenu.
   ⚠ EN RTL, LA CHEVRON DOIT SE RETOURNER : `·` est neutre dans les deux sens,
   c'est pourquoi c'est lui et pas un chevron. */
.ariane__cran + .ariane__cran::before { content: "·"; color: var(--encre-3); }
.ariane a { color: var(--encre-2); }
.ariane [aria-current="page"] { color: var(--encre); font-weight: 700; }

/* ---------------------------------------------------------------------------
   10.7.7  LA FICHE — LA PAGE PRODUIT
   --------------------------------------------------------------------------- */
.fiche-tete { background: var(--fond-2); }
.fiche { display: grid; gap: var(--s8); }
@media (min-width: 52rem) {
  .fiche { grid-template-columns: 18rem minmax(0, 1fr); gap: var(--s16); align-items: start; }
}
.fiche__couv { max-width: 18rem; }
.fiche__dit { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; }
.fiche__cat { margin: 0; font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; }
/* LE TITRE D'UN VOLUME EST EN LITERATA, comme sur sa couverture : c'est le même
   objet, dit deux fois, et la voix ne change pas entre les deux. */
.fiche__dit h1 { margin: 0; font-family: var(--titre); font-size: var(--t-2xl); line-height: 1.12; letter-spacing: -0.02em; }
.fiche__sous { margin: 0; font-size: var(--t-m); color: var(--encre-2); max-width: var(--mesure); }
.fiche__prix { margin: var(--s2) 0 0; }
.fiche__actes { display: flex; flex-wrap: wrap; gap: var(--s3); margin: var(--s3) 0 0; }
.b__poids { font-size: var(--t-xs); font-weight: 400; opacity: 0.8; margin-inline-start: var(--s2); }
/* L'AVERTISSEMENT DE VOLUME DE RECETTE, EN HAUT DE LA FICHE ET AVANT TOUT
   ARGUMENT. Il porte un mot et un fait, jamais une couleur seule. */
.fiche__test {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  margin: var(--s2) 0 0;
  padding: var(--s3) var(--s4);
  background: var(--carte);
  border: 1px solid var(--alerte);
  border-inline-start: 3px solid var(--alerte);
  border-radius: var(--rayon);
  font-size: var(--t-s);
}
.fiche__test b { color: var(--alerte); text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--t-xs); }
.fiche__niveau { margin: var(--s4) 0 0; font-size: var(--t-s); color: var(--encre-2); max-width: var(--mesure); }
.fiche__niveau b { text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--t-xs); color: var(--encre-3); display: block; }

/* LA RÉASSURANCE — QUATRE FAITS VÉRIFIABLES, ET AUCUN N'EST UN ARGUMENT DE
   VENTE. Ni livraison, ni retour, ni paiement, ni délai, ni chiffre, ni urgence.
   Le filet d'accent en tête de chaque cellule remplace l'icône : la charte
   interdit l'emoji-icône, et un jeu de pictogrammes pour quatre faits serait
   quatre dessins à justifier. */
.rassure {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: var(--s4);
  margin: var(--s6) 0 0;
  padding: 0;
  list-style: none;
}
.rassure__un {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding-block-start: var(--s2);
  border-block-start: 2px solid var(--accent);
  font-size: var(--t-xs);
  color: var(--encre-2);
}
.rassure__un b { font-size: var(--t-s); color: var(--encre); }

/* LE SOMMAIRE — dérivé des chapitres, avec leur résumé. Une table des matières
   qui n'est qu'une liste de titres ne sert que ceux qui savent déjà. */
.sommaire { margin: 0; padding: 0; list-style: none; counter-reset: none; }
.sommaire__un { max-width: none; border-block-end: 1px solid var(--filet); }
.sommaire__un:first-child { border-block-start: 1px solid var(--filet); }
.sommaire__lien {
  display: flex;
  gap: var(--s4);
  align-items: baseline;
  padding: var(--s4) var(--s2);
  text-decoration: none;
  color: inherit;
}
.sommaire__lien:hover { background: var(--accent-fond); }
.sommaire__n {
  flex: none;
  font-family: var(--titre);
  font-size: var(--t-l);
  font-weight: 700;
  color: var(--accent);
  min-width: 2ch;
}
.sommaire__txt { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.sommaire__txt b { font-family: var(--titre); font-size: var(--t-m); line-height: 1.25; }
.sommaire__lien:hover .sommaire__txt b { color: var(--accent); text-decoration: underline; }
.sommaire__txt span { font-size: var(--t-s); color: var(--encre-2); }

/* LES DÉTAILS — DES COUPLES « ÉTIQUETTE : VALEUR », JAMAIS UNE PHRASE.
   Même règle que le colophon, et pour la même raison : « {n} chapitres » serait
   faux au singulier. Un `<dl>` n'a pas d'accord à tenir. */
.details {
  display: grid;
  gap: 0;
  margin: 0;
  background: var(--carte);
  border: 1px solid var(--filet);
}
@media (min-width: 40rem) {
  .details { grid-template-columns: minmax(10rem, 16rem) minmax(0, 1fr); }
}
.details dt {
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--encre-3);
  padding: var(--s3) var(--s4);
  border-block-start: 1px solid var(--filet);
}
.details dd {
  margin: 0;
  padding: var(--s3) var(--s4);
  font-size: var(--t-s);
  border-block-start: 1px solid var(--filet);
}
.details dt:first-of-type, .details dt:first-of-type + dd { border-block-start: 0; }
@media (max-width: 40rem) {
  .details dt { padding-block-end: 0; border-block-start: 1px solid var(--filet); }
  .details dd { padding-block-start: var(--s1); border-block-start: 0; }
}
.details--colophon dd { max-width: var(--mesure); }

/* ---------------------------------------------------------------------------
   10.7.8  LE CHAPITRE — LA PAGE DE LECTURE
   C'est la seule page du site dont la forme est dictée par le confort de lecture
   et par rien d'autre : une colonne de 66 signes, rien dans les marges.
   --------------------------------------------------------------------------- */
/* LE CONTENEUR D'UNE PAGE DE CHAPITRE EST ETROIT, ET C'EST LA SEULE PAGE DU
   SITE OU IL L'EST. Mesure : la colonne de lecture fait 66 signes (642 px) et le
   conteneur du site 1170 px, donc une page de chapitre laissait 400 px de blanc a
   sa droite. La passe precedente avait couvert ce vide par une colonne de marge
   d'edition savante ; la marginalia etant devenue facultative, on RESSERRE. Une
   page de lecture n'a pas besoin de largeur, elle a besoin d'une mesure.
   ATTENTION : `--large` est redefini sur les deux sections du chapitre et non sur
   `main`, parce que l'en-tete, le pied et le fil d'ariane du socle lisent le meme
   jeton et doivent garder la largeur du site. */
.chap-tete, .chap-corps { --large: 52rem; }
.chap-tete { background: var(--fond-2); }
.chap-rang { margin: 0 0 var(--s3); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.07em; color: var(--encre-3); }
.chap-rang b { color: var(--encre-2); }
.chap-tete h1 { font-family: var(--titre); max-width: var(--mesure); }
.chap-tete .chapeau { max-width: var(--mesure); }
/* LE CORPS D'UN CHAPITRE EST EN LITERATA, et c'est le seul endroit du site où la
   police du LIVRE porte du texte courant. C'est la frontière d'objet appliquée
   jusqu'au bout : la librairie parle en Noto Sans, le livre parle en Literata. */
.chap-corps .prose,
.chap-corps .bloc__corps p,
.chap-corps .exo__item,
.chap-corps .recap__point,
.chap-corps .methode__pas p {
  font-family: var(--titre);
  font-size: var(--t-m);
  line-height: 1.65;
}
.chap-corps .bloc__corps { max-width: var(--mesure); }

/* LA PAGINATION. Un chapitre sans précédent ni suivant est un cul-de-sac : c'est
   elle qui transforme des pages indépendantes en un LIVRE. */
.pageage { display: grid; gap: var(--s4); }
@media (min-width: 46rem) {
  .pageage { grid-template-columns: 1fr auto 1fr; align-items: center; }
  /* ⚠ `grid-column` VIT DANS LA MEDIA QUERY, ET C'EST LA RECETTE QUI L'A DIT.
     Posée dehors, cette déclaration forçait le lien « chapitre suivant » en
     TROISIÈME colonne d'une grille qui n'en a qu'UNE sous 46rem : le navigateur
     crée alors deux colonnes IMPLICITES, et la page défile horizontalement.
     Mesuré : `scrollWidth` 406 pour un viewport de 360, le lien dépassant de 46 px
     et son picto de 29. Une propriété de PLACEMENT n'a aucun sens hors du contexte
     qui déclare la grille, et rien ne le signale — ça déborde, simplement.
     Ni le build, ni l'audit, ni le maillage, ni le contraste ne pouvaient le voir :
     il n'y a qu'un moteur qui compose la page qui le sait. */
  .pageage__suiv { grid-column: 3; text-align: end; }
  .pageage__suiv span { align-items: flex-end; text-align: end; }
}
.pageage__un {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4);
  background: var(--carte);
  border: 1px solid var(--filet-2);
  border-radius: var(--rayon);
  text-decoration: none;
  color: inherit;
  min-height: 44px;
}
.pageage__un:hover { border-color: var(--accent); }
.pageage__un span { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.pageage__un b { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.07em; color: var(--encre-3); }
.pageage__un span span { font-family: var(--titre); font-size: var(--t-s); font-weight: 700; }
.pageage__un:hover span span { color: var(--accent); text-decoration: underline; }
.pageage__som {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  justify-self: center;
  min-height: 44px;
  font-size: var(--t-s);
  font-weight: 700;
}

/* ---------------------------------------------------------------------------
   10.7.9  LES QUATRE FORMES PÉDAGOGIQUES
   Elles n'existent que dans les livres : c'est ce qui fait qu'une page de
   chapitre ne ressemble à aucune page de la devanture.
   --------------------------------------------------------------------------- */
.methode { margin: 0; padding: 0; list-style: none; counter-reset: none; display: grid; gap: var(--s4); }
.methode__pas {
  max-width: none;
  display: grid;
  gap: var(--s1);
  padding: var(--s4);
  background: var(--carte);
  border: 1px solid var(--filet);
  border-inline-start: 3px solid var(--accent);
}
.methode__pas p { margin: 0; }
.methode__n { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--accent); font-family: var(--corps); }

.recap { margin: 0; padding: var(--s6); list-style: none; background: var(--accent-fond); border: 1px solid var(--filet-2); display: grid; gap: var(--s3); }
.recap__point { max-width: none; display: flex; gap: var(--s3); align-items: start; }
.recap__point .ic { flex: none; color: var(--accent); margin-block-start: 0.2em; }

.exo { display: grid; gap: var(--s4); }
.exo__consigne { margin: 0; font-family: var(--corps); font-size: var(--t-s); color: var(--encre-2); padding: var(--s3) var(--s4); background: var(--fond-3); border-inline-start: 3px solid var(--filet-2); }
.exo__items { margin: 0; padding-inline-start: var(--s8); display: grid; gap: var(--s3); }
.exo__item { max-width: var(--mesure); }

.corriges { display: grid; gap: var(--s3); }
.corrige { background: var(--carte); border: 1px solid var(--filet); }
.corrige__q { display: flex; gap: var(--s3); align-items: baseline; padding: var(--s4); cursor: pointer; font-family: var(--corps); font-size: var(--t-s); }
.corrige__q::-webkit-details-marker { display: none; }
.corrige__n { flex: none; font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--accent); }
.corrige__r { padding: 0 var(--s4) var(--s4); border-block-start: 1px solid var(--filet); padding-block-start: var(--s4); }
.corrige__r p { margin: 0; max-width: var(--mesure); }
.corrige[open] .corrige__q { border-block-end: 0; }
/* ⚠ LA RÈGLE MODERNE S'ÉCRIT SÉPARÉMENT, ET C'EST UN PIÈGE DÉJÀ PAYÉ PAR LE BLOC
   DES QUESTIONS : à partir de Chrome 131, `content-visibility` sur
   `::details-content` masque le contenu d'un `details` fermé. Groupée avec la
   règle voisine, cette déclaration ferait tomber TOUTE la règle dans les moteurs
   qui ne connaissent pas le pseudo-élément. */
.corrige::details-content { content-visibility: visible; }

/* ---------------------------------------------------------------------------
   10.7.10  LA BANDE « PLANCHER »
   Une troisième bande de valeur, à côté de `reliure` et `accent`. Elle porte la
   surface sur laquelle les objets se posent, et c'est celle qui sert le plus :
   toute grille de couvertures vit dessus, parce que c'est l'écart entre le
   plancher et la carte blanche qui fait LEVER l'objet.
   ⚠ ELLE N'EST PAS UN ACCENT ET NE PEINT AUCUN ACTE : c'est `--fond-2`, une
   surface, et la garde de `audit-contenu.js` sur `--reliure*` n'est pas touchée.
   --------------------------------------------------------------------------- */
.section.bande--plancher { background: var(--fond-2); }

/* ---------------------------------------------------------------------------
   10.7.11  CE QUI EST MORT ET QUI SE COUPE À LA MAIN
   `.rayon__dos` et `.rayon__un` dessinaient les dos TITRÉS de l'ancienne
   étagère : ils n'ont plus de consommateur, le générateur rendant maintenant une
   grille de couvertures quand des volumes existent. On les coupe NOMMÉMENT, et
   jamais par un purgeur automatique : la leçon de flotte est mesurée (sur 659
   sélecteurs absents du HTML, 625 étaient vivants).
   L'étagère qui ATTEND, elle, reste entière et intacte : `.rayon__attente`,
   `.rayon__place`, `.rayon__planche`, `.rayon__vide` et `.rayon__etat` sont le
   rayon vide honnête, et c'est l'état que le site LIVRÉ sert.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   10.7.12  L'IMPRESSION D'UN LIVRE — LA `print.css` DU CHAPITRE, POSÉE ICI
   ⚠ CE N'EST PAS LA CHAÎNE D'EXPORT PDF, ET LA DETTE RESTE NOMMÉE.
   Ce bloc rend une page de chapitre imprimable proprement depuis un navigateur :
   les folios papier, les coupures propres, les corrigés DÉPLIÉS (un corrigé
   replié à l'impression est une page blanche), et l'interface entièrement
   retirée. Ce qui reste dû, et qui appartient au chantier du premier livre :
   `tools/exporter-pdf.js`, c'est-à-dire l'export PILOTÉ d'un volume entier en un
   seul fichier, avec les corrigés REPORTÉS en fin de volume et une pagination
   continue. Une feuille d'impression et une chaîne d'export ne sont pas la même
   chose, et confondre les deux ferait croire la dette remboursée.
   --------------------------------------------------------------------------- */
@media print {
  .ariane, .cat-barre, .cat-filtres, .rail, .pageage, .catalogue__aucun { display: none !important; }
  .fiche-tete, .chap-tete { background: none; }
  .section.bande--plancher { background: none; }
  .couv { box-shadow: none; border: 1px solid #000000; break-inside: avoid; }
  .carte-livre { border-color: #000000; break-inside: avoid; }
  .gratuit__mot { background: none; color: #000000; border: 1px solid #000000; }
  .catalogue { display: block; }
  .grille { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* LES CORRIGÉS SE DÉPLIENT À L'IMPRESSION. Un `details` fermé sur du papier est
     de l'encre perdue : on ne peut pas cliquer sur une feuille. */
  .corrige { border-color: #000000; break-inside: avoid; }
  .corrige > .corrige__r { display: block !important; }
  .corrige__q { cursor: auto; }
  .methode__pas, .recap, .exo__consigne, .details, .sommaire__un { break-inside: avoid; border-color: #000000; }
  .recap { background: none; }
  /* LE TEXTE D'UN CHAPITRE IMPRIMÉ GARDE SA POLICE DE LIVRE, et il retrouve une
     mesure de papier : 66 signes valent pour un écran, une page A4 en tient plus. */
  .chap-corps .bloc__corps { max-width: none; }
  .chap-corps .prose, .chap-corps .bloc__corps p { font-size: 11pt; line-height: 1.5; }
  .rassure { display: none !important; }
}

/* ==========================================================================
   10.6  L'IMPRESSION DES COMPOSANTS DE LA MAISON
   Le papier n'a ni trame de fond (une trame imprimée est de l'encre perdue et
   une lisibilité en moins), ni panneau flottant. Il n'a plus de bandeau de mise
   à jour à éteindre non plus : le composant est retiré du site (07/08/2026).
   ========================================================================== */
@media print {
  main::before { display: none !important; }
  .mega__b { display: none !important; }
  .mega__panneau { display: none !important; }
  .fig img, .feuillets > li { box-shadow: none; border-color: #000000; }
  .fig figcaption, .defs > div, .journal > li, .refs > li { border-color: #000000; }
  .rayon__planche { background: #000000; }
  .rayon__etat { background: #FFFFFF; color: #000000; border: 1px solid #000000; }
  .tabl th, .tabl td { border-color: #000000; }
  .tabl tbody tr:nth-child(even) { background: #FFFFFF; }
  .tableau-env { overflow: visible; border-color: #000000; }
  .journal > li, .refs > li, .feuillets > li, .voies > li, .fig { break-inside: avoid; }
}

/* ==== DEBUT DES COMPOSITIONS DE PAGE (section 11, integree) ==== */
/* ==========================================================================
   11. LES COMPOSITIONS DE PAGE — UNE PAR PAGE DE DEVANTURE

   🔴 ORDRE DE MAEL, 07/08/2026 : « les 14 pages de devanture sont plates,
   t'as les textes mais 0 design ». Chaque page de presentation a recu SA
   composition, ecrite par un agent dedie, et cette section les porte toutes.

   COMMENT CA TIENT, ET C'EST LA MECANIQUE QUE CETTE PASSE APPORTE :
     · `lib-html.js` pose une CLASSE DE PAGE sur `<main>` (`p-accueil`, `p-faq`,
       ...). Elle vit sur `<main>` et non sur `<body>` : l'en-tete, le pied et le
       panneau sont du SOCLE, et une classe sur `<body>` aurait permis a une page
       de repeindre le pied de tout le site ;
     · `build.js` pose le RANG du bloc (`data-bloc`) et celui de la section
       (`data-sec`). Le rang est celui du CONTENU, donc il ne bouge pas quand un
       bloc rend une chaine vide (ce qui arrive par regle de la maison), et il est
       identique dans les sept langues puisque la parite impose le meme nombre de
       blocs. Un `:nth-child` aurait glisse au premier bloc muet ;
     · TOUT SELECTEUR DE CETTE SECTION COMMENCE PAR SA CLASSE DE PAGE, et un
       validateur de sas le REFUSE autrement. C'est ce qui a permis a quatorze
       agents de composer en parallele sans qu'aucun puisse toucher la page d'un
       autre : la portee est garantie par le selecteur, pas par la discipline.

   ⚠ CETTE SECTION EST GENEREE, ET ELLE SE REGENERE ENTIEREMENT.
   Elle est reecrite depuis le dossier de sas a chaque integration, entre ses deux
   marqueurs. On ne la corrige donc PAS ici : on corrige le fichier de sas et on
   reintegre. Une correction faite ici serait perdue au passage suivant, en
   silence, ce qui est la pire facon de perdre du travail.
   ⚠ ET LE BUDGET EST TENU PAR FICHIER : 6 Kio par page, commentaires compris.
   Une composition forte tient en soixante lignes ; le budget est la contrainte
   qui force un parti au lieu d'un empilement.
   ========================================================================== */

/* --------------------------------------------------------------------------- */
/* ============================================================================
   ACCUEIL - la devanture en bandes alternées
   LE PARTI : un fronton signé ouvre la page, une photographie va jusqu'au
   bord du cadre juste après lui, puis les bandes déjà posées par le contenu
   (plancher, reliure, accent) reprennent ce rythme jusqu'au plat, sans que
   la composition dépende du seul bloc qui peut être vide.
   Primitives : la règle courte (le fronton du héros) - la pleine chasse
   (la première photographie, seule de la page à toucher le bord)
   ========================================================================== */

/* LE FRONTON DU HÉROS. Pas de bande de couleur ici : la page reste blanche,
   c'est elle qui ouvre le livre. Mais le titre reçoit la même règle courte
   qu'un titre de section plus bas (un filet d'accent au-dessus, jamais plus
   large que lui) : la signature de la page commence au premier mot, avant
   que le lecteur sache qu'elle va se répéter à chaque bande. */
.p-accueil .hero h1 {
  border-block-start: 3px solid var(--accent);
  padding-block-start: var(--s4);
  width: fit-content;
  max-width: 100%;
}

/* LA PLEINE CHASSE, UNE SEULE FOIS SUR LA PAGE. La première photographie
   suit le héros au ras et sort de la gouttière jusqu'au bord du conteneur ;
   la seconde (bloc 4) reste dans la mesure du bloc, plus discrète. Une
   devanture qui montre ses deux photographies au même format n'en montre
   aucune avec force.
   Le calcul annule exactement le padding du .wrap : la photo ne dépasse
   donc jamais sa propre boîte, quelle que soit la largeur d'écran, et rien
   ne peut déborder même sur un très grand moniteur. Perdre le filet et
   l'ombre est le même choix que le second cadre mobile du socle : une image
   à fond perdu n'a pas de tranche, elle EST la page. */
.p-accueil [data-bloc="0"] .fig--bandeau { margin-inline: calc(-1 * var(--gouttiere)); }
.p-accueil [data-bloc="0"] .fig--bandeau img { border: 0; box-shadow: none; }
.p-accueil [data-bloc="0"] .fig--bandeau figcaption { padding-inline: var(--gouttiere); }

/* LA DEVANTURE (bloc 2, bande--plancher déjà posée par le contenu quand le
   catalogue existe). Le volume en avant gagne une couverture plus large à
   partir d'un très grand écran : l'objet vedette mérite l'air qu'un tel
   écran laisse, plutôt que de rester à la taille d'un volume de rangée. La
   rangée elle-même passe en auto-fit : qu'elle porte quatre couvertures ou
   davantage, elle occupe toute sa largeur au lieu de laisser une réserve
   vidée à sa droite. */
@media (min-width: 74rem) {
  .p-accueil [data-bloc="2"] .vitrine__avant { grid-template-columns: 18rem minmax(0, 1fr); }
}
.p-accueil [data-bloc="2"] .vitrine__suite { grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr)); }

/* LA FERMETURE (bloc 12, bande--accent déjà posée par le contenu). Le
   bloc--marge plafonne sa colonne à la mesure de lecture ; posée sur une
   bande pleine largeur juste après le bloc 11 (les chiffres), lui aussi
   pleine largeur, cette colonne étroite laissait la moitié de la bande vide
   juste avant le plat. Les quatre liens reprennent donc toute la largeur de
   la bande, en frise adaptable qui répond à celle des chiffres. */
.p-accueil [data-bloc="12"] .bloc__corps { max-width: none; }
.p-accueil [data-bloc="12"] .liens-blocs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--s3) var(--s8);
}
.p-accueil [data-bloc="12"] .liens-blocs li { margin-block-end: 0; }

/* --------------------------------------------------------------------------- */
/* ============================================================================
   METHODE - la timeline a colonne vertebrale
   LE PARTI : le procede se lit comme une colonne qui descend, un chiffre
   imprime relie au suivant par un seul fil, et l'avertissement qu'elle
   produit a la fin est encadre comme le colophon qui le porte vraiment.
   Primitives : la regle courte (sur le nom de chaque etape) et le cartouche
   (l'avertissement, en papier de garde).
   ========================================================================== */

/* --- LA COLONNE VERTÉBRALE. Le socle donne déjà un chiffre dans un carré au
   filet ; ici il devient une FIGURE IMPRIMÉE (exception admise par la charte
   commune pour un grand numéro d'étape, via `clamp()`), et les carrés se
   relient par un seul fil. `--pas-marqueur` porte la taille une seule fois :
   le fil et le chiffre s'accordent sans qu'aucune valeur soit dupliquée. */
.p-methode .pas {
  --pas-marqueur: clamp(2.25rem, 2rem + 1vw, 2.75rem);
}
.p-methode .pas li {
  position: relative;
  align-items: flex-start;
  gap: var(--s6);
}
.p-methode .pas li::before {
  width: var(--pas-marqueur);
  height: var(--pas-marqueur);
  font-size: calc(var(--pas-marqueur) * 0.42);
  /* le zéro de tête lit comme un folio d'atelier ; le chiffre reste un
     `counter()`, donc dérivé du DOM et incapable de mentir. */
  content: counter(pas, decimal-leading-zero);
}
/* LE FIL vit dans le CREUX entre deux carrés, jamais à travers l'un d'eux :
   une ligne qui coupe un chiffre imprimé se lit comme un défaut d'impression,
   pas comme une reliure. Sa hauteur est l'écart exact que le socle pose déjà
   en `margin-block-end` (`--s6`) : aucune valeur n'est donc recopiée, et le
   fil reste juste si cet écart change un jour. */
.p-methode .pas li::after {
  content: "";
  position: absolute;
  inset-inline-start: calc((var(--pas-marqueur) - 2px) / 2);
  inset-block-start: var(--pas-marqueur);
  inset-block-end: calc(-1 * var(--s6));
  width: 2px;
  background: var(--filet-2);
}
/* rien ne pend après la dernière étape : un fil qui continuerait sous elle
   annoncerait une étape de plus, qui n'existe pas. */
.p-methode .pas li:last-child::after { display: none; }
/* LA DERNIÈRE ÉTAPE EST CELLE QUI REND LE LIVRE PUBLIC : son carré seul passe
   à l'encre d'accent (le même rôle que porte déjà le folio de section en
   marginalia ailleurs sur le site), jamais à un aplat, réservé au bouton
   d'acte par la charte commune. ⚠ VU À L'ÉCRAN, LA COULEUR SEULE NE SUFFIT
   PAS : l'accent et l'encre sont deux tons sombres voisins, et le regard ne
   les distingue pas à cette taille. Le trait s'épaissit donc aussi, pour que
   l'écart se voie même sans percevoir la teinte. */
.p-methode .pas li:last-child::before {
  border-width: 2px;
  border-color: var(--accent);
  color: var(--accent);
}

/* --- LA RÈGLE COURTE, reprise à l'échelle d'une ligne : le socle en ouvre
   déjà chaque `h2` d'un filet d'accent court, et chaque étape de la colonne
   s'ouvre maintenant de même. `strong` reste en `--corps` : un nom d'étape
   est un libellé, pas une voix de livre, seul le filet qui le précède
   s'ajoute. */
.p-methode .pas li strong::before {
  content: "";
  display: block;
  width: var(--s8);
  height: 2px;
  margin-block-end: var(--s2);
  background: var(--accent);
}

/* --- LE CARTOUCHE DE L'AVERTISSEMENT. Ce paragraphe DÉCRIT ce que porte le
   colophon de chaque livre ; `--fond-3` est nommé pour ça dans la charte
   commune (le papier de garde des colophons et cartouches techniques), donc
   le texte se pose ici sur la surface même qu'il raconte. Encadré sur ses
   quatre côtés et non en drapeau, pour ne pas se confondre avec `.note` au
   bloc suivant : deux avis distincts ne portent pas la même forme. */
.p-methode [data-bloc="4"] .lecture {
  padding: var(--s6);
  background: var(--fond-3);
  border: 1px solid var(--filet-2);
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   LICENCE ET REUTILISATION - le diptyque en regard
   LE PARTI : une plaque large et ouverte fait face a une plaque etroite et
   fermee, et la meme proportion se rejoue, une octave plus bas, entre la
   prose libre de l'ouverture et le cartouche resserre de l'exception.
   Primitives : le cartouche, la colonne resserree
   ========================================================================== */

/* LE DIPTYQUE. Le socle donne deux colonnes égales ; la loi de composition de
   cette page dit l'inverse exprès. Le permis est vaste, la réserve est
   étroite, et le rapport 3/2 le dit sans un mot de plus. */
@media (min-width: 46rem) {
  .p-licence .duo { grid-template-columns: 3fr 2fr; }
}

/* LA COLONNE AUTORISÉE RESPIRE : l'écart entre ses clauses s'ouvre, contre la
   réserve qui garde l'écart resserré du socle. Le vide entre les lignes dit
   le mot vaste aussi sûrement que la largeur de la colonne. */
.p-licence .duo__col:not(.duo__col--refus) li { margin-block-end: var(--s6); }

/* LA RÉSERVE DEVIENT UNE PLAQUE FERMÉE, pas une seconde colonne de même
   nature que la première. Fond-3 est le papier de garde de la maison, déjà
   chargé des cartouches techniques : ici il porte LA réserve nommée.
   La couture d'accent du socle, sur le bord de départ, reste seule intacte :
   c'est la charnière du diptyque. Les trois autres bords ne se ferment qu'au
   seuil où les deux plaques se font vraiment face. */
.p-licence .duo__col--refus { background: var(--fond-3); padding: var(--s6); }
@media (min-width: 46rem) {
  .p-licence .duo__col--refus {
    border-block: 1px solid var(--filet);
    border-inline-end: 1px solid var(--filet);
  }
}

/* LA MAQUETTE ÉDITORIALE EST L'UNIQUE EXCEPTION DE TOUTE L'ASSOCIATION : elle
   mérite une plaque, pas un paragraphe de plus parmi d'autres. Même fond-3 que
   la réserve du diptyque : la couleur devient le signe de ce qui reste à
   l'ODERSA, répété une seule fois de plus sur la page. La mesure se resserre
   en plus : la réserve se lit objectivement plus étroite que le reste de la
   page, une seconde fois et à une autre échelle. */
.p-licence [data-bloc="3"] {
  --mesure: 52ch;
  margin-block-start: var(--s16);
}
.p-licence [data-bloc="3"] .bloc__corps {
  background: var(--fond-3);
  border: 1px solid var(--filet);
  padding: var(--s8);
}
/* La règle courte de ce titre reprend l'encre de réserve déjà posée sur les
   puces de la colonne refusée : un seul signal, répété une seule fois. */
.p-licence [data-bloc="3"] .bloc__corps > h2 { border-block-start-color: var(--alerte); }

/* --------------------------------------------------------------------------- */
/* ============================================================================
   ENSEIGNANTS - LA GRILLE MODULAIRE A MODULES INEGAUX
   LE PARTI : les quatre usages se lisent en un regard parce qu'un seul module
   domine et que les trois autres pèsent ce qu'ils pèsent vraiment ; le duo qui
   suit se pose sur le même plateau plutôt que d'ouvrir un second geste.
   Primitives : le compteur dérivé (le folio numéroté de chaque carte)
   ========================================================================== */

/* ----------------------------------------------------------------------------
   LE CŒUR : QUATRE USAGES, UN SEUL DOMINANT (bloc 2)

   Une fiche qu'on lit entre deux cours ne tolère pas quatre cartes égales :
   quatre poids égaux se lisent comme une liste, jamais comme un cas principal
   et ses trois suites. L'ordre du contenu ne bouge pas (le premier gagnant
   reste le premier usage, celui de la salle de classe) ; seule son EMPRISE
   grandit. Le plateau porte --fond-2, le plancher qui fait lever les cartes
   (--carte reste blanche, la loi le veut).
   ---------------------------------------------------------------------------- */
.p-enseignants [data-bloc="2"] .cartes {
  background: var(--fond-2);
  padding: var(--s4);
  counter-reset: usage;
}
.p-enseignants [data-bloc="2"] .carte {
  display: flex;
  flex-direction: column;
  counter-increment: usage;
}
.p-enseignants [data-bloc="2"] .carte h3,
.p-enseignants [data-bloc="3"] .duo__col h3 {
  /* un libellé d'usage n'est pas une phrase de livre : voix d'interface. */
  font-family: var(--corps);
}
/* LE FOLIO DE CARTE. Dérivé du DOM (compteur), jamais écrit : la preuve qu'il
   y en a exactement quatre, jamais une promesse d'en compter cinq un jour.
   Poussé au pied de la carte, il absorbe l'écart de hauteur qu'une grille à
   modules inégaux crée forcément entre le module dominant et les modules
   d'appoint : chaque carte se referme sur son propre folio, quelle que soit
   la place qui lui reste. */
.p-enseignants [data-bloc="2"] .carte::after {
  content: counter(usage, decimal-leading-zero);
  margin-block-start: auto;
  padding-block-start: var(--s3);
  border-block-start: 1px solid var(--filet);
  color: var(--accent);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
}
/* LE MODULE DOMINANT : le cas le plus fréquent (la classe entière), couronné
   du même filet court que les titres de section, pour rester dans la voix de
   la maison plutôt que d'inventer un second signe. */
.p-enseignants [data-bloc="2"] .carte:first-child { padding: var(--s8); }
.p-enseignants [data-bloc="2"] .carte:first-child h3 {
  padding-block-start: var(--s2);
  border-block-start: 2px solid var(--accent);
  font-size: var(--t-xl);
}
@media (min-width: 46rem) {
  /* Trois colonnes, trois rangs : le premier usage prend 2x2 (le module qui
     pèse), les deux suivants tiennent chacun leur propre case, le quatrième
     (traduire, un geste qui sort du cadre de la classe) devient une bande
     entière sous les trois autres : une flowline, pas une case de plus. */
  .p-enseignants [data-bloc="2"] .cartes {
    padding: var(--s8);
    grid-template-columns: repeat(3, 1fr);
    grid-template-areas: "a a b" "a a c" "d d d";
  }
  .p-enseignants [data-bloc="2"] .carte:nth-child(1) { grid-area: a; }
  .p-enseignants [data-bloc="2"] .carte:nth-child(2) { grid-area: b; }
  .p-enseignants [data-bloc="2"] .carte:nth-child(3) { grid-area: c; }
  .p-enseignants [data-bloc="2"] .carte:nth-child(4) { grid-area: d; }
}

/* ----------------------------------------------------------------------------
   LE DUO, RANGÉ DANS LE MÊME PLATEAU (bloc 3)

   Le diptyque en regard (fascia épaisse, colonnes en vis-à-vis) est réservé à
   la page licence : ici, le duo n'est qu'une pièce de plus du plateau ouvert
   ci-dessus, pas un second geste de page. Même plancher, couture allégée à un
   simple filet de composition (jamais --alerte : demander n'est pas refuser,
   et le DOM servi ne porte d'ailleurs la classe .duo__col--refus sur aucune
   des deux colonnes de ce bloc).
   ---------------------------------------------------------------------------- */
.p-enseignants [data-bloc="2"] + [data-bloc="3"] { margin-block-start: var(--s8); }
.p-enseignants [data-bloc="3"] .duo {
  background: var(--fond-2);
  padding: var(--s6) var(--s4);
}
.p-enseignants [data-bloc="3"] .duo__col + .duo__col { border-block-start: 1px solid var(--filet); }
/* Colonne des demandes : ses puces passent à l'accent, un signal de lecture,
   jamais à --alerte qui dirait une erreur ou un refus. */
.p-enseignants [data-bloc="3"] .duo__col + .duo__col li { border-inline-start-color: var(--accent); }
@media (min-width: 46rem) {
  .p-enseignants [data-bloc="3"] .duo { padding: var(--s8); }
  .p-enseignants [data-bloc="3"] .duo__col + .duo__col {
    border-block-start: 0;
    border-inline-start: 1px solid var(--filet);
    margin-inline-start: 0;
    padding-inline-start: var(--s8);
  }
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   QUESTIONS FREQUENTES (faq) - l'index lateral collant
   LE PARTI : chaque groupe de questions porte son propre repere, titre et
   numero, qui tient le bord du regard tant que ce groupe defile - une page de
   dix groupes et 6300 px se lit alors comme dix chapitres courts et non comme
   un seul mur d'accordeons.
   Primitives : le cartouche (le bandeau de titre colle), le compteur derive
   (le numero de groupe, jamais ecrit a la main)
   ========================================================================== */

/* Le numéro de groupe se compte lui-même : dix blocs "faq" existent dans le
   contenu aujourd'hui, mais rien ici n'écrit "dix". Nommé "faqgrp" pour ne
   heurter aucun compteur du socle (pas, ref, feuillet). */
.p-faq { counter-reset: faqgrp; }

/* --- LE CARTOUCHE QUI RESTE AU BORD DU REGARD.
   position sticky ne sort jamais de son bloc (son conteneur de calage) :
   un titre de groupe se colle donc en haut tant que SES questions défilent,
   puis se relâche exactement quand le groupe suivant commence. C'est le
   bandeau qui, au récit du socle, "suit le doigt" une fois collé au mobile -
   ici il vaut à toutes les largeurs, pas seulement en dessous du seuil.
   Fond-3 (le papier de garde) et le filet du bas le rendent OPAQUE : sans eux,
   les questions qui remontent se liraient à travers le titre. Sa largeur
   reste fit-content (héritée du socle) : le filet d'accent au-dessus ne
   doit jamais mesurer plus que son texte, et l'élargir aurait rouvert la
   faute que la DA vient de corriger ailleurs. */
.p-faq .bloc__corps:has(> .qr) > h2 {
  counter-increment: faqgrp;
  position: sticky;
  inset-block-start: 0;
  background: var(--fond-3);
  border-block-end: 1px solid var(--filet);
  padding: var(--s3);
}
.p-faq .bloc__corps:has(> .qr) > h2::before {
  content: counter(faqgrp, decimal-leading-zero);
  font-family: var(--titre);
  font-size: var(--t-m);
  font-weight: 700;
  color: var(--accent);
}

/* --- UNE FAQ EST UNE CONVERSATION, PAS UN ACCORDEON POSE TEL QUEL.
   La réponse se lit en retrait, comme une réplique ; la question ouverte se
   teinte, pour dire quel tour parle. */
.p-faq .qr > div {
  border-inline-start: 2px solid var(--filet);
  margin-inline-start: var(--s1);
  padding-inline-start: var(--s4);
}
.p-faq .qr[open] summary { color: var(--accent); }

/* --- L'INDEX DEVIENT VRAIMENT LATERAL, AU-DESSUS DU SEUIL A DEUX COLONNES.
   Le socle ne pose sa grille de marge que si une marge existe dans le DOM
   (10.7.1) ; cette page n'en sert aucune, donc le corps du bloc reste en colonne
   unique. On rebâtit ici la seconde colonne pour y loger un vrai repère
   latéral : le même numéro, en grand, collé sur TOUTE la hauteur de son
   groupe (grid-row 1 à -1) plutôt que sur la seule ligne du titre - il
   reste donc visible même une fois le titre déjà remonté hors champ. */
@media (min-width: 64rem) {
  .p-faq .bloc__corps:has(> .qr) {
    display: grid;
    grid-template-columns: minmax(0, var(--mesure)) 1fr;
    column-gap: var(--s8);
    max-width: none;
  }
  .p-faq .bloc__corps:has(> .qr) > * { grid-column: 1; }
  .p-faq .bloc__corps:has(> .qr)::after {
    content: counter(faqgrp, decimal-leading-zero);
    grid-column: 2;
    grid-row: 1 / -1;
    position: sticky;
    inset-block-start: 0;
    font-family: var(--titre);
    font-weight: 700;
    font-size: var(--t-2xl);
    line-height: 1;
    color: var(--accent);
  }
}

/* Le papier ne défile pas : un bandeau teinté et un numéro sticky n'y ont
   aucun sens et n'y coûtent que de l'encre. */
@media print {
  .p-faq .bloc__corps:has(> .qr) > h2 { position: static; background: none; }
  .p-faq .bloc__corps:has(> .qr)::after { content: none; }
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   LA MAISON D'EDITION (apropos) - le rail de dates
   LE PARTI : le registre des decisions se tient comme un livre de comptes -
   un rail continu porte les dates au bord de depart, et le recit s'y adosse
   au lieu de s'empiler en fiches.
   Primitives : la colonne vertebrale (le rail continu du journal) ; la
   regle courte (la marque qui pointe chaque date sur le rail, reprise sur
   chaque titre de decision).
   ========================================================================== */

/* --- LE RAIL. Un seul filet continu, du premier au dernier fait du registre,
   au lieu des bordures repetees par entree du socle : c'est ce qui fait lire
   la colonne de dates comme UN rail tenu plutot que comme des traits coupes.
   Un pseudo-element en absolu et non une bordure posee sur chaque entree :
   un filet par ligne s'interromprait a chaque intervalle vertical. */
.p-apropos [data-bloc="2"] .journal {
  position: relative;
  padding-inline-start: var(--s6);
}
.p-apropos [data-bloc="2"] .journal::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 2px;
  background: var(--filet);
}
.p-apropos [data-bloc="2"] .journal > li { position: relative; }

/* LA MARQUE : un filet court d'accent qui pointe chaque decision sur le
   rail - la regle courte de la maison, au format d'un repere de registre
   plutot que d'un titre. Jamais plus long que l'ecart qu'il traverse. */
.p-apropos [data-bloc="2"] .journal > li::before {
  content: "";
  position: absolute;
  inset-inline-start: calc(-1 * var(--s6));
  inset-block-start: calc(var(--s6) + 0.2em);
  width: var(--s4);
  height: 3px;
  background: var(--accent);
}

@media (min-width: 46rem) {
  /* AU-DELA DU SEUIL OU LE SOCLE OUVRE DEUX COLONNES (8rem + la gouttiere
     var(--s8)), LE RAIL QUITTE LE BORD DE BLOC ET PASSE AU MILIEU DE CETTE
     GOUTTIERE : c'est la limite entre la date et le fait, exactement la
     place d'un filet de registre relie plutot qu'un ornement de bord. */
  .p-apropos [data-bloc="2"] .journal { padding-inline-start: 0; }
  .p-apropos [data-bloc="2"] .journal::before {
    inset-inline-start: calc(8rem + (var(--s8) / 2));
  }
  .p-apropos [data-bloc="2"] .journal > li::before {
    inset-inline-start: calc(8rem + (var(--s8) / 2) - var(--s2));
  }
  /* LA DATE SE CALE CONTRE LE RAIL au lieu de partir du bord de bloc : dans
     un registre tenu, la date pointe le fait, elle ne flotte pas devant lui. */
  .p-apropos [data-bloc="2"] .journal__date { text-align: end; }
}

/* LA REGLE COURTE, reprise sur chaque titre de decision : le meme geste que
   la marque du rail, a l'echelle du titre. Jamais large comme le filet qui
   court sous un h2 : une decision n'est pas une section. */
.p-apropos [data-bloc="2"] .journal__corps h3::before {
  content: "";
  display: block;
  width: var(--s8);
  height: 3px;
  margin-block-end: var(--s3);
  background: var(--accent);
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   CE QUI SE COMPTE - le chiffrier monumental
   LE PARTI : le relevé du bloc 0 devient un grand livre à deux colonnes
   partagées par toutes les lignes (le chiffre, sa désignation), chaque
   ligne opposant un nombre hors calibre à un libellé qui soutient, jamais
   une grille de cartes égales.
   Primitives : le décroché (le chiffre domine par l'échelle, la désignation
   soutient, ligne par ligne) et le compteur dérivé (le rang de chaque ligne
   se compte, il ne s'écrit jamais - la doctrine de la page, rejouée par la
   feuille elle-même).
   ========================================================================== */

/* --- LE CHIFFRIER. La grille de vignettes égales du socle, en auto-fit sur
   des cases de 9rem, est justement ce que la charte interdit : huit cases
   identiques se lisent comme un tableau de bord, pas comme un relevé. Elle
   devient un grand livre à deux colonnes partagées par toutes les lignes :
   un chiffre peut valoir 0 ou 168 sans jamais déplacer le départ de sa
   désignation, comme les montants d'un registre s'alignent à leur marge et
   non entre eux. display: contents laisse les deux enfants de chaque ligne
   rejoindre cette grille commune sans que leur conteneur n'ajoute sa propre
   boîte : c'est ce qui rend le partage de colonne possible sans toucher au
   DOM que le générateur écrit. */
.p-impact [data-bloc="0"] .chiffres {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: var(--s8);
  row-gap: 0;
  align-items: end;
  counter-reset: c-impact;
  border-block-start: 2px solid var(--filet);
}
.p-impact [data-bloc="0"] .chiffre { display: contents; }
.p-impact [data-bloc="0"] .chiffre__n,
.p-impact [data-bloc="0"] .chiffre__q {
  padding-block: var(--s4);
  border-block-end: 1px solid var(--filet);
}

/* LE CHIFFRE DOMINE PAR L'ÉCHELLE, PAS PAR LA LARGEUR DE SA COLONNE : le
   décroché tient ici à la taille du caractère, jamais à la place qu'il
   occupe sur la ligne. Le plafond commun de l'échelle est fait pour un
   intitulé, pas pour la figure que ce bloc doit porter : exception écrite
   au contrat, un clamp explicite prend le relais. Figures proportionnelles
   et non tabulaires, parce qu'une ligne ne compare jamais son chiffre à
   celui de sa voisine : rien n'exige ici la largeur fixe qui creuserait un
   vide autour d'un chiffre étroit comme le un. */
.p-impact [data-bloc="0"] .chiffre__n {
  grid-column: 1;
  font-size: clamp(2.25rem, 1.55rem + 3.2vw, 4.25rem);
  font-variant-numeric: lining-nums proportional-nums;
}

/* LA DÉSIGNATION SOUTIENT : remontée d'un cran de l'échelle pour rester
   lisible face à un chiffre hors calibre, sans jamais rivaliser avec lui.
   Le rang qui la précède se compte au lieu de s'écrire : même doctrine que
   les compteurs de la page, tenue cette fois par la feuille elle-même. */
.p-impact [data-bloc="0"] .chiffre__q {
  grid-column: 2;
  font-size: var(--t-s);
  counter-increment: c-impact;
}
.p-impact [data-bloc="0"] .chiffre__q::before {
  content: counter(c-impact, decimal-leading-zero);
  display: inline-block;
  min-width: 2ch;
  margin-inline-end: var(--s3);
  padding-inline-end: var(--s3);
  border-inline-end: 1px solid var(--filet);
  color: var(--encre-2);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  vertical-align: top;
}

/* LA DERNIÈRE LIGNE FERME LE RELEVÉ COMME LA PREMIÈRE BORNE L'OUVRE, sans
   rien affirmer sur sa valeur : ce n'est pas un total, cette page ne somme
   rien, c'est seulement la fin de la feuille. */
.p-impact [data-bloc="0"] .chiffre:last-child .chiffre__n,
.p-impact [data-bloc="0"] .chiffre:last-child .chiffre__q {
  border-block-end-width: 2px;
}

/* L'ÉCART APRÈS LE RELEVÉ SE CREUSE À LA MESURE DE SA MASSE NOUVELLE : le
   rythme du socle entre deux blocs convenait à des vignettes du plafond de
   l'échelle, il se lit trop court sous des chiffres qui montent à 4,25rem. */
.p-impact [data-bloc="1"] { margin-block-start: var(--s16); }

/* --- LE TABLEAU RIME AVEC LE RELEVÉ, DANS LE MÊME ORDRE. Le bloc 3 décrit,
   ligne à ligne et dans le même ordre que le bloc 0, ce que chaque compteur
   mesure : c'est l'appareil de preuve du chiffrier. Numéroter ses lignes
   avec le même mécanisme dérivé rend ce rappel lisible sans un mot de plus.
   Aucune largeur n'est touchée : l'enveloppe défile déjà seule, un préfixe
   posé dans une cellule existante ne crée pas de colonne neuve. */
.p-impact [data-bloc="3"] .tabl tbody { counter-reset: c-impact-t; }
.p-impact [data-bloc="3"] .tabl tbody th { counter-increment: c-impact-t; }
.p-impact [data-bloc="3"] .tabl tbody th::before {
  content: counter(c-impact-t, decimal-leading-zero);
  display: inline-block;
  margin-inline-end: var(--s2);
  color: var(--encre-2);
  font-family: var(--corps);
  font-weight: 400;
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   CONTACT - L'AIGUILLAGE
   LE PARTI : quatre voies numerotees comme des quais de depart, chacune
   menant a un destinataire nomme et refermee par une plaque d'adresse, pour
   qu'une page sans formulaire se lise comme un aiguillage et non comme un
   manque.
   Primitives : le compteur derive (la numerotation des voies), le cartouche
   (la plaque d'adresse qui clot chaque voie).
   ========================================================================== */

/* LE TABLEAU A QUATRE QUAIS, PAS TROIS PLUS UN ORPHELIN. Le socle ouvre les
   voies en auto-fit a 21rem ; applique a quatre cartes dans un conteneur de
   78rem, ce calcul rend trois colonnes et laisse la quatrieme voie seule sur
   sa ligne, la moitie du tableau vide a cote d'elle. Un aiguillage qui finit
   sur une impasse visuelle contredit son propre sujet : deux colonnes fixes
   ferment le tableau sans reste, comme un panneau a quatre quais. */
@media (min-width: 52rem) {
  .p-contact [data-bloc="0"] .voies { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* CHAQUE VOIE PORTE SON NUMERO, ET IL EST COMPTE, JAMAIS ECRIT : si une voie
   s'ajoutait ou disparaissait, la suite se renumeroterait seule. Le compteur
   vit sur la carte, la plaque de quai se lit devant le titre. */
.p-contact [data-bloc="0"] .voies { counter-reset: voie; }
.p-contact [data-bloc="0"] .voies li { counter-increment: voie; }
.p-contact [data-bloc="0"] .voies h3 {
  display: flex;
  align-items: center;
  gap: var(--s3);
}
.p-contact [data-bloc="0"] .voies h3::before {
  content: counter(voie);
  flex: none;
  display: grid;
  place-items: center;
  width: var(--s8);
  height: var(--s8);
  background: var(--accent);
  color: var(--acte-txt);
  font-family: var(--titre);
  font-weight: 700;
  font-size: var(--t-m);
}

/* LA DESTINATION PARLE PLUS FORT QUE LE MOTIF. "Qui lit" est toujours la
   premiere paire de la fiche : c'est le nom de la voie elle-meme, pas un
   detail parmi d'autres. Une plaque de quai avance sa destination, jamais
   son reglement. */
.p-contact [data-bloc="0"] .voies dd:first-of-type {
  font-size: var(--t-m);
  font-weight: 700;
  color: var(--accent);
}

/* LA PLAQUE D'ADRESSE EST UN CARTOUCHE, PAS UNE LIGNE QUI TRAINE. Sans
   formulaire, ce lien mailto est l'acte entier de la page : il ferme la voie
   comme une piece rapportee, bordee comme tout ce qui se clique (filet-2) et
   nappee comme une note (accent-fond) plutot que posee en simple texte au
   bas de la carte. */
.p-contact [data-bloc="0"] .voies__adresse {
  margin-block-start: auto;
  padding: 0;
  border: 1px solid var(--filet-2);
  background: var(--accent-fond);
}
.p-contact [data-bloc="0"] .voies__adresse a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--s4);
  text-decoration: none;
}
.p-contact [data-bloc="0"] .voies__adresse a:hover {
  background: var(--accent);
  color: var(--acte-txt);
}

/* LA CLOTURE REPREND LE MOTIF EN SOURDINE : une rangee de signaux courts
   plutot qu'une liste qui s'arrete sans le dire, chacun marque d'un filet de
   COMPOSITION (jamais de composant : ces lignes menent ailleurs, elles ne
   sont pas l'acte de cette page). */
.p-contact [data-bloc="4"] .liens-blocs li {
  padding-inline-start: var(--s4);
  border-inline-start: 2px solid var(--filet);
}
@media (min-width: 40rem) {
  .p-contact [data-bloc="4"] .liens-blocs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3) var(--s8);
  }
  .p-contact [data-bloc="4"] .liens-blocs li { margin-block-end: 0; }
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   SOUTIENS - les cadres reserves
   LE PARTI : la vacance se dit en prose libre et sans cadre (bloc 0), tandis
   que chaque condition verifiable aujourd'hui se pose dans une plaque
   fermee et registree au coin ; la place reservee n'attend donc jamais un
   logo en creux, elle est deja occupee par ce qui la remplirait.
   Primitives : la colonne resserree (le conteneur de toute la page), le
   compteur derive (le numero de chaque condition du duo)
   ========================================================================== */

/* Un financeur verifie, il ne parcourt pas : un conteneur plus etroit que le
   reste du site sert une lecture de dossier plutot qu'une vitrine. */
.p-soutiens { --large: 64rem; }

/* LES PLAQUES. Le socle a retire le filet qui pendait de chaque bloc (il vit
   maintenant sur la couverture d'un livre) ; cette page le redonne, ferme sur
   ses quatre cotes, aux trois blocs qui enoncent une condition tenable
   aujourd'hui : ce qu'un soutien change, ce qui est accepte ou refuse, ce
   qu'un financeur lira. L'ouverture (bloc 0), la photo (bloc 2) et la note
   (bloc 4) restent hors cadre : une plaque partout se lirait comme un
   gabarit repete, pas comme un choix. */
.p-soutiens [data-bloc="1"],
.p-soutiens [data-bloc="3"],
.p-soutiens [data-bloc="5"] {
  position: relative;
  border: 1px solid var(--filet);
  padding: var(--s6);
}

/* LE COIN REGISTRE. Une plaque qui pourrait un jour porter un nom se marque
   au coin, jamais au centre : le centre reste au texte qui existe deja.
   Meme filet court que celui que le socle pose au-dessus de chaque titre,
   repris ici sur l'objet plutot que sur le mot. */
.p-soutiens [data-bloc="1"]::before,
.p-soutiens [data-bloc="3"]::before,
.p-soutiens [data-bloc="5"]::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline-start: -1px;
  inline-size: var(--s8);
  block-size: 3px;
  background: var(--accent);
}

/* LE DUO, NUMEROTE. Cinq conditions par colonne aujourd'hui ; le
   compteur les compte depuis le DOM, il ne les annonce pas a la main. La
   lecture du badge (--encre sur --accent-clair) est la seule que la charte
   autorise sur cette surface. */
.p-soutiens [data-bloc="3"] .duo__col ul { counter-reset: critere; }
.p-soutiens [data-bloc="3"] .duo__col li {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
}
.p-soutiens [data-bloc="3"] .duo__col li::before {
  counter-increment: critere;
  content: counter(critere, decimal-leading-zero);
  flex: none;
  padding-inline: var(--s1);
  background: var(--accent-clair);
  color: var(--encre);
  font-family: var(--corps);
  font-weight: 700;
  font-size: var(--t-xs);
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   NOUVEAUTES - LA PILE DE FEUILLETS DATES
   LE PARTI : le registre reste un seul tome pose sur la page ; sous 40rem,
   ses lignes se detachent en feuillets empiles, chacun date et chacun cote.
   Primitives : le compteur derive (la cote de chaque feuillet).
   ========================================================================== */

/* LE TOME. Le tableau se lit comme un objet epais pose sur la page, meme tant
   qu'il reste un tableau : deux tranches dures reprennent la formule deja en
   usage sur les planches (`.fig img`), pour que la maison ne parle pas deux
   langues d'objet a deux endroits du meme site.
   `@media screen` : le socle imprime deja ce tableau proprement (bordures
   noires, overflow visible) et une ombre n'a rien a faire sur du papier. */
@media screen {
  .p-nouveautes [data-bloc="0"] .tableau-env {
    border: 1px solid var(--filet-2);
    box-shadow: 5px 5px 0 var(--fond), 6px 6px 0 var(--filet);
  }
  [dir="rtl"] .p-nouveautes [data-bloc="0"] .tableau-env {
    box-shadow: -5px 5px 0 var(--fond), -6px 6px 0 var(--filet);
  }
}

/* LE SEUIL EST FIXE A 40rem (640px), AU-DESSUS DES DEUX LARGEURS DU VERROU
   (500 et 360) : les deux passages mesures voient donc toujours la pile,
   jamais le tableau en defilement, et rien n'est ambigu sur ce qui est verifie. */
@media screen and (max-width: 40rem) {
  /* L'EN-TETE SORT DE LA VUE SANS SORTIR DE L'ARBRE D'ACCESSIBILITE. Un
     `display: none` retirerait aussi les intitules de colonne au lecteur
     d'ecran ; cette recette reprend a l'identique celle du socle (`.vh`), que
     je ne peux pas poser en classe puisque je n'ecris pas de HTML. */
  /* ⚠ SANS `!important`, ET LE SAS AVAIT RAISON DE LE REFUSER. La recette du
     socle (`.vh`) en porte un parce qu'elle doit battre n'importe quelle regle
     d'un composant quelconque ; ici le selecteur est deja a (0,2,3) et AUCUNE
     regle du socle ne pose `position` sur un `thead` (verifie a la feuille : la
     seule regle de `thead` est `.tabl thead th`, qui ne touche pas la position).
     Un `!important` dans un fichier de page bat les etats de theme et le
     contraste renforce, donc il ne doit exister nulle part en §11. */
  .p-nouveautes [data-bloc="0"] .tabl thead {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .p-nouveautes [data-bloc="0"] .tabl { display: block; }
  .p-nouveautes [data-bloc="0"] .tabl caption { display: block; }
  .p-nouveautes [data-bloc="0"] .tabl tbody { display: block; counter-reset: feuillet; }

  /* LE PLANCHER SOUS LA PILE : le vert clair fait lever chaque feuillet blanc,
     meme mecanique que les grilles de couvertures ailleurs sur le site. */
  .p-nouveautes [data-bloc="0"] .tableau-env { background: var(--fond-2); padding: var(--s4) var(--s3); }

  /* CHAQUE LIGNE DEVIENT UN FEUILLET : blanc franc, deux tranches dures
     resserrees (meme formule que le tome, pour un objet plus petit et repete).
     Le zebrage du socle est neutralise : il n'a plus de sens quand chaque
     ligne porte deja sa propre tranche. */
  .p-nouveautes [data-bloc="0"] .tabl tbody tr {
    counter-increment: feuillet;
    display: flex;
    flex-direction: column;
    gap: var(--s4);
    background: var(--carte);
    border: 1px solid var(--filet-2);
    box-shadow: 4px 4px 0 var(--fond-2), 5px 5px 0 var(--filet);
    padding: var(--s4) var(--s6);
    margin-block-end: var(--s6);
  }
  [dir="rtl"] .p-nouveautes [data-bloc="0"] .tabl tbody tr {
    box-shadow: -4px 4px 0 var(--fond-2), -5px 5px 0 var(--filet);
  }
  .p-nouveautes [data-bloc="0"] .tabl tbody tr:last-child { margin-block-end: 0; }
  .p-nouveautes [data-bloc="0"] .tabl tbody tr:nth-child(even) { background: var(--carte); }
  .p-nouveautes [data-bloc="0"] .tabl tbody tr > th,
  .p-nouveautes [data-bloc="0"] .tabl tbody tr > td {
    display: block; max-width: none; padding: 0; border: 0;
  }

  /* LA DATE EST LE MASTHEAD DU FEUILLET : elle n'a pas besoin d'etiquette, une
     date se reconnait a sa forme. Le numero qui la precede est DERIVE (le rang
     dans le registre, jamais invente) ; son alternative textuelle vide evite
     qu'une synthese vocale l'annonce en double de ce que `scope="row"` dit deja. */
  .p-nouveautes [data-bloc="0"] .tabl tbody tr > th[scope="row"] {
    font-size: var(--t-l);
    padding-block-end: var(--s2);
    border-block-end: 1px solid var(--filet);
  }
  .p-nouveautes [data-bloc="0"] .tabl tbody tr > th[scope="row"]::before {
    content: counter(feuillet) / "";
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem; min-height: 1.5rem;
    margin-inline-end: var(--s2);
    border: 1px solid var(--filet);
    font-size: var(--t-xs);
    color: var(--encre-2);
    vertical-align: middle;
  }

  /* CE QUE CHAQUE CELLULE DIT D'ELLE-MEME. `attr()` lit `data-etiquette`,
     pose par le generateur a partir de l'intitule de colonne deja ecrit au
     thead : la valeur reste juste dans les sept langues sans qu'aucun mot
     ne soit ecrit ici. */
  .p-nouveautes [data-bloc="0"] .tabl tbody tr > td::before {
    content: attr(data-etiquette);
    display: block;
    margin-block-end: var(--s1);
    font-size: var(--t-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--encre-2);
  }
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   SOURCES ET RÉFÉRENCES - l'appareil de notes numéroté
   LE PARTI : treize sources se numérotent d'un bout à l'autre sans jamais
   repartir de un, puis la même colonne s'arrête une fois sur l'aveu qui n'a
   pas de numéro à porter.
   Primitives : le cartouche, la colonne resserrée (les deux au bloc 5 seul)
   ========================================================================== */

/* NUMÉROTATION CONTINUE. Un appareil de notes se compte d'un bout à l'autre
   d'un texte, jamais remis à zéro à chaque sous-titre : les trois blocs
   "references" partagent donc UN SEUL compteur, posé sur la page entière ;
   chaque liste annule le counter-reset que le socle lui pose individuellement,
   sans quoi les trois listes repartiraient chacune de [1]. */
.p-sources { counter-reset: ref; }
.p-sources .refs { counter-reset: none; }

/* Le numéro grandit un cran ; la colonne suit en "auto" et non en valeur
   fixe, pour qu'un chiffre de plus ne puisse jamais chevaucher le texte. */
.p-sources .refs > li { grid-template-columns: minmax(2.25rem, auto) minmax(0, 1fr); }
.p-sources .refs > li::before { font-size: var(--t-m); }

/* Trois registres dans un seul appareil continu se lisent mieux bornés que
   flottants : le haut de chaque liste se ferme comme son bas se ferme déjà. */
.p-sources .refs > li:first-child { border-block-start: 1px solid var(--filet); }

/* Une bibliographie qu'on recopie se lit ligne à ligne ; sur les intitulés
   les plus longs, un mot seul rejeté à la ligne ralentit cette lecture-là. */
.p-sources .refs__quoi { text-wrap: pretty; }

/* La ligne entière signale qu'on s'apprête à suivre sa preuve, pas seulement
   le mot du lien : c'est ce qui rend treize lignes vraiment scannables. */
.p-sources .refs > li:has(.refs__lien:hover, .refs__lien:focus-visible) {
  background: var(--accent-fond);
}

/* ============================================================================
   BLOC 5 - L'AVEU QUI N'A PAS DE NUMÉRO

   La doctrine de la maison écrit que le doute se publie ; il ne s'excuse
   pas en petit, dans l'encre de correction, en bas de page. Son poids vient
   d'une plaque et d'un titre au cran le plus haut de la page - jamais d'une
   couleur d'alerte, jamais d'un rang de plus dans l'appareil qui précède.
   ========================================================================== */
.p-sources [data-bloc="5"] {
  --mesure: 52ch;   /* resserrée : un aveu se lit lentement, pas en diagonale */
  margin-block-start: var(--s16);
}
.p-sources [data-bloc="5"] .bloc__corps {
  background: var(--fond-3);
  border: 1px solid var(--filet-2);
  border-block-start: 3px solid var(--accent);
  padding: var(--s8);
}
/* Le listel du haut de la plaque porte déjà le signal que la règle courte du
   socle donne à chaque titre de bloc ; le répéter sur le h2 le dédoublerait
   à quelques pixels d'écart. */
.p-sources [data-bloc="5"] .bloc__corps > h2 {
  border-block-start: 0;
  padding-block-start: 0;
  font-size: var(--t-2xl);
}
.p-sources [data-bloc="5"] .lecture p {
  margin-block-end: var(--s6);
  text-wrap: pretty;
}
.p-sources [data-bloc="5"] .lecture p:last-child { margin-block-end: 0; }

/* --------------------------------------------------------------------------- */
/* ============================================================================
   CREDITS - la table des planches
   LE PARTI : chaque photographie devient une planche numerotee dont la
   licence se verifie au regard, sans jamais lire un paragraphe.
   Primitives : le compteur derive, le cartouche
   ========================================================================== */

/* LA GALERIE DEVIENT UN REGISTRE NUMEROTE. Le compteur porte sur les planches
   reellement rendues par le catalogue d'images : il compte ce que le
   generateur a ecrit, jamais un chiffre pose a la main, et il ne peut donc
   pas se desaccorder du nombre reel de photographies servies. */
.p-credits [data-bloc="1"] .credits-img { counter-reset: planche; }
.p-credits [data-bloc="1"] .credits-img > li { counter-increment: planche; }

/* LE TITRE PORTE SON NUMERO DE PLANCHE, DANS LA VOIX DEJA POSEE PAR LES
   FOLIOS DE MARGE DU SOCLE (Literata, encre d'accent) : une reprise du signe
   existant, pas un motif invente pour cette seule page. Zero rembourrage a
   deux chiffres pour que la colonne de numeros s'aligne d'une planche a
   l'autre au lieu de danser avec la longueur du chiffre.
   Le titre n'a qu'un seul noeud de texte, donc "display: flex" ne peut y
   separer aucun champ de donnee : le risque de collage qui a deja coute une
   regression sur cette page (les etiquettes de police) ne vaut que pour un
   paragraphe qui porte un lien voisin, jamais pour celui-ci. */
.p-credits [data-bloc="1"] .credits-img__tete {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  font-size: var(--t-m);
}
.p-credits [data-bloc="1"] .credits-img__tete::before {
  content: counter(planche, decimal-leading-zero);
  flex: none;
  font-family: var(--titre);
  font-size: var(--t-xl);
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* L'AUTEUR ET LA VERIFICATION PASSENT EN ENCRE SECONDAIRE, POUR QU'UNE FICHE
   SE LISE DU TITRE VERS LA PREUVE ET NON L'INVERSE. Le catalogue rend
   toujours quatre paragraphes dans le meme ordre (titre, auteur, licence,
   source) : seuls le second et le quatrieme sont vises ici, jamais le
   troisieme, qui porte la garde ci-dessous. La date de verification ferme la
   fiche comme un tampon, filet fin au-dessus, dans le plus petit corps. */
.p-credits [data-bloc="1"] .credits-img p:nth-of-type(2) { color: var(--encre-2); }
.p-credits [data-bloc="1"] .credits-img p:nth-of-type(4) {
  color: var(--encre-2);
  font-size: var(--t-xs);
  padding-block-start: var(--s1);
  border-block-start: 1px solid var(--filet);
}

/* LA GARDE UNIQUE DE CETTE PAGE : LE PARTAGE A L'IDENTIQUE SE VOIT SANS LIRE.
   Le selecteur d'attribut cible le lien reel que le generateur pose deja vers
   le texte de la licence : aucune donnee ajoutee, un fait derive du DOM
   exactement comme le compteur ci-dessus. Le cartouche reprend le langage
   deja pose pour le composant "note" du socle (nappe d'accent, filet de
   3 px) au lieu d'en inventer un troisieme : une seule garde visuelle pour
   la meme obligation, partout ou une photographie force ce site a republier
   sous la licence de sa source. Les cinq planches sans cette clause restent
   sans cartouche : l'oeil ne doit remarquer que l'exception. */
.p-credits [data-bloc="1"] .credits-img p:nth-of-type(3):has(a[href*="by-sa"]) {
  padding: var(--s2) var(--s3);
  background: var(--accent-fond);
  border-inline-start: 3px solid var(--accent);
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   PLAN DU SITE - l'arborescence en colonnes
   LE PARTI : le plan cesse d'être une liste de liens, il devient un index
   numéroté et mis en colonnes, ses fichiers techniques rangés à part.
   Primitives : le compteur dérivé, le cartouche
   ========================================================================== */

/* Le plan n'est pas de la prose : la mesure de lecture (66ch) n'a rien à
   protéger ici, elle empêcherait justement la mise en colonnes que la page
   réclame. Le bloc reprend toute la largeur du conteneur. */
.p-plan [data-bloc="0"] .bloc__corps { max-width: none; }

/* L'INDEX. Chaque liste porte son propre compteur (counter-reset sur le
   <ul>, donc chacune repart à 1) : c'est le rang de la page DANS CETTE LISTE,
   pas le folio du site, que le plan ne porte pas par entrée (voir le
   rapport). Un numéro dérivé du DOM habille le lien comme une entrée de
   table des matières plutôt que comme un lien de pied de page. */
.p-plan [data-bloc="0"] .plan-liste { counter-reset: rang; }
.p-plan [data-bloc="0"] .plan-liste li {
  counter-increment: rang;
  break-inside: avoid;
  margin-block-end: var(--s3);
  padding-inline-start: 2.4em;
  text-indent: -2.4em;
}
.p-plan [data-bloc="0"] .plan-liste li::before {
  content: counter(rang, decimal-leading-zero);
  display: inline-block;
  width: 1.7em;
  margin-inline-end: 0.3em;
  color: var(--encre-2);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* LA CARTE DU SITE. column-width et non column-count : le nombre de colonnes
   est une conséquence de la largeur, jamais un chiffre écrit, donc la liste
   tient aussi bien à 14 entrées qu'à 40. Sous deux colonnes de 13rem, une
   seule reste : c'est la même liste qu'au socle, juste numérotée. */
.p-plan [data-bloc="0"] .plan-liste:first-of-type {
  column-width: 13rem;
  column-gap: var(--s8);
}

/* LE CARTOUCHE TECHNIQUE. --fond-3 est déjà nommé pour les cartouches
   techniques au jeton lui-même (le papier de garde) : ces adresses ne sont
   pas des pages du site et ne méritent pas le même regard. Isolées dans leur
   cadre, elles arrêtent le flux au lieu de continuer la liste des pages
   comme une entrée de plus parmi les autres. */
.p-plan [data-bloc="0"] .plan-liste:last-of-type {
  max-width: var(--etroit);
  column-width: 15rem;
  column-gap: var(--s6);
  background: var(--fond-3);
  border: 1px solid var(--filet-2);
  border-radius: var(--rayon);
  padding: var(--s6);
  font-size: var(--t-s);
}

/* LA PLANCHE. Le conteneur n'a plus la mesure pour la retenir au bord de
   départ : sans ce centrage, l'image plafonnée à 46,25rem resterait collée
   au bord d'un conteneur large et laisserait un vide qui ne dit rien. Le
   filet qui la précède ferme la liste avant la planche. */
.p-plan [data-bloc="1"] {
  display: flex;
  justify-content: center;
  margin-block-start: var(--s8);
  padding-block-start: var(--s8);
  border-block-start: 1px solid var(--filet);
}

/* --------------------------------------------------------------------------- */
/* ============================================================================
   404 - la declaration typographique pleine page
   LE PARTI : le titre garde l'echelle commune ; une entaille geometrique,
   aussi haute que lui, porte seule le poids monumental de la page absente.
   Primitives : le decroche, pose deux fois (tete de page, puis liens face
   a la note)
   ========================================================================== */

.p-404 {
  /* L'ENTAILLE : un aplat auquel il manque un coin, angles nets, aucune
     courbe. C'est la figure que le mot ne peut pas ecrire ici (aucun
     content textuel n'est permis sur cette page). Une seule valeur,
     reemployee a deux echelles plus bas : la meme coupe, deux tailles. */
  --entaille: polygon(0 0, 100% 0, 100% 100%, 38% 100%, 38% 60%, 0 60%);
}
/* clip-path est un axe physique, sans equivalent logique : le coin manquant
   doit rester au meme bord LOGIQUE (fin de ligne, bas de bloc) quand la page
   passe en RTL, donc le polygone se retourne a la main. */
[dir="rtl"] .p-404 {
  --entaille: polygon(100% 0, 0 0, 0 100%, 62% 100%, 62% 60%, 100% 60%);
}

/* CETTE PAGE N'EST PAS UN LIVRE : son titre parle la voix de la librairie.
   Le socle pose Literata sur tout h1 du site par defaut ; le h2 de bloc a
   deja sa propre regle courte a la voix de l'interface, ce titre de tete
   de page n'avait encore personne pour la lui donner. */
.p-404 h1 {
  font-family: var(--corps);
}

/* LA TETE DE PAGE PORTE LE SIGNE. Sous le seuil, une bande entaillee ouvre
   la page avant le titre ; au-dela, elle devient une colonne aussi haute que
   le titre et son chapeau, une entaille pleine hauteur plutot qu'un simple
   bandeau. Le titre lui-meme ne grossit jamais au-dela de son cran : c'est
   la figure qui porte l'echelle monumentale, jamais le texte. */
.p-404 .tete-page > .wrap::before {
  content: "";
  display: block;
  height: clamp(3rem, 14vw, 4.5rem);
  margin-block-end: var(--s6);
  background: var(--accent-clair);
  clip-path: var(--entaille);
}
@media (min-width: 60rem) {
  .p-404 .tete-page > .wrap {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(10rem, 22vw, 16rem);
    column-gap: var(--s12);
  }
  .p-404 .tete-page > .wrap::before { display: none; }
  .p-404 .tete-page > .wrap > h1 { grid-column: 1; grid-row: 1; }
  .p-404 .tete-page > .wrap > .chapeau { grid-column: 1; grid-row: 2; }
  .p-404 .tete-page > .wrap::after {
    content: "";
    display: block;
    grid-column: 2;
    /* ⚠ 1 / -1 EST UN PIEGE ICI : sans grid-template-rows declare, la
       grille EXPLICITE n'a aucune piste de ligne, donc la ligne negative -1
       se compte contre elle et non contre les pistes IMPLICITES que h1 et
       le chapeau viennent de creer : l'entaille ne couvrait alors que la
       premiere. span compte les pistes reelles, pas la grille declaree. */
    grid-row: 1 / span 2;
    background: var(--accent-clair);
    clip-path: var(--entaille);
  }
}

/* PAR OU REPRENDRE, FACE A LA NOTE : le meme decroche qu'en tete de page,
   pose une seconde fois, pour que la page entiere se lise comme UN geste
   repete et non deux compositions voisines. */
@media (min-width: 60rem) {
  .p-404 [data-sec="0"] > .wrap {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    column-gap: var(--s12);
    align-items: start;
  }
  /* Ni l'un ni l'autre bloc ne porte de marginalia : on le fixe ici plutot
     que de compter sur le socle pour le deviner, colonne unique quoi qu'il
     arrive en amont. */
  .p-404 [data-sec="0"] > .wrap > .bloc--marge { display: block; }
}

/* L'ENTAILLE EST UN SIGNE A L'ECRAN, PAS UNE DEPENSE D'ENCRE : sur le
   papier, ou la maison retire deja toute trame decorative, elle se
   retire aussi. */
@media print {
  .p-404 .tete-page > .wrap::before,
  .p-404 .tete-page > .wrap::after { display: none; }
}

/* ==== FIN DES COMPOSITIONS DE PAGE ==== */
