/* ============================================================================
   LETTERS THAT WORK — la couche projet, et elle s'arrête là.

   🔴 `odersa-systeme.css` NE SE RETOUCHE PAS. Il est la copie OCTET POUR OCTET
   du skill odersa-design, et `build/tools/check-da.js` compare les deux à chaque
   passage : un diff vide est la preuve qu'aucun jeton n'a été inventé en
   chemin. C'est plus fort que la discipline, parce qu'une machine le tient.
   Une retouche UTILE du système remonte donc au skill, où elle profite aux
   treize sites de la flotte. Elle ne se glisse pas ici.

   CE FICHIER N'A DONC LE DROIT QUE DE COMPOSER : des composants NOMMÉS, bâtis
   uniquement avec les jetons déclarés par le système. Aucun jeton neuf, aucune
   couleur écrite en dur, aucun rayon au-delà de 3 px, aucune ombre, aucun
   dégradé, aucun second accent. Ce qui n'est pas composable avec les jetons
   existants se remonte, il ne s'invente pas.

   Ce que la couche projet doit vraiment ajouter, et pourquoi le système ne le
   porte pas : le système est fait pour des pages qui SE LISENT. Letters That Work a
   en plus une pièce qu'aucun autre site de la flotte n'a — une lettre qui se
   fabrique à l'écran puis SORT SUR DU PAPIER. Une lettre imprimée obéit à des
   conventions vieilles de deux siècles que rien dans une feuille de site web ne
   prévoit : le bloc expéditeur, le bloc destinataire, la date en haut à droite,
   l'objet en gras, la formule d'appel, la signature en bas à droite. C'est
   l'essentiel de ce fichier, et c'est légitimement propre au projet.
   ========================================================================== */

/* ═══════════ 1. LES BLOCS DE PAGE ═══════════
   Le générateur enveloppe chaque bloc dans `.bloc`. Deux blocs qui se suivent
   respirent ; le premier d'une bande ne pousse pas son titre vers le bas. */
.bloc + .bloc { margin-block-start: var(--s-10); }
.bloc > h2:first-child, .bloc > .kicker:first-child { margin-block-start: 0; }
.bloc > .kicker + h2 { margin-block-start: 0; }
.bloc > .lead { margin-block-end: var(--s-6); }

/* ═══════════ 2. LES QUESTIONS FRÉQUENTES ═══════════
   `details`/`summary` natif : ouvre au clavier, marche sans JavaScript, et
   l'état déplié est exposé par le navigateur.
   ⚠ La règle `::details-content` s'écrit SÉPARÉMENT de tout le reste. Chrome
   depuis la version 131 masque le contenu d'un `<details>` fermé par
   `content-visibility`, et un sélecteur qu'un navigateur ne connaît pas jette
   TOUTE sa règle : groupé avec d'autres déclarations, il les emporterait. */
.qr { border-block-end: 1px solid var(--rule); }
.qr:first-of-type { border-block-start: 1px solid var(--rule); }
.qr > summary {
  display: flex; align-items: baseline; gap: var(--s-3);
  padding-block: var(--s-4); cursor: pointer; list-style: none;
  font: 600 var(--t-m)/1.35 var(--serif); color: var(--ink); min-height: 44px;
}
.qr > summary::-webkit-details-marker { display: none; }
.qr > summary::after {
  content: ""; flex: none; margin-inline-start: auto; align-self: center;
  inline-size: 8px; block-size: 8px;
  border-inline-end: 1.8px solid var(--accent); border-block-end: 1.8px solid var(--accent);
  transform: rotate(45deg); transition: transform 180ms var(--easing);
}
.qr[open] > summary::after { transform: rotate(225deg); }
.qr > summary:hover { color: var(--accent); }
.qr > summary:focus-visible { outline: 2.5px solid var(--focus); outline-offset: -2px; }
.qr > div { padding-block-end: var(--s-5); }
.qr > div > p:last-child { margin-block-end: 0; }
.qr[open] > summary { color: var(--accent); }

/* ═══════════ 3. LES CHIFFRES ═══════════
   Employés dans une bande sombre. Le nombre est un chiffre d'appui : serif, et
   `tabular-nums` pour que deux nombres l'un sous l'autre s'alignent. */
