/* ==========================================================================
   one-vision.app — feuille de style unique
   --------------------------------------------------------------------------
   Direction : « plaque signalétique ». Tout ce que cette application décrit
   porte, dans la vraie vie, une plaque rivetée : la chaudière, l'ascenseur,
   l'extincteur. Une plaque, c'est un cadre, des libellés en petites capitales,
   des valeurs en caractères mécaniques, et une gravure qui ne s'efface pas.
   L'interface reprend ce vocabulaire — cartouche, filet de laiton, valeurs en
   chasse fixe — parce que c'est celui que ses utilisateurs lisent déjà.

   Aucun build, aucune dépendance externe : un seul fichier servi par
   WhiteNoise, polices comprises.
   ========================================================================== */

/* --- Polices auto-hébergées ---------------------------------------------- */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("../polices/archivo-latin.de2f1fe81758.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("../polices/archivo-latin-ext.589f7bc0fac4.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../polices/plexmono-400.79936b18df9f.woff2") format("woff2");
}
@font-face {
  font-family: "Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../polices/plexmono-600.5a2ce7798732.woff2") format("woff2");
}

/* --- Points de rupture ---------------------------------------------------
   Six échelons, et **un seul jeu de valeurs** : 30, 34, 40, 46, 58, 62 rem.
   Ils ne peuvent pas être des jetons — une requête de média ne lit pas les
   propriétés personnalisées —, d'où cette liste, et le test qui la tient
   (`core/tests/test_ruptures.py`).

   **Un `min-width` prend l'échelon, un `max-width` s'arrête juste avant.**
   Mesuré le 2026-09-17 : `62rem` était déclaré dans les deux sens, si bien
   qu'à cette largeur exacte le rail de bureau s'appliquait **en même temps**
   que les colonnes repliées. Un défaut qui ne se voit qu'à un pixel près,
   donc jamais en le cherchant — et toujours chez quelqu'un dont la fenêtre
   tombe pile là. La règle du centième de rem le ferme pour tous les
   échelons, y compris ceux qui n'ont pas encore de jumeau. */

/* --- Jetons -------------------------------------------------------------- */

:root {
  color-scheme: light dark;

  /* Couleur de l'organisation : c'est la seule personnalisation prévue.
     Elle ne touche que ce qui est cliquable, jamais la structure — une
     organisation qui choisit un rose vif ne doit pas casser la lisibilité. */
  --accent-brut: #2a4e7e;
  --accent: var(--accent-brut);
  --accent-fort: color-mix(in srgb, var(--accent-brut) 82%, #000);
  --accent-voile: color-mix(in srgb, var(--accent-brut) 9%, #fff);

  /* Un lien et un bouton plein n'ont pas le même besoin. Le lien se lit *sur*
     le fond de la page ; le bouton *est* le fond de son propre texte. Sur
     thème sombre, l'accent est éclairci pour rester lisible en lien — le
     reprendre en aplat avec du texte blanc donne un bouton délavé, illisible,
     alors qu'il porte l'action principale de l'écran. D'où deux jetons : la
     couleur de l'aplat, et l'encre qui va dessus. */
  --accent-plein: var(--accent-brut);
  --sur-accent: #fff;
  --sur-vert: #fff;

  --papier: #f1f3f1;
  --plan: #ffffff;
  --plan-creux: #e9ece9;
  --encre: #182225;
  --encre-2: #5c6a6d;
  --trait: #d7dcd9;
  --trait-fort: #b7c0bc;

  --laiton: #b07d2b;
  --laiton-encre: #8a6220;
  /* Lot F3 : qui a saisi une tâche au planning — deux teintes réservées à
     cette seule question, à côté du laiton du gestionnaire. */
  --acier: #3d6f8e;
  --ardoise: #5b5f7a;

  --vert: #2f6b4f;
  --vert-voile: #e2ede6;
  --ambre: #98520d; /* T-M03 : assombri (même teinte) — 4,40:1 sur --ambre-voile, sous le seuil 4,5:1 */
  --ambre-voile: #f7ecdd;
  --rouge: #a8342a;
  --rouge-voile: #f7e5e2;
  --gris-voile: #e4e8e5;

  --ombre: 0 1px 2px rgba(24, 34, 37, .05), 0 6px 18px -12px rgba(24, 34, 37, .28);
  /* --- Échelle typographique ------------------------------------------
     Un rapport unique de 1,125 ancré sur 1rem. Dix pas, et chacun se voit :
     le plus petit écart vaut 0,08rem, soit 1,3 px — au-delà du seuil où
     l'œil distingue deux tailles.

     **Pourquoi une échelle, et pourquoi celle-ci.** Mesuré le 2026-09-17 :
     la feuille portait 34 tailles littérales pour 106 usages, dont 25 paires
     séparées de moins de 0,02rem — trois dixièmes de pixel, indiscernables.
     Ce n'était pas une échelle mais une rampe continue, et son effet est
     l'inverse de ce qu'on attend d'une typographie : deux informations de
     rang différent se retrouvaient à 0,78 et 0,80rem, donc identiques à la
     lecture ; deux informations de même rang à 0,82 et 0,84, écrites à trois
     semaines d'écart. Rien ne hiérarchisait plus rien.

     **Une taille littérale dans un composant est désormais un défaut**, au
     même titre qu'une couleur littérale, et `core/tests/test_echelle.py` le
     fait échouer. Trois exceptions, nommées là-bas parce qu'elles sont
     justes : les `em` (relatifs à leur parent, c'est le but), les `px` et
     les `pt` des dessins SVG et des planches imprimées (coordonnées du tracé
     et millimètres de papier, pas du texte d'écran), et les `clamp()` des
     titres, qui sont de la typographie fluide voulue.

     **Un pas sans usage reste un pas.** `--texte-4xl` n'est employé nulle
     part aujourd'hui : c'est une échelle, pas un inventaire, et lui faire un
     trou casserait le rapport constant qui la rend lisible. Ne pas le
     « nettoyer ». */
  /* --- Échelle d'espacement --------------------------------------------
     Quinze pas, tirés des valeurs que la feuille employait réellement plutôt
     que d'un rapport choisi d'avance — c'est ce qui déplace le moins tout en
     supprimant les doublons.

     **Pourquoi une échelle.** Mesuré le 2026-09-17 : 47 valeurs littérales
     pour 403 usages, dont onze paires séparées de moins de 0,02rem — 0,58 et
     0,60 ; 0,28 et 0,30 ; 0,12 et 0,13. Trois dixièmes de pixel : personne ne
     les distingue, et personne ne les a décidées. La cause était la même que
     pour la typographie — aucun jeton, donc aucune valeur n'avait où vivre.

     La bascule a laissé **228 espacements inchangés** sur 394, 91 à moins
     d'un pixel, un seul à 8 px (le bandeau d'accueil). Ce qui change n'est
     pas l'aspect, c'est qu'un écart d'espacement veut désormais dire quelque
     chose.

     Les pas sont numérotés parce qu'il y en a quinze : un nom en toutes
     lettres pour chacun se périmerait au premier réglage. Le numéro dit le
     rang, et le rang dit la taille. */
  --pas-1: .1rem;
  --pas-2: .2rem;
  --pas-3: .3rem;
  --pas-4: .4rem;
  --pas-5: .5rem;
  --pas-6: .6rem;
  --pas-7: .8rem;
  --pas-8: 1rem;
  --pas-9: 1.2rem;
  --pas-10: 1.6rem;
  --pas-11: 2rem;
  --pas-12: 2.5rem;
  --pas-13: 3rem;
  --pas-14: 4rem;
  --pas-15: 6rem;

  --texte-xxs: .62rem;
  --texte-xs: .7rem;
  --texte-s: .79rem;
  --texte-m: .89rem;
  --texte-l: 1rem;
  --texte-xl: 1.13rem;
  --texte-2xl: 1.27rem;
  --texte-3xl: 1.42rem;
  --texte-4xl: 1.6rem;
  --texte-5xl: 1.8rem;
  --texte-6xl: 2.03rem;
  --texte-7xl: 2.28rem;

  --rayon: .5rem;
  --rayon-s: .3rem;

  --police: "Archivo", ui-sans-serif, system-ui, "Segoe UI", sans-serif;
  --mono: "Plex Mono", ui-monospace, "SFMono-Regular", "Consolas", monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --accent: color-mix(in srgb, var(--accent-brut) 55%, #fff);
    --accent-fort: color-mix(in srgb, var(--accent-brut) 35%, #fff);
    --accent-voile: color-mix(in srgb, var(--accent-brut) 26%, #101718);

    /* Aplat clair, encre sombre : l'inverse du thème clair. Le mélange avec
       du blanc garantit un fond clair quelle que soit la couleur choisie par
       l'organisation, donc une encre sombre lisible dans tous les cas. */
    --accent-plein: var(--accent);
    --sur-accent: #0f1516;
    --sur-vert: #0f1516;

    --papier: #101718;
    --plan: #171f21;
    --plan-creux: #1e282a;
    --encre: #e6ebe8;
    --encre-2: #98a6a5;
    --trait: #2a3538;
    --trait-fort: #3d4a4d;

    --laiton: #d3a054;
    --laiton-encre: #d3a054;
    --acier: #7fb2d3;
    --ardoise: #a3a7c7;

    --vert: #6cbb92;
    --vert-voile: #1a2f26;
    --ambre: #e0a35f;
    --ambre-voile: #322418;
    --rouge: #e8887c;
    --rouge-voile: #33201d;
    --gris-voile: #232d30;

    --ombre: 0 1px 2px rgba(0, 0, 0, .4);
  }
}

/* --- Base ---------------------------------------------------------------- */

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--papier);
  color: var(--encre);
  font-family: var(--police);
  font-size: var(--texte-l);
  line-height: 1.55;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-stretch: 112%;
  font-weight: 700;
  letter-spacing: -.011em;
  line-height: 1.15;
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem); }
h2 { font-size: var(--texte-xl); margin: var(--pas-12) 0 var(--pas-7); }
h3 { font-size: var(--texte-l); }

p { margin: 0 0 var(--pas-8); }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-underline-offset: .18em; }
a:hover { color: var(--accent-fort); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--rayon-s);
}

code, kbd, samp { font-family: var(--mono); font-size: .92em; }

hr { border: 0; border-top: 1px solid var(--trait); margin: var(--pas-11) 0; }

/* Le navigateur donne aux `figure` une marge de 40 px et aux `dl` une marge
   verticale. Dans une grille, elles rétrécissent les cases sans qu'on voie
   pourquoi. */
figure, dl { margin: 0; }

img { max-width: 100%; }

/* Le filet de laiton : rappel du souligné de « one-vision.app », posé sous les
   titres. C'est la signature du système, elle ne sert qu'à ça. */
.filet::after {
  content: "";
  display: block;
  width: 2.2rem;
  height: 2px;
  margin-top: var(--pas-6);
  background: var(--laiton);
}

/* --- Accessibilité --------------------------------------------------------
   T-K05. Deux composants, communs aux deux gabarits de base : le lien
   d'évitement (WCAG 2.4.1) et l'utilitaire qui cache visuellement un texte
   sans le retirer des lecteurs d'écran (une légende de table, un intitulé de
   colonne déjà porté par le contexte visuel). */

.lien-evitement {
  position: absolute;
  left: -999rem;
  top: 0;
  z-index: 200;
  padding: var(--pas-6) var(--pas-9);
  background: var(--accent-plein);
  color: var(--sur-accent);
  border-radius: 0 0 var(--rayon-s) 0;
  font-weight: 600;
  text-decoration: none;
}
.lien-evitement:focus-visible {
  left: 0;
  outline-offset: -2px;
}

.masque-visuel {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
  border: 0;
}

/* --- Étiquettes typographiques ------------------------------------------- */

.oeil {                       /* sur-titre : le libellé gravé d'une plaque */
  font-size: var(--texte-xs);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--encre-2);
  margin: 0 0 var(--pas-3);
  display: block;
}
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.sous-titre { color: var(--encre-2); margin: var(--pas-4) 0 var(--pas-10); max-width: 62ch; }
.meta { color: var(--encre-2); font-size: var(--texte-m); }
.chiffre {
  font-family: var(--mono);
  font-size: var(--texte-5xl);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

/* --- Icônes -------------------------------------------------------------- */

.ico {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -.18em;
}
.ico-g { width: 1.5em; height: 1.5em; }
.ico-xl { width: 2rem; height: 2rem; stroke-width: 1.3; }

/* Teinte d'une icône isolée — pictogramme de carte, en-tête d'état. Une
   icône dans un bouton ou une pastille hérite déjà de sa couleur via
   `currentColor` et n'a pas besoin de ces classes. */
.ico-laiton { color: var(--laiton); }
.ico-ambre { color: var(--ambre); }
.ico-vert { color: var(--vert); }

/* --- Marque -------------------------------------------------------------- */

.marque {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-6);
  text-decoration: none;
  color: var(--encre);
  font-stretch: 112%;
  font-weight: 700;
  letter-spacing: -.02em;
  font-size: var(--texte-xl);
}
.marque .logo { width: 1.75rem; height: 1.75rem; flex: none; }
/* La marque du pied de page est une variante, plus discrète que celle de
   l'en-tête — elle rappelle le nom, elle ne l'annonce pas. Elle vivait dans un
   `style=` du gabarit, c'est-à-dire hors de toute échelle et hors du thème. */
.pied-contenu .marque { font-size: var(--texte-l); }
.pied-contenu .marque .logo { width: 1.3rem; height: 1.3rem; }
.marque .mot { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.marque .mot i {
  /* **Le « .app » est la marque, pas un ornement** (2026-09-13). Le trait de
     laiton soulignait « vision » et remplaçait le tiret bas de « one-vision.app » ;
     le nom étant devenu un domaine, ce qui le distingue est son extension. Elle
     se teinte donc, et ne se souligne pas : un souligné sous « .app » se lirait
     comme un lien dans un élément qui en est déjà un. */
  font-style: normal;
  color: var(--laiton);
}
.marque img.logo-organisation { height: 1.9rem; width: auto; border-radius: 3px; }

/* La qualité du compte connecté, à côté du nom de l'espace : savoir avec quel
   chapeau on est entré évite de chercher pourquoi telle action manque. En
   minuscules et en retrait — c'est une précision, pas un titre. */
.marque .qualite {
  flex: none;
  color: var(--encre-2);
  font-weight: 500;
  font-size: var(--texte-s);
  letter-spacing: 0;
  white-space: nowrap;
}
@media (max-width: 29.99rem) { .marque .qualite { display: none; } }

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

.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pas-4);
  padding: var(--pas-6) var(--pas-8);
  border-radius: var(--rayon-s);
  border: 1px solid transparent;
  background: var(--accent-plein);
  color: var(--sur-accent);
  font: inherit;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
  /* Une cible tactile d'au moins 44 px (2,75 rem) — S-128, fiche de la campagne
     d'octobre 2026 : à 360 px, les boutons tombaient à 42 px. */
  min-height: 2.75rem;
}
.bouton:hover { background: var(--accent-fort); color: var(--sur-accent); }
.bouton.secondaire {
  background: transparent;
  color: var(--encre);
  border-color: var(--trait-fort);
}
.bouton.secondaire:hover { background: var(--plan-creux); color: var(--encre); border-color: var(--encre-2); }
.bouton.discret { background: transparent; color: var(--accent); padding-inline: .4rem; }
.bouton.discret:hover { background: var(--accent-voile); color: var(--accent-fort); }
.bouton.grand { padding: var(--pas-7) var(--pas-9); font-size: var(--texte-l); }
/* Un bouton qui termine un formulaire étroit occupe sa largeur : c'est la
   seule action de l'écran, elle n'a pas à être cherchée. */
.bouton.pleine-largeur { width: 100%; }
.actions { display: flex; flex-wrap: wrap; gap: var(--pas-6); align-items: center; }

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

label { font-weight: 500; font-size: var(--texte-m); }
/* Un champ, c'est une étiquette au-dessus de sa saisie. La règle vise le
   conteneur d'un champ — pas n'importe quel paragraphe posé dans un
   formulaire : appliquée à de la prose, elle faisait de chaque mot en gras une
   ligne à part, et les explications des écrans d'édition se lisaient en
   escalier. */
form p { margin-bottom: var(--pas-9); }
form p:has(> label) { display: flex; flex-direction: column; gap: var(--pas-3); }

/* Une case à cocher, elle, se lit avec son libellé : sur la même ligne, à
   gauche. Empilée comme un champ de saisie, elle flottait seule au milieu. */
