/* Administration du socle bio.
 *
 * POURQUOI CETTE FEUILLE A ÉTÉ REFAITE. Elle tenait en 141 lignes et son
 * en-tête disait: « volontairement sobre, la direction artistique du mémo 04
 * s'applique au site public, pas ici ». David a tranché l'inverse le 17
 * octobre 2026: « fais un effort pour l'administration, la mienne et celle du
 * commerçant, car c'est très très modeste visuellement, beaucoup trop
 * minimaliste. » Il a raison sur le fond: le commerçant qui paie 390 euros
 * arrive ici après une page de vente soignée, et découvrait une page blanche
 * en police système. L'écart de soin se lit comme un écart de sérieux.
 *
 * POURQUOI ON REPREND LES JETONS DU SITE PUBLIC ET QU'ON N'EN INVENTE PAS
 * D'AUTRES. Deux palettes pour un même produit, ce sont deux choses à tenir
 * d'accord, et le mémo 13 dit ce qu'il advient de deux endroits que rien
 * n'oblige à concorder. Les couleurs, les deux familles de caractères et les
 * rayons viennent donc de `magasin-bio.be/production/src/styles/tokens.css`,
 * repris ici en version courte: seuls les jetons réellement employés.
 *
 * POURQUOI LES POLICES NE SONT PAS RETÉLÉCHARGÉES. Elles sont servies depuis
 * `/polices/` par le même domaine, et l'administration vit sous `/gestion/`:
 * un visiteur qui a vu le site les a déjà en cache, et le cahier des charges
 * interdit de toute façon d'aller les chercher ailleurs.
 *
 * CE QUI DISTINGUE CET ÉCRAN DU SITE PUBLIC, et c'est délibéré: pas de formes
 * organiques, pas de grain, pas de dévoilement au défilement. Un back-office
 * se lit vite et souvent; la densité y vaut mieux que la mise en scène. On
 * garde la palette, la typographie et l'ombre franche, on laisse le décor.
 */

/* ═══════════════════════════ Polices ═══════════════════════════ */

@font-face {
  font-family: "Fraunces";
  src: url("/polices/fraunces-latin.woff2") format("woff2");
  font-weight: 600 800;
  font-style: normal;
  font-display: swap;
  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+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Schibsted Grotesk";
  src: url("/polices/schibsted-grotesk-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  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+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ═══════════════════════════ Jetons ═══════════════════════════ */

:root {
  color-scheme: light;

  --papier:      #FBF6EE;
  --papier-2:    #F3EADC;
  --carte:       #FFFDF8;
  --encre:       #1E2A22;
  --encre-2:     #3C4B40;
  --encre-3:     #5C6B5F;
  --trait:       #DCCFBB;

  --foret:       #10231A;
  --vert-900:    #14301F;
  --vert-700:    #1F4D32;
  --vert-500:    #2E6B45;
  --vert-100:    #DCE8DD;

  --argile-700:  #8A3B1E;
  --argile-500:  #C2572E;
  --argile-200:  #F0C9B4;

  --soleil-500:  #F2B705;
  --soleil-300:  #FFD45E;
  --soleil-100:  #FFF0C2;

  --betterave-500: #8A3A60;
  --betterave-200: #E8CBD9;

  --succes:      #2E6B45;
  --succes-fond: #DCE8DD;
  --alerte:      #8A6100;
  --alerte-fond: #FFF0C2;
  --erreur:      #A3201A;
  --erreur-fond: #F7DAD6;
  --info:        #1F5472;
  --info-fond:   #D9E6EE;

  --ff-titre: "Fraunces", "Hoefler Text", Georgia, serif;
  --ff-texte: "Schibsted Grotesk", "Helvetica Neue", Arial, sans-serif;

  --r-xs: 6px;  --r-sm: 10px;  --r-md: 16px;  --r-pill: 999px;
  --sh-1: 0 1px 2px rgba(30,42,34,.06), 0 2px 8px rgba(30,42,34,.05);
  --sh-2: 0 2px 4px rgba(30,42,34,.06), 0 10px 24px -6px rgba(30,42,34,.12);
  --sh-dur: 5px 5px 0 rgba(30,42,34,.13);

  --s-1: .25rem; --s-2: .5rem;  --s-3: .75rem; --s-4: 1rem;
  --s-5: 1.5rem; --s-6: 2rem;   --s-7: 2.5rem; --s-8: 3.5rem;

  --flanc: 16.5rem;
}

/* ═══════════════════════════ Socle ═══════════════════════════ */

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

body {
  margin: 0;
  font-family: var(--ff-texte);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--encre-2);
  background: var(--papier);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--ff-titre);
  font-weight: 700;
  color: var(--encre);
  letter-spacing: -.018em;
  line-height: 1.14;
  margin: 0;
}
h1 { font-size: 1.9rem; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.05rem; }

/* POURQUOI LES TITRES PRENNENT UNE MARGE HAUTE, relevé par David le 21 octobre
   2026 sur le récapitulatif de l'inscription: « titres en gras trop collés avec
   le texte en dessous et au-dessus ou les boutons au-dessus ».

   La règle ci-dessus les met tous à `margin: 0`, ce qui est juste pour une
   remise à zéro mais laisse l'espacement entièrement au `gap` du parent. Or le
   parent est le plus souvent `.formulaire`, dont le `gap` vaut `--s-2`, c'est à
   dire 8 pixels: assez pour séparer deux champs, pas pour détacher un titre du
   bloc qui le précède.

   POURQUOI EN HAUT ET NON EN BAS: un titre appartient à ce qui le suit. Une
   marge basse l'éloignerait de son propre contenu, ce qui est l'erreur
   symétrique et se voit tout autant. Le `gap` du parent suffit en dessous.

   `:first-child` l'annule, sinon un titre qui ouvre déjà une carte ou une
   section recevrait une marge par-dessus le padding de son conteneur. */
h2:not(:first-child),
h3:not(:first-child) { margin-block-start: var(--s-5); }

a { color: var(--vert-700); text-underline-offset: 3px; }
a:hover { color: var(--argile-700); }

:focus-visible { outline: 3px solid var(--argile-500); outline-offset: 2px; border-radius: 3px; }

/* POURQUOI un chiffre en Fraunces et en chasse fixe: les tableaux de bord
   s'alignent en colonnes, et des chiffres de largeurs différentes dansent
   d'une ligne à l'autre. La serif leur donne le poids que la page attend. */
.num { font-family: var(--ff-titre); font-variant-numeric: tabular-nums; }

/* ═══════════════════════ Coque et flanc ═══════════════════════ */

/* POURQUOI une grille à deux colonnes plutôt qu'un flanc en position fixe:
   une position fixe oblige à réserver la place à la main, et la réservation
   se désaccorde du jour où le flanc change de largeur. Ici, la colonne EST
   la réservation. */
.coque {
  display: grid;
  grid-template-columns: 1fr;
  min-height: 100vh;
}

.flanc {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  padding: var(--s-5) var(--s-4);
  background: var(--vert-900);
  color: var(--vert-100);
}

/* POURQUOI le nom du site est dans un `<b>` et l'espace vient de la marge du
   `<span>`: en `inline-flex` avec un `gap`, le « -bio.be » se détachait de
   « magasin » et la marque se lisait « magasin - bio.be ». */
.marque {
  display: inline-flex;
  align-items: baseline;
  font-family: var(--ff-titre);
  font-weight: 800;
  font-size: 1.12rem;
  color: var(--papier);
  text-decoration: none;
  letter-spacing: -.02em;
}
.marque i { font-style: normal; color: var(--soleil-300); }
.marque span {
  margin-inline-start: .45rem;
  font-family: var(--ff-texte);
  font-weight: 500;
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--vert-100);
  opacity: .72;
}

/* LE SELECTEUR DE SITE, pose entre la marque et le menu parce qu'il decrit ce
   que tout le flanc montre: les tuiles, la liste, les trois files, les
   finances et les statistiques se lisent dans le site choisi ici.

   POURQUOI LE BOUTON RESTE DANS LE FLUX ET N'EST PAS CACHE PAR DEFAUT: le
   `onchange` du gabarit envoie le formulaire sans lui, mais c'est un agrement.
   La regle de la maison sur la revelation au defilement vaut ici aussi, et le
   memo 00 section 13 la nomme: un agrement n'est jamais une condition
   d'affichage. Sans script, le bouton est le seul chemin. */
.choix-site {
  display: grid;
  gap: var(--s-2);
  margin-block: var(--s-4) var(--s-3);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid rgba(255,255,255,.14);
}
.choix-site label {
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--vert-100);
  opacity: .72;
}
.choix-site select {
  width: 100%;
  padding: .5rem .6rem;
  border: 1px solid rgba(255,255,255,.22);
  border-radius: var(--r-sm);
  background: rgba(255,255,255,.08);
  color: var(--papier);
  font: inherit;
}
.choix-site button {
  justify-self: start;
  padding: .3rem .6rem;
  border: 1px solid rgba(255,255,255,.22);
  border-radius: var(--r-sm);
  background: none;
  color: var(--vert-100);
  font: inherit;
  font-size: .8rem;
  cursor: pointer;
}
.choix-site button:hover { background: rgba(255,255,255,.08); color: var(--papier); }

.flanc nav { display: flex; flex-direction: column; gap: 2px; }
.flanc nav a {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: .62rem .8rem;
  border-radius: var(--r-sm);
  color: var(--vert-100);
  text-decoration: none;
  font-weight: 500;
}
.flanc nav a:hover { background: rgba(255,255,255,.08); color: var(--papier); }
.flanc nav a[aria-current="page"] {
  background: var(--papier);
  color: var(--vert-900);
  font-weight: 700;
}
.flanc nav svg { flex: none; width: 19px; height: 19px; }

