/* ============================================================================
 * Bonjour la toile — la feuille de style du site. Écrite à la main, sans
 * framework et sans JavaScript, chargée après `tokens.css`.
 *
 * ---------------------------------------------------------------------------
 * LE PARTI : « LE BLOC » (2026-09-01)
 *
 * Ce qui a été abandonné, et pourquoi. Le site suivait junipeer.io transposé
 * en mauve : une PILE DE BANDES pleine largeur (nuit, pâle, claire, nuit).
 * Mesuré en production, le résultat ne tenait pas :
 *
 *   - les surfaces du site occupaient 18 %, 25 %, puis 90 %, 98,5 % et 100 %
 *     de clarté. RIEN entre 25 et 90 : la page alternait deux extrêmes,
 *     quatre fois par page, et toutes les pages avaient la même pile ;
 *   - le ruban « pâle » sortait à chroma 0,05. Le cyan-menthe du modèle reste
 *     une couleur à cette saturation ; une teinte 305 s'y lit comme du GRIS.
 *     La bande censée porter la couleur arrivait à l'écran en blanc sale.
 *
 * Le remède n'est pas de remonter la saturation d'une bande, c'est d'arrêter
 * d'empiler des bandes :
 *
 *   UN SEUL FOND, DU PAPIER      `--papier` pour toute la page. Pas blanc :
 *                                un blanc cassé qui penche vers le mauve.
 *   DES FILETS DE 2 PX           Les sections sont séparées par un trait
 *                                d'encre, pas par un changement de fond.
 *   LE MAUVE EN APLAT            Il n'est plus un accent, il est une SURFACE.
 *                                Un bloc plein qui sort par le bord de
 *                                l'écran fait plus de bruit que quatre
 *                                bandes qui se relaient.
 *   TITRES EN MONOSPACE          Seul trait conservé du modèle précédent, et
 *                                il est devenu la signature du site.
 *   PLUS DE FILET À POINT        L'ornement dégradé était l'emprunt le plus
 *                                littéral à junipeer. Il est parti avec
 *                                `bl_filet()`.
 *
 * ---------------------------------------------------------------------------
 * TROIS SURFACES, TROIS RÔLES, ET C'EST TOUT
 *
 *   papier   le sol de toute la page
 *   aplat    le mauve plein, pour ce qui doit être vu de loin
 *   encre    les filets, le texte, les boutons pleins, le pied de page
 *
 * ---------------------------------------------------------------------------
 * COMMENT L'INVERSION FONCTIONNE
 *
 * `.aplat` et `.pied-site` REDÉFINISSENT les variables sémantiques (`--fg`,
 * `--bg`, `--border`, `--brand`, et les quatre `--btn-*`). Tout ce qui vit
 * dedans s'inverse tout seul : les boutons, les liens, les filets, les
 * listes. Aucun composant n'a de variante « sur fond mauve » à maintenir.
 * C'est la seule mécanique héritée de l'ancienne feuille, et la seule qui
 * méritait de l'être.
 *
 * ---------------------------------------------------------------------------
 * DEUX ÉPAISSEURS DE TRAIT, ET ELLES NE SE MÉLANGENT PAS
 *
 *   2 px d'encre   la STRUCTURE des pages publiques (filets de section,
 *                  bordures des blocs de vente, tête de liste).
 *   1 px `--border` le CHROME des écrans denses (champs, tableaux, tiroirs).
 *
 * L'espace client et l'administration sont des outils qu'on regarde une heure
 * d'affilée : leur passer 2 px d'encre autour de chaque champ les rendrait
 * illisibles. Le parti graphique vaut pour la vitrine, pas pour l'atelier.
 * ========================================================================== */

:root {
  /* ---------- Les tokens sémantiques, tirés de `tokens.css` ---------- */
  --bg:            var(--papier);
  --surface:       var(--papier-2);
  --surface-2:     var(--mauve-100);
  --fg:            var(--encre);
  --fg-2:          var(--encre-2);
  --fg-3:          var(--encre-3);
  --border:        var(--encre-4);
  --border-strong: oklch(72% 0.020 300);
  --brand:         var(--mauve-600);
  --brand-hover:   var(--mauve-700);
  --brand-soft:    var(--mauve-100);
  --brand-soft-fg: var(--mauve-800);
  --ring:          oklch(48% 0.190 305 / 0.35);

  /* Le trait de structure. Il ne change JAMAIS d'épaisseur : c'est lui qui
     tient la page maintenant que les fonds ne le font plus. */
  --filet: 2px solid var(--fg);

  /* Les boutons ne lisent que ces cinq variables : les inverser suffit à les
     faire fonctionner sur n'importe quelle surface.
     LE BOUTON PLEIN EST MAUVE. Il a été d'encre le temps d'une version : sur
     du papier, le noir commande, mais il ne dit pas de qui il est. Le seul
     endroit où la marque parle sur une page claire, c'est ce bouton. */
  --btn-fond:   var(--mauve-600);
  --btn-bord:   var(--mauve-600);
  --btn-txt:    var(--papier);
  --btn-fond-h: var(--encre);
  --btn-txt-h:  var(--papier);

  /* La hauteur de la barre. Elle est une VARIABLE parce que deux règles
     éloignées doivent s'accorder dessus : `min-height` sur la barre, et
     `scroll-padding-top` sur `html` — sans quoi une ancre saute SOUS
     l'en-tête collante et le titre visé se retrouve caché derrière. */
  --entete-h: 84px;
}

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

/* ⚠️ `scrollbar-gutter: stable` N'EST PAS UN DÉTAIL DE CONFORT. Sans lui, une
   page COURTE (Ma boîte vide, un onglet sans réponse) n'a pas de barre de
   défilement et une page longue en a une : le `.wrap`, qui se centre, se
   décale alors d'une demi-barre entre les deux. On passe de « Mes sites » à
   « Ma boîte » et tout l'écran glisse de sept pixels sur le côté. Signalé par
   Simon le 2026-09-01 : « le layout de ma boîte n'est pas à la même largeur,
   les éléments ne sont pas exactement à la même place ». La gouttière est
   maintenant réservée tout le temps. */