form p:has(> input[type="checkbox"]) {
  flex-direction: row-reverse;
  justify-content: flex-end;
  align-items: center;
  gap: var(--pas-5);
}
form p:has(> input[type="checkbox"]) label { font-weight: 500; }
form p.aide { color: var(--encre-2); font-size: var(--texte-m); margin-top: calc(var(--pas-7) * -1); }

input, select, textarea {
  font: inherit;
  width: 100%;
  padding: var(--pas-6) var(--pas-6);
  color: var(--encre);
  background: var(--plan);
  border: 1px solid var(--trait-fort);
  border-radius: var(--rayon-s);
  transition: border-color .12s ease, box-shadow .12s ease;
}
input[type="color"] { padding: var(--pas-2); height: 2.6rem; }
input[type="checkbox"], input[type="radio"] { width: auto; accent-color: var(--accent); }
input:focus, select:focus, textarea:focus {
  /* T-M03 : pas d'`outline: none` ici — il masquait l'anneau `:focus-visible`
     global au clavier ; le halo ci-dessous ne fait que s'y ajouter. */
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-voile);
}
input::placeholder, textarea::placeholder { color: var(--encre-2); opacity: .75; }
textarea { min-height: 6rem; resize: vertical; }
.formulaire-etroit { max-width: 26rem; }
/* L'œil d'un mot de passe (`_bouton_oeil.html`) : posé dans le champ, à
   droite, sur la hauteur de sa zone de saisie (2,75 rem = la cible tactile),
   et visible seulement quand le script est là (`html.avec-script`). */
.champ-secret { position: relative; display: block; }
.champ-secret input { display: block; padding-right: var(--pas-13); }
.bouton-oeil {
  display: none; position: absolute; right: 1px; bottom: 1px;
  width: 2.75rem; height: 2.75rem; align-items: center; justify-content: center;
  padding: 0; border: 0; border-radius: var(--rayon-s); background: transparent;
  color: var(--encre-2); cursor: pointer;
}
.avec-script .bouton-oeil { display: inline-flex; }
.bouton-oeil:hover { color: var(--encre); background: var(--plan-creux); }
.bouton-oeil .ico { width: 1.25rem; height: 1.25rem; }
.bouton-oeil[aria-pressed="true"] .ico-oeil,
.bouton-oeil[aria-pressed="false"] .ico-oeil-barre { display: none; }

.erreurs {
  border-left: 3px solid var(--rouge);
  background: var(--rouge-voile);
  color: var(--rouge);
  padding: var(--pas-6) var(--pas-7);
  border-radius: var(--rayon-s);
  margin: 0 0 var(--pas-9);
  list-style: none;
}
.erreurs li + li { margin-top: var(--pas-3); }
ul.errorlist {
  list-style: none; padding: var(--pas-5) var(--pas-7); margin: 0 0 var(--pas-6);
  background: var(--rouge-voile); color: var(--rouge);
  border-left: 3px solid var(--rouge); border-radius: var(--rayon-s);
  font-size: var(--texte-m);
}
.helptext, .helptext li { color: var(--encre-2); font-size: var(--texte-m); }

/* --- Cartes et grilles --------------------------------------------------- */

.grille {
  display: grid;
  gap: var(--pas-7);
  grid-template-columns: repeat(auto-fill, minmax(16.5rem, 1fr));
}
.grille.large { grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); }
.grille.serree { grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); }

/* Un formulaire d'édition et sa colonne d'appui — la photo, l'état de
   conformité, le périmètre. La colonne principale porte la saisie, la seconde
   ce qui la documente. En dessous d'un écran d'ordinateur, l'appui passe sous
   la saisie plutôt que de la comprimer.
   Sans cette règle, les trois écrans d'édition s'empilaient sur une seule
   colonne pleine largeur : un code postal s'étalait sur mille pixels. */
.deux-colonnes {
  display: grid;
  gap: var(--pas-8);
  grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr);
  align-items: start;
}
/* Deux blocs de saisie de même poids, eux, se partagent la largeur. */
.deux-colonnes.egales { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 61.99rem) {
  .deux-colonnes, .deux-colonnes.egales { grid-template-columns: minmax(0, 1fr); }
}

/* Les champs courts d'un même bloc se rangent côte à côte : un code postal,
   une année, un nombre de niveaux n'ont pas besoin de toute la largeur, et
   voisiner les rend lisibles comme une même information. Un champ long — un
   nom, une adresse, des notes — reprend la ligne entière. */
.champs { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 0 var(--pas-8); }
.champs > .pleine-ligne { grid-column: 1 / -1; }

/* Trois respirations nommées, plutôt que des marges écrites dans les
   gabarits : elles se répètent, donc elles appartiennent à la feuille. */
.espace-dessus { margin-top: var(--pas-8); }
.espace-dessus-large { margin-top: var(--pas-9); }
.espace-autour { margin: var(--pas-6) 0; }
.vide.compacte { padding: var(--pas-10) var(--pas-8); }
.sous-titre-bloc { margin: var(--pas-9) 0 var(--pas-5); }
.intro-bloc { margin: var(--pas-4) 0 var(--pas-7); }
.espace-dessus-court { margin-top: var(--pas-5); }

.carte {
  position: relative;
  display: block;
  padding: var(--pas-8) var(--pas-8);
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  color: inherit;
  text-decoration: none;
  transition: border-color .12s ease, box-shadow .12s ease;
}
a.carte:hover {
  border-color: var(--accent);
  box-shadow: var(--ombre);
}
/* T-K05 : `h2` **et** `h3` — une grille de cartes est parfois le premier
   niveau de sous-titre sous le `<h1>` de la page (« Mes sites »), parfois un
   niveau plus bas, sous un vrai `<h2>` de section (l'accueil public). Le
   composant visuel ne tranche pas la hiérarchie du document : chaque gabarit
   choisit le niveau qui prolonge correctement son propre `<h1>`, sans saut. */
.carte h2, .carte h3 { margin: 0 0 var(--pas-2); }
/* Une icône posée avant le titre — carte de choix, pictogramme d'étape —
   a besoin d'un peu d'air au-dessus du titre que `.carte h2`/`h3` seul ne
   donne pas. */
.carte svg + h2, .carte svg + h3 { margin: var(--pas-6) 0 var(--pas-4); }
.carte .meta + .meta { margin-top: var(--pas-1); }
.carte > .pastille, .carte > .timbre { margin-top: var(--pas-6); }

/* --- L'arborescence du parc ----------------------------------------------
   Une seule teinte porte la profondeur — le laiton de la charte, dilué vers
   le trait à mesure qu'on descend — et elle ne touche que le rail vertical.
   L'état du matériel garde vert/ambre/rouge, cantonnés aux pastilles : les
   deux systèmes ne se disputent jamais le même pixel. Une couleur vive par
   niveau ferait clignoter l'écran et entrerait en concurrence avec des
   couleurs qui signifient déjà quelque chose.

   Trois signaux redondants disent le niveau : le rail (coin de l'œil),
   l'indentation (confirmation), le poids de la police (un cran plus léger
   dès la première descente). */
.arbre {
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-5) 0;
  overflow: hidden;
}

.rang-1 { --rail: var(--laiton); --retrait: 0rem; }
.rang-2 { --rail: color-mix(in srgb, var(--laiton) 55%, var(--trait)); --retrait: 1.5rem; }
.rang-3 { --rail: color-mix(in srgb, var(--laiton) 30%, var(--trait)); --retrait: 3rem; }
.rang-4 { --rail: color-mix(in srgb, var(--laiton) 15%, var(--trait)); --retrait: 4.5rem; }

.noeud > .ligne {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
  gap: var(--pas-6);
  margin-left: calc(var(--pas-8) + var(--retrait));
  padding: var(--pas-6) var(--pas-8) var(--pas-6) var(--pas-7);
  border-left: 3px solid var(--rail);
  cursor: pointer;
  transition: background-color .12s ease;
}
.noeud.sans-composant > .ligne { grid-template-columns: auto 1fr auto; cursor: default; }
.noeud > .ligne:hover { background: var(--accent-voile); }
/* Le marqueur natif de `<details>` ferait doublon avec le chevron dessiné. */
.noeud > summary::-webkit-details-marker { display: none; }
.noeud > summary { list-style: none; }

.arbre .chevron { color: var(--encre-2); transition: transform .12s ease; }
details.noeud[open] > .ligne .chevron { transform: rotate(90deg); }

.arbre .pictogramme { color: var(--laiton); }
.rang-2 .pictogramme, .rang-3 .pictogramme, .rang-4 .pictogramme { color: var(--encre-2); }

.arbre .identite { display: flex; flex-direction: column; gap: var(--pas-1); min-width: 0; }
.arbre .nom { font-weight: 600; }
.rang-2 .nom, .rang-3 .nom, .rang-4 .nom { font-weight: 500; }
a.nom { color: inherit; text-decoration: none; }
a.nom:hover { color: var(--accent); text-decoration: underline; }

.arbre .bout { display: flex; align-items: center; gap: var(--pas-4); flex-wrap: wrap; justify-content: flex-end; }
.arbre .compteur {
  font-family: var(--mono); font-size: var(--texte-s); font-variant-numeric: tabular-nums;
  color: var(--encre-2); white-space: nowrap;
}

/* Ajouter un composant depuis le nœud auquel il appartient : c'est ce geste
   qui remplace la liste déroulante « Fait partie de ». */
.arbre .ajout {
  display: inline-flex; align-items: center; gap: var(--pas-3);
  border: 1px dashed var(--trait-fort); border-radius: 2rem;
  padding: var(--pas-2) var(--pas-6);
  font-size: var(--texte-s); font-weight: 500;
  color: var(--encre-2); text-decoration: none; white-space: nowrap;
}
.arbre .ajout:hover, .arbre .ajout:focus-visible {
  color: var(--accent); border-color: var(--accent); border-style: solid;
}
.arbre .ajout .ico { width: 14px; height: 14px; }

.actions-noeud {
  display: flex; gap: var(--pas-7); align-items: center; flex-wrap: wrap;
  margin-left: calc(var(--pas-8) + var(--retrait));
  padding: var(--pas-1) var(--pas-8) var(--pas-5) var(--pas-11);
  border-left: 3px solid var(--rail);
  font-size: var(--texte-s);
}

/* Le même arbre sert à déclarer, pas seulement à consulter : une case à
   cocher remplace alors le lien. Le libellé fait partie de la cible — cocher
   « Hivernage » doit marcher en cliquant sur le mot, pas seulement sur les
   douze pixels de la case. */
.arbre .case-libelle {
  display: flex; align-items: center; gap: var(--pas-6);
  min-width: 0; cursor: pointer; user-select: none;
}
.arbre .case-libelle input[type="checkbox"] { flex: none; width: 1.05rem; height: 1.05rem; cursor: pointer; }
.arbre .case-libelle:hover .nom { color: var(--accent); }

/* « À vous » : une compétence que le professionnel a écrite lui-même. Un
   timbre, pas une couleur de rail — la couleur dit déjà le niveau. */
/* T-M03 : `--laiton-encre` (déjà prévue pour du texte, cf. `.p-laiton` plus
   bas) plutôt que `--laiton` — 3,10:1 sur ce fond, sous le seuil 4,5:1. */
.pastille.p-laiton { background: color-mix(in srgb, var(--laiton) 14%, var(--plan)); color: var(--laiton-encre); }

/* Un repère en fin de page : une proposition, pas une alerte. Le filet de
   laiton la rattache à la charte sans lui donner le poids d'un avertissement. */
.encadre-laiton {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--pas-8) var(--pas-10); flex-wrap: wrap;
  border-left: 3px solid var(--laiton);
  background: var(--plan);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  padding: var(--pas-8) var(--pas-9);
}
.encadre-laiton p { margin: var(--pas-3) 0 0; max-width: 46rem; }

.legende-rangs {
  display: flex; align-items: center; gap: var(--pas-4) var(--pas-8); flex-wrap: wrap;
  margin: var(--pas-4) var(--pas-8) 0; padding-top: var(--pas-6);
  border-top: 1px solid var(--trait);
  font-size: var(--texte-s); color: var(--encre-2);
}
.legende-rangs .echantillon { width: 3px; height: 1rem; border-radius: 2px; background: var(--rail); }

@media (max-width: 33.99rem) {
  .rang-2 { --retrait: .8rem; }
  .rang-3 { --retrait: 1.6rem; }
  .rang-4 { --retrait: 2.4rem; }
  .noeud > .ligne { grid-template-columns: auto auto 1fr; }
  .noeud.sans-composant > .ligne { grid-template-columns: auto 1fr; }
  .arbre .bout { grid-column: 1 / -1; justify-content: flex-start; }
}

/* Le liseré d'état : sur une carte comme sur une ligne de tableau, la couleur
   est portée par un filet à gauche, jamais par un aplat — un fond coloré
   derrière du texte se lit mal en plein soleil. */
.carte.etat-rouge { border-left: 3px solid var(--rouge); }
.carte.etat-ambre { border-left: 3px solid var(--ambre); }
.carte.etat-vert { border-left: 3px solid var(--vert); }

.vignette {
  display: block;
  aspect-ratio: 16 / 9;
  width: calc(100% + 2.1rem);
  margin: calc(var(--pas-8) * -1) calc(var(--pas-8) * -1) var(--pas-7);
  border-radius: calc(var(--rayon) - 1px) calc(var(--rayon) - 1px) 0 0;
  object-fit: cover;
  background: var(--plan-creux);
}

/* --- Cartouche : l'en-tête de toute fiche -------------------------------- */

.cartouche {
  position: relative;
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-9) var(--pas-9);
  margin-bottom: var(--pas-10);
  box-shadow: var(--ombre);
}
.cartouche::before {                /* le filet gravé, en haut à gauche */
  content: "";
  position: absolute;
  top: 0; left: 1.2rem;
  width: 2.6rem; height: 3px;
  background: var(--laiton);
  border-radius: 0 0 2px 2px;
}
.cartouche-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--pas-7) var(--pas-9);
}
/* Un code temporaire à saisir ailleurs (appairage passerelle) : le contour
   reprend le laiton du filet gravé, pour dire « ceci sort de l'ordinaire »
   sans introduire une nouvelle couleur. Remplace un `style="border-color:
   var(--laiton)"` en ligne — la même faute que celle déjà éliminée sur les
   icônes le 2026-08-21, revenue ici le temps qu'un composant lui manque. */
.cartouche-laiton { border-color: var(--laiton); }
.cartouche h1 { margin-bottom: var(--pas-1); }
.cartouche .sous-titre { margin: var(--pas-3) 0 0; }
.cartouche .actions { margin-top: var(--pas-8); }
.cartouche-photo {
  width: 100%; max-height: 13rem; object-fit: cover;
  border-radius: var(--rayon-s);
  margin-bottom: var(--pas-8);
}
/* La plaque signalétique, dans la plaque de la fiche : une vignette qu'on
   ouvre pour la lire — assez grande pour qu'on devine qu'il y a du texte. */
.vignette-plaque { display: inline-block; line-height: 0; }
.vignette-plaque img {
  max-height: 5rem; max-width: 100%;
  border: 1px solid var(--trait); border-radius: var(--rayon-s);
}

/* Les champs de la plaque : libellé gravé au-dessus, valeur en chasse fixe. */
.plaque {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin: var(--pas-9) 0 0;
  border-top: 1px solid var(--trait);
}
.plaque > div {
  padding: var(--pas-6) var(--pas-7) var(--pas-1) 0;
  border-right: 1px solid var(--trait);
}
.plaque > div:last-child { border-right: 0; }
.plaque > div + div { padding-left: var(--pas-7); }
.plaque dt {
  font-size: var(--texte-xs);
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--encre-2);
}
.plaque dd {
  margin: var(--pas-1) 0 0;
  font-family: var(--mono);
  font-size: var(--texte-m);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* --- Pastilles et timbres ------------------------------------------------ */

.pastille {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-3);
  padding: var(--pas-1) var(--pas-5);
  border-radius: 2rem;
  font-size: var(--texte-s);
  font-weight: 600;
  white-space: nowrap;
  background: var(--gris-voile);
  color: var(--encre-2);
}
.pastille::before {
  content: "";
  width: .4rem; height: .4rem; border-radius: 50%;
  background: currentColor;
  flex: none;
}
.pastille.sans-point::before { display: none; }
.p-vert { background: var(--vert-voile); color: var(--vert); }
.p-ambre { background: var(--ambre-voile); color: var(--ambre); }
.p-rouge { background: var(--rouge-voile); color: var(--rouge); }
.p-gris { background: var(--gris-voile); color: var(--encre-2); }
.p-laiton { background: color-mix(in srgb, var(--laiton) 15%, transparent); color: var(--laiton-encre); }
/* Les deux rendus d'un même état partagent le vocabulaire des tons : la
   pastille pour une ligne de tableau, le timbre pour l'en-tête d'une fiche.
   Un modèle décide du ton une fois, les deux composants suivent. */