.chiffre__n {
  font: 600 var(--t-2xl)/1 var(--serif); margin: 0;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
.chiffre__q {
  margin: var(--s-2) 0 0; font-size: var(--t-s); line-height: 1.45;
  color: var(--on-dark-muted); max-width: 24ch;
}
.band--dark .chiffre__n { color: #FFFFFF; }

/* ═══════════ 4. LE VERBATIM D'UN TEXTE OFFICIEL ═══════════
   Une citation de texte de loi n'est pas une citation d'ambiance : elle se
   distingue du commentaire qui l'entoure, et elle porte sa source visible.
   ⚠ Une adresse brute ne se coupe PAS, et un `overflow-x: hidden` la
   tronquerait en silence : `overflow-wrap: anywhere` sur la source. */
main blockquote:not(.pullquote) {
  margin: var(--s-6) 0; padding: var(--s-5); background: var(--bg-alt);
  border: 1px solid var(--rule); border-inline-start: 3px solid var(--ink-mid);
}
main blockquote:not(.pullquote) > p {
  margin: 0; font: 400 var(--t-s)/1.7 var(--sans); color: var(--ink-soft);
}
main blockquote:not(.pullquote) > footer {
  margin-block-start: var(--s-3); padding-block-start: var(--s-3);
  border-block-start: 1px solid var(--rule);
}
main blockquote cite, main blockquote cite a { font-style: normal; overflow-wrap: anywhere; }

/* ═══════════ 5. LE SÉLECTEUR DE LANGUES, ÉTAT « EN ATTENTE » ═══════════
   Les sept langues sont dans le sélecteur dès le premier jour, parce que la
   structure du site est multilingue par construction. Mais six d'entre elles ne
   sont pas encore écrites : elles sortent SANS href, donc ce ne sont pas des
   liens, donc elles ne sont ni cliquables ni focalisables. Un lien mort sur
   toutes les pages d'un site serait un mensonge silencieux.
   L'apparence dit la même chose que le balisage — et jamais par la seule
   couleur : `--ink-mid` tient 6,90:1, et le pointillé sous le code porte
   l'information pour qui ne distingue pas les nuances. */
.lang a[aria-disabled="true"] {
  color: var(--ink-mid);
  text-decoration: underline dotted 1px;
  text-underline-offset: 3px;
  cursor: default;
}
.lang a[aria-disabled="true"]:hover { color: var(--ink-mid); }

/* ═══════════ 6. LE FORMULAIRE D'ATELIER ═══════════
   L'outil vit dans un cadre à filet, avec l'accent en tête : c'est le geste du
   gabarit « page outil » du skill, posé ici sous un nom plutôt que répété en
   ligne dans chaque page. */
.atelier {
  border: 1px solid var(--rule); border-block-start: 3px solid var(--accent);
  padding: clamp(var(--s-5), 4vw, var(--s-8)); background: var(--bg);
}
.atelier > *:first-child { margin-block-start: 0; }
.atelier fieldset { border-color: var(--rule); }
.atelier legend { color: var(--ink); }
/* la zone de résultat existe DÈS LE DÉPART, vide mais présente : sinon la page
   saute quand le résultat arrive. `aria-live` la fait annoncer. */
.atelier__sortie { margin-block-start: var(--s-6); min-block-size: 3rem; }
/* la barre de progression de l'arbre de questions : elle dit où on en est, en
   MOTS autant qu'en forme. */
.atelier__pas {
  display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center;
  font: 600 var(--t-xs)/1 var(--sans); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-mid); margin-block-end: var(--s-5);
}
.atelier__jauge { flex: 1 1 6rem; block-size: 3px; background: var(--rule-soft); min-inline-size: 4rem; }
.atelier__jauge > span { display: block; block-size: 100%; background: var(--accent); }

/* Les choix d'une question : de vrais boutons radio, agrandis en cibles de 44 px.
   Un choix se voit de deux façons, le filet ET le remplissage, jamais la seule
   couleur. */
.choix { display: grid; gap: var(--s-3); margin-block: var(--s-4); }
.choix > label {
  display: flex; gap: var(--s-3); align-items: flex-start;
  border: 1px solid var(--rule); padding: var(--s-4); cursor: pointer;
  min-height: 44px; transition: border-color 160ms var(--easing), background-color 160ms var(--easing);
}
.choix > label:hover { border-color: var(--accent); background: var(--accent-bg); }
.choix input { margin: 0.25em 0 0; flex: none; accent-color: var(--accent); min-inline-size: 1.1em; min-block-size: 1.1em; }
.choix > label:has(input:checked) { border-color: var(--accent); border-inline-start-width: 3px; background: var(--accent-bg); }
.choix > label:has(input:focus-visible) { outline: 2.5px solid var(--focus); outline-offset: 2px; }
.choix__quoi { display: block; font-weight: 600; }
.choix__aide { display: block; font-size: var(--t-s); color: var(--ink-mid); margin-block-start: var(--s-1); }

/* ═══════════ 7. LA LETTRE ═══════════
   LA PIÈCE PROPRE À CE PROJET. Une lettre administrative a une mise en page
   conventionnelle, et cette convention est ce qui la fait LIRE par un service
   qui en reçoit trois cents par jour. Elle est donc dessinée pour le PAPIER
   d'abord, et l'écran n'en est qu'un aperçu.

   Le corps passe en serif : une lettre se lit sur papier, et c'est là que le
   serif gagne. Ce n'est pas un second choix typographique, c'est le serif du
   système employé pour ce qu'il sait faire. */
.lettre {
  border: 1px solid var(--rule); background: var(--bg);
  padding: clamp(var(--s-5), 5vw, var(--s-12));
  font: 400 var(--t-base)/1.6 var(--serif); color: var(--ink);
  max-inline-size: 21cm; margin-inline: auto;
}
.lettre p { max-width: none; margin: 0 0 1em; }
.lettre__haut { display: grid; gap: var(--s-8); margin-block-end: var(--s-8); }
@media (min-width: 40rem) { .lettre__haut { grid-template-columns: 1fr 1fr; } }
.lettre__bloc { font-size: var(--t-s); line-height: 1.55; }
.lettre__bloc > span { display: block; }
.lettre__dest { text-align: end; }
.lettre__lieudate { text-align: end; margin-block-end: var(--s-8); font-size: var(--t-s); }
.lettre__objet { margin-block-end: var(--s-6); }
.lettre__objet strong { font-family: var(--sans); font-size: var(--t-s); }
.lettre__signature { margin-block-start: var(--s-10); text-align: end; font-size: var(--t-s); }
.lettre__pieces { margin-block-start: var(--s-8); padding-block-start: var(--s-4); border-block-start: 1px solid var(--rule); }
.lettre__pieces h3 { font: 600 var(--t-xs)/1.3 var(--sans); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mid); margin: 0 0 var(--s-3); }
.lettre__pieces ul { font-family: var(--sans); font-size: var(--t-s); margin: 0; }
/* un emplacement encore vide dans la lettre : il se VOIT, pour que personne
   n'imprime une lettre à trous sans s'en apercevoir. Et il porte son mot. */