html {
  -webkit-text-size-adjust: 100%;
  color-scheme: light;
  scrollbar-gutter: stable;
  /* ⚠️ SANS CECI, LES QUATRE ANCRES DU SITE ATTERRISSENT SOUS L'EN-TÊTE.
     `#essai` sur les trois fiches de module et `#ajouter` dans l'espace
     visent un titre : l'en-tête étant collante, le navigateur l'amène pile
     au bord haut de la fenêtre, c'est-à-dire DERRIÈRE la barre. Le titre
     visé disparaît, et le visiteur croit que le lien n'a rien fait. */
  scroll-padding-top: calc(var(--entete-h) + var(--sp-6));
}

/* ⚠️ LE PIED DE PAGE COLLE EN BAS, ET IL FAUT LE DIRE ICI. Une page courte le
   laissait flotter au milieu de l'écran, avec du papier vide en dessous : vu
   sur Ma boîte quand un onglet ne contient rien. `<main>` prend la place qui
   reste, le pied ne se comprime pas. */
body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--fg);
  font: 400 var(--fs-16)/var(--lh-relaxed) var(--font-sans), ui-sans-serif, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
body > main { flex: 1 0 auto; }
body > .pied-site, body > .entete-site { flex: 0 0 auto; }

img, svg, iframe { max-width: 100%; }

a { color: var(--brand); text-decoration: none; }
a:hover { color: var(--brand-hover); text-decoration: underline; text-underline-offset: 3px; }

:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

code, pre, kbd { font-family: var(--font-mono), ui-monospace, monospace; }

/* ⚠️ LE FILET DE SÉCURITÉ DES TITRES SANS CLASSE. Les titres du site portent
   tous une classe (`.titre-page`, `.titre-section`…), mais un `<h2>` écrit
   sans classe dans une page à venir ne doit pas sortir en Times : il tombe
   ici, en monospace, comme les autres. L'ancienne `tokens.css` faisait
   l'inverse et imposait un serif à tout titre nu. */
h1, h2, h3, h4 {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-weight: 500;
  letter-spacing: var(--tracking-snug);
  color: var(--fg);
}

/* ------------------------------------------------------------- La structure */

/* ⚠️ CE `padding` EST LA MARGE LATÉRALE DE TOUT LE SITE, ET IL SE FAIT
   ÉCRASER PAR ACCIDENT. Une classe posée À CÔTÉ de `.wrap` (même poids, mais
   écrite plus bas dans le fichier) qui déclare `padding` en RACCOURCI remet
   les côtés à zéro sans le dire : le contenu vient alors coller aux deux
   bords de l'écran. C'est arrivé à `.espace` et à `.portail`, c'est-à-dire à
   tout l'espace privé et à toutes les pages de connexion.
   RÈGLE : une classe qui accompagne `.wrap` règle `padding-block`, jamais
   `padding`. */
.wrap { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 var(--sp-8); }
.wrap--etroit { max-width: 780px; }

/* Une section. Le filet du haut la sépare de la précédente : c'est le seul
   séparateur du site, il n'y a plus de changement de fond pour le faire. */
.coupe { width: 100%; border-top: var(--filet); padding: clamp(48px, 6vw, 80px) 0; }
.coupe--nue { border-top: 0; }
.coupe--serree { padding: clamp(32px, 4vw, 56px) 0; }

/* Le bandeau de mentions : une ligne prise entre deux filets, qui DÉFILE.
   ---------------------------------------------------------------------------
   ⚠️ LE RUBAN PORTE DEUX FOIS LE MÊME GROUPE, ET C'EST CE QUI REND LA BOUCLE
   INVISIBLE. On translate le ruban de -50 %, c'est-à-dire exactement la
   largeur d'un groupe : à la fin de l'animation, le second groupe occupe la
   place où le premier a commencé, et le saut au début du cycle suivant ne se
   voit pas. Retirer le doublon fait réapparaître un trou qui traverse
   l'écran à chaque tour.

   ⚠️ LE DOUBLON EST `aria-hidden` : il n'existe que pour l'œil. Sans ça, un
   lecteur d'écran annonce chaque mention deux fois.

   ⚠️ L'ANIMATION NE DÉMARRE QUE SI LE VISITEUR NE DEMANDE PAS LE CONTRAIRE.
   Le bloc `prefers-reduced-motion` en fin de fichier coupe toutes les
   animations du site ; ici le ruban reste simplement immobile sur son
   premier groupe, ce qui est un état lisible. */
.bandeau { border-top: var(--filet); border-bottom: var(--filet); padding: var(--sp-5) 0; overflow: hidden; }
.bandeau__ruban { display: flex; width: max-content; }
.bandeau__groupe { display: flex; align-items: center; flex: none; }
.bandeau__groupe > * { padding-right: var(--sp-12); }
/* La puce carrée entre deux mentions : le même signe que `.puces`, en plus petit.
   ⚠️ ELLE EST POSÉE DEVANT CHAQUE MENTION, PAS ENTRE DEUX FRÈRES. Écrite
   `* + *`, elle sautait la PREMIÈRE mention de chaque groupe — donc la
   couture entre la fin d'un groupe et le début du suivant n'en avait pas, et
   deux mentions s'y collaient sans séparateur une fois sur deux. Signalé par
   Simon : « il n'y a pas de petit rond entre Aucun traceur posé et 3 modules,
   tous en ligne ». Sur un ruban qui boucle, il n'y a pas de « premier ». */