/* Le bloc du compte est poussé en bas par la marge automatique: il n'a pas à
   connaître la hauteur de ce qui le précède. */
.compte-flanc {
  margin-top: auto;
  padding-top: var(--s-4);
  border-top: 1px solid rgba(255,255,255,.14);
  display: grid;
  gap: var(--s-2);
}
.compte-flanc .nom { font-weight: 600; color: var(--papier); }
.compte-flanc .role {
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--soleil-300);
}

.zone { display: flex; flex-direction: column; min-width: 0; }

.barre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-5) var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--trait);
  background: var(--carte);
}
.barre .titrage { display: grid; gap: .15rem; min-width: 0; }
.barre .surtitre {
  font-size: .76rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--encre-3);
}
.barre .actions { display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* `flex: 1` pousse le pied en bas quand la page est courte: sans lui, il
   remonte se coller au contenu et la coque paraît inachevée. */
main { flex: 1; padding: var(--s-5); display: grid; gap: var(--s-5); align-content: start; }

/* POURQUOI `min-width: 0` sur les enfants de la grille. Un élément de grille
   vaut `min-width: auto` par défaut, c'est-à-dire « jamais plus étroit que
   mon contenu ». La carte refusait donc de descendre sous la largeur
   minimale du tableau qu'elle contient, et c'est la PAGE qui débordait:
   570 px de large pour une fenêtre de 390. L'enveloppe du tableau a beau
   savoir défiler, elle n'en a jamais eu l'occasion. */
main > * { min-width: 0; }

@media (min-width: 64rem) {
  /* POURQUOI la coque porte elle-même la couleur du flanc, en dégradé net.
     Le flanc est collant et haut d'une fenêtre: sur une page plus longue que
     l'écran, sa colonne se vidait sous lui et laissait voir le papier, comme
     si le menu s'arrêtait au milieu de la page. Peindre la bande sur le
     conteneur la fait descendre jusqu'en bas sans ajouter de balise. */
  .coque {
    grid-template-columns: var(--flanc) 1fr;
    background: linear-gradient(to right, var(--vert-900) 0 var(--flanc), transparent var(--flanc));
  }
  .flanc { position: sticky; top: 0; height: 100vh; }
  .barre { padding: var(--s-6) var(--s-7) var(--s-5); }
  main { padding: var(--s-6) var(--s-7) var(--s-8); }
  h1 { font-size: 2.2rem; }
}

/* ═══════════════════════════ Cartes ═══════════════════════════ */

.carte {
  background: var(--carte);
  border: 1px solid var(--trait);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.carte > .tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--trait);
}
.carte > .corps { padding: var(--s-5); }

/* ═══════════════════════ Tuiles de chiffres ═══════════════════ */

.tuiles {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
  /* POURQUOI `min(13rem, 100%)` et non `13rem`: à 390 px, deux tuiles de
     208 px plus l'écart débordaient de la fenêtre, et toute la page prenait
     une barre de défilement horizontale. Avec le `min`, la piste se replie
     sur une colonne quand la place manque au lieu de forcer la largeur. */
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}
/* POURQUOI LA COULEUR FAIT LE TOUR DE LA TUILE, ET NE TIENT PAS SUR UN CÔTÉ.
   Ces tuiles portaient un filet de 5 px à gauche, vert, jaune ou terre cuite
   selon l'état. David l'a écarté le 17 octobre 2026: « les encarts avec un
   seul côté coloré, c'est typiquement IA cette représentation. » La consigne
   était d'ailleurs déjà écrite dans les fichiers rédigés du site, deux fois,
   dans les mots du graphiste: « bordure complète, jamais de filet coloré sur
   un seul côté ».

   Un état se dit donc par le cadre entier et un fond teinté, comme les
   encarts du site. Une tuile sans état garde le cadre neutre. */
.tuile {
  padding: var(--s-5);
  background: var(--carte);
  border: 1px solid var(--trait);
  border-radius: var(--r-md);
  box-shadow: var(--sh-dur);
}
.tuile.att { border-color: var(--alerte); background: var(--alerte-fond); }
.tuile.urg { border-color: var(--erreur); background: var(--erreur-fond); }

/* POURQUOI le texte secondaire fonce sur une tuile teintée: `--encre-3` est
   calibré pour le papier, pas pour un fond jaune ou rose. Sur ces deux-là il
   tombait sous le seuil WCAG AA, relevé par axe sur « signalements
   nouveaux ». La même phrase doit se lire aussi bien dans les deux états. */
.tuile.att .quoi, .tuile.att .appoint,
.tuile.urg .quoi, .tuile.urg .appoint { color: var(--encre-2); }

.tuile .valeur {
  display: block;
  font-family: var(--ff-titre);
  font-weight: 800;
  font-size: 2.6rem;
  line-height: 1;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--encre);
}
.tuile .quoi {
  display: block;
  margin-top: var(--s-2);
  font-size: .9rem;
  font-weight: 500;
  color: var(--encre-3);
}
.tuile .appoint { display: block; margin-top: var(--s-1); font-size: .82rem; color: var(--encre-3); }

/* ═══════════════════════════ Tableaux ═══════════════════════════ */

/* POURQUOI le tableau est enveloppé: sur un écran étroit, un tableau de
   quatre colonnes déborde. L'enveloppe le fait défiler seule, et la page
   garde sa largeur. */
.table-enveloppe { overflow-x: auto; }

/* POURQUOI une largeur minimale au tableau: sans elle, il se comprime au lieu
   de défiler, et « Le Panier de Campagne » tombe sur trois lignes dans une
   colonne de 80 px. Mieux vaut faire glisser le tableau que le tasser. */
.table-enveloppe table { min-width: 34rem; }

table { width: 100%; border-collapse: collapse; font-size: .94rem; }
thead th {
  position: sticky;
  top: 0;
  background: var(--papier-2);
  text-align: left;
  padding: .7rem var(--s-4);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--encre-3);
  border-bottom: 1px solid var(--trait);
  white-space: nowrap;
}
tbody td { padding: .78rem var(--s-4); border-bottom: 1px solid var(--trait); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--papier-2); }
tbody td:first-child { font-weight: 600; color: var(--encre); }
tbody a { text-decoration: none; }
tbody a:hover { text-decoration: underline; }

/* ═══════════════════════════ Pastilles ═══════════════════════════ */