.pastilles { display: flex; flex-wrap: wrap; gap: var(--pas-3); }

/* Le timbre : l'état d'un objet, encadré comme un tampon de contrôle. */
.timbre {
  display: inline-flex;
  align-items: center;
  gap: var(--pas-4);
  padding: var(--pas-3) var(--pas-6);
  border: 1.5px solid currentColor;
  border-radius: var(--rayon-s);
  font-size: var(--texte-xs);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--encre-2);
}
.timbre.vert { color: var(--vert); }
.timbre.ambre { color: var(--ambre); }
.timbre.rouge { color: var(--rouge); }
.timbre.laiton { color: var(--laiton-encre); }
.timbre.gris { color: var(--encre-2); }
/* Un timbre peut être un lien — le garant d'une garantie, qu'on veut joindre
   au moment où le matériel lâche. Il doit alors se voir cliquable. */
a.timbre { text-decoration: none; }
a.timbre:hover { background: color-mix(in srgb, currentColor 10%, transparent); }

/* --- Lecteur de QR natif (lot M5, templates/terrain/_scan_natif.html) ----- */

/* Au-dessus du champ du code court, dans l'application des stores seulement :
   le geste principal quand l'étiquette est lisible, le champ restant le
   recours juste en dessous. */
.scan-natif {
  display: grid;
  gap: var(--pas-6);
  margin-block: var(--pas-8);
}

/* `hidden` veut dire caché, quel que soit le composant. Tout composant qui pose
   un `display` bat le `[hidden]` du navigateur ; la feuille le corrigeait au
   cas par cas (`.scan-natif`, `.rail a`, `.liste-nette li`, `form p`, la
   tournée), et le sixième cas a échappé : l'alerte « Le réseau est là » du
   mode autonome restait affichée sans réseau (recette mobile du 2026-10-05).
   Les règles particulières restent, inoffensives ; celle-ci tient les
   suivantes. */
[hidden] { display: none !important; }

/* Le viseur du lecteur (F3) : l'image de l'objectif, décodée dans la page.
   `.bouton` et `.alerte` posent un `display` qui battrait `[hidden]`. */
.scan-natif [hidden] { display: none; }
.lecteur-qr { display: grid; gap: var(--pas-6); justify-items: center; }
.lecteur-qr-consigne { margin: 0; font-weight: 600; }
.lecteur-qr-image {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  background: var(--plan-creux);
  border: 1px solid var(--trait-fort);
  border-radius: var(--rayon);
}

/* --- Les constats d'un passage (M4.6, templates/terrain/_constats.html) --- */

/* « Signaler un constat ? » : Non par défaut, et rien d'autre ne se voit —
   des constats ouverts avec leur gravité et leur suite déjà cochées se
   lisaient comme un constat commencé (recette du 2026-09-30). Le script de
   `_comportements.html` tient l'affichage ; sans lui, `:has()` fait le même
   tri, et tout reste saisissable. Le serveur relit l'interrupteur. */
.interrupteur-constats { margin: 0 0 var(--pas-6); }
.interrupteur-constats .question { margin: 0 0 var(--pas-4); font-weight: 600; }
.choix-en-ligne { display: flex; flex-wrap: wrap; gap: var(--pas-9); margin: 0; }
.choix-en-ligne label { display: inline-flex; align-items: center; gap: var(--pas-4); }
.constats:has([data-constats-non]:checked) [data-constats-liste] { display: none; }
.constats-ajouter { margin-top: var(--pas-6); }

/* --- Le mode autonome (M4.6, templates/terrain/accueil_hors_connexion.html) */

.autonome-gestes, .choix-connexion { display: grid; gap: var(--pas-6); margin-block: var(--pas-8); }

/* --- Préparer ma tournée (M4.7.2, static/js/tournee.js) ------------------ */

/* Ce que le script montre ou cache : `.bouton`, `.pastille`, `.codes li` et
   `.liste-nette` posent un `display` qui bat le `[hidden]` du navigateur —
   un « Voir les codes » resterait visible sur un site sans coffre. */
[data-tournee-autonome] [hidden],
[data-tournee-effacer][hidden] { display: none; }

/* --- La file des envois hors connexion (M4.2, static/js/envois.js) ------- */

/* Tout ce qui suit est posé par le script, dans des zones que les gabarits
   laissent vides : sans lui, rien ne s'affiche — ni place réservée, ni texte.
   Les tons sont ceux des pastilles (HC-17, HC-19) ; aucune règle n'est écrite
   depuis le script. */
.envois-reseau { margin: var(--pas-4) 0 0; }
.envois-reseau:empty,
[data-envois-compteur]:empty,
[data-envois-bandeau]:empty,
[data-envois-avis]:empty { display: none; }
/* `.rail a { display: flex }` bat le `[hidden]` du navigateur : l'entrée de
   la file resterait visible sur un appareil qui n'a rien en attente. */
.rail a[hidden] { display: none; }
.rail .compteur-envois { margin-left: auto; font-family: var(--mono); }
.barre-basse a { position: relative; }
.barre-basse .compteur-envois {
  position: absolute;
  top: 0;
  left: calc(50% + var(--pas-4));
  font-family: var(--mono);
}
.file-envois-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--pas-6);
}
.file-envois-liste { margin-block: var(--pas-8); }
.file-envois-liste .envoi {
  align-items: flex-start;
  padding-top: var(--pas-6);
  border-top: 1px solid var(--trait);
}
.envoi-vignette {
  width: 4rem;
  height: 4rem;
  object-fit: cover;
  border-radius: var(--rayon-s);
  flex: none;
}
.envoi-corps { flex: 1 1 auto; min-width: 0; display: grid; gap: var(--pas-3); }
.envoi-corps p { margin: 0; }
.envoi-titre { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pas-5); }
.envoi-gestes { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pas-5); margin-top: var(--pas-3); }
.envoi-gestes form { margin: 0; }

/* --- Alertes ------------------------------------------------------------- */

.alerte {
  display: flex;
  gap: var(--pas-6);
  align-items: flex-start;
  border: 1px solid var(--trait);
  border-left: 3px solid var(--ambre);
  background: var(--plan);
  padding: var(--pas-7) var(--pas-8);
  border-radius: var(--rayon-s);
  margin-bottom: var(--pas-6);
  /* Son propre alignement, jamais hérité : une alerte posée dans un panneau
     centré (confirmation, erreur) ne doit pas voir son texte centré avec lui. */
  text-align: left;
}
.alerte .ico { color: var(--ambre); margin-top: var(--pas-1); }
.alerte.grave { border-left-color: var(--rouge); }
.alerte.grave .ico { color: var(--rouge); }
.alerte.succes { border-left-color: var(--vert); }
/* Une information, pas un avertissement : « ce prestataire n'a rien déclaré »
   n'est le reproche de personne. L'ambre serait un contresens. */
.alerte.info { border-left-color: var(--accent); }
.alerte.info .ico { color: var(--accent); }
.alerte.succes .ico { color: var(--vert); }
.alerte strong { font-weight: 600; }
/* L'alerte est une rangée (icône, texte) : un texte fait de plusieurs
   éléments s'y met dans un seul corps, sinon chacun devient une colonne. */
.alerte-corps { flex: 1 1 auto; min-width: 0; }
.alerte-geste { display: block; }
.alerte-geste .bouton { margin-top: var(--pas-4); }

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

.defilant {
  overflow-x: auto;
  /* Contient ses descendants positionnés (`.masque-visuel` d'un en-tête de
     colonne) : sans quoi ils échappent au défilement et élargissent la page
     (S-128, fiche d'un équipement à 360 px : 567 px). */
  position: relative;
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
}
table { width: 100%; border-collapse: collapse; font-size: var(--texte-m); }
th, td { text-align: left; padding: var(--pas-6) var(--pas-7); border-bottom: 1px solid var(--trait); vertical-align: top; }
thead th {
  background: var(--plan-creux);
  color: var(--encre-2);
  font-size: var(--texte-xs);
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--accent-voile); }
td.num, th.num, td .mono { font-variant-numeric: tabular-nums; }
td.date, td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.defilant + .defilant { margin-top: var(--pas-8); }

.vide {
  color: var(--encre-2);
  padding: var(--pas-11) var(--pas-8);
  text-align: center;
  background: var(--plan);
  border: 1px dashed var(--trait-fort);
  border-radius: var(--rayon);
}
td.vide { border: 0; background: none; }
.vide .ico-xl { color: var(--trait-fort); display: block; margin: 0 auto var(--pas-6); }
.vide p { max-width: 34ch; margin-inline: auto; }
/* Une absence de résultat n'est pas un résultat : elle occupe la largeur de la
   section, jamais la première case d'une grille — sinon on la lit comme une
   fiche, et l'œil cherche les suivantes. */
.grille > .vide { grid-column: 1 / -1; }

/* --- Dépliant ------------------------------------------------------------
   Un formulaire secondaire n'occupe pas le milieu d'une fiche : il se déplie
   quand on en a besoin. Ouvert d'office tant qu'il n'y a rien à montrer à sa
   place — c'est alors la seule chose à faire dans la section.
   `<details>` natif : pas une ligne de JavaScript, et le clavier fonctionne. */

.depliant { margin-bottom: var(--pas-9); }
.depliant > summary {
  display: inline-flex; align-items: center; gap: var(--pas-4);
  padding: var(--pas-5) var(--pas-7);
  border: 1px solid var(--trait-fort); border-radius: var(--rayon-s);
  background: var(--plan);
  font-weight: 600; font-size: var(--texte-m); color: var(--encre);
  cursor: pointer; user-select: none; list-style: none;
}
.depliant > summary::-webkit-details-marker { display: none; }
.depliant > summary::after {
  content: "+"; font-family: var(--mono); color: var(--encre-2); margin-left: var(--pas-1);
}
.depliant[open] > summary::after { content: "−"; }
.depliant > summary:hover { background: var(--plan-creux); border-color: var(--encre-2); }
.depliant > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.depliant[open] > summary { margin-bottom: var(--pas-7); }

/* --- Aide contextuelle ----------------------------------------------------
   Un petit « ? » à côté du titre d'écran, sur le même principe que
   `.depliant` (un <details> natif, sans JavaScript) mais réduit à une icône :
   le texte n'a pas sa place ouverte en permanence à côté d'un titre.
   Composant posé le 2026-08-26 ; le contenu de chaque écran se pose via
   `{% include "_aide_ecran.html" with texte=... %}`, jamais copié à la main. */

.aide-ecran { position: relative; display: inline-flex; vertical-align: middle; margin-left: var(--pas-4); }
/* **`--laiton-encre` et non `--laiton` pour ce qui est lu.** Mesuré le
   2026-09-17 : le laiton sur un cartouche donne 3,61:1 en thème clair, sous
   le seuil AA de 4,5:1 qu'exige un texte ; `--laiton-encre` en donne 5,46:1.
   Le trait de la bordure, lui, garde le laiton — une bordure n'est pas du
   texte, et 3:1 lui suffit. En thème sombre les deux jetons valent la même
   couleur : rien n'y change. */
.aide-ecran > summary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  border: 1.5px solid var(--laiton); background: var(--plan);
  color: var(--laiton-encre); cursor: pointer; user-select: none; list-style: none;
}
.aide-ecran > summary::-webkit-details-marker { display: none; }
.aide-ecran > summary .ico { width: 1.1rem; height: 1.1rem; }
.aide-ecran > summary:hover { background: var(--laiton); color: var(--sur-accent); }
.aide-ecran > summary:focus-visible { outline: 2px solid var(--laiton); outline-offset: 2px; }
.aide-ecran[open] > summary { background: var(--laiton); border-color: var(--laiton); color: var(--sur-accent); }
.aide-ecran-contenu {
  position: absolute; z-index: 5; top: 2rem; left: 0;
  width: min(26rem, calc(100vw - 2.4rem));
  max-height: min(28rem, calc(100vh - 5rem));
  overflow-y: auto;
  padding: var(--pas-7) var(--pas-8);
  background: var(--plan); border: 1px solid var(--trait-fort); border-radius: var(--rayon-s);
  box-shadow: var(--ombre);
  font-size: var(--texte-m); font-weight: 400; color: var(--encre-2);
}
/* T-K05 : jamais `h4` ici. Ce panneau est inclus **dans** le `<h1>` de
   l'écran (`_aide_ecran.html`, juste après le titre) — un vrai `<h4>` y
   introduirait un sous-titre à l'intérieur du titre de la page, doublement
   faux : il casse la hiérarchie des titres (h4 avant même que le h1 ne se
   referme) et se retrouve récité dans le nom accessible du h1 lui-même.
   `.aide-titre` porte la même présentation, sans être un titre. */
.aide-ecran-contenu .aide-titre { display: block; font-size: var(--texte-s); color: var(--encre); margin: var(--pas-7) 0 var(--pas-2); font-weight: 600; }
.aide-ecran-contenu .aide-titre:first-child { margin-top: 0; }
.aide-ecran-contenu ul { margin: var(--pas-2) 0 0; padding-left: var(--pas-9); }
.aide-ecran-contenu li + li { margin-top: var(--pas-2); }

/* --- Fil d'Ariane -------------------------------------------------------- */

.fil {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pas-3);
  font-size: var(--texte-s);
  color: var(--encre-2);
  margin: 0 0 var(--pas-7);
  padding: 0;
  list-style: none;
}
.fil a { color: var(--encre-2); text-decoration: none; }
.fil a:hover { color: var(--accent); text-decoration: underline; }
.fil li { display: flex; align-items: center; gap: var(--pas-3); }
.fil li + li::before { content: "/"; color: var(--trait-fort); }
.fil [aria-current] { color: var(--encre); font-weight: 500; }

/* ==========================================================================
   Coquille publique (accueil, connexion, inscription, terrain)
   ========================================================================== */

.entete-publique {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pas-8);
  flex-wrap: wrap;
  padding: var(--pas-7) clamp(1rem, 4vw, 2.5rem);
  border-bottom: 1px solid var(--trait);
  background: var(--plan);
}
.entete-publique nav { display: flex; align-items: center; gap: var(--pas-3); flex-wrap: wrap; }
.entete-publique nav a { color: var(--encre); text-decoration: none; padding: var(--pas-4) var(--pas-6); border-radius: var(--rayon-s); font-size: var(--texte-m); }
.entete-publique nav a:hover { background: var(--plan-creux); }
.entete-publique nav a.bouton { color: var(--sur-accent); }
.entete-publique nav a.bouton.secondaire { color: var(--encre); }
.entete-publique form { margin: 0; }

.page-publique { max-width: 46rem; margin: 0 auto; padding: var(--pas-12) clamp(1rem, 4vw, 1.5rem) var(--pas-14); }
/* Une page qui ne porte qu'un panneau — connexion, mot de passe oublié — se
   pose au milieu de la hauteur disponible. Collée en haut d'un grand écran,
   elle donne l'impression d'une page inachevée. */
.page-publique.centree {
  max-width: 28rem;
  min-height: calc(100vh - 14rem);
  display: flex; flex-direction: column; justify-content: center;
}
.centre-texte { text-align: center; }
.panneau + .centre-texte { margin-top: var(--pas-9); }

.pied {
  border-top: 1px solid var(--trait);
  background: var(--plan);
  padding: var(--pas-11) clamp(1rem, 4vw, 2.5rem);
  color: var(--encre-2);
  font-size: var(--texte-m);
}
.pied-contenu {
  max-width: 72rem; margin: 0 auto;
  display: flex; flex-wrap: wrap; gap: var(--pas-8) var(--pas-11); justify-content: space-between; align-items: center;
}
.pied a { color: var(--encre-2); }
.pied-copyright {
  max-width: 72rem; margin: var(--pas-8) auto 0; padding-top: var(--pas-8);
  border-top: 1px solid var(--trait);
  font-size: var(--texte-s); text-align: center;
}