.bandeau__groupe > *::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  background: var(--brand);
  margin-right: var(--sp-12);
  vertical-align: 0.1em;
}
@media (prefers-reduced-motion: no-preference) {
  .bandeau__ruban { animation: bandeau-defile 45s linear infinite; }
}
/* ⚠️⚠️ LA TRANSLATION VAUT UN SIXIÈME PARCE QU'IL Y A SIX COPIES. Le ruban
   glisse d'exactement UNE largeur de groupe : c'est ce qui rend le retour au
   début invisible. Six copies, donc -100/6 %.

   ⚠️ ET SIX N'EST PAS UN NOMBRE AU HASARD. Un ruban n'a pas de trou tant que

        (nombre de copies - 1) x largeur d'un groupe >= largeur de l'écran

   Avec deux copies (la première version), la couverture utile valait UNE
   largeur de groupe, mesurée à 1083 px sur l'accueil : moins que n'importe
   quel écran de bureau. Un trou traversait donc la page à chaque tour, ce que
   Simon a vu tout de suite — « on voit la fin assez vite ». Six copies
   couvrent 5 x 1083 = 5415 px, soit au-delà du 4K.

   Changer le nombre de copies dans `bl_bandeau()` oblige à changer ce
   pourcentage, et l'inverse aussi. Les deux vivent ensemble ou pas du tout. */
@keyframes bandeau-defile {
  from { transform: translateX(0); }
  to   { transform: translateX(-16.6667%); }
}

/* L'APLAT : le mauve employé comme surface. Il redéfinit tout, donc ce qui
   vit dedans n'a pas de variante à écrire. */
.aplat {
  --bg: var(--mauve-600);
  --surface: var(--mauve-700);
  --surface-2: var(--mauve-700);
  --fg: oklch(98% 0.012 305);
  --fg-2: oklch(90% 0.045 305);
  --fg-3: oklch(82% 0.055 305);
  --border: oklch(62% 0.140 305);
  --border-strong: oklch(70% 0.110 305);
  --brand: oklch(98% 0.012 305);
  --brand-hover: oklch(93% 0.045 305);
  --btn-fond: oklch(98% 0.012 305);
  --btn-bord: oklch(98% 0.012 305);
  --btn-txt: var(--mauve-700);
  --btn-fond-h: var(--encre);
  --btn-txt-h: var(--papier);
  background: var(--bg);
  color: var(--fg);
}
.aplat--fonce { --bg: var(--mauve-700); --btn-txt: var(--mauve-800); }

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

/* ⚠️ LES TITRES SONT EN MONOSPACE, INTERLIGNE ~1. C'est le trait du site :
   l'enlever suffit à faire disparaître toute l'allure. */
.titre-page, .titre-section, .titre-bloc, .titre-espace {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--fg);
  margin: 0;
}
/* ⚠️⚠️ UN TITRE DE HÉROS TIENT 18 SIGNES PAR LIGNE, PAS UN DE PLUS.
   C'est la seule règle à retenir de ce bloc, et elle est arithmétique :

     Geist Mono a une avance de 0,6 em EXACTEMENT (mesuré au canvas sur la
     page, pas estimé). La colonne du héros fait 36rem = 576 px, moins 48 px
     de marge à droite : 528 px utiles. À 48 px de corps, un signe occupe
     28,8 px, donc 528 / 28,8 = 18,3.

   ⚠️ CE QUI ARRIVE QUAND ON L'OUBLIE : le <br> reste, MAIS le morceau trop
   long se replie tout seul par-dessus. Un titre coupé en deux sort alors sur
   quatre lignes, sans erreur ni avertissement. Vécu en production : « Un avis
   en un clic, / sans écrire un mot. » (19 signes de chaque côté) s'affichait
   sur QUATRE lignes, et « Vous payez pour / retirer notre nom. » sur trois.
   Personne ne le voit dans le code, tout le monde le voit à l'écran.

   Donc : en écrivant un titre de héros, compter chaque morceau entre <br>.
   Changer la colonne (36rem), la marge (48 px) ou ce corps (3rem), c'est
   refaire ce calcul ET relire les cinq titres. */
.titre-page { font-size: clamp(2rem, 4.2vw, 3rem); line-height: 1.04; }
.titre-section { font-size: clamp(1.5rem, 2.9vw, 2.25rem); line-height: 1.12; }
.titre-espace { font-size: clamp(1.5rem, 2.6vw, 2rem); line-height: 1.1; }
.titre-bloc { font-size: var(--fs-18); line-height: 1.3; }

.chapeau {
  margin: var(--sp-5) 0 0;
  max-width: 52ch;
  font-size: var(--fs-16);
  line-height: var(--lh-relaxed);
  color: var(--fg-2);
}
.mesure { max-width: 62ch; }
.secondaire { color: var(--fg-2); }
.discret { color: var(--fg-3); font-size: var(--fs-14); }
.rien { margin: 0; }
.centre { text-align: center; }
.centre .chapeau, .centre .mesure { margin-left: auto; margin-right: auto; }