.etat {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .2rem .62rem .24rem;
  border-radius: var(--r-pill);
  font-size: .78rem;
  font-weight: 600;
  line-height: 1.4;
  border: 1px solid transparent;
  white-space: nowrap;
}
.etat::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.etat-publie    { background: var(--succes-fond); color: var(--succes); border-color: #bcd4bf; }
.etat-brouillon { background: var(--alerte-fond); color: var(--alerte); border-color: #e8d49a; }
.etat-depublie  { background: var(--erreur-fond); color: var(--erreur); border-color: #e7b9b4; }

/* ═══════════════════════════ Boutons ═══════════════════════════ */

button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: .62rem 1.15rem;
  border: 2px solid transparent;
  border-radius: var(--r-pill);
  font-family: var(--ff-texte);
  font-size: .95rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  background: var(--vert-700);
  color: var(--papier);
  transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
button:hover, .btn:hover { background: var(--vert-900); color: var(--papier); }
button:active, .btn:active { transform: translateY(1px); }
button:disabled { opacity: .55; cursor: progress; }

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

.btn-vente { background: var(--soleil-500); color: var(--vert-900); }

/* POURQUOI UN BOUTON DE DESTRUCTION SE DESSINE EN CREUX ET NON EN PLEIN. Un
   aplat rouge attire l'œil et la main: c'est ce qu'on veut d'un bouton
   principal, et l'inverse de ce qu'on veut ici. Le contour rouge dit « geste
   grave » sans réclamer le clic, et le plein n'arrive qu'au survol, quand la
   main est déjà posée dessus. */
.btn-danger {
  background: transparent;
  color: var(--erreur);
  border-color: var(--erreur);
}
.btn-danger:hover { background: var(--erreur); color: var(--papier); }

/* La carte qui porte un geste sans retour se distingue de celles qui corrigent.
   Le fond reste clair: c'est une mise en garde, pas une alarme. */
/* LA BARRE DE COPIE DES HORAIRES, posée le 21 octobre 2026.

   POURQUOI ELLE SE DISTINGUE DE LA GRILLE SANS S'EN DÉTACHER. Elle n'est pas un
   champ du formulaire: elle remplit les autres. Un fond légèrement différent le
   dit, et le trait du haut la rattache à ce qu'elle commande. */
.horaires-copie {
  display: grid;
  gap: var(--s-3);
  margin-block-end: var(--s-5);
  padding: var(--s-4);
  background: var(--papier-2);
  border-radius: var(--r-sm);
}
.horaires-copie-titre { margin: 0; font-weight: 600; }
.horaires-copie-jours { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.horaires-copie-choix { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.horaires-copie-plages { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* POURQUOI LA CASE EST CACHÉE ET NON RETIRÉE: elle reste la commande, donc le
   clavier l'atteint et le lecteur d'écran l'annonce. C'est l'étiquette qui se
   dessine, et `:focus-within` rend le parcours au clavier visible. */
/* ⚠️ POURQUOI CES TROIS ANNULATIONS, David le 21 octobre 2026: « modification
   des horaires sur une fiche. Le lundi est déformé. »

   Il n'était pas déformé, il était désaligné, et la cause est une règle qui ne
   le visait pas. `.formulaire label { margin-top: var(--s-3) }` pose une marge
   haute à TOUS les labels d'un formulaire, et `.formulaire label:first-of-type`
   l'annule pour le premier. Ces pastilles sont des `<label>`: les six dernières
   descendaient de 12 px, « Lu » restait en place, et l'œil lisait une pastille
   d'une autre taille.

   Une pastille de jour n'est pas une étiquette de champ. Elle reprend donc la
   main sur sa marge, sur sa largeur, qui doit être la même pour les sept quel
   que soit le texte, et sur son retrait, pour que le conteneur flex ne les
   compresse pas quand la ligne est étroite. */
.jeton-jour {
  position: relative;
  display: inline-flex;
  flex: none;
  margin-top: 0;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding-inline: 0;
  background: var(--carte);
  border: 1.5px solid var(--trait);
  border-radius: var(--r-pill);
  cursor: pointer;
  user-select: none;
}
.jeton-jour input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.jeton-jour:has(input:checked) {
  background: var(--vert-700);
  border-color: var(--vert-700);
  color: var(--papier);
  font-weight: 600;
}
.jeton-jour:has(input:focus-visible) { outline: 3px solid var(--argile-500); outline-offset: 2px; }

/* ⚠️ CETTE RÈGLE EXISTE POUR SA SPÉCIFICITÉ, et sans elle la correction
   ci-dessus ne corrigeait rien. `.formulaire label` vaut (0,1,1) et `.jeton-jour`
   (0,1,0): la première l'emporte, où qu'elle soit dans le fichier, et sa
   `margin-top` serait restée. Une annulation qui perd la cascade est une
   annulation qui n'a pas lieu, et c'est le genre de correction qu'on croit
   faite. Vérifié en relisant la ligne 661 avant de déployer. */
.formulaire .jeton-jour,
.horaires-copie-jours .jeton-jour { margin-top: 0; }

/* POURQUOI UN BOUTON QUI RESSEMBLE À UN LIEN: ces cinq-là ne font que cocher
   des cases. Les dessiner comme des boutons les mettrait au même rang que
   « Appliquer », qui est le seul à agir. */
.btn-lien {
  padding: 4px 0;
  background: none;
  border: 0;
  color: var(--vert-700);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.btn-lien:hover { color: var(--argile-700); }

/* POURQUOI L'ADRESSE EST DANS UN `fieldset`. Ce sont quatre champs qui ne se
   corrigent QUE tous ensemble: changer la rue sans le code postal donne une
   adresse qui n'existe pas. Le cadre le dit à l'œil, et la légende le dit aux
   lecteurs d'écran, qui annoncent le groupe avant chaque champ. */
.bloc-adresse {
  display: grid;
  gap: var(--s-2);
  margin-block: var(--s-4);
  padding: var(--s-4);
  border: 1px solid var(--trait);
  border-radius: var(--r-sm);
}
.bloc-adresse legend {
  padding-inline: var(--s-2);
  font-weight: 600;
  color: var(--encre);
}

.carte-danger { border-color: var(--erreur); }
.carte-danger > .tete { border-bottom-color: var(--erreur); }
.carte-danger > .tete h2 { color: var(--erreur); }
.btn-vente:hover { background: var(--soleil-300); color: var(--vert-900); }

/* Le bouton qui n'en est pas un: déconnexion dans le flanc, lien dans une
   ligne de texte. Il garde la sémantique d'un bouton, qui soumet un
   formulaire, et l'apparence d'un lien. */
button.lien {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--vert-100);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
}
button.lien:hover { background: none; color: var(--soleil-300); }

/* ═══════════════════════════ Formulaires ═══════════════════════════ */

/* ═══════════════════ La carte d'un parcours en étapes ════════════ */

/* POURQUOI cette carte est traitée à part des autres écrans de gestion: les
   écrans en tableau — fiches, finances, statistiques — veulent toute la
   largeur, et une liste de 221 lignes centrée dans une colonne étroite serait
   absurde. Un parcours, lui, est une suite de questions courtes: `.formulaire`
   plafonnait déjà à 30rem, mais collé au bord gauche d'une carte deux fois
   plus large, ce qui laissait un vide sur toute la droite de l'encart.

   POURQUOI la colonne entière et pas seulement le formulaire: le titre, les
   pastilles d'étape et la phrase d'introduction sont des frères du formulaire,
   pas ses enfants. Centrer le seul formulaire l'aurait décalé de son propre
   titre. C'est la colonne qui se centre, et tout s'aligne sur le même axe.

   La phrase d'introduction y gagne au passage: elle courait sur toute la
   largeur de la carte, une longueur de ligne que personne ne lit confortablement. */
.carte-parcours > .corps {
  /* 42rem et non 34: mesuré sur la page servie, les quatre pastilles d'étape
     demandent 586 px et n'en avaient que 544. Elles repassaient donc à la
     ligne, « 4 Récapitulatif » seul sur une seconde rangée, ce qui donnait
     l'impression d'un parcours à deux lignes d'étapes. À 42rem elles tiennent
     sur une ligne avec 86 px de marge, assez pour une police un peu plus
     large ou un libellé un peu plus long. */
  max-width: 42rem;
  margin-inline: auto;
}

/* Le titre et les pastilles se touchaient — écart mesuré: 0 px, le h1 n'ayant
   pas de marge basse. Les pastilles sont une barre d'avancement, pas un
   sous-titre: elles ont besoin de leur propre respiration. */
.carte-parcours .jalons { margin-top: 1rem; }

/* POURQUOI le plafond de `.formulaire` saute ICI et seulement ici: 30rem
   convient à l'écran de connexion, deux champs courts au milieu d'une carte
   étroite. Dans le parcours, il laissait les champs à 480 px dans une colonne
   de 672, soit le vide à droite que la colonne centrée venait justement de
   supprimer. Les champs occupent maintenant la colonne. */
.carte-parcours .formulaire { max-width: none; }

/*
 * Le titre, les étapes et le bouton d'envoi se centrent; les champs non.
 *
 * POURQUOI CETTE LIGNE DE PARTAGE. Un parcours en quatre écrans se lit comme
 * une page de couverture: le titre dit où l'on est, les pastilles où l'on en
 * est, le bouton où l'on va. Ces trois-là encadrent le formulaire et gagnent
 * à être sur l'axe de la colonne.
 *
 * Les étiquettes de champ, elles, restent à gauche, et ce n'est pas une
 * inconséquence: on lit une étiquette juste avant de taper dans le champ
 * qu'elle annonce, et une étiquette centrée au-dessus d'un champ pleine
 * largeur oblige l'oeil à chercher son début à chaque ligne. Un formulaire
 * se parcourt de haut en bas sur un seul bord.
 *
 * Les pastilles suivent le titre parce qu'elles forment un seul bloc avec
 * lui — un titre centré au-dessus d'une rangée collée à gauche se lirait
 * comme un oubli. Demandé par David le 18 octobre 2026 pour le titre et le
 * bouton; les pastilles, c'est moi, et une ligne suffit à l'annuler.
 */
.carte-parcours h1 { text-align: center; }
.carte-parcours .jalons { justify-content: center; }
.carte-parcours .formulaire button[type="submit"] { justify-self: center; }

/* Le texte reste aligné à gauche DANS la colonne: une étiquette centrée
   au-dessus de son champ oblige l'oeil à chercher son début à chaque ligne. */

.formulaire { display: grid; gap: var(--s-2); max-width: 30rem; }
.formulaire label { font-weight: 600; color: var(--encre); margin-top: var(--s-3); }
.formulaire label:first-of-type { margin-top: 0; }
.formulaire button[type="submit"] { margin-top: var(--s-4); justify-self: start; }

input[type="email"], input[type="password"], input[type="search"],
input[type="text"], input[type="url"], input[type="tel"], select, textarea {
  width: 100%;
  padding: .62rem .8rem;
  border: 1px solid var(--trait);
  border-radius: var(--r-xs);
  font: inherit;
  color: var(--encre);
  background: var(--carte);
}
input:hover, select:hover, textarea:hover { border-color: var(--encre-3); }
input::placeholder { color: var(--encre-3); opacity: .7; }

/* POURQUOI la grille n'a que deux colonnes et pourquoi le libellé doit tenir
   dans UN seul élément: chaque enfant occupe une cellule. Un libellé écrit en
   « texte + <span> + texte » faisait trois enfants, donc une case, un bout de
   phrase, et le reste renvoyé à la ligne suivante sous la case. */
label.case {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-3);
  align-items: start;
  margin-top: var(--s-3);
  font-weight: 400;
  line-height: 1.5;
}
label.case input[type="checkbox"] { width: 20px; height: 20px; margin-top: .18rem; accent-color: var(--vert-700); }

.discret { color: var(--encre-3); font-size: .875rem; }

/* POURQUOI CES DEUX RÈGLES ARRIVENT SI TARD, relevé le 21 octobre 2026 en
   écrivant les écrans du rappel de mot de passe: `.aide` et `.intro` étaient
   employées par `demander-reprise.html.twig` et `connexion.html.twig`, et
   AUCUNE DES DEUX n'avait de règle dans cette feuille. Les deux paragraphes
   sortaient donc en texte courant, à la taille du corps, séparés par la seule
   marge par défaut du navigateur.

   C'est la même famille que le reste: une classe écrite dont la règle n'arrive
   jamais. Et c'est une part de ce que David signale depuis trois jours sous le
   nom de « manque d'aération entre les boutons et le texte »: `.aide` sert
   précisément à poser les liens de sortie sous un formulaire, là où il le
   voyait collé.

   `.aide` prend sa marge EN HAUT, par le même raisonnement que les titres plus
   haut: elle appartient à ce qui la précède et doit s'en détacher. `.intro`
   la prend en bas, parce qu'elle annonce ce qui suit. */
/* POURQUOI CETTE CLASSE EXISTE, David le 21 octobre 2026: « l'encart avec les
   prix d'un abonnement est toujours identique. Je t'ai demandé de centrer
   l'encart, de réduire sa taille et de centrer son contenu. »

   POURQUOI PAS `body.seuil`, qui centre déjà et borne à 34rem: elle est faite
   pour les écrans SANS navigation, la connexion et le rappel de mot de passe.
   Elle retire le menu latéral et le pied, et l'écran d'abonnement en a besoin:
   on y arrive depuis son tableau de bord et on doit pouvoir y retourner.

   POURQUOI LE CONTENU EST CENTRÉ ICI ET NULLE PART AILLEURS dans la gestion:
   cet écran porte trois boutons de prix et deux phrases, rien d'autre. Un texte
   long centré se lit mal, et c'est pour cela que la règle est portée par une
   classe qu'on pose, plutôt que par `.carte`. */
/* La carte du remerciement, après un paiement. Elle porte la couleur de la
   réussite sans crier: le commerçant vient de payer, il attend une confirmation
   calme, pas une fanfare. */
.carte-merci { border-color: var(--succes, var(--trait)); }
.carte-merci > .corps > p + p { margin-top: var(--s-4); }

.carte-etroite { width: min(32rem, 100%); margin-inline: auto; }
.carte-etroite > .corps { text-align: center; }
.carte-etroite .formulaire { max-width: none; justify-items: center; }
.carte-etroite .formulaire button[type="submit"] { justify-self: center; }
.carte-etroite .message { text-align: start; }

.aide {
  margin-block-start: var(--s-5);
  color: var(--encre-3);
  font-size: .875rem;
  line-height: 1.5;
}
.aide a { color: inherit; }
.intro {
  margin-block: 0 var(--s-4);
  color: var(--encre-2);
  font-size: 1.0625rem;
}

/* Barre d'outils d'une liste: la recherche et ses boutons sur une ligne. */
.recherche { display: flex; gap: var(--s-2); align-items: end; flex-wrap: wrap; }
.recherche label { font-weight: 600; color: var(--encre); }
.recherche .champ { display: grid; gap: var(--s-1); min-width: 15rem; flex: 0 1 26rem; }

/* ═══════════════════════════ Messages ═══════════════════════════ */

/* POURQUOI ce bloc n'est PAS en `flex`. Il l'était, avec un `gap`, et un
   message porte du texte courant: chaque morceau entre deux balises devenait
   un élément de flexion, et le `gap` s'insérait entre eux. « Au Pré Vert,
   Boussu » se lisait « Au Pré Vert , Boussu », avec une espace avant la
   virgule. Un conteneur de texte se met en bloc. */
/* Même règle ici: le cadre fait le tour. Ce bloc portait un bord gauche de
   5 px, qui est la forme que David a nommée. */
.message {
  display: block;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--trait);
  border-radius: var(--r-sm);
  background: var(--carte);
}
.message.erreur  { border-color: var(--erreur); background: var(--erreur-fond); color: var(--erreur); }
.message.succes  { border-color: var(--succes); background: var(--succes-fond); color: var(--succes); }
.message.info    { border-color: var(--info);   background: var(--info-fond);   color: var(--info); }

/* ═══════════════════════════ Vide et pagination ═══════════════════ */

/* POURQUOI un état vide dessiné et non une phrase grise: c'est le premier
   écran qu'on voit sur une installation neuve, et c'est là qu'on décide si
   l'outil est fini ou pas. */
.vide {
  display: grid;
  justify-items: center;
  gap: var(--s-3);
  padding: var(--s-8) var(--s-5);
  text-align: center;
  color: var(--encre-3);
}
.vide svg { width: 92px; height: 92px; }
.vide p:not(.actions-ligne) { max-width: 42ch; margin: 0; }

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--trait);
}
.pagination .pages { display: flex; gap: var(--s-2); }

footer.pied {
  padding: var(--s-5) var(--s-7);
  border-top: 1px solid var(--trait);
  font-size: .875rem;
  color: var(--encre-3);
}

/* ═══════════════════ Connexion et revendication ═══════════════════ */

/* Ces deux pages n'ont pas de flanc: on n'y est pas encore entré. Elles se
   présentent comme une carte posée au milieu, sur le fond du site. */
body.seuil { display: grid; place-items: center; padding: var(--s-5); min-height: 100vh; }
body.seuil .boite { width: min(34rem, 100%); display: grid; gap: var(--s-5); }
body.seuil header { display: flex; justify-content: center; }
body.seuil .marque { color: var(--encre); font-size: 1.3rem; }
/* POURQUOI l'opacité est remise à 1: la règle du flanc pose `opacity: .72`,
   qui convient sur le vert sombre et ne convient pas sur le papier. Héritée
   telle quelle, elle donnait #899287 sur #FBF6EE, soit 2,99 pour un seuil de
   4,5 — relevé par axe sur les deux pages de seuil. */
body.seuil .marque span { color: var(--encre-3); opacity: 1; }
body.seuil .marque i { color: var(--argile-700); }
body.seuil main { padding: 0; }
body.seuil .carte > .corps { padding: var(--s-6); }
body.seuil footer.pied { padding: 0; border: 0; text-align: center; }

/* Le parcours de revendication montre un contrat entier: il lui faut plus de
   large que l'écran de connexion. */
body.seuil.large .boite { width: min(52rem, 100%); }

/*
 * ⚠️ DANS UNE CARTE DE SEUIL, UN CHAMP PREND LA LARGEUR ET LE BOUTON SE
 * CENTRE. David, le 26 octobre 2026, sur « C'est mon commerce »: « même
 * remarque, centrer le contenu ». Et la règle est plus ancienne que ça — elle
 * date du formulaire de revendication de magasin-bio: « bouton centré et le
 * texte en-dessous aussi ».
 *
 * CE QUI SE PASSAIT: la carte fait 52 rem, les champs gardaient leur largeur
 * naturelle, et la moitié droite restait vide sur toute la hauteur. C'est la
 * demi-colonne, dans un formulaire.
 *
 * Ces trois règles valent pour les trois domaines: le même socle sert
 * magasin-bio.be, ferme-bio.be et, demain, producteur-bio.be.
 */
/*
 * ⚠️ TOUT LE CONTENU DE LA CARTE EST CENTRÉ, ET PAS SEULEMENT LE BOUTON. Ma
 * première passe centrait le bouton et élargissait les champs; David a renvoyé
 * la capture: titre, phrase, étiquettes et champs restaient à gauche d'une
 * carte de 52 rem, la moitié droite vide sur toute la hauteur. « Dans un
 * encart comme celui-là, le contenu centré » — puis, une heure plus tard,
 * « centrer, centrer, centrer », et « sur tous les sites ».
 *
 * Une carte de seuil ne porte qu'une chose à faire: entrer un code, donner une
 * adresse, choisir un mot de passe. Un seul axe, donc.
 */
body.seuil .carte,
body.seuil .carte > .corps { text-align: center; }

body.seuil .carte form,
body.seuil .carte > .corps > div { justify-items: center; }

/* Le champ prend la largeur de la carte, et son texte reste à gauche: un
   courriel centré dans son cadre se lit mal, et le curseur saute. */
body.seuil .carte input:not([type="checkbox"]):not([type="radio"]),
body.seuil .carte textarea,
body.seuil .carte select { width: 100%; text-align: start; }

body.seuil .carte label { display: block; }

/*
 * ⚠️ ET LE FORMULAIRE N'A PAS DE PLAFOND DANS UNE CARTE DE SEUIL. `.formulaire`
 * borne à 30 rem (plus haut dans cette feuille), ce qui est juste dans un
 * tableau de bord où le formulaire vit à côté d'autre chose. Dans une carte de
 * seuil, qui ne porte que lui, le plafond laissait les champs à 58 % d'une
 * carte de 52 rem: centrés, donc, mais avec du vide des deux côtés.
 *
 * Mesuré le 27 octobre 2026 sur `/gestion/revendiquer/demande`, capture de
 * David à l'appui: « Ce formulaire, 4ème fois. Centrer ET pleine largeur du
 * contenu dans l'encart. Pareil pour magasin-bio. » Les deux moitiés de la
 * demande sont là: le centrage au-dessus, la largeur ici. Même dispense que
 * `.carte-parcours` et `.carte-etroite`, pour la même raison.
 */
body.seuil .carte .formulaire { max-width: none; }

/* Les listes et les encarts de conditions gardent leur alignement: un contrat
   centré ne se lit pas. */
body.seuil .carte ul,
body.seuil .carte ol,
body.seuil .carte .conditions,
body.seuil .carte .conditions * { text-align: start; }

/* ───────────────────────────────────────────────────────────────────────────
   L'encart des conditions générales, au moment de la case à cocher.

   Repris du dispositif de FoodIt.Be, que David a nommé le 17 octobre 2026, et
   des mesures qui l'ont imposé là-bas. Aucun script.

   ⚠️ SUR TÉLÉPHONE, LE CONTRAT NE VIT PAS DANS UNE FENÊTRE QUI DÉFILE.
   Mesuré chez FoodIt le 16/09: le cadre faisait 340 × 272 px, un tiers de
   l'écran, pour 40 460 px de contenu — cent quarante-neuf hauteurs de cadre à
   faire défiler DANS une page qui défile déjà, juste au-dessus de la case
   « J'ai lu ». Le pouce qui veut descendre la page tombe dedans et n'en sort
   plus. On ne met jamais une zone de défilement dans une page qui défile, sur
   un écran tactile. Le cadre perd donc sa hauteur propre et le contrat entier
   passe dans le `<details>` natif.

   ⚠️ LE CADRE EST LE FRÈRE DU `<details>`, PAS SON ENFANT. Enfant, la règle
   au-dessus de 48 rem comptait sur `display: block` pour rouvrir le contenu
   d'un `details` fermé. Elle ne le rouvre pas: un navigateur récent le masque
   par `content-visibility` sur `::details-content`, que `display` ne couvre
   pas. Mesuré le 17/09/2026 en production à 1280 px: résumé `display: none`,
   cadre `display: block` de 458 × 272 px, et RIEN À L'ÉCRAN — les conditions
   invisibles sur tout écran large, au-dessus d'une case qui dit « J'ai lu et
   j'accepte ».

   Et le sens de la panne s'inverse: sans cette feuille, les deux s'affichent.
   Un contrat qu'on ne voit pas est le seul défaut qui ne se rattrape pas.
   ─────────────────────────────────────────────────────────────────────────── */
details.cg-repli { margin: var(--s-4) 0 var(--s-2); }
details.cg-repli > summary {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  font-weight: 600;
  color: var(--encre);
  cursor: pointer;
}

.cg { display: none; }
details.cg-repli[open] ~ .cg {
  display: block;
  height: auto;
  overflow: visible;
}

/* Au-dessus de 48 rem il y a la place: le `<details>` disparaît entièrement et
   le cadre reprend sa hauteur et son défilement propre, quel que soit l'état
   du résumé. C'est ce qui rend la règle sûre: elle ne dépend plus de la façon
   dont un navigateur masque le contenu d'un `details` fermé. */
@media (min-width: 48rem) {
  details.cg-repli { display: none; }
  .cg { display: block; height: 17rem; overflow-y: auto; }
}

.cg {
  margin: var(--s-2) 0 var(--s-4);
  padding: var(--s-4) var(--s-5);
  background: var(--papier-2);
  border: 1px solid var(--trait);
  border-radius: var(--r-sm);
  font-size: .88rem;
  line-height: 1.6;
}
.cg h2 { margin: var(--s-4) 0 var(--s-2); font-size: .98rem; }
.cg h2:first-child { margin-top: 0; }
.cg p { margin: 0 0 .55rem; }
.cg ul { margin: 0 0 .55rem; padding-left: 1.1rem; }
.cg li { margin: .15rem 0; }

/* La version d'un seul tenant: coupée à son point, « Version 1. / 0 » ne se
   lit plus comme une version. */
.case .version { white-space: nowrap; font-weight: 600; }

/* ═══════════════════ Fiche d'un établissement ═══════════════════ */

/* Les faits qu'on ne corrige pas ici: deux colonnes d'étiquettes et de
   valeurs, qui se replient sur une seule quand la place manque. */
.faits { margin: 0; display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
.faits > div { display: grid; gap: .1rem; }
.faits dt { font-size: .76rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--encre-3); }
.faits dd { margin: 0; color: var(--encre); }

/* Un formulaire de correction tient plus large qu'un formulaire de connexion:
   une description sur 30 rem se lit mal. */
.formulaire.large { max-width: 44rem; }
.formulaire .duo { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr)); }
.formulaire .duo > div { display: grid; gap: var(--s-2); }
.formulaire .duo label { margin-top: 0; }
textarea { min-height: 7rem; resize: vertical; line-height: 1.6; }