.panneau {
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-10);
  box-shadow: var(--ombre);
}
.panneau > h1 { margin-bottom: var(--pas-3); }
/* Un panneau de confirmation ou d'erreur pose une icône d'état avant son
   titre ; celui-ci a alors besoin d'un espace au-dessus, en plus de
   `margin-bottom`. */
.panneau svg + h1 { margin: var(--pas-6) 0 var(--pas-3); }

/* ==========================================================================
   Coquille applicative : rail à gauche sur grand écran, barre en bas sur
   téléphone. Le pouce atteint la barre basse d'une seule main.
   ========================================================================== */

.barre-haute {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--pas-7);
  padding: var(--pas-6) clamp(.8rem, 3vw, 1.4rem);
  background: color-mix(in srgb, var(--plan) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--trait);
}
.barre-haute .marque { margin-right: auto; min-width: 0; }
.barre-haute .compte {
  display: flex; align-items: center; gap: var(--pas-5);
  font-size: var(--texte-m); color: var(--encre-2);
}
.barre-haute form { margin: 0; }
/* Au téléphone, la loupe et la déconnexion gardent leurs 44 px (S-128) :
   l'écart se resserre, les initiales du compte s'effacent comme la qualité
   (le compte se lit dans « Mes paramètres »), la marque garde au moins son
   logo — sans quoi le badge « bêta » venait le recouvrir — et le logo d'une
   organisation se réduit à la largeur du nôtre (rejeu S-128 du 2026-10-10,
   relecture mesurée à 320, 360 et 479 px). */
@media (max-width: 29.99rem) {
  .barre-haute { gap: var(--pas-4); }
  .barre-haute .compte { display: none; }
  .barre-haute .marque { min-width: 1.75rem; }
  .marque img.logo-organisation { max-width: 1.75rem; object-fit: contain; }
  /* Dans l'application, « bêta » quitte la barre (Augustin, 2026-10-11) ; à
     320 px « appli » mordait encore de 6 à 9 px sur la loupe : le badge se
     resserre. `.marque` devant, sinon la règle de base, plus bas, l'emporte. */
  .marque .mention-appli { margin-left: 0; padding: var(--pas-1) var(--pas-2); }
}
.langue select {
  width: auto; padding: var(--pas-3) var(--pas-10) var(--pas-3) var(--pas-6);
  font-size: var(--texte-s); background-color: transparent;
  border-color: var(--trait);
}
.jeton-compte {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.9rem; height: 1.9rem; border-radius: 50%;
  background: var(--accent-voile); color: var(--accent-fort);
  font-size: var(--texte-s); font-weight: 700;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
.bouton-icone {
  display: inline-flex; align-items: center; justify-content: center;
  /* 2,75 rem = 44 px : une cible tactile (S-128) — déconnexion, pagination.
     `flex: none` : dans la barre haute à 360 px, la loupe rétrécissait à 26 px
     comme n'importe quel élément flexible (rejeu S-128 du 2026-10-10). */
  width: 2.75rem; height: 2.75rem; padding: 0; flex: none;
  background: transparent; border: 1px solid transparent; border-radius: var(--rayon-s);
  color: var(--encre-2); cursor: pointer;
}
.bouton-icone:hover { background: var(--plan-creux); color: var(--encre); }

.corps { display: flex; align-items: flex-start; }

.rail {
  display: none;
  position: sticky;
  top: 3.4rem;
  flex: none;
  width: 14.5rem;
  height: calc(100vh - 3.4rem);
  padding: var(--pas-8) var(--pas-6);
  border-right: 1px solid var(--trait);
  overflow-y: auto;
}
.rail .oeil { padding: 0 var(--pas-6); margin: var(--pas-10) 0 var(--pas-4); }
.rail .oeil:first-child { margin-top: 0; }
.rail a,
.rail .entree-rail {
  display: flex;
  align-items: center;
  gap: var(--pas-6);
  padding: var(--pas-5) var(--pas-6);
  border-radius: var(--rayon-s);
  color: var(--encre);
  text-decoration: none;
  font-size: var(--texte-m);
  font-weight: 500;
  transition: background-color .12s ease, color .12s ease;
}
.rail a .ico,
.rail .entree-rail .ico { color: var(--encre-2); }
.rail a:hover,
.rail .entree-rail:hover { background: var(--plan-creux); }
/* Lot M2.1 : « Menu complet » est un geste (POST), donc un bouton — qui doit
   se lire comme une entrée du rail, pas comme un bouton de formulaire. */
.rail .entree-rail {
  width: 100%;
  border: 0;
  background: transparent;
  font-family: inherit;
  text-align: start;
  cursor: pointer;
}
.rail a[aria-current="page"] {
  background: var(--accent-voile);
  color: var(--accent-fort);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--accent);
}
.rail a[aria-current="page"] .ico { color: var(--accent); }
.rail .compteur { margin-left: auto; font-family: var(--mono); font-size: var(--texte-s); color: var(--encre-2); }

/* `portail:plus` (§ 13 de l'audit du 2026-08-26) : le même rail que la
   colonne de gauche, mais à plat dans le contenu de la page — c'est la seule
   façon d'atteindre sur téléphone les rubriques qu'un rail masqué et une
   barre de cinq icônes ne pouvaient pas montrer. Aucune règle de `.rail`
   n'est reprise ici : seules celles qui la sortaient de la colonne fixe. */
.rail-a-plat .rail {
  display: block;
  position: static;
  width: auto;
  height: auto;
  padding: 0;
  border-right: none;
  overflow: visible;
}

.conteneur {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 76rem;
  margin: 0 auto;
  padding: var(--pas-9) clamp(.9rem, 3vw, 1.6rem) var(--pas-15);
}

.barre-basse {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 20;
  display: flex;
  justify-content: space-around;
  padding: var(--pas-3) var(--pas-3) calc(var(--pas-3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--plan) 94%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--trait);
}
.barre-basse a {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--pas-1);
  padding: var(--pas-3) var(--pas-1);
  border-radius: var(--rayon-s);
  color: var(--encre-2);
  text-decoration: none;
  font-size: var(--texte-xs);
  font-weight: 600;
}
.barre-basse a .ico { width: 1.35rem; height: 1.35rem; }
.barre-basse a[aria-current="page"] { color: var(--accent); }

@media (min-width: 62rem) {
  .rail { display: block; }
  .barre-basse { display: none; }
  .conteneur { padding-bottom: var(--pas-14); }
}

/* --- Section de page ----------------------------------------------------- */

.section-tete {
  display: flex;
  /* **Centré, et non aligné sur la ligne de base.** À droite d'un titre de
     section se tient maintenant un bouton — une boîte rembourrée, pas une
     ligne de texte : l'aligner sur la ligne de base du `h2` le ferait
     dépasser vers le bas. Le lien qui s'y trouvait avant s'accommodait des
     deux. */
  align-items: center;
  justify-content: space-between;
  gap: var(--pas-6) var(--pas-9);
  flex-wrap: wrap;
  margin: var(--pas-12) 0 var(--pas-7);
}
.section-tete h2 { margin: 0; }
/* `.lien-suite` marque un renvoi vers un écran de configuration — d'abord posé
   à côté d'un titre de section, réutilisé partout où un bloc mène ailleurs
   sans que ce soit sinon annoncé (ex. une vignette dans une carte). */
.lien-suite { font-size: var(--texte-m); font-weight: 600; text-decoration: none; white-space: nowrap; color: var(--accent); }
.lien-suite:hover { text-decoration: underline; }
/* Posé sur un `<button>` (un geste qui se lit comme un renvoi : « Retirer de la
   couverture »), il perd le fond du navigateur — en thème sombre, ce fond clair
   sous l'accent tombait à 1,94:1 (S-126, 2026-10-10). */
button.lien-suite { background: none; border: 0; padding: 0; font-family: inherit; cursor: pointer; }
/* Deux liens de suite dans un en-tête de section : ils restent groupés à
   droite plutôt que de se séparer aux deux bouts de la ligne. */
.liens-section { display: flex; gap: var(--pas-8); flex-wrap: wrap; }

/* Un bouton qui doit se lire comme un lien inline — dans une cellule de
   tableau, à côté d'un vrai lien. */
.bouton-texte {
  background: none; border: 0; padding: 0; margin-left: var(--pas-6);
  font: inherit; font-size: .9em; color: var(--accent); cursor: pointer;
  text-decoration: underline; text-underline-offset: .18em;
}
.bouton-texte:hover { color: var(--laiton-encre); }

/* Un `<form>` posé au fil du texte ou dans une cellule de tableau, à côté
   d'un lien — sans quoi il force un retour à la ligne. */
.forme-en-ligne { display: inline; }

/* Filtres : des onglets, pas une phrase de liens séparés par des points. */
.filtres { display: flex; flex-wrap: wrap; gap: var(--pas-3); margin: 0 0 var(--pas-9); }
.filtres a {
  padding: var(--pas-3) var(--pas-6);
  border: 1px solid var(--trait);
  border-radius: 2rem;
  background: var(--plan);
  color: var(--encre-2);
  font-size: var(--texte-s);
  font-weight: 500;
  text-decoration: none;
}
.filtres a:hover { border-color: var(--trait-fort); color: var(--encre); }
.filtres a[aria-current] { background: var(--encre); border-color: var(--encre); color: var(--papier); }

/* Filtres à listes déroulantes, quand les valeurs sont trop nombreuses pour
   tenir en onglets — le portefeuille de clients et de sites d'un compte.
   Alignés sur une ligne au large, empilés sur un téléphone. */
.filtres-choix {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--pas-6) var(--pas-7);
  margin: 0 0 var(--pas-9);
  padding: var(--pas-7) var(--pas-7);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  background: var(--plan);
}
.filtres-choix p { margin: 0; }
.filtres-choix label {
  display: block;
  margin-bottom: var(--pas-2);
  color: var(--encre-2);
  font-size: var(--texte-s);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.filtres-choix select { min-width: 12rem; max-width: 100%; }
.filtres-actions { display: flex; align-items: center; gap: var(--pas-7); }

/* Pagination des listes du portail (T-D08, `_pagination.html`). Deux boutons
   d'icône, un état textuel entre les deux — jamais une suite de numéros de
   page : une plaque compte des rangées, elle ne les numérote pas une à une.
   Le grisage aux bornes reprend celui du `.lien-suite` ci-dessus : le clic ne
   ferait rien de plus qu'un rechargement à l'identique. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--pas-6);
  margin: var(--pas-9) 0 0;
}
.pagination-etat {
  color: var(--encre-2);
  font-size: var(--texte-s);
  font-variant-numeric: tabular-nums;
}
.pagination .bouton-icone[aria-disabled="true"] {
  color: var(--encre-2);
  opacity: .4;
  cursor: not-allowed;
  pointer-events: none;
}

/* --- Compteurs du tableau de bord ---------------------------------------- */

.compteurs {
  display: grid;
  gap: var(--pas-7);
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  margin-bottom: var(--pas-10);
}
.compteur-carte {
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-7) var(--pas-8);
  text-decoration: none;
  color: inherit;
  display: block;
}
a.compteur-carte:hover { border-color: var(--accent); }
.compteur-carte .oeil { display: flex; align-items: center; gap: var(--pas-3); }
.compteur-carte .chiffre { margin-top: var(--pas-1); }
/* Le code d'appairage de la passerelle se lit à distance, recopié à la main
   sur un boîtier : c'est de l'affichage, pas du texte courant. Variante
   nommée plutôt qu'un `style=` dans le gabarit. */
.chiffre-code { font-size: var(--texte-7xl); letter-spacing: .12em; }
.compteur-carte.alerte-rouge { border-left: 3px solid var(--rouge); }
.compteur-carte.alerte-rouge .chiffre { color: var(--rouge); }
.compteur-carte.alerte-ambre { border-left: 3px solid var(--ambre); }
.compteur-carte.alerte-ambre .chiffre { color: var(--ambre); }

/* --- Quotas d'offre (lot B7, CdC facturation § 8.2) ---------------------- */
/* Le grisage n'est qu'une annonce — le contrôle qui fait foi reste côté
   serveur (`offres.controles.peut_creer`). Ces deux composants n'affichent
   que ce que la vue a déjà tranché : un `Verdict`, jamais recalculé ici. */

/* Plusieurs jauges sur un même écran (« Mes prestataires » : PRESTATAIRES et
   CONTRATS) se lisent côte à côte plutôt qu'empilées. */
.jauges-quota { display: flex; flex-wrap: wrap; gap: 0 var(--pas-10); }

/* « 2 / 3 prestataires » — sobre, chasse fixe sur les valeurs lues. */
.jauge-quota {
  color: var(--encre-2);
  font-size: var(--texte-m);
  margin: 0 0 var(--pas-7);
}
.jauge-quota .mono { color: var(--encre); font-size: var(--texte-l); }
/* Au plafond ou en dépassement hérité (CdC § 8.4) : on le constate, on ne le
   masque jamais — un simple rappel de ton, pas une alerte de statut (celle-ci
   reste réservée aux pastilles d'état, `CLAUDE.md` § « Un état décide de son
   ton »). */
.jauge-quota-pleine .mono { color: var(--ambre); }

/* Message de refus + appel à l'action, à côté d'un bouton grisé. */
.avis-quota {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--pas-5);
  color: var(--ambre);
  font-size: var(--texte-m);
  margin: var(--pas-6) 0 0;
}
.avis-quota .ico { color: var(--ambre); align-self: center; }

/* Un bouton de soumission grisé reste visible — jamais masqué — avec son
   infobulle : c'est l'annonce, pas le rempart. `[aria-disabled]` couvre les
   liens stylés en bouton (`<a>` n'a pas d'attribut `disabled` natif) ; le
   pointeur y est coupé pour que le geste rejoue la même annonce partout. */
.bouton:disabled,
.bouton[aria-disabled="true"] {
  background: var(--plan-creux);
  color: var(--encre-2);
  border-color: var(--trait);
  cursor: not-allowed;
  pointer-events: none;
}
.bouton:disabled:hover,
.bouton[aria-disabled="true"]:hover { background: var(--plan-creux); color: var(--encre-2); }
/* Sur un écran tactile, pas de survol, donc pas d'infobulle : le toucher
   reste possible, et `templates/_comportements.html` écrit la raison (`title`)
   en clair sous le bouton au lieu de suivre le lien (recette de
   l'application Android, 2026-09-30). */
@media (hover: none) {
  .bouton[aria-disabled="true"] { pointer-events: auto; }
}

/* Un envoi en cours (M4.6, `templates/_comportements.html`) : le bouton touché
   se grise et le curseur attend, pour qu'on voie que le geste a été pris —
   sans l'attribut natif de désactivation, qui retirerait son nom et sa valeur
   de l'envoi. Posé avec `aria-busy="true"`, retiré au retour par
   l'historique ou au bout de dix secondes. */
.envoi-en-cours { cursor: wait; }
.bouton.envoi-en-cours,
.bouton.envoi-en-cours:hover {
  background: var(--plan-creux);
  color: var(--encre-2);
  border-color: var(--trait);
}

/* Une page qui se charge : un filet laiton en tête d'écran, posé par
   `_comportements.html` à la sortie de la page. Sans lui, l'application —
   qui n'a pas de barre de progression — ne montrait rien pendant les
   secondes d'un tableau de bord lourd, et l'on touchait deux fois. */
html.chargement::before {
  content: "";
  position: fixed;
  inset: 0 0 auto;
  height: var(--pas-2);
  background: var(--laiton);
  z-index: 1000;
  animation: chargement 1.2s ease-in-out infinite alternate;
}
@keyframes chargement {
  from { opacity: .35; }
  to { opacity: 1; }
}

/* Le même grisage sur les liens qui ne sont pas stylés en bouton (lot B0) :
   le « Composant » de l'arbre du parc. Le pointeur y est coupé comme sur un
   bouton, et pour la même raison : la vue refuserait de toute façon un geste
   précis — créer au plafond — et l'infobulle porte déjà l'annonce. À ne pas
   confondre avec le grisage d'une fonctionnalité absente, juste en dessous,
   qui laisse au contraire le lien pleinement cliquable parce qu'il mène à une
   explication.
   La règle citait aussi les « lien-suite » d'en-tête de section : ils sont
   devenus des boutons le 2026-09-21, et le sélecteur reste ici pour ceux qui
   renaîtraient ailleurs — le comportement est le même des deux côtés. */
.lien-suite[aria-disabled="true"],
.arbre .ajout[aria-disabled="true"] {
  color: var(--encre-2);
  border-color: var(--trait);
  cursor: not-allowed;
  pointer-events: none;
}