.oeil {
  display: block;
  font: 500 var(--fs-12)/1 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--brand);
  margin-bottom: var(--sp-4);
}
.meta {
  font: 500 var(--fs-12)/1.3 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--fg-2);
}
.nombre {
  font: 500 var(--fs-44)/1 var(--font-mono), monospace;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* La règle sous un titre de page. Elle a remplacé le filet dégradé à point :
   un trait plein, de la largeur de la colonne, sans ornement. */
.regle { height: 2px; background: var(--fg); margin: var(--sp-6) 0; width: 100%; }

/* ------------------------------------------------------------------ Boutons */

/* Angle droit, bordure de 2 px, et le plein est de l'ENCRE : sur du papier,
   c'est le noir qui commande, pas la couleur. Le mauve arrive au survol. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 13px 25px;
  border: 2px solid var(--btn-bord);
  border-radius: 0;
  background: var(--btn-fond);
  color: var(--btn-txt);
  font: 500 var(--fs-16)/1.2 var(--font-sans), sans-serif;
  cursor: pointer;
  transition: background var(--dur) ease, border-color var(--dur) ease, color var(--dur) ease;
}
.btn:hover {
  background: var(--btn-fond-h);
  border-color: var(--btn-fond-h);
  color: var(--btn-txt-h);
  text-decoration: none;
}
.btn[disabled] { opacity: .4; cursor: not-allowed; }

.btn--ligne { background: transparent; color: var(--fg); border-color: currentColor; }
.btn--ligne:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); }

.btn--danger { background: transparent; color: var(--danger); border-color: var(--danger); }
.btn--danger:hover { background: var(--danger); color: var(--papier); border-color: var(--danger); }

.btn--petit { padding: 8px 16px; font-size: var(--fs-14); }

.actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }

.fleche { font-weight: 500; }
.fleche::after { content: " \2192"; display: inline-block; transition: margin var(--dur) ease; }
.fleche:hover { text-decoration: none; }
.fleche:hover::after { margin-left: 4px; }

/* ------------------------------------------------------------ En-tête du site */

/* Une seule en-tête, la même partout. Elle vivait auparavant DANS la bande du
   héros et changeait de ton selon la page ; il n'y a plus de bande, donc plus
   de ton : du papier, et un filet dessous. */
/* ⚠️ ELLE COLLE, ET TROIS DÉTAILS LA RENDENT HABITABLE.
   1. `background` OPAQUE, sinon le contenu défile visiblement au travers.
      Il est déjà là, mais il devient obligatoire : ne pas le retirer.
   2. `z-index` AU-DESSUS DE TOUT. Les deux tiroirs du menu montent à 50 ;
      ils sont DANS l'en-tête, donc c'est elle qui doit passer devant la
      page, et eux suivent dans son contexte d'empilement.
   3. `scroll-padding-top` sur `html` (voir plus haut) : une ancre qui saute
      doit s'arrêter SOUS la barre, pas derrière.
   Le filet de 2 px sert alors de séparation quand la page glisse dessous. */
.entete-site {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg);
  border-bottom: var(--filet);
}
.entete-site__barre {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: var(--entete-h);
  padding-top: var(--sp-4);
  padding-bottom: var(--sp-4);
}

/* La marque est le seul serif du site, et c'est elle qui doit tenir la barre.
   Elle est fluide pour ne pas écraser le bouton « Menu » sur un téléphone. */
.marque {
  font-family: var(--font-display), Georgia, serif;
  font-size: clamp(1.5rem, 1.95vw, 1.875rem);
  line-height: 1.1;
  letter-spacing: var(--tracking-snug);
  color: var(--fg);
  white-space: nowrap;
}
.marque:hover { color: var(--fg); text-decoration: none; }
.marque b { font-weight: 400; color: var(--brand); }

.nav-liens { display: flex; align-items: center; gap: var(--sp-6); margin-left: auto; }
/* ⚠️ `:not(.btn)` N'EST PAS DÉCORATIF. Sans lui, `.nav-liens > a` (0,1,1)
   l'emporte sur `.btn` (0,1,0) et le BOUTON hérite de la couleur du texte de
   l'en-tête. Un bouton ne prend jamais la couleur de son voisinage. */
.nav-liens > a:not(.btn) {
  font-size: var(--fs-15);
  font-weight: 500;
  color: var(--fg);
  padding: 4px 0;
  border-bottom: 2px solid transparent;
}
.nav-liens > a:not(.btn):hover { text-decoration: none; border-bottom-color: var(--border-strong); }
.nav-liens > a:not(.btn).actif { border-bottom-color: var(--brand); }
.nav-liens > .btn { padding: 10px 18px; font-size: var(--fs-15); }

/* ------------------------------------------------- Le menu du compte

   Le bouton porte le nom d'utilisateur, le tiroir porte tout le reste. C'est
   un <details> natif — même mécanique que le menu du téléphone, zéro
   JavaScript.

   ⚠️ LE `<summary>` EST LE BOUTON LUI-MÊME. Il porte `.btn`, donc il faut lui
   retirer la puce que le navigateur dessine (deux propriétés, pas une : les
   moteurs WebKit ne lisent pas `list-style`), et lui rendre le `inline-flex`
   que `display: list-item` écraserait. */
.compte-menu { position: relative; }
.compte-menu > summary {
  display: inline-flex;
  list-style: none;
  cursor: pointer;
  gap: var(--sp-2);
  white-space: nowrap;
}
.compte-menu > summary::-webkit-details-marker { display: none; }
/* ⚠️ L'ÉTAT « ON EST DEDANS » NE TOUCHE QUE LA BORDURE. Repeindre aussi le
   TEXTE en mauve le rendait illisible au survol : `.btn--ligne:hover` remplit
   le bouton et éclaircit le texte, mais cette règle-ci est plus spécifique et
   gardait le mauve. */
.compte-menu > summary.actif { border-color: var(--brand); }

/* Le chevron : un carré penché, pas une police d'icônes. */
.compte-menu__chevron {
  width: 6px; height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur) ease;
}
.compte-menu[open] > summary > .compte-menu__chevron { transform: translateY(1px) rotate(-135deg); }

.compte-menu__tiroir {
  position: absolute;
  right: 0; top: calc(100% + 10px);
  min-width: 220px;
  background: var(--papier);
  border: 2px solid var(--fg);
  padding: var(--sp-3) 0;
  display: flex;
  flex-direction: column;
  z-index: 50;
}
.compte-menu__tiroir a { padding: 9px var(--sp-5); font-size: var(--fs-15); color: var(--fg); }
.compte-menu__tiroir a:hover { text-decoration: none; background: var(--surface-2); }
.compte-menu__tiroir a.actif { color: var(--brand); }
.compte-menu__tiroir hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-3) 0; }

/* Le menu du téléphone : un <details> natif, zéro JavaScript. */
.menu-mobile { display: none; margin-left: auto; position: relative; }
.menu-mobile > summary {
  list-style: none;
  cursor: pointer;
  padding: 9px 14px;
  border: 2px solid currentColor;
  font: 500 var(--fs-13)/1 var(--font-mono), monospace;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--fg);
}
.menu-mobile > summary::-webkit-details-marker { display: none; }
.menu-mobile__tiroir {
  position: absolute;
  right: 0; top: calc(100% + 10px);
  min-width: 220px;
  background: var(--papier);
  border: 2px solid var(--fg);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  z-index: 50;
}
.menu-mobile__tiroir a { font-size: var(--fs-15); color: var(--fg); }
.menu-mobile__tiroir a.actif { color: var(--brand); }
.menu-mobile__titre {
  margin-top: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
  font: 500 var(--fs-12)/1 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--fg-3);
}