.lettre .atrou {
  background: var(--etat-alerte-bg); border-block-end: 2px solid var(--etat-alerte);
  padding-inline: 0.2em; font-family: var(--sans); font-size: 0.92em;
}

/* ═══════════ 8. IMPRESSION DE LA LETTRE ═══════════
   Le système cache déjà la bannière, la navigation et les boutons à
   l'impression. Il reste à faire une chose que lui ne peut pas savoir : la
   lettre doit sortir SEULE, sur une page propre, sans l'en-tête du site ni le
   pied ni le reste de la page autour.
   `.imprime-la-lettre` est posée sur <html> par le script au moment où le
   visiteur demande l'impression de sa lettre, et retirée après. Un site qui
   imprime sa lettre AVEC son en-tête et son pied de page produit un courrier
   qu'aucun service n'ouvre. */
@media print {
  .lettre { border: 0; padding: 0; max-inline-size: none; font-size: 12pt; color: #000; }
  .lettre .atrou { background: none; border-block-end: 1px solid #000; }
  .atelier { border: 0; padding: 0; }
  .qr[open] > summary, .qr > summary { color: #000; }
  /* les questions fréquentes s'impriment DÉPLIÉES : un accordéon fermé sur du
     papier est de l'information perdue. */
  .qr > div { display: block !important; }
  .atelier__pas, .choix, .atelier__jauge { display: none !important; }
}
@media print {
  .imprime-la-lettre .site-head,
  .imprime-la-lettre .site-foot,
  .imprime-la-lettre .fil,
  .imprime-la-lettre .page-head,
  .imprime-la-lettre .prefoot,
  .imprime-la-lettre .bloc:not(.bloc--lettre) { display: none !important; }
}

/* ═══════════ 9. COMPOSITIONS PAR PAGE ═══════════
   🔴 CHAQUE RÈGLE DE CETTE SECTION COMMENCE PAR SA CLASSE DE PAGE `.p-<id>`.
   C'est ce qui permet à plusieurs agents de composer plusieurs pages sans se
   marcher dessus : la portée est garantie par le sélecteur, pas par la
   discipline. Une règle qui n'aurait pas de classe de page contaminerait tout
   le site à la première correction, et `tools/audit-contenu.js` la refuse.
   Aucun `!important` ici : il battrait les thèmes et le contraste renforcé. */

/* l'accueil : le titre du premier écran prend la plus grande échelle, et sa
   mesure est plafonnée pour qu'il tombe en trois lignes, pas en six. */
.p-accueil .band:first-child h1 {
  font-size: var(--t-3xl); line-height: 1.04; letter-spacing: -0.022em; max-width: 20ch;
}
.p-accueil .band:first-child .lead { font-size: var(--t-m); }

/* l'atelier respire sur toute la largeur, mais la lettre garde sa mesure de
   papier : c'est la classe `.lettre` qui la plafonne, pas la page. */
.p-atelier .atelier { max-inline-size: 62rem; }

/* ═══════════ 10. CE QUI MANQUAIT À L'ATELIER ═══════════
   Ajouté par la phase du cœur, en composant avec les jetons existants et rien
   d'autre. Chaque bloc dit le défaut réel qu'il corrige. */

/* --- 10.1 `hidden` NE SUFFIT PAS QUAND UNE RÈGLE D'AUTEUR POSE UN `display`.
   L'attribut `hidden` vaut `display: none` dans la feuille du NAVIGATEUR, et
   une feuille d'auteur bat toujours celle du navigateur, quelle que soit la
   spécificité. `.btn` déclare `inline-flex` et `.choix > label` déclare `flex` :
   sans ces deux lignes, un bouton « masqué » reste visible et un modèle filtré
   hors de sa famille continue de s'afficher. Le défaut ne se voit pas en
   relisant le script, seulement à l'écran. */
.btn[hidden], .choix > label[hidden], .champ[hidden],
.atelier__etape[hidden], .atelier [data-question][hidden] { display: none; }

/* --- 10.2 LA JAUGE PORTE SON AVANCE EN ATTRIBUT, PAS EN STYLE EN LIGNE.
   Un `style="inline-size:40%"` posé par le script échapperait au thème, au
   contraste renforcé et à l'impression, et la feuille cesserait d'être le
   domicile de la forme. Six étapes, six largeurs, et le remplissage part de
   zéro : sans largeur déclarée, un `<span>` en display block occuperait toute la
   jauge et l'avancement mentirait dès la première étape. */
.atelier__jauge > span { inline-size: 0; transition: inline-size 200ms var(--easing); }
.atelier__jauge[data-avance="1"] > span { inline-size: 8%; }
.atelier__jauge[data-avance="2"] > span { inline-size: 25%; }
.atelier__jauge[data-avance="3"] > span { inline-size: 42%; }
.atelier__jauge[data-avance="4"] > span { inline-size: 59%; }
.atelier__jauge[data-avance="5"] > span { inline-size: 76%; }
.atelier__jauge[data-avance="6"] > span { inline-size: 100%; }

/* --- 10.3 SANS JAVASCRIPT, L'ATELIER DISPARAÎT.
   Un formulaire qui n'agit sur rien est pire qu'une absence de formulaire : il
   fait saisir des réponses qui ne produiront aucune lettre. La classe `no-js`
   est retirée par la ligne en ligne du gabarit dès qu'un script s'exécute ; tant
   qu'elle est là, le `<noscript>` de l'outil porte seul la méthode à la main. */
.no-js .atelier { display: none; }

/* --- 10.4 LA PORTÉE D'UN MODÈLE, DANS LA LISTE DES CHOIX.
   Universel ou propre à un pays : l'information est dans les MOTS, la forme ne
   fait que la détacher de la phrase qui précède. */
.choix__portee {
  display: inline-block; margin-block-start: var(--s-2);
  font: 700 var(--t-xs)/1.3 var(--sans); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent); border-inline-start: 3px solid var(--accent);
  padding-inline-start: var(--s-2);
}

/* --- 10.5 LES PARAGRAPHES CONDITIONNELS, SUR LA FICHE STATIQUE.
   La fiche d'un modèle porte la lettre ENTIÈRE, variantes comprises : c'est ce
   qui la rend recopiable à la main. Un paragraphe qui ne s'écrit que dans un cas
   donné dit donc lequel, en mots, à sa place dans la lettre. Dans l'atelier, ce
   tri est fait par le script et ces paragraphes n'apparaissent jamais ainsi. */
.lettre__variante { border-inline-start: 2px solid var(--rule); padding-inline-start: var(--s-4); }
.lettre__cas {
  display: block; font: 700 var(--t-xs)/1.4 var(--sans);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-mid);
  margin-block-end: var(--s-2);
}

/* ═══════════ 11. IMPRIMER LA LETTRE DE L'ATELIER ═══════════
   La section 8 isole déjà la lettre en cachant tout `.bloc` qui ne porte pas
   `bloc--lettre`. Mais l'atelier VIT dans ce bloc-là : le formulaire, la barre
   de boutons et le titre de l'outil s'imprimeraient avec la lettre. Le script
   marque donc d'un nom tout ce qui n'appartient pas au papier, et le cadre de
   l'outil s'efface pour que la lettre sorte seule, sur une page propre. */
@media print {
  .imprime-la-lettre .atelier__saisie,
  .imprime-la-lettre .atelier__hors-lettre { display: none !important; }
  .imprime-la-lettre .atelier,
  .imprime-la-lettre .atelier__sortie { border: 0; padding: 0; margin: 0; min-block-size: 0; }
  .imprime-la-lettre .bloc--lettre > h2,
  .imprime-la-lettre .bloc--lettre > .lead { display: none !important; }
  /* sur une fiche de modèle, la lettre s'imprime avec ses variantes : leur
     condition doit rester lisible en noir sur blanc. */
  .lettre__cas { color: #000; }
  .lettre__variante { border-inline-start: 1px solid #000; }
}

/* ═══════════ 12. LA PASTILLE D'ACCESSIBILITÉ ═══════════
   Le composant que `pastilleAccessibilite()` de `lib-html.js` sert sur les 45
   pages, la 404 comprise. Sa géométrie est celle du `#pupitre-fab` relevé sur
   `https://odersa.org/accessibilite` le 14/08/2026 : un onglet posé au bord de
   fin de la page, un mot vertical en capitales sur la surface la plus sombre du
   système. Le pourquoi des écarts avec la mère — un `<a>` et non un `<button>`,
   pas d'icône, pas de disparition sans script — est écrit en entier au-dessus de
   la fonction, dans `lib-html.js`, et ne se recopie pas ici.

   QUATRE POINTS QUI NE SE DEVINENT PAS EN RELISANT CES RÈGLES :

   · EN `writing-mode: vertical-rl`, LES AXES LOGIQUES TOURNENT AVEC LE TEXTE,
     ET LES DÉCALAGES DE POSITION TOURNENT AUSSI. Les propriétés logiques se
     résolvent dans le mode d'écriture de l'élément LUI-MÊME : ici l'axe d'INLINE
     devient le vertical (la longueur du mot) et l'axe de BLOC devient
     l'horizontal (l'épaisseur de l'onglet).
       Pour la TAILLE, c'est ce qu'on veut : la cible de 44 px du critère 2.5.5
     est l'épaisseur de l'onglet, donc un `min-block-size`. L'écrire en
     `min-inline-size` allongerait le mot sans jamais épaissir l'onglet.
       Pour la POSITION, c'est un piège, et il a été payé une fois ici : écrit en
     `inset-block-start: 50%` + `inset-inline-end: 0`, l'onglet s'est retrouvé au
     MILIEU de l'écran, en bas — parce que le navigateur avait lu, très
     correctement, `right: 50%` et `bottom: 0`. LES DÉCALAGES SONT DONC ÉCRITS EN
     PHYSIQUE, `top` et `right`, qui ne tournent pas et ne se relisent pas de
     travers. C'est le genre d'erreur qu'aucun outil ne signale : la règle
     s'applique, la feuille est valide, et le composant est ailleurs qu'où on
     croit l'avoir mis. Qui déplace cet onglet le vérifie À L'ÉCRAN.

   · L'ONGLET FIXE QUITTE SON BORD SOUS 60rem, ET C'EST LA RÈGLE DE REFLOW.
     Un élément fixé au bord de fin recouvre le texte dès que la page n'a plus
     de marge à lui donner : à 320 px de large, c'est du contenu perdu (1.4.10),
     et à 200 % de zoom, la même chose (1.4.4) — un écran de 1280 px zoomé à
     200 % ne fait plus que 640 px de large en pixels CSS. LES DEUX CAS SONT LE
     MÊME CAS, et une seule bascule les couvre : sous le seuil, la pastille
     rejoint le flux du document. Elle y est déjà à sa place, puisque
     `lib-html.js` l'écrit APRÈS le pied — elle s'y lit comme la dernière ligne
     du pied, sur la même surface sombre que lui.

   · LE CERCLE DE FOCUS EST LE ROUGE DES SURFACES NOIRES, ET IL RENTRE.
     Sur `--bg-deep`, `--focus` ne tient que 2,07:1 : le système déclare
     `--accent-on-dark` exactement pour ce cas, à 7,74:1 sur le noir. Et
     l'`outline-offset` est NÉGATIF pour que le cercle reste posé sur l'onglet :
     un cercle projeté vers l'extérieur d'un élément fixé se retrouve sur ce que
     la page peint derrière lui, qui change d'une page à l'autre — son contraste
     ne serait plus mesurable.

   · ELLE NE S'IMPRIME PAS. Un onglet de navigation sur du papier est de l'encre
     perdue, et sur la lettre de l'atelier il tomberait en travers du courrier.
     `position: fixed` s'imprime sur CHAQUE feuille : ne pas la cacher se paie
     autant de fois qu'il y a de pages. */
.pastille-a11y {
  position: fixed;
  /* EN PHYSIQUE, ET C'EST VOULU : voir le premier point du commentaire. */
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  z-index: 40;
  writing-mode: vertical-rl;
  display: flex;
  align-items: center;
  justify-content: center;
  /* l'axe de BLOC est l'horizontal ici : c'est l'épaisseur de l'onglet, donc la
     cible de 44 px. Voir le premier point du commentaire ci-dessus. */
  min-block-size: 44px;
  padding-block: var(--s-3);
  padding-inline: var(--s-4);
  background: var(--bg-deep);
  color: var(--on-dark);
  border-radius: 0;
  font: 600 var(--t-xs)/1 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
}
.pastille-a11y:hover { color: var(--accent-on-dark); text-decoration: underline; }
.pastille-a11y:focus-visible {
  outline: 2.5px solid var(--accent-on-dark);
  outline-offset: -2.5px;
  color: var(--accent-on-dark);
}

/* sous le seuil, elle rentre dans le flux et devient la dernière ligne du pied. */
@media (max-width: 59.99rem) {
  .pastille-a11y {
    position: static;
    top: auto;
    right: auto;
    transform: none;
    writing-mode: horizontal-tb;
    /* le mot est redevenu horizontal : les axes logiques ont retourné avec lui,
       et la hauteur de cible est de nouveau un `min-block-size`. */
    min-block-size: 44px;
    padding-block: var(--s-4);
    padding-inline: var(--s-6);
    letter-spacing: 0.14em;
  }
}

@media print {
  .pastille-a11y { display: none !important; }
}