/* L'annonce d'un axe de facturation (lot B0) : elle n'interdit rien, elle
   chiffre. Ni ambre ni rouge, donc — vert, ambre et rouge disent un état
   partout ailleurs, et un prix n'en est pas un. Le laiton est la teinte que
   le projet réserve à ce qui informe sans juger. */
.avis-quota.avis-annonce .ico { color: var(--laiton); }

/* Le texte à copier (FA-3) : une plaque en chasse fixe, lue caractère par
   caractère ; elle se replie à la largeur de l'écran (jamais de défilement
   horizontal de la page) et se sélectionne d'un geste. */
.a-copier {
  font-family: var(--mono); font-size: var(--texte-s);
  background: var(--plan-creux); border: 1px solid var(--trait); border-radius: var(--rayon-s);
  padding: var(--pas-5) var(--pas-7); margin: 0 0 var(--pas-6);
  white-space: pre-wrap; overflow-wrap: anywhere; user-select: all;
}

/* Fonctionnalité absente de l'offre (B6) : à la différence d'un quota au
   plafond, le lien reste pleinement cliquable — c'est `pointer-events` qui
   distingue les deux : ici rien ne le coupe, la destination explique déjà
   pourquoi (`offres.controles.message_fonctionnalite_absente`, posé en
   infobulle). Un lien qui disparaît se lit comme un bug ; un lien grisé qui
   mène à une explication se lit comme une offre. */
.bouton-verrouille { opacity: .55; }
.bouton-verrouille:hover { opacity: .8; }
.lien-verrouille { color: var(--encre-2); }
.lien-verrouille:hover { color: var(--accent); }

/* ==========================================================================
   Agenda du planning
   ========================================================================== */

.agenda-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--pas-6);
  margin-bottom: var(--pas-7);
}
.agenda-tete .mois-courant {
  font-stretch: 112%; font-weight: 700; font-size: var(--texte-xl);
  text-transform: capitalize; letter-spacing: -.01em;
}
.navigation-mois { display: flex; align-items: center; gap: var(--pas-3); }
.navigation-mois a {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2.1rem; height: 2.1rem; padding: 0 var(--pas-6);
  border: 1px solid var(--trait); border-radius: var(--rayon-s);
  color: var(--encre); text-decoration: none; background: var(--plan);
  font-size: var(--texte-m); font-weight: 600;
}
.navigation-mois a:hover { border-color: var(--accent); color: var(--accent); }

.agenda {
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  overflow: hidden;
}
.agenda-jours,
.agenda-grille { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.agenda-jours { background: var(--plan-creux); border-bottom: 1px solid var(--trait); }
.agenda-jours span {
  padding: var(--pas-4) var(--pas-5);
  font-size: var(--texte-xs); font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--encre-2); text-align: center;
}
.agenda-case {
  min-height: 6.6rem;
  padding: var(--pas-3) var(--pas-3) var(--pas-5);
  border-right: 1px solid var(--trait);
  border-bottom: 1px solid var(--trait);
  display: flex; flex-direction: column; gap: var(--pas-2);
}
.agenda-case:nth-child(7n) { border-right: 0; }
.agenda-grille > .agenda-case:nth-last-child(-n+7) { border-bottom: 0; }
.agenda-case.hors-mois { background: var(--papier); }
/* Assez effacé pour qu'on voie où commence le mois, assez contrasté pour
   rester une date lisible : le filet de séparation ne se lit pas, un
   quantième si. */
.agenda-case.hors-mois .numero { color: color-mix(in srgb, var(--encre-2) 80%, var(--papier)); }
.agenda-case.week-end { background: color-mix(in srgb, var(--plan-creux) 40%, var(--plan)); }
/* Le quantième reste calé à droite ; le « + » de planification occupe le coin
   gauche, laissé libre. Une ligne de tête plutôt qu'un positionnement absolu :
   la case est déjà une colonne flex, et un élément détaché passerait sous les
   événements dès que la case se remplit. */
.case-tete { display: flex; align-items: center; gap: var(--pas-2); }
.agenda-case .numero {
  font-family: var(--mono); font-size: var(--texte-s); color: var(--encre-2);
  align-self: flex-end; line-height: 1.2;
  margin-left: auto;
}
/* Discret mais jamais invisible : le geste doit être découvrable sur une case
   vide, sans pour autant faire concurrence aux événements. Pas de survol seul
   — sur un téléphone, il n'y en a pas. */
.ajout-jour {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.1rem; height: 1.1rem;
  border: 1px solid var(--trait); border-radius: 50%;
  color: var(--encre-2); background: transparent;
  font-size: var(--texte-s); line-height: 1; text-decoration: none;
  opacity: .55;
}
.ajout-jour:hover,
.ajout-jour:focus-visible {
  opacity: 1; color: var(--laiton); border-color: var(--laiton);
}
.agenda-case.aujourdhui { box-shadow: inset 0 0 0 2px var(--accent); }
.agenda-case.aujourdhui .numero {
  color: var(--sur-accent); background: var(--accent-plein);
  border-radius: 1rem; padding: var(--pas-1) var(--pas-4); font-weight: 600;
}
.evenement {
  display: block;
  padding: var(--pas-2) var(--pas-3);
  border-left: 3px solid var(--encre-2);
  border-radius: 2px;
  background: var(--gris-voile);
  color: var(--encre);
  font-size: var(--texte-xs);
  line-height: 1.3;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.evenement:hover { text-decoration: underline; }
.evenement.e-rouge { background: var(--rouge-voile); border-left-color: var(--rouge); color: var(--rouge); }
.evenement.e-ambre { background: var(--ambre-voile); border-left-color: var(--ambre); color: var(--ambre); }
.evenement.e-vert { background: var(--vert-voile); border-left-color: var(--vert); color: var(--vert); }
.evenement.e-bleu { background: var(--accent-voile); border-left-color: var(--accent); color: var(--accent-fort); }
.evenement.e-gris { background: var(--gris-voile); border-left-color: var(--encre-2); color: var(--encre-2); }
/* 2026-10-21 : ce qui est derrière nous — fait, dispensé (« Pas cette fois »),
   annulé — se barre, dans le ton de son état (`barree_au_planning`, `ton`) :
   vert pour le fait, gris pour ce qui n'a pas eu à l'être. */
.barre { text-decoration: line-through; }
.evenement.barre:hover { text-decoration: line-through underline; }
/* Lot F3 : le rail gauche dit qui a saisi la tâche (gestionnaire, prestataire,
   client) ; l'état, lui, reste dans le fond et la couleur du texte. Les deux
   ne se disputent donc jamais le même pixel. */
.evenement.o-laiton { border-left-color: var(--laiton); }
.evenement.o-acier { border-left-color: var(--acier); }
.evenement.o-ardoise { border-left-color: var(--ardoise); }
.legende .c-laiton { background: var(--laiton); }
.legende .c-acier { background: var(--acier); }
.legende .c-ardoise { background: var(--ardoise); }
/* Le laiton distingue ce qu'un humain a posé de ce qu'un programme réclame.
   Vert, ambre et rouge disent déjà un état partout ailleurs : les emprunter
   ici ferait dire deux choses à la même couleur. */
.evenement.e-laiton {
  background: color-mix(in srgb, var(--laiton) 14%, var(--plan));
  border-left-color: var(--laiton);
  color: var(--laiton-encre);
}
.evenement .heure { font-family: var(--mono); font-size: var(--texte-xs); opacity: .85; }
/* L'heure d'un passage, dans la liste du jour : en chasse fixe comme toute
   valeur lue caractère par caractère, et assez discrète pour que le geste
   reste ce qu'on lit en premier. */
/* Bande de conformité : une case par période close d'un contrat, verte si un
   passage a eu lieu. Une note globale ne se plaide pas ; « ce trimestre-là n'a
   pas eu de passage », si. */
.bande-conformite {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin: var(--pas-6) 0 var(--pas-2);
}
.case-periode {
  width: 1.15rem;
  height: .55rem;
  border-radius: 2px;
  background: var(--trait);
}
.case-periode.vert { background: var(--vert); }
.case-periode.ambre { background: var(--ambre); }
.case-periode.rouge { background: var(--rouge); }
.case-periode.gris { background: var(--encre-2); }

/* Cases à cocher en colonne, pour un périmètre qu'on ouvre ou qu'on restreint.
   Le libellé porte la case : la cible du clic fait toute la largeur. */
.case {
  display: flex;
  align-items: center;
  gap: var(--pas-5);
  padding: var(--pas-2) 0;
  cursor: pointer;
}
.case input { margin: 0; }
fieldset {
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-6) var(--pas-7);
  margin: 0;
}
fieldset legend {
  padding: 0 var(--pas-3);
  color: var(--encre-2);
  font-size: var(--texte-s);
  letter-spacing: .04em;
  text-transform: uppercase;
}
/* Une photo, prise à l'instant ou choisie (`_deux_sources_photo.html`,
   2026-10-03 ; `terrain/_champ_photo.html`, recette mobile du 2026-10-05) :
   les deux sources côte à côte, chacune un `_choix_fichier.html`. */
.sources-photo { display: grid; gap: var(--pas-6); margin-bottom: var(--pas-9); }
.sources-photo p { margin-bottom: 0; }
.sources-photo label { display: inline-flex; align-items: center; gap: var(--pas-3); }
.sources-photo p.aide { margin-top: 0; }
.choix-fichiers { display: flex; flex-wrap: wrap; gap: var(--pas-5); align-items: flex-start; }
/* Choisir un fichier (`_choix_fichier.html`, 6.4.11) : le champ natif reste
   là pour le clavier et le lecteur d'écran, masqué à l'œil seul (les
   propriétés de `.masque-visuel`) ; son `<label>` est le bouton, plein pour
   l'appareil photo, secondaire pour le fichier. Le focus du champ se voit sur
   son bouton. Spécificité au-dessus de `.terrain label` et `.terrain input`. */
.choix-fichier { display: inline-flex; flex-direction: column; align-items: flex-start; max-width: 100%; }
.choix-fichier > input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
  border: 0;
}
.choix-fichier > label.bouton { display: inline-flex; margin: 0; font-weight: 600; }
.choix-fichier > input[type="file"]:focus-visible + label {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.choix-fichier-nom {
  font-family: var(--mono);
  font-size: var(--texte-s);
  color: var(--encre-2);
  overflow-wrap: anywhere;
}
.choix-fichier-nom:not(:empty) { margin-top: var(--pas-2); }
/* Ce qui n'appartient qu'à une entreprise : un liseré qui dit « ceci ne sort
   pas d'ici », sans crier. */
.bloc-texte.interne {
  border-left: 3px solid var(--laiton);
  background: var(--plan);
}

/* Le temps de route entre deux arrêts d'une tournée : une respiration entre
   les cartes, pas une ligne de tableau. */
.trajet {
  display: flex;
  align-items: center;
  gap: var(--pas-4);
  margin: var(--pas-5) 0 var(--pas-5) var(--pas-9);
  padding-left: var(--pas-7);
  border-left: 2px dashed var(--trait-fort);
  color: var(--encre-2);
  font-size: var(--texte-s);
}
.arret { margin-bottom: var(--pas-2); }

/* La couleur d'agenda d'un intervenant : une pastille, pas un aplat — elle
   distingue une tournée sans repeindre la ligne. */
.jeton-couleur {
  display: inline-block;
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  margin-right: var(--pas-3);
  vertical-align: baseline;
}

/* Une ligne sortie reste lisible, mais cesse de réclamer l'attention. */
tr.sortie { opacity: .55; }

/* Une liste sans puces ni marges, pour énumérer sans meubler. */
.liste-plate { list-style: none; margin: var(--pas-4) 0; padding: 0; }
.liste-plate li { padding: var(--pas-3) 0; border-bottom: 1px solid var(--trait); }
.liste-plate li:last-child { border-bottom: 0; }

.jour-liste .heure {
  font-family: var(--mono);
  font-size: var(--texte-s);
  color: var(--encre-2);
  margin-right: var(--pas-3);
}
.et-plus { font-size: var(--texte-xs); font-weight: 600; color: var(--accent); text-decoration: none; padding-left: var(--pas-3); }
.et-plus:hover { text-decoration: underline; color: var(--accent-fort); }

.legende {
  display: flex; flex-wrap: wrap; gap: var(--pas-5) var(--pas-9);
  margin-top: var(--pas-7); font-size: var(--texte-s); color: var(--encre-2);
}
.legende span { display: inline-flex; align-items: center; gap: var(--pas-3); }
.legende i { width: .7rem; height: .7rem; border-radius: 2px; display: inline-block; }
.legende i.c-accent { background: var(--accent); }
.legende i.c-ambre { background: var(--ambre); }
.legende i.c-rouge { background: var(--rouge); }
.legende i.c-laiton { background: var(--laiton); }
.legende i.c-vert { background: var(--vert); }
.legende i.c-gris { background: var(--encre-2); }

/* Sur téléphone, les pastilles d'événement deviennent des barres : sept
   colonnes ne laissent pas la place au texte, mais la densité reste lisible. */
@media (max-width: 45.99rem) {
  .agenda-case { min-height: 4.4rem; padding: var(--pas-2) var(--pas-2) var(--pas-3); }
  .evenement { font-size: 0; padding: 0; height: .45rem; border-left-width: 0; border-radius: 1px; }
  .evenement .heure { display: none; }
  .evenement.e-rouge { background: var(--rouge); }
  .evenement.e-ambre { background: var(--ambre); }
  .evenement.e-vert { background: var(--vert); }
  .evenement.e-bleu { background: var(--accent-plein); }
  .evenement.e-laiton { background: var(--laiton); }
  .evenement.e-gris { background: var(--encre-2); }
  .agenda-case .numero { font-size: var(--texte-xs); }
  .et-plus { font-size: var(--texte-xxs); padding-left: var(--pas-1); }
  /* Le « + » reste touchable au doigt, mais ne mange pas la case : la barre
     d'événements passe à .45rem, lui à la même échelle. */
  .ajout-jour { width: 1rem; height: 1rem; font-size: var(--texte-xs); }
}

/* --- Liste d'agenda ------------------------------------------------------ */

.jour-liste { display: flex; gap: var(--pas-7); padding: var(--pas-7) 0; border-bottom: 1px solid var(--trait); }
.jour-liste:last-child { border-bottom: 0; }
.jour-liste .date-bloc {
  flex: none; width: 3.1rem; text-align: center;
  font-family: var(--mono); line-height: 1.1;
}
.jour-liste .date-bloc .j { font-size: var(--texte-3xl); font-weight: 600; display: block; }
.jour-liste .date-bloc .m { font-size: var(--texte-xxs); text-transform: uppercase; color: var(--encre-2); letter-spacing: .08em; }
.jour-liste.est-aujourdhui .date-bloc { color: var(--accent); }
.jour-liste ul { list-style: none; margin: 0; padding: 0; flex: 1 1 auto; display: grid; gap: var(--pas-3); min-width: 0; }

/* ==========================================================================
   Assistant de configuration
   ========================================================================== */

.entete-assistant {
  display: flex; flex-wrap: wrap; gap: var(--pas-5) var(--pas-8);
  align-items: center; justify-content: space-between;
  margin-bottom: var(--pas-5);
}
.entete-assistant .oeil { margin: 0; }
.fil-etapes { display: flex; flex-wrap: wrap; gap: var(--pas-3); margin: 0 0 var(--pas-6); list-style: none; padding: 0; }
.fil-etapes li {
  display: inline-flex; align-items: center; gap: var(--pas-3);
  font-size: var(--texte-s); font-weight: 600;
  padding: var(--pas-2) var(--pas-6); border-radius: 2rem;
  background: var(--gris-voile); color: var(--encre-2);
}
.fil-etapes li .num { font-family: var(--mono); }
.fil-etapes li.faite { background: var(--vert-voile); color: var(--vert); }
.fil-etapes li.courante { background: var(--accent-plein); color: var(--sur-accent); }
.jauge { height: .3rem; background: var(--gris-voile); border-radius: 1rem; overflow: hidden; margin-bottom: var(--pas-10); }
.jauge div { height: 100%; background: var(--accent-plein); }

.choix { display: grid; gap: var(--pas-5); grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); margin-bottom: var(--pas-10); }
.choix label {
  display: flex; gap: var(--pas-6); align-items: flex-start;
  padding: var(--pas-6) var(--pas-7);
  background: var(--plan);
  border: 1px solid var(--trait); border-radius: var(--rayon-s);
  cursor: pointer; font-weight: 400;
  transition: border-color .12s ease, background-color .12s ease;
}
.choix label:hover { border-color: var(--accent); }
.choix label:has(input:checked) { border-color: var(--accent); background: var(--accent-voile); }
.choix input { width: auto; margin-top: var(--pas-3); }
.choix strong { font-weight: 600; }
.choix .obligatoire { font-size: var(--texte-xs); color: var(--ambre); font-weight: 600; }
.choix .conseil { font-size: var(--texte-s); color: var(--encre-2); }