@media (max-width: 900px) {
  .nav-liens { display: none; }
  .menu-mobile { display: block; }
  /* Une barre COLLANTE de 84 px prend un dixième d'un écran de téléphone, en
     permanence. Tant qu'elle défilait avec la page, sa hauteur ne coûtait
     rien passé le premier écran ; maintenant si. Elle se resserre, et
     `scroll-padding-top` suit toute seule puisqu'elle lit la variable. */
  :root { --entete-h: 62px; }
}

/* ------------------------------------------------------------------- Le héros

   LE GESTE DU SITE. Une grille de deux colonnes sur toute la largeur : le
   texte à gauche, aligné sur la gouttière de `.wrap`, et un APLAT MAUVE à
   droite qui court jusqu'au bord de l'écran. Plus l'écran est large, plus le
   bloc est grand.

   ⚠️ L'ALIGNEMENT SE FAIT AVEC LE `padding-left` DU PARENT, PAS AVEC UN
   `.wrap` À L'INTÉRIEUR. `.heros-bloc` est un enfant direct de <main>, donc
   son `100%` vaut la largeur de la page : `50% - 600px + 32px` retombe
   exactement sur la gouttière de `.wrap` (dont la boîte fait 1200 px, 32 px
   de marge comprise). Un `.wrap` posé dans la colonne de gauche, lui,
   calculerait ses pourcentages sur la CELLULE et dériverait. */
.heros-bloc {
  display: grid;
  grid-template-columns: minmax(0, 36rem) minmax(0, 1fr);
  align-items: stretch;
  padding-left: max(var(--sp-8), calc(50% - 600px + var(--sp-8)));
  /* Le héros se ferme par un filet, comme toute section. Il n'en avait pas :
     sur les fiches de module, où la section suivante est une `.coupe--nue`,
     le bas du bloc se dissolvait dans le papier et l'aplat semblait flotter. */
  border-bottom: var(--filet);
}
/* ⚠️ SANS CETTE RÈGLE, LE FILET FAIT 4 px. `.bandeau` porte déjà un filet en
   haut ; collé sous le héros qui vient d'en gagner un en bas, les deux
   s'additionnent. C'est le héros qui garde le sien : il est la fin du bloc. */
.heros-bloc + .bandeau { border-top: 0; }
.heros-bloc__texte {
  padding: clamp(48px, 6vw, 88px) var(--sp-12) clamp(48px, 6vw, 88px) 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-6);
}
.heros-bloc__texte > * { margin: 0; }
.heros-bloc__aplat {
  padding: clamp(36px, 4vw, 56px) clamp(24px, 3vw, 40px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-5);
}

/* Un héros sans aplat : les pages qui n'ont rien à montrer à droite (le 404).
   Le texte occupe la pleine mesure au lieu d'une demi-colonne vide. */
.heros-bloc--seul { grid-template-columns: minmax(0, 1fr); padding-right: max(var(--sp-8), calc(50% - 600px + var(--sp-8))); }
.heros-bloc--seul .heros-bloc__texte { padding-right: 0; max-width: 46rem; }

/* ⚠️ LE HÉROS SE DÉPILE À 1150 px, PAS À 900 COMME LE RESTE DU SITE, ET LE
   CHIFFRE EST CALCULÉ. L'aplat porte l'extrait d'intégration, dont la ligne
   la plus longue (`<script src="https://bonjourlatoile.com/bl.js"`) fait 47
   signes. En monospace, 47 × 0,6 × 14 px = 395 px de large, incompressibles :
   c'est le VRAI code qu'on demande de coller, on ne peut ni le raccourcir ni
   mentir dessus.

   La colonne de texte fait 576 px et la gouttière 32 : à 1151 px de fenêtre
   (barre de défilement déduite) l'aplat reçoit 528 px, dont 459 utiles une
   fois sa marge intérieure ôtée. Il reste 64 px de marge sur les 395
   nécessaires. En dessous, `overflow-x: auto` sortirait une barre de
   défilement EN PLEIN HÉROS (vu en production le 2026-09-01, quand la
   colonne faisait 544 px et le seuil 900).

   Ce seuil est LIÉ à la largeur de la colonne : elle est passée de 34rem à
   36rem pour que les titres tiennent en 18 signes, donc le seuil a suivi.
   Toucher l'une sans l'autre ramène la barre de défilement. */
@media (max-width: 1150px) {
  .heros-bloc { grid-template-columns: minmax(0, 1fr); padding-left: 0; }
  .heros-bloc__texte { padding: var(--sp-16) var(--sp-8); }
  .heros-bloc__aplat { padding-inline: var(--sp-8); }
  .heros-bloc--seul { padding-right: 0; }
}

/* Les points forts : une liste à puces carrées avec une amorce en gras. */
.puces { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-4); max-width: 54ch; }
.puces li { position: relative; padding-left: 22px; color: var(--fg-2); line-height: var(--lh-normal); }
.puces li::before {
  content: "";
  position: absolute; left: 0; top: 10px;
  width: 8px; height: 8px;
  background: var(--brand);
}
.puces b { color: var(--fg); font-weight: 600; }

/* --------------------------------------------------- Le catalogue en liste */

/* ⚠️ UNE LISTE, PAS UNE GRILLE DE CARTES. Trois cartes arrondies côte à côte
   cessent de fonctionner dès la dixième entrée. Un sommaire tenu par des
   filets se parcourt de haut en bas et supporte trente modules. */
.liste { border-top: var(--filet); }
.liste__item {
  position: relative;
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--sp-4) var(--sp-6);
  padding: var(--sp-6) 0;
  border-bottom: 1px solid var(--fg);
  transition: background var(--dur) ease;
}
.liste__item:hover { background: color-mix(in oklab, var(--fg) 7%, transparent); }
.liste__num { font: 500 var(--fs-13)/1.4 var(--font-mono), monospace; color: var(--brand); }
.liste__nom {
  margin: 0 0 4px;
  font-family: var(--font-mono), monospace;
  font-size: var(--fs-18);
  font-weight: 500;
  letter-spacing: -0.01em;
}
.liste__nom a { color: var(--fg); }
.liste__nom a:hover { color: var(--brand); text-decoration: none; }
.liste__nom a::after { content: ""; position: absolute; inset: 0; }
.liste__quoi { margin: 0; color: var(--fg-2); font-size: var(--fs-15); max-width: 60ch; }
.liste__etat { justify-self: end; white-space: nowrap; }