/* POURQUOI l'enveloppe d'un tableau est atteignable au clavier: elle défile
   sur un écran étroit, et une zone qui défile sans arrêt de tabulation est
   inatteignable pour qui n'a pas de souris. Relevé par axe, règle
   « scrollable-region-focusable », à 390 px. */
.table-enveloppe:focus-visible { outline: 3px solid var(--argile-500); outline-offset: -3px; }

/* ------------------------------------------------------------
   L'espace du commerçant. Deux formes que l'administration de
   l'exploitant n'avait pas: la liste de ce qui manque à une
   fiche, et la rangée de boutons au bas d'une carte.
   ------------------------------------------------------------ */

/* Ce qui manque à une fiche pour paraître.

   POURQUOI UNE PUCE DESSINÉE ET NON UN DISQUE: chaque entrée est une chose à
   faire, pas un élément d'énumération. Le trait oblique se lit comme une case
   vide, et il tient en noir et blanc.

   POURQUOI PAS DE FILET COLORÉ SUR LE CÔTÉ: la consigne du graphiste, reprise
   par David deux fois, et vérifiée à chaque poussée par
   outils/verifier-filets-colores.mjs. L'encart porte une bordure complète ou
   rien. */
/* POURQUOI LA LISTE N'A PAS DE FOND, alors que le constat juste au-dessus en
   a un. Les deux portaient le même jaune, et sur un téléphone cela donnait
   deux pavés de la même couleur l'un sur l'autre pour dire une seule chose.
   Le constat annonce, la liste détaille: un seul des deux a besoin de se
   détacher. */