.champ-recherche { position: relative; }
.champ-recherche .suggestions {
  position: absolute; z-index: 5; top: 100%; left: 0; right: 0;
  margin: var(--pas-2) 0 0; padding: var(--pas-3);
  list-style: none;
  background: var(--plan);
  border: 1px solid var(--trait); border-radius: var(--rayon-s);
  box-shadow: var(--ombre);
  max-height: 14rem; overflow-y: auto;
}
.champ-recherche .suggestions li { padding: var(--pas-5) var(--pas-6); border-radius: var(--rayon-s); cursor: pointer; }
.champ-recherche .suggestions li:hover,
.champ-recherche .suggestions li:focus { background: var(--accent-voile); }

/* ==========================================================================
   Écran de terrain — gros doigts, une main, plein soleil
   ========================================================================== */

.terrain { max-width: 34rem; margin: 0 auto; }
.terrain fieldset {
  border: 1px solid var(--trait);
  background: var(--plan);
  border-radius: var(--rayon);
  padding: var(--pas-8);
  margin: 0 0 var(--pas-8);
}
.terrain legend {
  display: inline-flex; align-items: center; gap: var(--pas-4);
  font-weight: 700; font-stretch: 112%; font-size: var(--texte-l);
  padding: 0 var(--pas-4);
}
.terrain label { display: block; margin-bottom: var(--pas-3); font-weight: 500; }
.terrain .champ { margin-bottom: var(--pas-7); }
.terrain .champ:last-child { margin-bottom: 0; }
.terrain textarea, .terrain input, .terrain select { padding: var(--pas-7); font-size: var(--texte-l); }
.terrain textarea { min-height: 7.5rem; }
.terrain .bornes { color: var(--encre-2); font-weight: 400; font-size: var(--texte-m); }
.terrain .principal {
  width: 100%;
  padding: var(--pas-8);
  font-size: var(--texte-l);
  background: var(--vert);
  border-color: var(--vert);
  color: var(--sur-vert);
}
.terrain .principal:hover { background: color-mix(in srgb, var(--vert) 84%, #000); }
.codes {
  border: 1px solid var(--rouge);
  border-left-width: 3px;
  background: var(--rouge-voile);
  padding: var(--pas-7) var(--pas-8);
  border-radius: var(--rayon-s);
  margin-bottom: var(--pas-8);
}
.codes ul { list-style: none; padding: 0; margin: var(--pas-6) 0 0; display: grid; gap: var(--pas-3); }
.codes li { display: flex; gap: var(--pas-5); align-items: baseline; justify-content: space-between; }
.codes .valeur { font-family: var(--mono); font-weight: 600; font-size: var(--texte-l); letter-spacing: .04em; }

/* --- Étiquettes QR ------------------------------------------------------- */

.planche { display: flex; flex-wrap: wrap; gap: var(--pas-7); }
.etiquette {
  width: 8.5cm;
  border: 1px dashed #9aa3a0;
  border-radius: var(--rayon-s);
  padding: var(--pas-7);
  text-align: center;
  background: #fff;
  color: #111;
  page-break-inside: avoid;
}
.etiquette .marque-etiquette {
  display: flex; align-items: center; justify-content: center; gap: var(--pas-3);
  font-size: var(--texte-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: #666; margin-bottom: var(--pas-4);
}
.etiquette img { width: 4.5cm; height: 4.5cm; }
.etiquette .libelle { font-weight: 700; font-stretch: 112%; font-size: var(--texte-l); margin: var(--pas-3) 0 0; }
.etiquette .lieu { color: #555; font-size: var(--texte-m); margin: var(--pas-1) 0 var(--pas-4); }
.etiquette .code {
  font-family: var(--mono); font-size: var(--texte-xl);
  letter-spacing: .08em; font-weight: 600;
}

@media print {
  .barre-haute, .barre-basse, .rail, .entete-publique, .pied, .consigne,
  .no-print, .sans-impression, .fil { display: none !important; }
  /* Un bouton est un geste, et un geste ne s'imprime pas (S-129, fiche d'un site
     et planning : « Signaler un problème », « Planifier »… sur le papier). */
  .bouton, button { display: none !important; }
  body { background: #fff; }
  .conteneur, .page-publique { padding: 0; max-width: none; }
}

/* La zone qui réduit une longue liste (`data-filtrable`, `_comportements.html`) :
   collée au-dessus de sa liste, de la même largeur. */
.filtre-liste {
  display: block;
  width: 100%;
  margin-bottom: var(--pas-2);
}

/* « appli » à côté du logo, dans l'application mobile seulement : dit où l'on
   est, sans rien changer à ce qu'on peut faire. Laiton, la couleur de la
   plaque — ni un état, ni une alerte. */
.mention-appli {
  display: inline-block;
  margin-left: var(--pas-4);
  padding: var(--pas-1) var(--pas-4);
  border: 1px solid var(--laiton);
  border-radius: 999px;
  color: var(--laiton-encre);
  font-size: var(--texte-xs);
  font-weight: 600;
  letter-spacing: .02em;
}

/* La mention « bêta » — **ambre, pas rouge**. Rouge dirait qu'une chose ne va
   pas ; ambre dit qu'elle n'a pas encore fini de se faire, exactement comme
   `.a-completer` sur un texte juridique inachevé. Discrète et non cliquable :
   c'est une précision, pas une action. */
.mention-beta {
  display: inline-block;
  margin-left: var(--pas-4);
  padding: var(--pas-1) var(--pas-4);
  border: 1px solid var(--ambre);
  border-radius: 999px;
  background: color-mix(in srgb, var(--ambre) 14%, transparent);
  color: var(--ambre);
  font-size: var(--texte-xs);
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: lowercase;
  vertical-align: middle;
  /* Le badge suit la marque dans un `<a>` : sans cela, il hériterait du
     soulignement du lien au survol et se lirait comme une seconde
     destination. */
  text-decoration: none;
}

/* Une liste d'intitulés et de valeurs — l'identité de facturation sur le
   récapitulatif de commande, et tout couple « ce que c'est / ce que ça vaut »
   qui viendra ensuite. Un `<dl>`, parce que c'est exactement ce qu'il décrit ;
   la feuille de Django n'en stylait aucun, d'où ce composant plutôt qu'un
   `<style>` dans le gabarit.

   La valeur est en chasse fixe dès qu'elle se lit caractère par caractère
   (numéro d'entreprise, TVA) : c'est la règle de la charte, et c'est ce qui
   rend un chiffre transposé visible. */
.paire-liste { margin: 0; display: grid; gap: var(--pas-6); }
.paire-liste > div { display: flex; flex-direction: column; gap: var(--pas-1); }
.paire-liste dt {
  font-size: var(--texte-s); color: var(--encre-2);
  letter-spacing: .01em; text-transform: uppercase;
}
.paire-liste dd { margin: 0; font-weight: 600; }
/* Pas `.vide` : ce nom porte déjà l'état vide d'une section entière —
   cadre pointillé et centrage — et l'emprunter poserait un encadré au
   milieu d'une liste. */
.paire-liste dd.non-renseigne {
  font-weight: 400; color: var(--encre-2); font-style: italic;
}

/* --- Page tarifaire ------------------------------------------------------- */

/* La grille est **engendrée depuis le catalogue** : le nombre de cartes n'est
   pas connu d'avance, et une offre peut entrer ou sortir sans qu'on retouche
   la feuille. D'où `auto-fit` plutôt qu'un nombre de colonnes écrit en dur. */
.page-tarifs { max-width: 68rem; }
.grille-offres {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--pas-8);
  margin-top: var(--pas-9);
}
.offre-carte {
  display: flex;
  flex-direction: column;
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: var(--pas-9) var(--pas-9) var(--pas-9);
}
.offre-carte h2 { margin: 0; font-size: var(--texte-l); }

/* **HT et TTC ont la même taille**, délibérément : l'article 7.1 des CGV veut
   l'un pour les professionnels et l'autre pour les particuliers, et un prix
   TTC en petits caractères ne satisferait pas l'article VI.2 du Code de droit
   économique. */
.offre-prix { margin: var(--pas-6) 0 var(--pas-2); line-height: 1.4; }
.offre-prix .montant {
  font-family: var(--mono);
  font-size: var(--texte-2xl);
  font-weight: 600;
}
.offre-prix .regime { font-size: var(--texte-s); color: var(--encre); opacity: .7; }
.offre-prix .separateur { margin: 0 var(--pas-3); color: var(--trait-fort); }
.offre-prix .periode { display: block; font-size: var(--texte-s); opacity: .75; }
.offre-prix .montant-absent {
  font-size: var(--texte-l);
  color: var(--ambre);
  font-weight: 600;
}
.offre-presentation { font-size: var(--texte-m); line-height: 1.55; margin: var(--pas-5) 0 var(--pas-7); }
.offre-carte .oeil { margin-top: var(--pas-3); }

.offre-limites li,
.offre-fonctions li {
  display: flex;
  align-items: baseline;
  gap: var(--pas-5);
  padding: var(--pas-3) 0;
  border-bottom: 1px solid var(--trait);
  font-size: var(--texte-s);
}
.offre-limites li:last-child { border-bottom: none; }
.offre-limites .limite-nom { flex: 1; }
.offre-limites .limite-valeur { text-align: right; font-weight: 600; }
.offre-fonctions { margin-top: var(--pas-6); }
.offre-fonctions li { border-bottom: none; align-items: center; }
/* Grisée, jamais retirée : comparer deux offres est tout l'objet de la page. */
.offre-fonctions .fonction-absente { opacity: .45; text-decoration: line-through; }
.offre-action { margin-top: auto; padding-top: var(--pas-8); }

/* --- Documents contractuels ---------------------------------------------- */

/* Un texte juridique se lit en continu, pas en survolant : on desserre
   l'interligne et on numérote visuellement les articles. La largeur vient de
   `.page-publique` (46 rem), qui est déjà la bonne mesure pour de la prose. */
.document-legal p { line-height: 1.65; margin: var(--pas-6) 0; }
.document-legal h2 {
  margin: var(--pas-11) 0 var(--pas-6);
  padding-top: var(--pas-7);
  border-top: 1px solid var(--trait);
  font-size: var(--texte-l);
}
.document-legal h2.titre-annexe {
  margin-top: var(--pas-13);
  border-top-width: 3px;
  border-top-color: var(--laiton);
}
.document-legal .liste-legale { margin: var(--pas-5) 0 var(--pas-7) var(--pas-9); padding: 0; }
.document-legal .liste-legale li { line-height: 1.6; margin: var(--pas-3) 0; }

/* Le tableau des finalités (politique de confidentialité) et celui des
   traceurs (politique cookies) : cinq colonnes de prose juridique. **Le
   défilement est sur l'enveloppe, jamais sur la page** — un corps de page qui
   part de travers est un défaut de la charte, et c'est l'article 12.1 du RGPD
   qui impose que ça reste lisible « de façon concise et compréhensible », donc
   aussi sur un téléphone. */
.document-legal .tableau-legal {
  overflow-x: auto;
  margin: var(--pas-8) 0 var(--pas-9);
  border: 1px solid var(--trait);
  border-radius: 4px;
}
.document-legal .tableau-legal table {
  border-collapse: collapse;
  width: 100%;
  min-width: 44rem;
  font-size: var(--texte-m);
}
.document-legal .tableau-legal th,
.document-legal .tableau-legal td {
  border-bottom: 1px solid var(--trait);
  padding: var(--pas-5) var(--pas-6);
  text-align: left;
  vertical-align: top;
  line-height: 1.45;
}
.document-legal .tableau-legal th {
  background: var(--plan-creux);
  font-weight: 600;
  position: sticky;
  top: 0;
}
.document-legal .tableau-legal tr:last-child td { border-bottom: none; }

/* Un champ qu'il reste à compléter. **Ambre, et non rouge** : rouge dit qu'une
   chose ne va pas, or ce texte est un brouillon assumé — ambre dit qu'il
   attend quelqu'un. Il reste visible à l'impression : c'est justement sur
   papier qu'on relit un contrat, et un trou invisible se signerait. */
.a-completer {
  background: color-mix(in srgb, var(--ambre) 22%, transparent);
  border-bottom: 2px solid var(--ambre);
  color: inherit;
  padding: 0 var(--pas-1);
  border-radius: 2px;
  font-weight: 600;
}

@media print {
  .document-legal h2 { break-after: avoid; }
  .a-completer { border-bottom: 2px solid #000; background: none; }
  /* Sur papier il n'y a pas de défilement : le tableau doit tenir en largeur,
     quitte à réduire la fonte, plutôt que d'être coupé à droite. */
  .document-legal .tableau-legal { overflow: visible; }
  .document-legal .tableau-legal table { min-width: 0; font-size: var(--texte-xs); }
  .document-legal .tableau-legal th { position: static; }
}

/* --- Divers -------------------------------------------------------------- */

.galerie { display: grid; gap: var(--pas-6); grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }
.galerie figure { margin: 0; border-radius: var(--rayon-s); overflow: hidden; border: 1px solid var(--trait); background: var(--plan); }
.galerie a { display: block; text-decoration: none; }
.galerie a:hover { border-color: var(--accent); }
.galerie img { display: block; width: 100%; aspect-ratio: 4/3; object-fit: cover; }
.galerie .legende-photo { font-size: var(--texte-s); color: var(--encre-2); padding: var(--pas-3) var(--pas-4); display: flex; align-items: center; gap: var(--pas-4); }
.galerie .actions-photo { display: flex; gap: var(--pas-3); padding: 0 var(--pas-4) var(--pas-4); }
.galerie .actions-photo .bouton-texte { margin-left: 0; }

.liste-nette { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--pas-4); }
/* `flex-wrap` : un passage prévu du mode autonome aligne sept champs ; sans
   retour à la ligne, il imposait 467 px à un écran de 412 et élargissait toute
   la page (recette mobile du 2026-10-05). */
.liste-nette li { display: flex; flex-wrap: wrap; gap: var(--pas-6); align-items: baseline; }
/* Une pastille longue (une raison traduite) revient à la ligne dans une liste :
   `nowrap` lui donnait la largeur de sa phrase, et la page défilait (S-128,
   `/espace/` à 360 px). */
.liste-nette .pastille { white-space: normal; }
/* `li { display: flex }` ci-dessus bat, en spécificité, le `[hidden] { display:
   none }` de la feuille par défaut du navigateur — un filtre posant `.hidden`
   en JavaScript (ex. templates/portail/equipement.html) n'avait donc aucun
   effet visuel. Restaure la règle par défaut avec une spécificité suffisante. */
.liste-nette li[hidden] { display: none; }
.liste-nette .ico { color: var(--encre-2); }

/* Même piège, même correctif, pour les champs de formulaire masqués en
   JavaScript : `form p:has(> label) { display: flex }` bat lui aussi le
   `[hidden]` par défaut. Sert au choix du genre de client, où les deux blocs
   d'identité sont rendus puis l'un des deux replié. */
form p[hidden] { display: none; }

.bloc-texte { background: var(--plan); border: 1px solid var(--trait); border-radius: var(--rayon); padding: var(--pas-8) var(--pas-9); }

/* Une ligne d'intervention corrigée au compte rendu : le matériel en tête, son
   résultat et son observation dessous. Le filet gauche sépare les lignes sans
   ajouter une seconde boîte dans la boîte. */
.ligne-rapport { border-left: 2px solid var(--trait); padding-left: var(--pas-7); margin-top: var(--pas-7); }
.ligne-rapport:first-of-type { margin-top: var(--pas-4); }
.ligne-rapport-titre { margin: 0 0 var(--pas-3); }

.mini-carte { border-radius: var(--rayon-s); overflow: hidden; }
.mini-carte iframe { display: block; width: 100%; height: 220px; border: 0; }

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

/* ==========================================================================
   Page d'accueil
   ========================================================================== */

.heros {
  display: grid;
  gap: var(--pas-12);
  align-items: center;
  max-width: 72rem;
  margin: 0 auto;
  padding: clamp(2.5rem, 6vw, 4.5rem) clamp(1rem, 4vw, 2.5rem) clamp(2rem, 5vw, 3.5rem);
}
@media (min-width: 58rem) {
  .heros { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: var(--pas-13); }
}
.heros h1 {
  font-size: clamp(2.1rem, 1.2rem + 3.4vw, 3.5rem);
  font-stretch: 118%;
  font-weight: 800;
  line-height: 1.03;
  letter-spacing: -.032em;
  margin: var(--pas-2) 0 var(--pas-8);
}
.heros .accroche { font-size: var(--texte-l); color: var(--encre-2); max-width: 46ch; margin-bottom: var(--pas-10); }
.heros .mention { font-size: var(--texte-m); color: var(--encre-2); margin-top: var(--pas-9); max-width: 44ch; }

/* La plaque : un objet, pas une capture d'écran. Légèrement de travers parce
   qu'aucune plaque n'est vissée droite. */
.heros-plaque { display: flex; flex-direction: column; align-items: center; }
.plaque-objet {
  position: relative;
  width: min(20rem, 100%);
  padding: var(--pas-9) var(--pas-9) var(--pas-9);
  background: linear-gradient(158deg, var(--plan), color-mix(in srgb, var(--plan-creux) 70%, var(--plan)));
  border: 1px solid var(--trait-fort);
  border-radius: .7rem;
  box-shadow: 0 18px 40px -22px rgba(24, 34, 37, .55);
  text-align: center;
  transform: rotate(-1.6deg);
  transition: transform .35s ease;
}
.plaque-objet:hover { transform: rotate(0deg); }
.plaque-objet::before {
  content: "";
  position: absolute; top: 0; left: 1.2rem;
  width: 2.6rem; height: 3px; background: var(--laiton); border-radius: 0 0 2px 2px;
}
.plaque-entete {
  display: flex; justify-content: space-between; gap: var(--pas-8);
  font-family: var(--mono); font-size: var(--texte-xxs); font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--encre-2);
  border-bottom: 1px dashed var(--trait-fort); padding-bottom: var(--pas-6);
}
.plaque-qr {
  display: block; width: 8.2rem; height: 8.2rem; margin: var(--pas-8) auto var(--pas-7);
  /* Un QR se lit sur du blanc : la vignette garde son fond, thème sombre compris. */
  background: #fff; padding: var(--pas-4); border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .08);
}
.plaque-titre { font-stretch: 112%; font-weight: 700; font-size: var(--texte-l); margin: 0; }
.plaque-code {
  font-family: var(--mono); font-size: var(--texte-m); letter-spacing: .12em;
  color: var(--encre-2); margin: var(--pas-1) 0 0;
}
.plaque-champs {
  display: grid; grid-template-columns: 1fr 1fr;
  margin: var(--pas-7) 0 var(--pas-8); border-top: 1px solid var(--trait); text-align: left;
}
.plaque-champs > div { padding: var(--pas-6) var(--pas-6) var(--pas-1) 0; }
.plaque-champs > div + div { border-left: 1px solid var(--trait); padding-left: var(--pas-6); }
.plaque-champs dt {
  font-size: var(--texte-xxs); font-weight: 600; letter-spacing: .13em;
  text-transform: uppercase; color: var(--encre-2);
}
.plaque-champs dd { margin: var(--pas-1) 0 0; font-family: var(--mono); font-size: var(--texte-m); }
.plaque-legende { font-size: var(--texte-s); color: var(--encre-2); max-width: 32ch; text-align: center; margin: var(--pas-9) 0 0; }

/* --- Bande inversée : la chaîne du modèle -------------------------------- */

.bande { background: var(--encre); color: var(--papier); }
.bande-contenu { max-width: 72rem; margin: 0 auto; padding: clamp(2.5rem, 5vw, 3.5rem) clamp(1rem, 4vw, 2.5rem); }
.bande h2 { color: var(--papier); }
.bande .sous-titre { color: color-mix(in srgb, var(--papier) 72%, var(--encre)); }

.chaine {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--pas-5);
  list-style: none; margin: var(--pas-10) 0 0; padding: 0;
  counter-reset: maillon;
}
.chaine li {
  display: inline-flex; align-items: center; gap: var(--pas-5);
  padding: var(--pas-5) var(--pas-7);
  border: 1px solid color-mix(in srgb, var(--papier) 22%, transparent);
  border-radius: 2rem;
  font-size: var(--texte-m); font-weight: 600;
  background: color-mix(in srgb, var(--papier) 7%, transparent);
}
.chaine li .ico { color: var(--laiton); }
.chaine li:not(.fin)::after {
  content: "";
  width: 1.1rem; height: 1px;
  background: var(--laiton);
  margin-left: var(--pas-3);
}
.chaine li.fin { border-color: var(--laiton); }

/* --- Sections publiques -------------------------------------------------- */

.section-publique { max-width: 72rem; margin: 0 auto; padding: clamp(2rem, 4vw, 2.9rem) clamp(1rem, 4vw, 2.5rem); }
.section-publique .carte h3 { margin: var(--pas-6) 0 var(--pas-4); }
.section-publique .carte .ico-xl { display: block; }
.section-publique .carte .pastille { margin-top: var(--pas-7); }

/* Une seule séquence à l'ouverture : le texte, puis la plaque. */
@keyframes surgir { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.heros-texte > * { animation: surgir .5s ease both; }
.heros-texte > :nth-child(2) { animation-delay: .06s; }
.heros-texte > :nth-child(3) { animation-delay: .12s; }
.heros-texte > :nth-child(4) { animation-delay: .18s; }
.heros-texte > :nth-child(5) { animation-delay: .24s; }
.heros-plaque { animation: surgir .6s .2s ease both; }

/* Le premier titre d'une section porte déjà l'espacement de la section. */
.section-publique > h2:first-child,
.bande-contenu > h2:first-child { margin-top: 0; }

/* La coquille publique occupe la hauteur de la fenêtre : sur une page courte
   — connexion, message d'erreur — le pied reste en bas plutôt qu'au milieu. */
body:has(> .entete-publique) {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
/* `width: 100%` parce que les marges automatiques de `.page-publique`, dans une
   colonne flexible, empêchent `main` de s'étirer : il prenait la largeur de
   son contenu, et le `min-width` d'un tableau légal (prévu pour défiler dans
   `.tableau-legal`) élargissait toute la page sur un téléphone — 324 px de
   trop sur les politiques cookies et confidentialité (recette du 2026-10-05). */
body:has(> .entete-publique) > main { flex: 1 0 auto; width: 100%; }

/* Case de confirmation d'une action destructrice : elle doit se lire avant le
   bouton, pas à côté de lui. */
.confirmation {
  display: flex;
  gap: var(--pas-6);
  align-items: flex-start;
  margin-bottom: var(--pas-7);
  font-weight: 400;
  font-size: var(--texte-m);
  max-width: 52ch;
}
.confirmation input { width: auto; margin-top: var(--pas-2); }

/* ==========================================================================
   Synoptique — le tableau de contrôle d'une installation
   --------------------------------------------------------------------------
   Sombre dans les deux thèmes, et c'est délibéré : un instrument se lit sur
   fond noir. C'est aussi ce qui sépare d'un coup d'œil ce que *mesure* la
   maison de ce qu'on *gère* dans le logiciel.
   ========================================================================== */

.synoptique {
  /* **La palette du pupitre vit ici en entier.** Quatre jetons étaient
     déclarés et douze couleurs écrites à côté, dans les cadrans, les jauges
     et les voyants — mesuré le 2026-09-17. Le pupitre était donc réglable
     sur son fond et illisible sur tout le reste : changer le vert d'un
     voyant demandait de le retrouver à quatre endroits. Elles sont toutes
     ici désormais, et une couleur littérale sous `.synoptique` est un défaut
     comme ailleurs.

     Ce qui ne change pas : ce pupitre reste **sombre dans les deux thèmes**,
     et c'est délibéré — un instrument se lit sur fond noir. */
  --instrument-fond: #101a1d;
  --instrument-trait: #26383d;
  --instrument-encre: #dfe8e7;
  --instrument-gris: #8ba0a2;
  --instrument-piste: #24343a;
  --instrument-bande: #4f8f6d;
  --instrument-bon: #6cbb92;
  --instrument-hors: #e8887c;
  --instrument-hors-trait: #6d3b36;
  --instrument-jauge-haut: #4b8fb8;
  --instrument-jauge-bas: #2f5d7c;
  --instrument-alerte-haut: #c9615a;
  --instrument-alerte-bas: #8c3d38;
  --instrument-seuil: #d3a054;
  --instrument-eteint: #3a4a4e;
  --instrument-attention: #e0a35f;
  /* Le relief d'un cartouche posé sur le pupitre : un voile de blanc, pas
     une couleur. Écrit en jeton parce qu'il servait à trois endroits avec
     deux valeurs différentes (4 % et 5 %), ce que personne n'avait décidé. */
  --instrument-relief: color-mix(in srgb, #ffffff 4%, transparent);

  background: var(--instrument-fond);
  color: var(--instrument-encre);
  border: 1px solid var(--instrument-trait);
  border-radius: var(--rayon);
  padding: var(--pas-8) var(--pas-9) var(--pas-9);
  margin-bottom: var(--pas-8);
}
.synoptique header {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--pas-8);
  border-bottom: 1px solid var(--instrument-trait);
  padding-bottom: var(--pas-6); margin-bottom: var(--pas-7);
}
.synoptique .oeil { color: var(--instrument-gris); margin: 0; }
.synoptique .horodate { font-size: var(--texte-xs); color: var(--instrument-gris); }
.synoptique a { color: inherit; text-decoration: none; }

.cadrans {
  display: grid; gap: var(--pas-6);
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
}
.cadran {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: var(--pas-4);
  padding: var(--pas-6) var(--pas-5) var(--pas-7);
  background: var(--instrument-relief);
  border: 1px solid var(--instrument-trait);
  border-radius: var(--rayon-s);
  text-align: center;
}
.cadran svg { width: 100%; max-width: 6.5rem; display: block; }
.cadran .piste { fill: none; stroke: var(--instrument-piste); stroke-width: 9; stroke-linecap: round; }
.cadran .bande { fill: none; stroke: var(--instrument-bande); stroke-width: 3; stroke-linecap: round; opacity: .85; }
.cadran .aiguille {
  fill: none; stroke: var(--laiton); stroke-width: 9; stroke-linecap: round;
  transition: stroke-dasharray .4s ease;
}
.cadran.etat-bon .aiguille { stroke: var(--instrument-bon); }
.cadran.etat-hors .aiguille { stroke: var(--instrument-hors); }

.cadran figcaption { display: grid; gap: var(--pas-1); }
.cadran .lecture {
  font-family: var(--mono); font-size: var(--texte-3xl); font-weight: 600;
  line-height: 1.05; letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.cadran .lecture i { font-style: normal; font-size: .62em; color: var(--instrument-gris); margin-left: .15em; }
.cadran .nom { font-size: var(--texte-s); color: var(--instrument-gris); }
.cadran .echelle { font-size: var(--texte-xxs); color: var(--instrument-gris); }
.cadran.lecture-seule { justify-content: center; gap: var(--pas-2); }
.cadran.etat-hors { border-color: var(--instrument-hors-trait); }

.cadran.compteur { justify-content: center; gap: var(--pas-2); }
.cadran.compteur .ico { width: 1.3rem; height: 1.3rem; color: var(--instrument-gris); }

.cadran.interrupteur { justify-content: center; gap: var(--pas-4); }
.cadran.interrupteur .voyant {
  width: .6rem; height: .6rem; border-radius: 50%;
  background: var(--instrument-gris);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--instrument-gris) 18%, transparent);
}
.cadran.interrupteur.allume .voyant {
  background: var(--instrument-bon);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--instrument-bon) 18%, transparent);
}

/* La zone cliquable couvre le cadran sans envelopper son contenu : un `figure`
   ne peut pas vivre dans un `a`. */
.lien-cadran { position: absolute; inset: 0; border-radius: var(--rayon-s); }
.lien-cadran:hover { background: var(--instrument-relief); }
.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* --- Niveau de cuve ------------------------------------------------------ */

.cadran.niveau .jauge-verticale {
  position: relative;
  width: 2.6rem; height: 4.6rem;
  border: 1px solid var(--instrument-trait);
  border-radius: .25rem;
  background: transparent;
  overflow: hidden;
}
.cadran.niveau .remplissage {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: linear-gradient(180deg, var(--instrument-jauge-haut), var(--instrument-jauge-bas));
}
.cadran.niveau.etat-hors .remplissage { background: linear-gradient(180deg, var(--instrument-alerte-haut), var(--instrument-alerte-bas)); }
.cadran.niveau .seuil {
  position: absolute; left: 0; right: 0; height: 1px;
  background: repeating-linear-gradient(90deg, var(--instrument-seuil) 0 3px, transparent 3px 6px);
}

/* --- Zones climatisées --------------------------------------------------- */

.zones { display: grid; gap: var(--pas-6); grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); }
.zone {
  display: grid; gap: var(--pas-1);
  padding: var(--pas-6) var(--pas-7);
  background: var(--instrument-relief);
  border: 1px solid var(--instrument-trait);
  border-left: 3px solid var(--instrument-eteint);
  border-radius: var(--rayon-s);
}
.zone.allumee { border-left-color: var(--instrument-bon); }
.zone .voyant {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--instrument-eteint); display: inline-block;
}
.zone.allumee .voyant { background: var(--instrument-bon);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--instrument-bon) 18%, transparent); }
.zone .nom { font-size: var(--texte-s); color: var(--instrument-gris); }
.zone .lecture { font-family: var(--mono); font-size: var(--texte-3xl); font-weight: 600; }
.zone .lecture i { font-style: normal; font-size: .6em; color: var(--instrument-gris); }
.zone .consigne, .zone .etat-texte { font-size: var(--texte-xs); color: var(--instrument-gris); }