@media (max-width: 620px) {
  .liste__item { grid-template-columns: 2.5rem minmax(0, 1fr); }
  .liste__etat { grid-column: 2; justify-self: start; }
}

/* --------------------------------------------------------------- Les états */

.etat {
  display: inline-flex; align-items: center; gap: 7px;
  font: 500 var(--fs-12)/1 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--fg-3);
}
.etat::before { content: ""; width: 7px; height: 7px; background: currentColor; }
.etat--ok { color: var(--success); }
.etat--attente { color: var(--warning); }
.etat--refuse { color: var(--danger); }
.etat--brand { color: var(--brand); }
.etat--muet { color: var(--fg-3); }
.etat--muet::before { background: none; box-shadow: inset 0 0 0 1px currentColor; }
/* Sur l'aplat, les couleurs d'état doivent remonter en clarté pour rester
   lisibles sur du mauve plein. */
.aplat .etat--ok { color: oklch(90% 0.13 150); }
.aplat .etat--attente { color: oklch(90% 0.12 80); }
.aplat .etat--refuse { color: oklch(88% 0.11 25); }

/* ---------------------------------------------------------------- Les blocs */

/* Le bloc de l'ATELIER : 1 px, calme, il y en a quarante à l'écran. */
.bloc {
  background: var(--surface);
  border: 1px solid var(--border);
  padding: var(--sp-8);
}
.bloc--serre { padding: var(--sp-6); }
.bloc--teinte { background: var(--surface-2); border-color: var(--border-strong); }
/* Le bloc de la VITRINE : 2 px d'encre, comme les filets de section. */
.bloc--fort { border: var(--filet); background: transparent; }

/* ⚠️ `.bloc.aplat` A BESOIN DE CETTE RÈGLE, ET C'EST UNE COLLISION DE
   CASCADE, PAS UN CAPRICE. `.aplat` pose le fond mauve, mais `.bloc` est
   écrit PLUS BAS dans le fichier : à poids égal (0,1,0), c'est lui qui gagne
   et le bloc repartait en `--surface`, c'est-à-dire en papier sur du papier.
   Sert à distinguer un palier payant d'un palier gratuit sans changer la
   forme des deux cartes. */
.bloc.aplat { background: var(--mauve-600); border: 0; }

.grille { display: grid; gap: var(--sp-6); }
.grille--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grille--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grille--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .grille--3, .grille--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .grille--2, .grille--3, .grille--4 { grid-template-columns: minmax(0, 1fr); } }

/* Les étapes : un numéro monospace sous un filet net. Le numéro n'est pas un
   ornement, il dit l'ORDRE dans lequel les choses se font. */
.etape { border-top: var(--filet); padding-top: var(--sp-4); }
.etape__num { display: block; font: 500 var(--fs-24)/1 var(--font-mono), monospace; color: var(--brand); margin-bottom: var(--sp-3); }
.etape p { margin: var(--sp-2) 0 0; }

/* ------------------------------------------------------------- La plaque */

.plaque { border: var(--filet); background: var(--surface); }
.plaque__corps { padding: var(--sp-4); background: var(--papier-2); }
.plaque__legende {
  border-top: var(--filet);
  padding: var(--sp-3) var(--sp-4);
  font: 400 var(--fs-13)/1.5 var(--font-mono), monospace;
  color: var(--fg-2);
  background: transparent;
}

/* ------------------------------------------------------------ Code à copier */

/* Sur l'aplat, l'extrait n'a pas de fond à lui : il EST sur le mauve, et un
   cadre autour d'un cadre n'ajoute rien. */
pre.extrait {
  margin: 0;
  background: var(--encre);
  color: oklch(92% 0.05 305);
  padding: var(--sp-5) var(--sp-6);
  overflow-x: auto;
  font-size: var(--fs-13);
  line-height: 1.7;
  text-align: left;
}
pre.extrait code { background: none; padding: 0; color: inherit; font-size: inherit; }
.aplat pre.extrait { background: transparent; color: var(--fg); padding: 0; font-size: var(--fs-14); }

/* ---------------------------------------------------- L'extrait qui se tape

   Sur une FICHE DE MODULE (jamais sur l'accueil), l'extrait d'intégration
   s'écrit ligne à ligne, comme sous les doigts de quelqu'un. C'est la seule
   animation du site, et elle a un travail : la page dit « deux lignes de
   HTML » — les voir apparaître le prouve mieux qu'un bloc figé.

   ⚠️ TOUT EST DANS LE `@media (prefers-reduced-motion: no-preference)`, ET
   L'INVERSE SERAIT UN DÉFAUT GRAVE. Le masque est un `clip-path` : hors de
   cette requête, il n'est JAMAIS posé, donc le code est visible d'emblée. Si
   on écrivait le `clip-path` à l'extérieur et qu'on coupait seulement
   l'animation (ce que fait le bloc global en fin de fichier), l'extrait
   resterait masqué POUR TOUJOURS chez qui demande moins de mouvement — un
   écran vide, sans erreur, pour les gens qui en ont le plus besoin.

   ⚠️ `--signes`, `--duree` et `--retard` SONT POSÉS PAR LE PHP, ligne par
   ligne (`bl_extrait_anime`). Le nombre de pas d'un `steps()` doit valoir le
   nombre de signes de CETTE ligne, sinon la frappe saute des blocs entiers
   au lieu d'avancer d'un caractère.

   La ligne vide du milieu n'a pas de contenu : sans hauteur imposée elle
   s'écrase à zéro et le bloc perd sa respiration. */