.manques {
  margin: var(--s-4) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: .55rem;
}
.manques li {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  color: var(--encre-2);
  font-size: .96rem;
}
.manques li::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: .24rem;
  border: 2px solid var(--alerte);
  border-radius: 3px;
}

/* La rangée de boutons au bas d'une carte.

   POURQUOI « flex-wrap » ET NON UNE GRILLE À DEUX COLONNES: il y a parfois un
   bouton, parfois deux, et une grille laisserait une demi-colonne vide dans
   le premier cas. C'est la forme que David a relevée trois fois sur le site
   public, et elle n'a pas plus sa place ici. */
.actions-ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
  margin: var(--s-5) 0 0;
}

/* Le titre d'une carte porte parfois un appoint en gris. Sans cette règle il
   hérite de la graisse et de la taille du titre, et les deux se confondent. */
.carte > .tete h2 .appoint {
  font-family: var(--ff-texte);
  font-size: .9rem;
  font-weight: 500;
  color: var(--encre-3);
}

/* Le constat en tête d'une carte: une ligne, un signe, et ce qui se passe.

   POURQUOI UNE BORDURE COMPLÈTE ET NON UN FILET À GAUCHE: c'est la forme que
   David a écartée deux fois, et que le graphiste avait déjà écrite dans les
   consignes du site — « bordure complète, jamais de filet coloré sur un seul
   côté ». outils/verifier-filets-colores.mjs la refuse à chaque poussée. */