/* --- Consommables -------------------------------------------------------- */

.echeances { margin-top: var(--pas-8); border-top: 1px solid var(--instrument-trait); padding-top: var(--pas-7); }
.echeances ul { list-style: none; margin: var(--pas-5) 0 0; padding: 0; display: grid; gap: var(--pas-3); }
.echeances li {
  display: flex; justify-content: space-between; gap: var(--pas-8); align-items: baseline;
  font-size: var(--texte-s); padding: var(--pas-2) var(--pas-4); border-radius: 3px;
}
.echeances li a { color: var(--instrument-encre); }
.echeances li span { color: var(--instrument-gris); }
.echeances .etat-attention { background: rgba(224, 163, 95, .12); }
.echeances .etat-attention span { color: var(--instrument-attention); }
.echeances .etat-hors { background: rgba(232, 136, 124, .14); }
.echeances .etat-hors span { color: var(--instrument-hors); }

/* --- Relevés d'un appareil sur la fiche du site -------------------------- */

.releves { display: grid; gap: var(--pas-1); margin: var(--pas-6) 0 0; }
.releves > div {
  display: flex; justify-content: space-between; gap: var(--pas-8); align-items: baseline;
  padding: var(--pas-3) 0; border-bottom: 1px solid var(--trait);
}
.releves > div:last-child { border-bottom: 0; }
.releves dt { font-size: var(--texte-s); color: var(--encre-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.releves dd { margin: 0; font-family: var(--mono); font-size: var(--texte-m); white-space: nowrap; font-variant-numeric: tabular-nums; }
.releves dd .unite { color: var(--encre-2); font-size: .82em; margin-left: .2em; }
.releves dd .absente { color: var(--trait-fort); }

/* --- Courbe d'une mesure ------------------------------------------------- */

.courbe { background: var(--plan); border: 1px solid var(--trait); border-radius: var(--rayon); padding: var(--pas-8); }
.courbe svg { width: 100%; height: auto; display: block; }
.courbe .aire { fill: var(--accent); opacity: .1; }
.courbe .trace { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.courbe .repere { stroke: var(--trait); stroke-width: 1; stroke-dasharray: 3 3; }
.courbe .etiquette { font-family: var(--mono); font-size: 9px; fill: var(--encre-2); }
.courbe .point-final { fill: var(--accent); }
.courbe .survol { fill: transparent; }
.courbe .survol:hover { fill: var(--accent); fill-opacity: .08; }
.courbe-legende {
  display: flex; flex-wrap: wrap; gap: var(--pas-4) var(--pas-9);
  margin-top: var(--pas-6); font-size: var(--texte-s); color: var(--encre-2);
}

/* Un appareil instrumenté porte un synoptique : il lui faut la largeur. */
.grille.appareils { grid-template-columns: repeat(auto-fill, minmax(26rem, 1fr)); }
@media (max-width: 39.99rem) { .grille.appareils { grid-template-columns: 1fr; } }

/* --- Cadran à aiguille : la valeur vit au centre de l'arc ---------------- */

.cadran.arc { padding: var(--pas-6) var(--pas-5) var(--pas-5); }
.cadran.arc svg { max-width: 100%; }
.cadran .valeur-arc {
  font-family: var(--mono); font-size: 25px; font-weight: 600;
  fill: var(--instrument-encre); letter-spacing: -.5px;
}
.cadran .unite-arc { font-size: 11px; fill: var(--instrument-gris); }
.cadran .borne { font-family: var(--mono); font-size: 8px; fill: var(--instrument-gris); }
.cadran.arc figcaption { margin-top: calc(var(--pas-2) * -1); }

/* Toutes les tuiles d'un synoptique ont la même hauteur : un tableau de bord
   dont les cases sautent ne se lit pas d'un coup d'œil. */
.cadran { min-height: 9.2rem; justify-content: center; }

/* ===================================================================
   « Comprendre one-vision.app » — les composants de la page d'explication
   ===================================================================

   Posés le 2026-09-22. **Aucun schéma de cette page n'est une image** : tout
   est du texte dans des blocs, ce qui se traduit, se lit à la voix, se
   sélectionne et suit le thème sombre. Trois fichiers d'image par langue
   auraient dit la même chose à moins de monde, et auraient menti le jour où
   le produit change.

   Ces classes servent la page d'aide, le manuel et — plus tard — les écrans
   de bienvenue : elles vivent donc ici, jamais dans un `<style>` de gabarit. */

.page-publique.page-large { max-width: 62rem; }

.chapeau {
  font-size: var(--texte-xl);
  color: var(--encre-2);
  max-width: 44rem;
  margin: var(--pas-7) 0 var(--pas-10);
}

/* Le sommaire d'une page longue : des ancres, pas une navigation — d'où le
   trait unique qui les rassemble plutôt qu'un cadre par lien. */
.sommaire-pages {
  display: flex; flex-wrap: wrap; gap: var(--pas-4) var(--pas-9);
  padding: var(--pas-7) 0;
  border-top: 1px solid var(--trait);
  border-bottom: 1px solid var(--trait);
  font-size: var(--texte-s);
}

.section-comprendre { margin-top: var(--pas-13); }
.section-comprendre > h2 {
  font-size: var(--texte-3xl);
  margin: 0 0 var(--pas-7);
}
.section-comprendre > .meta { max-width: 44rem; }

/* --- Le bâtiment et ce qui gravite autour -------------------------------
   Une colonne au téléphone, une croix sur un écran large. Les filets ne
   paraissent qu'en croix : au téléphone, ils pointeraient vers le vide. */
.schema-astre {
  display: grid;
  gap: var(--pas-8);
  margin: var(--pas-9) 0;
}
.astre-centre, .astre-satellite {
  position: relative;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--pas-3);
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-9);
}
.astre-centre {
  border-color: var(--laiton);
  box-shadow: var(--ombre);
}
.astre-centre strong { font-size: var(--texte-xl); }
.astre-satellite strong { font-size: var(--texte-l); }
.astre-centre .meta, .astre-satellite .meta { margin: 0; }

@media (min-width: 46rem) {
  .schema-astre {
    grid-template-columns: 1fr minmax(13rem, 1fr) 1fr;
    align-items: center;
    grid-template-areas:
      "gestion centre client"
      ".       travail     .";
  }
  .astre-centre   { grid-area: centre; }
  .s-gestion      { grid-area: gestion; }
  .s-client       { grid-area: client; }
  .s-travail      { grid-area: travail; }

  /* Les filets. Ils sortent de chaque satellite vers le centre, et la
     gouttière de la grille fait le reste : aucune coordonnée à tenir. */
  .s-gestion::after, .s-client::after, .s-travail::after {
    content: ""; position: absolute; background: var(--trait-fort);
  }
  .s-gestion::after, .s-client::after { top: 50%; width: var(--pas-8); height: 1px; }
  .s-gestion::after { left: 100%; }
  .s-client::after  { right: 100%; }
  .s-travail::after { left: 50%; bottom: 100%; width: 1px; height: var(--pas-8); }
}

/* --- Une chaîne de maillons ---------------------------------------------
   « Organisation › Client › Bâtiment » : la seule chaîne de rattachement, et
   plus loin celle du contrat. Le chevron est décoratif — il ne porte aucun
   sens qu'un lecteur d'écran doive entendre, d'où le `content` en CSS. */
.chaine {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--pas-4) var(--pas-5);
  margin: var(--pas-9) 0 var(--pas-7);
}
.maillon {
  background: var(--plan-creux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-s);
  padding: var(--pas-4) var(--pas-7);
  font-size: var(--texte-s);
}
.maillon-fort {
  background: var(--accent-voile);
  border-color: var(--accent);
  color: var(--accent-fort);
  font-weight: 600;
}
.maillon + .maillon::before {
  content: "›";
  color: var(--encre-2);
  margin-right: var(--pas-6);
  margin-left: calc(var(--pas-6) * -1);
}
.note-schema { max-width: 44rem; }