pre.extrait .ligne { display: block; }
pre.extrait .ligne:empty { height: 1.7em; }

@media (prefers-reduced-motion: no-preference) {
  .extrait--anime .ligne {
    clip-path: inset(0 100% 0 0);
    animation: frappe var(--duree) steps(var(--signes)) var(--retard) forwards;
  }
}

/* Le curseur d'éditeur : un carré plein qui clignote. Deux emplois, une seule
   déclaration — deux copies finiraient par ne plus se ressembler.

     `.extrait--curseur`        au bout d'un extrait FIGÉ (l'accueil, qui ne
                                se tape pas : sa colonne est la première chose
                                qu'on voit et une frappe s'y regarderait au
                                lieu de se lire) ;
     `.extrait--anime .ligne--fin`  dans la dernière ligne de l'extrait qui se
                                tape. ⚠️ Il vit DEDANS, pas après le bloc :
                                posé derrière le même `clip-path` que le
                                texte, il se découvre exactement quand la
                                frappe atteint la fin. Un curseur réglé par un
                                `animation-delay` séparé se désynchroniserait
                                au premier mot changé.

   ⚠️ CETTE RÈGLE EST HORS DU BLOC `prefers-reduced-motion`, CONTRAIREMENT AU
   MASQUE. Le carré doit exister dans tous les cas ; c'est son clignotement
   qui est du mouvement, et le bloc global en fin de feuille l'éteint tout
   seul. Le curseur reste alors plein et immobile, ce qui se lit très bien. */
.extrait--curseur::after,
.extrait--anime .ligne--fin::after {
  content: "";
  display: inline-block;
  width: 0.55em; height: 1em;
  background: currentColor;
  margin-left: 2px;
  vertical-align: -0.15em;
  animation: curseur 1.1s step-end infinite;
}

@keyframes frappe { to { clip-path: inset(0 0 0 0); } }
@keyframes curseur { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

code {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.92em;
  background: var(--surface-2);
  padding: 0.1em 0.35em;
  color: var(--fg);
}
code.jeton {
  background: var(--surface-2);
  border: 1px solid var(--border);
  padding: 2px 6px;
  font-size: var(--fs-13);
  color: var(--brand-soft-fg);
  word-break: break-all;
}
/* ⚠️ SUR L'APLAT, `code.jeton` PART EN MAUVE SUR MAUVE. Ses deux couleurs
   viennent de `--surface-2` et `--brand-soft-fg`, que `.aplat` redéfinit
   respectivement en mauve foncé et en mauve foncé : le jeton devenait un
   rectangle illisible. Il passe en creux, comme le reste du bloc. */
.aplat code.jeton { background: transparent; border-color: var(--border); color: var(--fg); }

/* ---------------------------------------------------------- Formulaires */

.champ-groupe { margin-bottom: var(--sp-5); }
.etiquette { display: block; margin-bottom: 6px; font-size: var(--fs-13); font-weight: 500; color: var(--fg-2); }
.champ {
  width: 100%;
  padding: 12px 14px;
  background: var(--papier-2);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  color: var(--fg);
  font: 400 var(--fs-15)/1.4 var(--font-sans), sans-serif;
}
.champ:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--ring); }
.champ--auto { width: auto; min-width: 260px; }
select.champ { padding-right: var(--sp-8); }
.aide { margin: 6px 0 0; font-size: var(--fs-13); color: var(--fg-3); }

.ligne-champs { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; }

/* Le champ piege du formulaire de contact : invisible pour un humain, offert
   au robot qui remplit tout. Il sort de l'ecran plutot que de prendre
   `display: none`, que les robots serieux savent reconnaitre. */
.piege { position: absolute; left: -9999px; top: -9999px; }
.case { display: flex; gap: var(--sp-3); align-items: flex-start; font-size: var(--fs-15); color: var(--fg-2); }
.case input { margin-top: 4px; accent-color: var(--brand); }

/* ------------------------------------------------------------------- Avis */

.avis {
  border: 1px solid var(--border);
  border-left: 4px solid var(--fg-3);
  background: var(--surface);
  padding: var(--sp-4) var(--sp-5);
  margin: 0 0 var(--sp-6);
  font-size: var(--fs-15);
}
.avis--erreur { border-left-color: var(--danger); background: var(--danger-soft); color: var(--danger); }
.avis--ok { border-left-color: var(--success); background: var(--success-soft); color: var(--success); }
.avis--info { border-left-color: var(--brand); background: var(--brand-soft); color: var(--brand-soft-fg); }
.avis a { color: inherit; text-decoration: underline; }

/* ---------------------------------------------------------------- Tableaux */

.tableau-cadre { border: 1px solid var(--border); overflow-x: auto; background: var(--surface); }
table.tableau { width: 100%; border-collapse: collapse; font-size: var(--fs-14); }
table.tableau th {
  text-align: left;
  padding: var(--sp-3) var(--sp-4);
  font: 500 var(--fs-12)/1 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--fg-3);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
table.tableau td { padding: var(--sp-4); border-bottom: 1px solid var(--border); vertical-align: middle; }
table.tableau tr:last-child td { border-bottom: 0; }
table.tableau tr.eteinte { opacity: .5; }
td.actions-cellule { white-space: nowrap; }
td.actions-cellule form { display: inline; }
td.actions-cellule .btn { margin-left: 6px; }

/* ------------------------------------------------------- Onglets sans JS */

/* ⚠️ LA BARRE D'ONGLETS DOIT ÊTRE UN <nav>, PAS UN <div> : `:nth-of-type`
   compte par NOM DE BALISE, une barre en <div> deviendrait le premier div de
   la fratrie et décalerait toutes les vues d'un cran. */