.constat {
  display: flex; align-items: flex-start; gap: .7rem;
  margin: 0 0 var(--s-4);
  padding: var(--s-3) var(--s-4);
  border: 1px solid; border-radius: var(--r-md);
  font-size: .96rem;
}
.constat svg { flex: none; width: 22px; height: 22px; margin-top: .05rem; }
.constat span { flex: 1 1 auto; }
.constat-ok    { color: var(--succes); background: var(--succes-fond); border-color: #bcd4bf; }
.constat-faire { color: var(--alerte); background: var(--alerte-fond); border-color: #e8d49a; }

/* Le ruban des quatre-vingt-dix jours.

   POURQUOI UN RUBAN ET NON UN NOMBRE DE JOURS EN GRIS. C'est la promesse
   commerciale du site, écrite sur la page des tarifs. Rangée dans une liste
   de faits elle se lisait comme une mention légale; posée à plat elle répond
   à la seule question que le commerçant se pose entre le jour 1 et le jour
   90. Le curseur est placé en pourcentage, donc la même image sert tout du
   long, sans qu'aucune valeur ne soit calculée dans la feuille.

   POURQUOI « aria-label » SUR LE CONTENEUR ET « aria-hidden » SUR LES BORNES:
   un lecteur d'écran qui lit « Jour 1, la fiche paraît, entre les deux, aucun
   appel, jour 90 » sans voir le curseur n'apprend pas où l'on en est. La
   phrase du conteneur le dit, et la ligne sous le ruban la redit à l'écran. */
.ruban { margin: var(--s-5) 0; }
/* POURQUOI LES DIX PIXELS DE MARGE: le curseur fait 20 px et se centre sur sa
   position. Au jour 1 il est à 0 %, donc la moitié de son disque sortirait du
   bloc. La marge lui laisse la place aux deux bouts, et les bornes s'alignent
   dessus. */
.ruban-bande {
  margin-inline: 10px;
  position: relative;
  height: 14px;
  background: var(--papier-2);
  border: 2px solid var(--encre);
  border-radius: var(--r-pill);
}
.ruban-plein {
  position: absolute; inset: 0 auto 0 0;
  background: var(--soleil-300);
  border-radius: var(--r-pill);
}
.ruban-curseur {
  position: absolute; top: 50%;
  width: 20px; height: 20px; margin: -10px 0 0 -10px;
  background: var(--vert-500);
  border: 2px solid var(--encre);
  border-radius: 50%;
}
.ruban-bornes {
  display: flex; justify-content: space-between; gap: var(--s-4);
  margin: .6rem 10px 0;
}
.ruban-bornes span {
  display: grid; gap: .1rem;
  flex: 1 1 0;
  font-size: .8rem; color: var(--encre-3);
}
.ruban-bornes b { font-size: .84rem; color: var(--encre); }
.ruban-bornes .milieu { text-align: center; }
.ruban-bornes .fin { text-align: right; }
.ruban-dit { margin: var(--s-4) 0 0; color: var(--encre-2); }

/* La carte d'offre, la seule de cet espace. Fond soleil plein, bordure
   complète: elle se distingue par sa couleur, pas par un filet sur un bord. */
.carte-offre { background: var(--soleil-100); }
.carte-offre h2 { margin: 0 0 var(--s-3); font-size: 1.15rem; }
/* PAS DE LARGEUR MAXIMALE ICI. David a relevé trois fois des demi-colonnes
   sur le site public, et la consigne vaut pour l'administration: « utilise
   toute la largeur, partout, pour tout ». Une mesure de 72 caractères laisse
   la moitié droite vide dans la colonne de cet espace. */
/* ⚠️ `:not(.actions-ligne)` N'EST PAS UNE PRÉCAUTION, C'EST LA CORRECTION.
   David, 21 octobre 2026: « sur la page gestion du commerçant, encore un manque
   d'aération entre les boutons et le texte ».

   `.actions-ligne` porte `margin: var(--s-5) 0 0`, mais un groupe de boutons
   est écrit `<p class="actions-ligne">`, et `.carte-offre p` est plus
   spécifique: la marge du haut tombait à zéro, et les boutons se collaient au
   paragraphe. Le défaut ne se voyait QUE dans les cartes qui remettent la marge
   des paragraphes à zéro, ce qui explique qu'il ait survécu à la correction des
   titres de ce matin.

   Une règle qui vise un élément par son NOM attrape aussi celui qui porte une
   classe: la seconde dit une intention, le premier ne dit qu'une forme. */
.carte-offre p:not(.actions-ligne) { margin: 0; color: var(--encre-2); }
.carte-offre .actions-ligne { color: var(--encre-2); }

@media (max-width: 599px) {
  .ruban-bornes { display: grid; }
  .ruban-bornes .milieu, .ruban-bornes .fin { text-align: start; }
}

/* ═══════════════ Le parcours de déclaration, quatre écrans ═══════════════ */

/* Les pastilles de progression.

   POURQUOI UNE LISTE ORDONNÉE ET NON DES DIV: un parcours a un ordre, et un
   lecteur d'écran doit l'entendre. `aria-current="step"` dit laquelle est en
   cours; la couleur seule ne le dirait qu'à ceux qui la voient.

   POURQUOI LES PASTILLES SE REPLIENT EN COLONNE SOUS 36 REM plutôt que de
   rétrécir: à quatre pastilles sur une ligne de téléphone, les intitulés
   passent à deux caractères par mot ou débordent. Un parcours dont on ne lit
   plus les étapes ne guide plus personne. */
/*
 * ⚠️ UNE SEULE LIGNE, A TOUTE LARGEUR. Le fil portait `flex-wrap: wrap`, et la
 * quatrieme etape tombait seule sous les trois autres des que la largeur
 * manquait. David l'a releve deux fois.
 *
 * Ce qui tient la ligne: `nowrap`, des intitules qui se laissent reduire
 * (`min-width: 0` sur le li, sinon un enfant flex refuse de passer sous sa
 * largeur de contenu), et en dessous de 48rem les intitules des etapes autres
 * que la courante s'effacent. Les pastilles numerotees restent, l'etape en
 * cours garde son nom, et la ligne ne casse jamais.
 */
.jalons {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-5);
  padding: 0;
  list-style: none;
  counter-reset: none;
}
.jalons li {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
  color: var(--encre-3);
  font-size: .9rem;
}
.jalons li span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 48rem) {
  .jalons li:not(.courant) span { display: none; }
}
.jalons li b {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.65rem;
  height: 1.65rem;
  border: 2px solid var(--trait);
  border-radius: 50%;
  background: var(--carte);
  font-size: .82rem;
  font-weight: 700;
}
.jalons li.fait { color: var(--encre-2); }
.jalons li.fait b { border-color: var(--vert-500); background: var(--vert-100); color: var(--vert-900); }
.jalons li.courant { color: var(--encre); font-weight: 600; }
.jalons li.courant b { border-color: var(--vert-700); background: var(--vert-700); color: var(--papier); }

/* POURQUOI CET ÉCRAN SORT DES 30 REM DU FORMULAIRE: sept jours de quatre
   champs d'heure n'y tiennent pas, et le récapitulatif non plus. La consigne de
   David vaut ici comme ailleurs — « utilise toute la largeur, partout, pour
   tout ». */
.formulaire-large { max-width: none; }

/* Les horaires, jour par jour, deux plages par jour.

   POURQUOI DEUX PLAGES: « un commerce ferme le midi. Une plage unique par jour
   obligerait à mentir ou à perdre l'information », migration 0002.

   POURQUOI UNE GRILLE À COLONNES SOUPLES ET NON UN TABLEAU: le libellé du jour
   doit rester lisible à côté de ses champs sur un téléphone, et un tableau de
   cinq colonnes y impose un défilement horizontal. */
.horaires {
  margin: var(--s-5) 0 0;
  padding: var(--s-4);
  border: 1px solid var(--trait);
  border-radius: var(--r-sm);
  background: var(--papier-2);
}
.horaires legend {
  padding: 0 var(--s-2);
  font-weight: 700;
  color: var(--encre);
}
.horaires .jour {
  display: grid;
  grid-template-columns: 7rem 1fr;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  padding: var(--s-2) 0;
  border-top: 1px solid var(--trait);
}
.horaires .jour:first-of-type { border-top: 0; }

/* POURQUOI LE NOM DU JOUR TIENT LES DEUX LIGNES, ET LES PLAGES LA SECONDE
   COLONNE. La grille a deux colonnes et le jour porte TROIS enfants: son nom et
   ses deux plages. Sans placement explicite, la première plage allait en
   colonne 2 et la seconde retombait en colonne 1, sous le nom du jour — décalée
   de sept rems vers la gauche par rapport à celle du dessus. David l'a relevé le
   19 octobre 2026: « l'alignement des heures n'est pas correct ».

   Le nom occupe donc les deux rangées, et les deux plages la colonne 2. Les
   quatre champs d'un jour s'alignent alors sur la même verticale. */
.horaires .jour b {
  grid-column: 1;
  grid-row: 1 / span 2;
  align-self: start;
  padding-block-start: .6rem;
  color: var(--encre);
  font-size: .92rem;
}
.horaires .plage {
  grid-column: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.horaires .plage + .plage { margin-top: 0; }

/* POURQUOI `input[type="time"]` EST DÉCLARÉ À PART: il n'était pas dans la
   liste des champs stylés de cette feuille, et il serait resté à l'apparence
   native du navigateur au milieu de champs dessinés. Quatorze champs par écran:
   l'écart se voit. */
input[type="time"] {
  padding: .5rem .6rem;
  border: 1px solid var(--trait);
  border-radius: var(--r-xs);
  font: inherit;
  color: var(--encre);
  background: var(--carte);
  min-width: 7.5rem;
}
input[type="time"]:focus-visible { outline: 2px solid var(--vert-700); outline-offset: 1px; }

@media (max-width: 36rem) {
  .horaires .jour { grid-template-columns: 1fr; }
  .horaires .jour b { grid-column: 1; grid-row: auto; padding-block-start: 0; }
  .horaires .plage { grid-column: 1; }
  .horaires .plage + .plage { margin-top: var(--s-2); }
}

/* Le récapitulatif: ce qui est déclaré, relu avant de signer. */
/* POURQUOI UNE MARGE HAUTE ICI, David le 21 octobre 2026: « inscrire un second
   commerce, "ce que vous déclarez" est trop près de "commerce" ». Le `h2` prend
   bien sa marge, mais EN HAUT seulement, par la règle argumentée plus haut: un
   titre appartient à ce qui le suit. Entre lui et cette liste il ne restait
   donc rien, et la première étiquette se collait au titre.

   C'est à la liste de s'écarter, pas au titre de pousser vers le bas: la règle
   des titres vaut pour les onze écrans, celle-ci pour le seul récapitulatif. */
.recapitulatif {
  margin: var(--s-4) 0 var(--s-5);
  display: grid;
  gap: var(--s-3) var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}
.recapitulatif dt {
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--encre-3);
}
.recapitulatif dd { margin: .15rem 0 0; color: var(--encre); }

/* L'étiquette que seul un lecteur d'écran lit.

   POURQUOI PAS `display: none` NI `visibility: hidden`: les deux retirent
   l'élément de l'arbre d'accessibilité, donc personne ne l'entend. Les
   quatorze champs d'heure d'un écran portent chacun son étiquette — « Mercredi,
   ouverture 2 » — et sans elle un lecteur d'écran annonce quatorze fois
   « heure », ce qui ne dit rien. */
.invisible {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ═══════════ L'examen d'une déclaration ═══════════ */

/* POURQUOI UN SEUL AJOUT ICI: l'écran d'examen est bâti avec les formes que
   cette feuille porte déjà — `.carte`, `.tete`, `.faits`, `.actions-ligne`,
   `.table-enveloppe`, `.etat-*`. Seuls les titres de niveau trois d'une carte
   n'avaient pas de règle, parce qu'aucun écran n'en portait jusqu'ici. */
.carte h3 {
  margin: var(--s-5) 0 var(--s-2);
  font-size: 1rem;
  font-weight: 700;
  color: var(--encre);
}
.carte h3:first-of-type { margin-top: var(--s-4); }
.carte > ul { margin: 0 0 var(--s-2); padding-inline-start: 1.25rem; color: var(--encre-2); }

/* ═══════════════════ Les photos d'une fiche ═══════════════════ */

/* POURQUOI UNE GRILLE À COLONNES SOUPLES ET NON UNE LARGEUR FIXE: cet écran
   porte trois vignettes sur une fiche gratuite et jusqu'à soixante sur une
   formule Premium. Une largeur fixe laisserait une demi-colonne vide dans le
   premier cas — la forme que David a relevée trois fois — et déborderait dans
   le second. */
.photos {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
  margin: 0 0 var(--s-5);
  padding: 0;
  list-style: none;
}
.photos > li {
  display: grid;
  gap: var(--s-2);
  padding: var(--s-2);
  border: 1px solid var(--trait);
  border-radius: var(--r-sm);
  background: var(--papier-2);
}

/* POURQUOI UN RAPPORT IMPOSÉ ET `object-fit`: les photos arrivent en portrait
   comme en paysage, et une grille dont chaque cellule a sa hauteur propre
   devient illisible. Le recadrage est à l'affichage seulement — le fichier,
   lui, garde ses proportions. */
.photos img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-xs);
  background: var(--trait);
}
.photo-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  font-size: .86rem;
}
.photo-pied form { margin: 0; }
.photo-pied button { padding: .35rem .7rem; font-size: .82rem; }