/* --- Un flux : un parcours en étapes, jamais une image -------------------
   Le manuel de l'application dessine ses schémas (§ 14.2) avec ce composant,
   le plus proche voisin de `.chaine`/`.maillon` ci-dessus : une étape par
   `<li>`, le lieu en `.oeil` (laiton), l'action en `<strong>` (encre), le
   détail en `.meta`. En colonne sous 46 rem — le manuel se lit d'abord sur un
   téléphone — et en ligne au-dessus, comme `.schema-astre`.

   La flèche entre deux étapes est un `::after`, jamais un caractère : elle
   reprend le glyphe `#i-fleche` du sprite, pris en masque pour hériter du
   laiton sans dupliquer de SVG dans le gabarit (`img-src` et `style-src`
   admettent déjà `data:` et le nonce de la page — rien d'exécutable n'y
   transite). Posée en position absolue dans l'écart entre deux cartes
   (`calc(var(--pas-7) * -1)`, le même geste que la marge négative), elle
   pivote de 90° en colonne pour pointer vers le bas. */
.flux {
  --fleche-flux: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M4.5 12h14M13 6.5l5.5 5.5-5.5 5.5'/%3E%3C/svg%3E");
  display: flex;
  flex-direction: column;
  gap: var(--pas-7);
  list-style: none;
  margin: var(--pas-9) 0 0;
  padding: 0;
}
.flux li {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--pas-3);
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-9);
}
.flux li .oeil { color: var(--laiton); }
.flux li strong { font-size: var(--texte-l); color: var(--encre); }
.flux li .meta { margin: 0; }
.flux li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: calc(var(--pas-7) * -1);
  width: 1.3rem;
  height: 1.3rem;
  transform: translate(-50%, 50%) rotate(90deg);
  background-color: var(--laiton);
  -webkit-mask-image: var(--fleche-flux);
  mask-image: var(--fleche-flux);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

@media (min-width: 46rem) {
  .flux { flex-direction: row; flex-wrap: wrap; align-items: stretch; }
  .flux li { flex: 1 1 11rem; }
  .flux li:not(:last-child)::after {
    left: auto; bottom: auto;
    top: 50%; right: calc(var(--pas-7) * -1);
    transform: translate(50%, -50%) rotate(0deg);
  }
}

/* --- Les trois familles d'accès -----------------------------------------
   Dans chaque carte, l'accès complet puis l'accès réduit, décalé d'un cran :
   c'est la règle même du produit — un accès réduit ne fait qu'enlever des
   lignes à celui dont il dépend. Le décalage la montre sans la dire. */
.grille-familles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--pas-8);
  margin: var(--pas-9) 0;
}
.famille {
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-9);
}
.famille h3 {
  font-size: var(--texte-s);
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--encre-2);
  margin: 0 0 var(--pas-7);
}
.famille-role + .famille-role { margin-top: var(--pas-7); }
.famille-titre {
  display: block;
  font-weight: 600;
  font-size: var(--texte-l);
}
.famille-role .meta { margin: var(--pas-3) 0 0; }
.famille-reduit {
  border-left: 2px solid var(--trait-fort);
  padding-left: var(--pas-7);
  margin-left: var(--pas-4);
}

/* --- Deux panneaux côte à côte ------------------------------------------ */
.grille-deux {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--pas-8);
  margin: var(--pas-9) 0;
}
.panneau-schema {
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-9);
}
.panneau-schema h3 { font-size: var(--texte-l); margin: 0 0 var(--pas-5); }
.panneau-schema .meta { margin: var(--pas-5) 0 0; }

/* --- Un emboîtement ------------------------------------------------------
   Les zones comme les quatre niveaux de matériel. L'indentation est portée
   par la classe du rang et non par un `<ol>` imbriqué : la liste reste plate,
   donc lisible à la voix dans l'ordre où elle se lit à l'œil. */
.arbre-niveaux {
  list-style: none;
  margin: var(--pas-7) 0 0;
  padding: 0;
}
.arbre-niveaux .niveau {
  position: relative;
  padding: var(--pas-4) 0 var(--pas-4) var(--pas-8);
  border-left: 1px solid var(--trait-fort);
  font-size: var(--texte-m);
}
.arbre-niveaux .niveau::before {
  content: ""; position: absolute; left: 0; top: 50%;
  width: var(--pas-6); height: 1px; background: var(--trait-fort);
}
.niveau-2 { margin-left: var(--pas-8); }
.niveau-3 { margin-left: var(--pas-11); }
.niveau-4 { margin-left: var(--pas-12); }
.arbre-niveaux .rang {
  display: inline-block;
  min-width: 5.5rem;
  font-size: var(--texte-xs);
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--encre-2);
}

/* --- Une liste où chaque point ouvre sur une phrase ---------------------- */
.liste-explicative {
  list-style: none;
  margin: var(--pas-9) 0 0;
  padding: 0;
  max-width: 46rem;
}
.liste-explicative li {
  padding: var(--pas-7) 0;
  border-top: 1px solid var(--trait);
  color: var(--encre-2);
}
.liste-explicative li strong { display: block; color: var(--encre); }

/* Une remarque qui éclaire sans avertir — même filet de laiton que
   `.encadre-laiton`, mais du texte au fil et non une carte avec une action. */
.encadre {
  border-left: 3px solid var(--laiton);
  background: var(--plan);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  padding: var(--pas-8) var(--pas-9);
  margin: var(--pas-9) 0;
  max-width: 46rem;
  color: var(--encre-2);
}
.encadre strong { color: var(--encre); }

/* --- Une liste de cases qui portent plus qu'un mot ----------------------
   `.case` suffit quand l'étiquette tient en une ligne. Ici chaque case porte
   un intitulé, un client et une pastille de statut : il faut deux lignes et
   une zone cliquable large — une case à cocher visée au doigt sur un
   téléphone ne doit pas demander de viser la case elle-même. */
.liste-cases { list-style: none; margin: var(--pas-7) 0 0; padding: 0; }
.liste-cases > li + li { margin-top: var(--pas-4); }
.liste-cases label {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: var(--pas-3) var(--pas-7);
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-s);
  padding: var(--pas-7);
  cursor: pointer;
}
.liste-cases input { grid-row: span 2; align-self: center; margin: 0; }
.case-titre { font-weight: 600; }
.case-detail {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--pas-3) var(--pas-6);
  grid-column: 2;
  color: var(--encre-2);
  font-size: var(--texte-s);
}

/* Une case de plaque qui prend toute la largeur : un texte libre — les pannes
   récurrentes d'un matériel — ne se lit pas dans une colonne de 9 rem, et le
   couper en deux le rendrait moins clair que le reste de la plaque. */
.plaque > div.pleine-largeur,
.fiche > div.pleine-largeur { grid-column: 1 / -1; border-right: 0; }

/* Le correspondant sur une carte de résultat : une ligne de plus, alignée sur
   son icône. Pas un lien — la carte entière en est déjà un, et un lien dans un
   lien n'est pas cliquable de façon prévisible. */
.contact-carte {
  display: flex; align-items: center; gap: var(--pas-5);
  margin-top: var(--pas-4);
  padding-top: var(--pas-4);
  border-top: 1px solid var(--trait);
}
.contact-carte .ico { flex: none; }

/* L'aperçu du texte de facturation (FA-4) : lu caractère par caractère, donc en
   chasse fixe ; les lignes longues passent à la ligne plutôt que de déborder. */
.apercu-texte {
  font-family: var(--mono);
  font-size: var(--texte-m);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--plan);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--pas-8) var(--pas-9);
  margin: 0;
}

/* Les photos exigées (NT-2b) : une ligne par endroit, quatre réglages. Les
   nombres se lisent en chasse fixe ; la cellule des vues garde de quoi en
   nommer deux sans défiler. La ligne neuve et le niveau « passage » posent
   les mêmes champs en grille. */
.grille-exigences th[scope="row"] { font-weight: 600; min-width: 11rem; }
.grille-exigences input[type="number"] { width: 5rem; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.grille-exigences textarea { min-width: 14rem; min-height: 0; }
.grille-exigence {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--pas-6) var(--pas-8);
}

/* --- Conseils techniques (6.4, CT-1) -------------------------------------
   Un dépliant natif par conseil (titre visible, texte replié), puis
   l'avertissement du bloc, jamais replié. Légers : une fiche en porte
   plusieurs, `.depliant` (un bouton) en ferait une pile de boutons. */
.conseils { margin-bottom: var(--pas-9); }
.conseil { border-bottom: 1px solid var(--trait); }
.conseil > summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--pas-4);
  padding: var(--pas-5) 0; cursor: pointer; list-style: none;
  font-weight: 600; font-size: var(--texte-m); color: var(--encre);
}
.conseil > summary::-webkit-details-marker { display: none; }
.conseil > summary::after {
  content: "+"; font-family: var(--mono); color: var(--encre-2); margin-left: auto;
}
.conseil[open] > summary::after { content: "−"; }
.conseil > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.conseil-corps { padding-bottom: var(--pas-6); }
.conseil-corps p { margin: 0 0 var(--pas-4); }
.conseils-avertissement {
  margin: var(--pas-6) 0 0; padding: var(--pas-5) var(--pas-7);
  border-left: 3px solid var(--laiton); background: var(--plan);
  font-size: var(--texte-m); color: var(--encre-2);
}

/* --- Lecture de la plaque (6.5, EQ-6) -------------------------------------
   Un encart d'état dont le ton vient du modèle (`AnalysePlaque.ton`), la
   mention sous chaque geste qui envoie la photo, et l'écran de vérification :
   une rangée par valeur lue, la valeur en chasse fixe (lue caractère par
   caractère). Jetons et échelles seuls. */
.encart-lecture {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--pas-4) var(--pas-6);
  padding: var(--pas-5) var(--pas-7); margin: var(--pas-4) 0;
  border-left: 3px solid var(--laiton); background: var(--plan);
  font-size: var(--texte-m);
}
.encart-lecture.ton-vert { border-left-color: var(--vert); }
.encart-lecture.ton-ambre { border-left-color: var(--ambre); }
.encart-lecture.ton-gris { border-left-color: var(--trait); }
.mention-plaque { margin: var(--pas-5) 0 0; font-size: var(--texte-s); color: var(--encre-2); }
.lecture-plaque .meta[id] { margin-top: var(--pas-4); }

.verification-plaque { display: grid; gap: var(--pas-8); }
@media (min-width: 46rem) {
  .verification-plaque { grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); align-items: start; }
}
.verification-plaque img { max-width: 100%; border: 1px solid var(--trait); border-radius: var(--rayon-s); }
.rangee-plaque {
  display: grid; gap: var(--pas-3) var(--pas-7);
  padding: var(--pas-6) 0; border-bottom: 1px solid var(--trait);
}
@media (min-width: 46rem) {
  .rangee-plaque { grid-template-columns: 10rem minmax(0, 1fr) minmax(0, 1fr); align-items: center; }
}
.rangee-plaque .valeur-lue,
.rangee-plaque input[type="text"] { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.rangee-plaque label.case { display: inline-flex; align-items: center; gap: var(--pas-3); font-weight: 400; }
.rangee-plaque .pastille { justify-self: start; }