/* ⚠️ CES ONGLETS ONT LONGTEMPS PORTÉ LE STYLE DE L'ATELIER : 14 px de
   grotesque grise posés sur un filet de 1 px. Ça passait quand ils ne
   servaient qu'aux trois preuves de propriété, dans l'espace client. Depuis
   qu'ils portent les démonstrations de l'ACCUEIL, ils se retrouvent au milieu
   d'une page tenue par des filets d'encre de 2 px et des intertitres
   monospace : le composant sonnait comme une pièce d'administration tombée
   dans la vitrine, et Simon l'a relevé.

   Ils parlent maintenant la langue du site, la même que `.espace-nav` :
   monospace, capitales espacées, filet de 2 px sous la barre. Un seul style
   pour les deux emplois — une variante « vitrine » aurait fini par diverger
   de l'originale sans que personne ne s'en aperçoive. */
.onglets { margin-top: var(--sp-4); }
.onglets input { position: absolute; opacity: 0; pointer-events: none; }
.onglets__barre { display: flex; flex-wrap: wrap; gap: var(--sp-8); border-bottom: var(--filet); }
.onglets__barre label {
  padding: 0 0 var(--sp-4);
  cursor: pointer;
  font: 500 var(--fs-14)/1 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--fg-3);
  border-bottom: 2px solid transparent;
  /* -2 px, pas -1 : le filet de la barre en fait deux, et l'onglet actif doit
     le recouvrir exactement, sinon son soulignement flotte au-dessus. */
  margin-bottom: -2px;
}
.onglets__barre label:hover { color: var(--fg); }
.onglets__vue { display: none; padding-top: var(--sp-8); }
.onglets input:nth-of-type(1):checked ~ .onglets__barre label:nth-of-type(1),
.onglets input:nth-of-type(2):checked ~ .onglets__barre label:nth-of-type(2),
.onglets input:nth-of-type(3):checked ~ .onglets__barre label:nth-of-type(3) {
  /* L'onglet ouvert passe à l'ENCRE, son soulignement au mauve. Même partage
     que `.espace-nav a.actif`, et pour la même raison : un texte repeint en
     mauve se bat avec le survol, qui le repeint aussi. La couleur dit « je
     suis lisible », la barre dit « je suis ici ». */
  color: var(--fg); border-bottom-color: var(--brand);
}
.onglets input:nth-of-type(1):checked ~ .onglets__vue:nth-of-type(1),
.onglets input:nth-of-type(2):checked ~ .onglets__vue:nth-of-type(2),
.onglets input:nth-of-type(3):checked ~ .onglets__vue:nth-of-type(3) { display: block; }

/* ------------------------------------------------------------ Espace client */

/* `padding-block`, pas `padding` : voir l'avertissement sur `.wrap`. */
.espace { padding-block: var(--sp-10) var(--sp-24); }
.espace-nav { display: flex; flex-wrap: wrap; gap: var(--sp-6); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-10); }
.espace-nav a {
  padding: 0 0 var(--sp-3);
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  font: 500 var(--fs-14)/1 var(--font-mono), monospace;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--fg-3);
}
.espace-nav a:hover { color: var(--fg); text-decoration: none; }
.espace-nav a.actif { color: var(--fg); border-bottom-color: var(--brand); }

.entete-page { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-10); }

/* ------------------------------------------------------------- Le portail */

.portail { min-height: calc(100dvh - 240px); display: grid; place-items: center; padding-block: var(--sp-16); }
.portail__carte { width: min(440px, 100%); }
.portail h1 { margin: 0 0 var(--sp-2); font-size: var(--fs-32); line-height: 1.1; }
.portail .sous { margin: 0 0 var(--sp-8); color: var(--fg-3); font-size: var(--fs-15); }
.portail .btn { width: 100%; justify-content: center; }
.portail__pied { margin: var(--sp-6) 0 0; text-align: center; font-size: var(--fs-14); color: var(--fg-3); }

/* --------------------------------------------------------------- Pied de page */

/* La seule autre surface pleine du site. Il ferme la page en encre, comme les
   filets qui la découpent. */
.pied-site {
  --bg: var(--encre);
  --fg: var(--papier);
  --fg-2: oklch(80% 0.015 300);
  --fg-3: oklch(64% 0.018 300);
  --border: oklch(32% 0.020 300);
  --brand: var(--mauve-300);
  --brand-hover: oklch(93% 0.045 305);
  --btn-fond: var(--papier);
  --btn-bord: var(--papier);
  --btn-txt: var(--encre);
  --btn-fond-h: var(--mauve-600);
  --btn-txt-h: var(--papier);
  background: var(--bg);
  color: var(--fg);
  padding: var(--sp-16) 0 var(--sp-12);
}
.pied-site__grille { display: flex; flex-wrap: wrap; gap: var(--sp-10); justify-content: space-between; align-items: flex-start; }
.pied-site nav { display: flex; flex-wrap: wrap; gap: var(--sp-6); }
.pied-site a { color: var(--fg-2); font-size: var(--fs-14); }
.pied-site a:hover { color: var(--fg); }
.pied-site .marque { color: var(--fg); font-size: var(--fs-24); }
.pied-site__droit { font-size: var(--fs-13); color: var(--fg-3); max-width: 36ch; }

/* ------------------------------------------------------------------ Utiles */

.pile > * + * { margin-top: var(--sp-4); }
.rangee { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* -------------------------------------------------- Éditeurs de modules */
/*
   L'éditeur d'un formulaire ou d'un quiz n'a pas une ligne de JavaScript :
   chaque geste est un bouton d'envoi. Les blocs sont donc nombreux et il faut
   qu'ils respirent, sinon l'écran devient un mur de champs.
*/
.editeur .champ-groupe { margin-bottom: var(--sp-4); }
.editeur .bloc .grille { gap: var(--sp-4); }
.editeur .entete-page { align-items: center; }
.editeur textarea.champ { min-height: 0; }

/* La réponse d'un visiteur : une définition par champ déclaré. */
.reponse { margin: 0; display: grid; grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); gap: var(--sp-2) var(--sp-6); }
.reponse dt { font-size: var(--fs-13); font-weight: 500; color: var(--fg-3); }
.reponse dd { margin: 0; color: var(--fg-2); overflow-wrap: anywhere; }

@media (max-width: 640px) {
  .reponse { grid-template-columns: 1fr; gap: 0; }
  .reponse dd { margin-bottom: var(--sp-3); }
}