/* Le sélecteur de fichier, qui n'était pas déclaré dans cette feuille et
   gardait l'apparence native au milieu de champs dessinés. */
input[type="file"] {
  width: 100%;
  padding: .55rem .7rem;
  border: 1px dashed var(--trait);
  border-radius: var(--r-xs);
  font: inherit;
  color: var(--encre-2);
  background: var(--carte);
}
input[type="file"]:focus-visible { outline: 2px solid var(--vert-700); outline-offset: 1px; }

/* DEUX CARTES CÔTE À CÔTE SUR L'ÉCRAN DE FICHE.

   POURQUOI CETTE GRILLE EXISTE. « Corriger la fiche » et « Horaires » étaient
   empilées, chacune pleine largeur pour un contenu de 42 rems: l'écran portait
   deux grands vides à droite, et David les a relevés le 19 octobre 2026.

   POURQUOI `start` ET NON `stretch`: les deux cartes n'ont pas la même hauteur
   et n'ont aucune raison de l'avoir. Étirées, celle qui est courte montre un
   grand fond vide sous son bouton — le vide qu'on vient de retirer, remis au
   même endroit sous une autre forme.

   POURQUOI LE SEUIL EST À 64rem ET NON AU `36rem` DU RESTE DE LA FEUILLE: ce
   n'est pas une question de téléphone, c'est une question de place. Deux
   formulaires de cette densité tiennent côte à côte à partir d'environ mille
   pixels; en dessous, les champs d'heures se serrent et la lecture se perd. */
.duo-cartes { display: grid; gap: var(--s-5); align-items: start; }
.duo-cartes > .colonne-cartes { display: grid; gap: var(--s-5); align-content: start; }

@media (min-width: 64rem) {
  .duo-cartes { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* La carte vit dans la grille: son plafond de largeur propre la recentrerait
     dans sa colonne et rouvrirait un vide à l'intérieur même de la colonne. */
  .duo-cartes .carte { margin: 0; max-width: none; }
}

/* LE FORMULAIRE D'AJOUT D'UNE PHOTO, EN DEUX MOITIÉS.

   Il tenait dans un quart de la carte, le reste en fond vide. Ses deux moitiés
   sont de nature différente: le fichier et ce qu'on attend de lui d'un côté, ce
   qu'on en dit et l'envoi de l'autre.

   POURQUOI UNE GRILLE ET NON `columns`: des colonnes de texte couperaient entre
   un libellé et son champ. Une grille place chaque moitié entière. */
.photo-ajout { display: grid; gap: var(--s-4); }
.photo-ajout > div { display: grid; gap: var(--s-2); align-content: start; }

/* PAS DE DEUX COLONNES ICI, et c'est la correction du même jour. La carte des
   photos vit désormais dans une colonne à demi-écran: y couper son formulaire
   en deux le rendrait illisible, chaque moitié tenant dans un quart d'écran.
   C'est exactement ce que David a relevé — « l'encart à moitié de l'espace
   alloué, et le contenu en pleine largeur de cet encart ». Le contenu occupe
   donc toute la largeur de sa carte, et la carte toute sa colonne. */

/* LA FILE DE RELECTURE.

   Deux valeurs côte à côte, l'ancienne d'abord: on n'accepte pas un texte, on
   accepte un REMPLACEMENT, et sans ce qu'il remplace « très bien » et « c'est
   pire qu'avant » se ressemblent. */
.prop-texte {
  margin: var(--s-2) 0 0;
  padding: var(--s-3);
  border: 1px solid var(--trait);
  border-radius: var(--r-xs);
  background: var(--papier-2);
  white-space: pre-wrap;
}
.prop-neuve { border-color: var(--vert-700); background: var(--carte); }

/* LA CONSÉQUENCE DE L'ACCEPTATION, entre les deux valeurs et les boutons.
   Pour les horaires, la colonne de gauche est un compte de jours et celle de
   droite un texte de plages: la perte était là, lisible seulement à qui
   comptait lui-même. Discrète quand accepter ne retire rien, en ambre quand
   elle retire des jours connus — la même couleur que les autres constats à
   faire de cet écran, et jamais le rouge, qui dirait « erreur » là où il
   s'agit d'un arbitrage. */
.prop-consequence {
  margin: var(--s-3) 0 0;
  font-size: .88rem;
  color: var(--encre-2);
}
.prop-consequence-perte {
  padding: var(--s-2) var(--s-3);
  border: 1px solid #e8d49a;
  border-radius: var(--r-xs);
  background: var(--alerte-fond);
  color: var(--alerte);
  font-weight: 600;
}

/* POURQUOI LE MOTIF EST ENTRE LES DEUX BOUTONS: refuser l'exige, accepter
   l'ignore. Placé après « Refuser », il se remplit une fois sur deux après coup;
   placé avant, il se lit comme une condition des deux. Entre les deux, il
   appartient visiblement au second. */
.prop-decision {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: center;
  margin-block-start: var(--s-4);
}
.prop-decision input[type="text"] { flex: 1 1 18rem; min-width: 12rem; }

/* LA TUILE DE MODÉRATION EST DEVENUE UN LIEN, et un lien posé dans une tuile
   n'occupe que la hauteur de son texte: le reste du cadre cesse d'être
   cliquable, et la cible devient plus petite qu'elle n'en a l'air. Il prend donc
   toute la surface, et garde la disposition que la tuile donnait à ses trois
   lignes. La couleur et le soulignement restent ceux de la tuile: c'est une
   zone cliquable, pas un lien dans une phrase. */
.tuile > a {
  display: flex;
  flex-direction: column;
  gap: inherit;
  color: inherit;
  text-decoration: none;
}
.tuile > a:hover .quoi { text-decoration: underline; }
.tuile > a:focus-visible { outline: 2px solid var(--vert-700); outline-offset: 3px; border-radius: var(--r-xs); }

/* LES BOUTONS D'ACTION SONT CENTRÉS SOUS LEUR FORMULAIRE.

   POURQUOI CETTE RÈGLE EST ICI ET NON SUR UN SEUL ÉCRAN. David l'a demandée le
   19 octobre 2026 pour le parcours d'inscription — « tu sais aligner au centre
   tous les éléments », puis « centre le titre, le bouton Continuer » — et je
   l'avais posée sur `.carte-parcours` seulement. Il me l'a redit le soir, et il
   avait raison: « tu ne tiens pas compte de mes précédentes remarques avec les
   formulaires et les boutons d'action ». Une consigne de mise en page vaut pour
   la maison entière, pas pour l'écran où elle a été donnée.

   POURQUOI SEULEMENT DANS `.formulaire`, ET PAS TOUS LES BOUTONS. Les actions
   en ligne d'un élément — « Retirer » sous une photo, « Publier » et « Refuser »
   sur une proposition — appartiennent à l'élément qu'elles touchent. Centrées
   sous la carte, elles cesseraient de dire de quoi elles parlent. Leurs
   formulaires ne portent pas `.formulaire`, et c'est ce qui les distingue.

   POURQUOI LES DEUX PROPRIÉTÉS: `justify-self` place l'élément dans une grille,
   `margin-inline` dans un flux ordinaire. Les formulaires de ce dossier sont
   des grilles, mais un bloc ajouté demain pourrait ne pas l'être, et la règle
   ne doit pas dépendre de ce détail. */
.formulaire button[type="submit"] {
  justify-self: center;
  margin-inline: auto;
}

/* ET LE TEXTE SOUS LE BOUTON PREND LE MÊME AXE. David, le 25 octobre 2026:
   « Formulaire de revendication, bouton centré et le texte en-dessous aussi. »
   Le bouton l'était déjà, par la règle ci-dessus; le texte ne l'était pas.

   ⚠️ CE QUI MANQUAIT N'ÉTAIT PAS QU'UNE QUESTION D'AXE. Sur les deux écrans de
   revendication, la ligne d'état du contrôle anti-robot était écrite AVANT le
   bouton dans le gabarit, donc rendue au-dessus de lui: « vérification en
   cours » apparaissait à distance de l'action qui venait de la déclencher.
   Elle est passée sous le bouton dans `demander-reprise.html.twig` et
   `revendiquer.html.twig` — `altcha.js` la retrouve par `querySelector`,
   l'ordre lui est indifférent.

   POURQUOI LA RÈGLE PORTE SUR LA MAISON ENTIÈRE et non sur les seuls écrans de
   revendication: c'est la leçon du 18 octobre, écrite trente lignes plus haut.
   Une consigne de mise en page vaut pour la maison, pas pour l'écran où elle a
   été donnée — et une règle recopiée pour un seul écran est la famille de
   défauts n° 1 de ce dossier.

   POURQUOI `~ *` ET NON UN CENTRAGE DU FORMULAIRE ENTIER: seul ce qui SUIT le
   bouton lui appartient. Les étiquettes de champ restent à gauche, pour la
   raison déjà donnée — on lit une étiquette juste avant de taper dans le champ
   qu'elle annonce. */
.formulaire button[type="submit"] ~ * { text-align: center; }

/* LE RUBAN DE L'ABONNEMENT. Demande de David du 21 octobre 2026, « un decompte
   en temps avec une ligne qui avance en meme temps que les jours ». Il reprend
   le ruban des quatre-vingt-dix jours sans rien lui ajouter, sauf deux choses:
   un peu d'air au-dessus, puisqu'il suit le tableau des faits, et une teinte
   d'alerte quand l'echeance approche.

   `.ruban-echeance .ruban-plein` porte deux classes la ou `.ruban-plein` n'en
   porte qu'une: la surcharge l'emporte par specificite, quel que soit l'ordre
   des regles dans la feuille. Une annulation qui perd la cascade est le defaut
   releve le 21 octobre sur `.jeton-jour`, qui aurait ete ecrite pour rien. */
.ruban-abo { margin-block-start: var(--s-6); }
.ruban-echeance .ruban-plein { background: var(--argile-200); }
.ruban-echeance .ruban-curseur { background: var(--argile-700); }
.ruban-bornes sup { font-size: .7em; }

/* ============================================================
   LE SUPPORT PAR TICKET
   ============================================================
   Demande de David du 21 octobre 2026, point 11 de son releve: « support par
   ticket comme dans le projet PTmain ? ». Trois ecrans: la liste du
   commercant, le fil d'un ticket, la file de l'exploitant.

   Rien n'est invente ici: les etats reprennent `.etat`, les cartes reprennent
   les tokens de la feuille, et le tableau de la file est le `table` nu que les
   quatre autres ecrans d'administration utilisent deja. */

/* Les trois etats d'un ticket, dans la famille de `.etat-publie` et consorts.
   « attente » prend la teinte d'alerte: c'est le seul etat qui demande quelque
   chose a celui qui le lit. */
.etat-ouvert  { background: var(--succes-fond); color: var(--succes); border-color: #bcd4bf; }
.etat-attente { background: var(--alerte-fond); color: var(--alerte); border-color: #e8d49a; }
.etat-clos    { background: var(--papier-2);    color: var(--encre-3); border-color: var(--trait); }

.etat-ligne { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; margin-block-end: var(--s-6); }

/* LA LISTE DES TICKETS DU COMMERCANT. Des cartes et non un tableau: il en a
   trois ou quatre, et un tableau de quatre lignes sur telephone se lit mal. */
/* `list-style: none` ET `padding: 0`: ces listes-ci sont des cartes, pas des
   enumerations. Vu a l'ecran le 22 octobre 2026, une puce flottait a gauche de
   chaque carte. */
.tickets { display: grid; gap: var(--s-4); margin-block-start: var(--s-5); list-style: none; padding: 0; }

/* Les deux colonnes de l'ecran de support. En dessous de 1100 px elles
   s'empilent: le formulaire d'abord, puisque celui qui arrive ici a une
   question avant d'avoir un historique. */
.support-deux { display: grid; gap: var(--s-7); align-items: start; }
@media (min-width: 1100px) {
  .support-deux { grid-template-columns: minmax(22rem, 32rem) 1fr; }
}
.support-liste h2 { margin-block-start: 0; }
.carte-formulaire.formulaire { max-width: none; }

/* Le champ de réponse d'un fil prend la largeur des messages qu'il suit:
   `.formulaire` est borné à 30rem, ce qui est juste pour un formulaire de
   saisie mais étroit sous une conversation qui occupe toute la colonne. */
.fil + .formulaire { max-width: none; }
.ticket a {
  display: grid; gap: .35rem;
  padding: var(--s-5);
  background: var(--carte); border: 1.5px solid var(--trait); border-radius: var(--r-md);
  color: inherit; text-decoration: none;
}
.ticket a:hover { border-color: var(--vert-500); }
.ticket a:focus-visible { outline: 3px solid var(--soleil-300); outline-offset: 3px; }
.ticket-sujet { font-weight: 650; }
.ticket-faits { display: flex; flex-wrap: wrap; gap: .3rem var(--s-4); font-size: .86rem; color: var(--encre-3); }

/* LE FIL. Les messages se lisent de haut en bas, dans l'ordre ou ils ont ete
   ecrits. Celui du support se distingue par un fond et un filet COMPLET: pas
   de bord colore sur un seul cote, demande de David du 16 octobre 2026. */
.fil { display: grid; gap: var(--s-5); margin-block: var(--s-5) var(--s-7); list-style: none; padding: 0; }
.fil-message { padding: var(--s-5); border: 1.5px solid var(--trait); border-radius: var(--r-md); background: var(--carte); }
.fil-support { background: var(--vert-100); border-color: #bcd4bf; }
.fil-qui { display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; margin: 0 0 .4rem; }
.fil-corps { margin: 0; white-space: normal; }

/* Le formulaire d'ouverture, pose en carte pour le detacher de la liste qui le
   suit: ce sont deux choses differentes sur le meme ecran. */
.carte-formulaire {
  padding: var(--s-6);
  background: var(--carte); border: 1.5px solid var(--trait); border-radius: var(--r-md);
  margin-block-end: var(--s-7);
}
.carte-formulaire h2 { margin-block-start: 0; }

.a-droite { text-align: right; }

/* ============================================================
   LES DEMANDES D'ACCES A UNE FICHE
   ============================================================
   Mesure du 22 octobre 2026: 612 fiches publiees, 58 atteignables par un envoi
   direct, 554 qui ne l'etaient pas. Ces 554 lisaient « ecrivez-nous depuis la
   page de contact en indiquant le nom et l'adresse du commerce ».

   DES CARTES ET NON UN TABLEAU: la decision se prend sur ce qu'on lit — le mot
   du demandeur, l'adresse qu'il propose, ce que la fiche porte deja. Un tableau
   a six colonnes oblige a ouvrir chaque ligne pour decider, c'est-a-dire a ne
   jamais decider. */
.demandes { display: grid; gap: var(--s-6); margin-block-start: var(--s-5); list-style: none; padding: 0; }
.demande {
  padding: var(--s-6);
  background: var(--carte); border: 1.5px solid var(--trait); border-radius: var(--r-md);
}
.demande-attente { border-color: var(--vert-500); }
.demande-tete { display: flex; align-items: baseline; flex-wrap: wrap; gap: .4rem var(--s-4); }
.demande-tete b { font-size: 1.05rem; }

/* Les faits en grille: ils se comparent, donc ils se lisent cote a cote. */
.demande-faits {
  display: grid; gap: var(--s-4) var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  margin-block: var(--s-5) 0;
}
.demande-faits dt { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: var(--encre-3); }
.demande-faits dd { margin: .15rem 0 0; }

/* Le mot du demandeur se detache: c'est la seule chose qu'il a ecrite, et
   c'est sur elle que la decision se prend. */
.demande-mot {
  margin-block-start: var(--s-5);
  padding: var(--s-5);
  background: var(--papier-2); border-radius: var(--r-sm);
}
.demande-decision { display: grid; gap: var(--s-4); margin-block-start: var(--s-5); max-width: none; }
.demande-decision input[type="text"] { max-width: 32rem; }

/* ============================================================
   LA COURBE D'EVOLUTION
   ============================================================
   Demande de David du 22 octobre 2026: « un graphique qui montre l'evolution
   des clics sur tout ce que contient la fiche serait genial. Un excellent
   argument de vente. »

   Le SVG est engendre au rendu par Domaine\Courbe, sans aucune bibliotheque:
   une courbe de trente points ne justifie pas quarante kilo-octets de
   JavaScript, et celle-ci s'imprime.

   `preserveAspectRatio="none"` sur le SVG: la courbe s'etire en largeur avec
   l'ecran, ce qui est le comportement attendu d'un graphique de serie
   temporelle. Les traits, eux, gardent leur epaisseur par
   `vector-effect: non-scaling-stroke`, sinon l'etirement les deformerait. */
.courbe { margin-block-start: var(--s-5); }
.courbe svg { width: 100%; height: 180px; display: block; }
.courbe-aire-vues { fill: var(--soleil-100); }
.courbe-trait-vues { stroke: var(--soleil-300); stroke-width: 2.5; vector-effect: non-scaling-stroke; }
.courbe-trait-clics { stroke: var(--vert-500); stroke-width: 2.5; vector-effect: non-scaling-stroke; stroke-dasharray: 5 4; }

.courbe-legende {
  display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-6);
  margin-block-start: var(--s-4); list-style: none; padding: 0;
  font-size: .9rem; color: var(--encre-2);
}
.courbe-legende li { display: flex; align-items: center; gap: 8px; }
.courbe-legende i { width: 16px; height: 3px; border-radius: 2px; display: block; }
.courbe-puce-vues { background: var(--soleil-300); }
.courbe-puce-clics { background: var(--vert-500); }

/* LA FILE DES BIERES, et pourquoi elle a sa propre separation.

   Une carte de moderation porte UNE proposition; une carte de cette file-ci
   porte toute la gamme d'une brasserie, parce que verifier demande d'ouvrir son
   site et qu'une fois suffit pour six bieres. Sans un trait entre elles, les
   trois boutons de la quatrieme se lisent comme ceux de la troisieme, et c'est
   exactement l'endroit ou l'on publie la mauvaise.

   Le trait est celui des listes de cet ecran, et le retrait reprend `--s-4`:
   aucune valeur neuve. */
.biere-file + .biere-file {
  margin-block-start: var(--s-5);
  padding-block-start: var(--s-5);
  border-block-start: 1px solid var(--trait);
}
.biere-file h3 { margin-block-end: var(--s-2); }
.prop-decision select { flex: 1 1 16rem; min-width: 11rem; }
